Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ C/C++ Language Potentially Breaking Changes
C++ Specific Potentially Breaking Changes
-----------------------------------------

- Clang now more aggressively optimizes away stores to objects after they are
dead. This behavior can be disabled with ``-fno-lifetime-dse``.

ABI Changes in This Version
---------------------------

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ CODEGENOPT(EnableSegmentedStacks , 1, 0, Benign) ///< Set when -fsplit-stack is
CODEGENOPT(StackClashProtector, 1, 0, Benign) ///< Set when -fstack-clash-protection is enabled.
CODEGENOPT(NoImplicitFloat , 1, 0, Benign) ///< Set when -mno-implicit-float is enabled.
CODEGENOPT(NullPointerIsValid , 1, 0, Benign) ///< Assume Null pointer deference is defined.
CODEGENOPT(StrictLifetimes , 1, 0, Benign) ///< Assume an object is dead
///< after its destructor returns.
CODEGENOPT(OpenCLCorrectlyRoundedDivSqrt, 1, 0, Benign) ///< -cl-fp32-correctly-rounded-divide-sqrt
CODEGENOPT(HIPCorrectlyRoundedDivSqrt, 1, 1, Benign) ///< -fno-hip-fp32-correctly-rounded-divide-sqrt
CODEGENOPT(DisableBlockSignatureString, 1, 0, Benign) ///< Set when -fdisable-block-signature-string is enabled.
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2965,6 +2965,20 @@ attribute as undefined behavior. (And, thus the optimizer may assume that any
pointer used in such a way must not have been null and optimize away the
branches accordingly.) On by default.}]>;

defm lifetime_dse
: BoolFOption<"lifetime-dse", CodeGenOpts<"StrictLifetimes">, DefaultTrue,
NegFlag<SetFalse, [], [ClangOption, CC1Option],
"Do not aggressively delete stores after the end of "
"an object's lifetime">,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Aggressively delete stores after the end of an "
"object's lifetime">,
BothFlags<[], [ClangOption, CLOption]>>,
DocBrief<
[{When enabled, mark an object dead after its destructor returns which allows the
the optimizer to perform more aggressive optimizations in some cases, particularly dead
store elimination.}]>;

defm use_line_directives : BoolFOption<"use-line-directives",
PreprocessorOutputOpts<"UseLineDirectives">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
Expand Down
18 changes: 17 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2780,7 +2780,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
}

// Apply `nonnull`, `dereferenceable(N)` and `align N` to the `this` argument,
// unless this is a thunk function.
// unless this is a thunk function. Add dead_on_return to the `this` argument
// in base class destructors to aid in DSE.
// FIXME: fix this properly, https://reviews.llvm.org/D100388
if (FI.isInstanceMethod() && !IRFunctionArgs.hasInallocaArg() &&
!FI.arg_begin()->type->isVoidPointerType() && !IsThunk) {
Expand Down Expand Up @@ -2813,6 +2814,21 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
.getAsAlign();
Attrs.addAlignmentAttr(Alignment);

const auto *DD = dyn_cast_if_present<CXXDestructorDecl>(
CalleeInfo.getCalleeDecl().getDecl());
if (DD && CodeGenOpts.StrictLifetimes) {
const CXXRecordDecl *ClassDecl =
dyn_cast<CXXRecordDecl>(DD->getDeclContext());
// TODO(boomanaiden154): We are being intentionally conservative here
// as we gain experience with this optimization. We should remove the
// condition for non-virtual bases after more testing. We cannot add
// dead_on_return if we have virtual base classes because they will
// generally still be live after the base object destructor.
if (ClassDecl->getNumBases() == 0 && ClassDecl->getNumVBases() == 0)
Attrs.addDeadOnReturnAttr(llvm::DeadOnReturnInfo(
getMinimumClassObjectSize(ClassDecl).getQuantity()));
}

ArgAttrs[IRArgs.first] = llvm::AttributeSet::get(getLLVMContext(), Attrs);
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5681,6 +5681,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptOutFlag(CmdArgs, options::OPT_fdelete_null_pointer_checks,
options::OPT_fno_delete_null_pointer_checks);

Args.addOptOutFlag(CmdArgs, options::OPT_flifetime_dse,
options::OPT_fno_lifetime_dse);

// LLVM Code Generator Options.

if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ_quadword_atomics)) {
Expand Down
72 changes: 72 additions & 0 deletions clang/test/CodeGenCXX/destructor-dead-on-return.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --filter "define" --version 6
// Check that we appropriately annotate destructors with a dead_on_return
// attribute.
//
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-lifetime-dse -emit-llvm %s -o - | FileCheck %s --check-prefix NO-LIFETIME-DSE

void opaque(char*);

class Foo {
public:
~Foo();

private:
char FooVar[4];
};

// CHECK-LABEL: define dso_local void @_ZN3FooD1Ev(
// CHECK-SAME: ptr noundef nonnull align 1 dead_on_return(4) dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// NO-LIFETIME-DSE-LABEL: define dso_local void @_ZN3FooD1Ev(
// NO-LIFETIME-DSE-SAME: ptr noundef nonnull align 1 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
Foo::~Foo() {
opaque(FooVar);
}

class Bar {
public:
~Bar();

private:
char BarVar[8];
};

// CHECK-LABEL: define dso_local void @_ZN3BarD1Ev(
// CHECK-SAME: ptr noundef nonnull align 1 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// NO-LIFETIME-DSE-LABEL: define dso_local void @_ZN3BarD1Ev(
// NO-LIFETIME-DSE-SAME: ptr noundef nonnull align 1 dereferenceable(8) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
Bar::~Bar() {
opaque(BarVar);
}

class BarInheritsFoo : public Foo {
public:
~BarInheritsFoo();

private:
char BarVar[12];
};

// CHECK-LABEL: define dso_local void @_ZN14BarInheritsFooD1Ev(
// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(16) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// NO-LIFETIME-DSE-LABEL: define dso_local void @_ZN14BarInheritsFooD1Ev(
// NO-LIFETIME-DSE-SAME: ptr noundef nonnull align 1 dereferenceable(16) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
BarInheritsFoo::~BarInheritsFoo() {
opaque(BarVar);
}

class BarVirtualInheritsFoo : public virtual Foo {
public:
~BarVirtualInheritsFoo();

private:
char BarVar[16];
};

// CHECK-LABEL: define dso_local void @_ZN21BarVirtualInheritsFooD1Ev(
// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(24) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// NO-LIFETIME-DSE-LABEL: define dso_local void @_ZN21BarVirtualInheritsFooD1Ev(
// NO-LIFETIME-DSE-SAME: ptr noundef nonnull align 8 dereferenceable(24) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
BarVirtualInheritsFoo::~BarVirtualInheritsFoo() {
opaque(BarVar);
}
Loading