Skip to content

Commit a47e140

Browse files
[clang][CodeGen] Set dead_on_return on indirect pointer arguments
Let Clang emit `dead_on_return` attribute on indirect pointer arguments, namely, large aggregates that the ABI mandates be passed by value, but lowered to an indirect argument. Writes to such arguments are not observable by the caller after the callee returns. This should desirably enable further MemCpyOpt/DSE optimizations. Previous discussion: https://discourse.llvm.org/t/rfc-add-dead-on-return-attribute/86871.
1 parent cad62df commit a47e140

File tree

84 files changed

+853
-843
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+853
-843
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2852,8 +2852,17 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
28522852
if (AI.getInReg())
28532853
Attrs.addAttribute(llvm::Attribute::InReg);
28542854

2855-
if (AI.getIndirectByVal())
2855+
// Depending on the ABI, this may be either a byval or a dead_on_return
2856+
// argument.
2857+
if (AI.getIndirectByVal()) {
28562858
Attrs.addByValAttr(getTypes().ConvertTypeForMem(ParamType));
2859+
} else {
2860+
// If the argument type has a non-trivial destructor that the caller has
2861+
// to invoke, this cannot be a dead_on_return argument.
2862+
const auto *RD = ParamType->getAsCXXRecordDecl();
2863+
if (!RD || (RD && RD->hasTrivialDestructor()))
2864+
Attrs.addAttribute(llvm::Attribute::DeadOnReturn);
2865+
}
28572866

