Files
clang-p2996/clang/test/CodeGenCXX/trivial_abi.cpp
Nikita Popov 158d72d728 [Clang] Set writable and dead_on_unwind attributes on sret arguments (#77116)
Set the writable and dead_on_unwind attributes for sret arguments. These
indicate that the argument points to writable memory (and it's legal to
introduce spurious writes to it on entry to the function) and that the
argument memory will not be used if the call unwinds.

This enables additional MemCpyOpt/DSE/LICM optimizations.
2024-01-11 09:46:54 +01:00

280 lines
9.7 KiB
C++

// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s
// CHECK: %[[STRUCT_SMALL:.*]] = type { ptr }
// CHECK: %[[STRUCT_LARGE:.*]] = type { ptr, [128 x i32] }
// CHECK: %[[STRUCT_TRIVIAL:.*]] = type { i32 }
// CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32 }
struct __attribute__((trivial_abi)) Small {
int *p;
Small();
~Small();
Small(const Small &) noexcept;
Small &operator=(const Small &);
};
struct __attribute__((trivial_abi)) Large {
int *p;
int a[128];
Large();
~Large();
Large(const Large &) noexcept;
Large &operator=(const Large &);
};
struct Trivial {
int a;
};
struct NonTrivial {
NonTrivial();
~NonTrivial();
int a;
};
struct HasTrivial {
Small s;
Trivial m;
};
struct HasNonTrivial {
Small s;
NonTrivial m;
};
struct B0 {
virtual Small m0();
};
struct B1 {
virtual Small m0();
};
struct D0 : B0, B1 {
Small m0() override;
};
// CHECK-LABEL: define{{.*}} i64 @_ZThn8_N2D02m0Ev(
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[CALL:.*]] = tail call i64 @_ZN2D02m0Ev(
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[RETVAL]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to ptr
// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_DIVE2:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[RETVAL]], i32 0, i32 0
// CHECK: %[[V3:.*]] = load ptr, ptr %[[COERCE_DIVE2]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V3]] to i64
// CHECK: ret i64 %[[COERCE_VAL_PI]]
Small D0::m0() { return {}; }
// CHECK: define{{.*}} void @_Z14testParamSmall5Small(i64 %[[A_COERCE:.*]])
// CHECK: %[[A:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[A]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to ptr
// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[A]])
// CHECK: ret void
// CHECK: }
void testParamSmall(Small a) noexcept {
}
// CHECK: define{{.*}} i64 @_Z15testReturnSmallv()
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[RETVAL]])
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[RETVAL]], i32 0, i32 0
// CHECK: %[[V0:.*]] = load ptr, ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V0]] to i64
// CHECK: ret i64 %[[COERCE_VAL_PI]]
// CHECK: }
Small testReturnSmall() {
Small t;
return t;
}
// CHECK: define{{.*}} void @_Z14testCallSmall0v()
// CHECK: %[[T:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[T]])
// CHECK: %[[CALL1:.*]] = call noundef ptr @_ZN5SmallC1ERKS_(ptr {{[^,]*}} %[[AGG_TMP]], ptr noundef nonnull align 8 dereferenceable(8) %[[T]])
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[AGG_TMP]], i32 0, i32 0
// CHECK: %[[V0:.*]] = load ptr, ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V0]] to i64
// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]])
// CHECK: %[[CALL2:.*]] = call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[T]])
// CHECK: ret void
// CHECK: }
void testCallSmall0() {
Small t;
testParamSmall(t);
}
// CHECK: define{{.*}} void @_Z14testCallSmall1v()
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv()
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[AGG_TMP]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to ptr
// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[AGG_TMP]], i32 0, i32 0
// CHECK: %[[V0:.*]] = load ptr, ptr %[[COERCE_DIVE1]], align 8
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint ptr %[[V0]] to i64
// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]])
// CHECK: ret void
// CHECK: }
void testCallSmall1() {
testParamSmall(testReturnSmall());
}
// CHECK: define{{.*}} void @_Z16testIgnoredSmallv()
// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv()
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], ptr %[[AGG_TMP_ENSURED]], i32 0, i32 0
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to ptr
// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8
// CHECK: %[[CALL1:.*]] = call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[AGG_TMP_ENSURED]])
// CHECK: ret void
// CHECK: }
void testIgnoredSmall() {
testReturnSmall();
}
// CHECK: define{{.*}} void @_Z14testParamLarge5Large(ptr noundef %[[A:.*]])
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5LargeD1Ev(ptr {{[^,]*}} %[[A]])
// CHECK: ret void
// CHECK: }
void testParamLarge(Large a) noexcept {
}
// CHECK: define{{.*}} void @_Z15testReturnLargev(ptr dead_on_unwind noalias writable sret(%[[STRUCT_LARGE]]) align 8 %[[AGG_RESULT:.*]])
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5LargeC1Ev(ptr {{[^,]*}} %[[AGG_RESULT]])
// CHECK: ret void
// CHECK: }
Large testReturnLarge() {
Large t;
return t;
}
// CHECK: define{{.*}} void @_Z14testCallLarge0v()
// CHECK: %[[T:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE]], align 8
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5LargeC1Ev(ptr {{[^,]*}} %[[T]])
// CHECK: %[[CALL1:.*]] = call noundef ptr @_ZN5LargeC1ERKS_(ptr {{[^,]*}} %[[AGG_TMP]], ptr noundef nonnull align 8 dereferenceable(520) %[[T]])
// CHECK: call void @_Z14testParamLarge5Large(ptr noundef %[[AGG_TMP]])
// CHECK: %[[CALL2:.*]] = call noundef ptr @_ZN5LargeD1Ev(ptr {{[^,]*}} %[[T]])
// CHECK: ret void
// CHECK: }
void testCallLarge0() {
Large t;
testParamLarge(t);
}
// CHECK: define{{.*}} void @_Z14testCallLarge1v()
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
// CHECK: call void @_Z15testReturnLargev(ptr dead_on_unwind writable sret(%[[STRUCT_LARGE]]) align 8 %[[AGG_TMP]])
// CHECK: call void @_Z14testParamLarge5Large(ptr noundef %[[AGG_TMP]])
// CHECK: ret void
// CHECK: }
void testCallLarge1() {
testParamLarge(testReturnLarge());
}
// CHECK: define{{.*}} void @_Z16testIgnoredLargev()
// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
// CHECK: call void @_Z15testReturnLargev(ptr dead_on_unwind writable sret(%[[STRUCT_LARGE]]) align 8 %[[AGG_TMP_ENSURED]])
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN5LargeD1Ev(ptr {{[^,]*}} %[[AGG_TMP_ENSURED]])
// CHECK: ret void
// CHECK: }
void testIgnoredLarge() {
testReturnLarge();
}
// CHECK: define{{.*}} i32 @_Z20testReturnHasTrivialv()
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_TRIVIAL:.*]], align 4
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_TRIVIAL]], ptr %[[RETVAL]], i32 0, i32 0
// CHECK: %[[V0:.*]] = load i32, ptr %[[COERCE_DIVE]], align 4
// CHECK: ret i32 %[[V0]]
// CHECK: }
Trivial testReturnHasTrivial() {
Trivial t;
return t;
}
// CHECK: define{{.*}} void @_Z23testReturnHasNonTrivialv(ptr dead_on_unwind noalias writable sret(%[[STRUCT_NONTRIVIAL:.*]]) align 4 %[[AGG_RESULT:.*]])
// CHECK: %[[CALL:.*]] = call noundef ptr @_ZN10NonTrivialC1Ev(ptr {{[^,]*}} %[[AGG_RESULT]])
// CHECK: ret void
// CHECK: }
NonTrivial testReturnHasNonTrivial() {
NonTrivial t;
return t;
}
// CHECK: define{{.*}} void @_Z18testExceptionSmallv()
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: call noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[AGG_TMP]])
// CHECK: invoke noundef ptr @_ZN5SmallC1Ev(ptr {{[^,]*}} %[[AGG_TMP1]])
// CHECK: call void @_Z20calleeExceptionSmall5SmallS_(i64 %{{.*}}, i64 %{{.*}})
// CHECK-NEXT: ret void
// CHECK: landingpad { ptr, i32 }
// CHECK: call noundef ptr @_ZN5SmallD1Ev(ptr {{[^,]*}} %[[AGG_TMP]])
// CHECK: br
// CHECK: resume { ptr, i32 }
void calleeExceptionSmall(Small, Small);
void testExceptionSmall() {
calleeExceptionSmall(Small(), Small());
}
// CHECK: define{{.*}} void @_Z18testExceptionLargev()
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE]], align 8
// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_LARGE]], align 8
// CHECK: call noundef ptr @_ZN5LargeC1Ev(ptr {{[^,]*}} %[[AGG_TMP]])
// CHECK: invoke noundef ptr @_ZN5LargeC1Ev(ptr {{[^,]*}} %[[AGG_TMP1]])
// CHECK: call void @_Z20calleeExceptionLarge5LargeS_(ptr noundef %[[AGG_TMP]], ptr noundef %[[AGG_TMP1]])
// CHECK-NEXT: ret void
// CHECK: landingpad { ptr, i32 }
// CHECK: call noundef ptr @_ZN5LargeD1Ev(ptr {{[^,]*}} %[[AGG_TMP]])
// CHECK: br
// CHECK: resume { ptr, i32 }
void calleeExceptionLarge(Large, Large);
void testExceptionLarge() {
calleeExceptionLarge(Large(), Large());
}
// PR42961
// CHECK: define{{.*}} @"_ZN3$_08__invokeEv"()
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_SMALL]], align 8
// CHECK: %[[CALL:.*]] = call{{.*}} @"_ZNK3$_0clEv"
// CHECK: %[[COERCEDIVE:.*]] = getelementptr{{.*}} %[[COERCE]]
// CHECK: %[[COERCEVALIP:.*]] = inttoptr{{.*}} %[[CALL]]
// CHECK: call {{.*}}memcpy{{.*}} %[[RETVAL]]{{.*}} %[[COERCE]]
// CHECK: %[[COERCEDIVE1:.*]] = getelementptr{{.*}} %[[RETVAL]]
// CHECK: %[[TMP:.*]] = load{{.*}} %[[COERCEDIVE1]]
// CHECK: %[[COERCEVALPI:.*]] = ptrtoint{{.*}} %[[TMP]]
// CHECK: ret{{.*}} %[[COERCEVALPI]]
Small (*fp)() = []() -> Small { return Small(); };