28582867
auto *Decl = ParamType->getAsRecordDecl();
28592868
if (CodeGenOpts.PassByValueIsNoAlias && Decl &&

clang/test/CodeGen/64bit-swiftcall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ TEST(struct_big_1)
239239
// CHECK-LABEL: define {{.*}} void @return_struct_big_1(ptr dead_on_unwind noalias writable sret
240240

241241
// Should not be byval.
242-
// CHECK-LABEL: define {{.*}} void @take_struct_big_1(ptr{{( %.*)?}})
242+
// CHECK-LABEL: define {{.*}} void @take_struct_big_1(ptr dead_on_return{{( %.*)?}})
243243

244244
/*****************************************************************************/
245245
/********************************* TYPE MERGING ******************************/

clang/test/CodeGen/AArch64/byval-temp.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ void example(void) {
3030
// Then, memcpy `l` to the temporary stack space.
3131
// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[byvaltemp]], ptr align 8 %[[l]], i64 64, i1 false)
3232
// Finally, call using a pointer to the temporary stack space.
33-
// CHECK-O0-NEXT: call void @pass_large(ptr noundef %[[byvaltemp]])
33+
// CHECK-O0-NEXT: call void @pass_large(ptr dead_on_return noundef %[[byvaltemp]])
3434
// Now, do the same for the second call, using the second temporary alloca.
3535
// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[byvaltemp1]], ptr align 8 %[[l]], i64 64, i1 false)
36-
// CHECK-O0-NEXT: call void @pass_large(ptr noundef %[[byvaltemp1]])
36+
// CHECK-O0-NEXT: call void @pass_large(ptr dead_on_return noundef %[[byvaltemp1]])
3737
// CHECK-O0-NEXT: ret void
3838
//
3939
// At O3, we should have lifetime markers to help the optimizer re-use the temporary allocas.
@@ -58,15 +58,15 @@ void example(void) {
5858
// Then, memcpy `l` to the temporary stack space.
5959
// CHECK-O3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[byvaltemp]], ptr align 8 %[[l]], i64 64, i1 false)
6060
// Finally, call using a pointer to the temporary stack space.
61-
// CHECK-O3-NEXT: call void @pass_large(ptr noundef %[[byvaltemp]])
61+
// CHECK-O3-NEXT: call void @pass_large(ptr dead_on_return noundef %[[byvaltemp]])
6262
//
6363
// The lifetime of the temporary used to pass a pointer to the struct ends here.
6464
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0(i64 64, ptr %[[byvaltemp]])
6565
//
6666
// Now, do the same for the second call, using the second temporary alloca.
6767
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0(i64 64, ptr %[[byvaltemp1]])
6868
// CHECK-O3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[byvaltemp1]], ptr align 8 %[[l]], i64 64, i1 false)
69-
// CHECK-O3-NEXT: call void @pass_large(ptr noundef %[[byvaltemp1]])
69+
// CHECK-O3-NEXT: call void @pass_large(ptr dead_on_return noundef %[[byvaltemp1]])
7070
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0(i64 64, ptr %[[byvaltemp1]])
7171
//
7272
// Mark the end of the lifetime of `l`.
@@ -88,12 +88,12 @@ void example_BitInt(void) {
8888
// CHECK-O0-NEXT: [[LOADEDV:%.*]] = trunc i256 [[TMP0]] to i129
8989
// CHECK-O0-NEXT: [[STOREDV:%.*]] = sext i129 [[LOADEDV]] to i256
9090
// CHECK-O0-NEXT: store i256 [[STOREDV]], ptr [[INDIRECT_ARG_TEMP]], align 16
91-
// CHECK-O0-NEXT: call void @pass_large_BitInt(ptr noundef [[INDIRECT_ARG_TEMP]])
91+
// CHECK-O0-NEXT: call void @pass_large_BitInt(ptr dead_on_return noundef [[INDIRECT_ARG_TEMP]])
9292
// CHECK-O0-NEXT: [[TMP1:%.*]] = load i256, ptr [[L]], align 16
9393
// CHECK-O0-NEXT: [[LOADEDV1:%.*]] = trunc i256 [[TMP1]] to i129
9494
// CHECK-O0-NEXT: [[STOREDV1:%.*]] = sext i129 [[LOADEDV1]] to i256
9595
// CHECK-O0-NEXT: store i256 [[STOREDV1]], ptr [[INDIRECT_ARG_TEMP1]], align 16
96-
// CHECK-O0-NEXT: call void @pass_large_BitInt(ptr noundef [[INDIRECT_ARG_TEMP1]])
96+
// CHECK-O0-NEXT: call void @pass_large_BitInt(ptr dead_on_return noundef [[INDIRECT_ARG_TEMP1]])
9797
// CHECK-O0-NEXT: ret void
9898
//
9999
// CHECK-O3-LABEL: define dso_local void @example_BitInt(
@@ -108,13 +108,13 @@ void example_BitInt(void) {
108108
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr [[INDIRECT_ARG_TEMP]])
109109
// CHECK-O3-NEXT: [[STOREDV:%.*]] = sext i129 [[LOADEDV]] to i256
110110
// CHECK-O3-NEXT: store i256 [[STOREDV]], ptr [[INDIRECT_ARG_TEMP]], align 16, !tbaa [[TBAA6]]
111-
// CHECK-O3-NEXT: call void @pass_large_BitInt(ptr noundef [[INDIRECT_ARG_TEMP]])
111+
// CHECK-O3-NEXT: call void @pass_large_BitInt(ptr dead_on_return noundef [[INDIRECT_ARG_TEMP]])
112112
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr [[INDIRECT_ARG_TEMP]])
113113
// CHECK-O3-NEXT: [[TMP1:%.*]] = load i256, ptr [[L]], align 16, !tbaa [[TBAA6]]
114114
// CHECK-O3-NEXT: [[LOADEDV1:%.*]] = trunc i256 [[TMP1]] to i129
115115
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr [[INDIRECT_ARG_TEMP1]])
116116
// CHECK-O3-NEXT: [[STOREDV1:%.*]] = sext i129 [[LOADEDV1]] to i256
117117
// CHECK-O3-NEXT: store i256 [[STOREDV1]], ptr [[INDIRECT_ARG_TEMP1]], align 16, !tbaa [[TBAA6]]
118-
// CHECK-O3-NEXT: call void @pass_large_BitInt(ptr noundef [[INDIRECT_ARG_TEMP1]])
118+
// CHECK-O3-NEXT: call void @pass_large_BitInt(ptr dead_on_return noundef [[INDIRECT_ARG_TEMP1]])
119119
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr [[INDIRECT_ARG_TEMP1]])
120120
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr [[L]])

clang/test/CodeGen/AArch64/pure-scalable-args-empty-union.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void f0(S0 *p) {
1919
use0(*p);
2020
}
2121
// CHECK-C: declare void @use0(<vscale x 4 x float>, <vscale x 4 x float>, <vscale x 4 x float>, <vscale x 4 x float>)
22-
// CHECK-CXX: declare void @use0(ptr noundef)
22+
// CHECK-CXX: declare void @use0(ptr dead_on_return noundef)
2323

2424
#ifdef __cplusplus
2525

0 commit comments

Comments
 (0)