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.
229 lines
6.2 KiB
C++
229 lines
6.2 KiB
C++
// RUN: %clang_cc1 -fno-elide-constructors -S -emit-llvm %s -triple x86_64-unknown-linux-gnu -o - | FileCheck %s
|
|
// RUN: %clang_cc1 -fno-elide-constructors -S -emit-llvm %s -triple x86_64-unknown-linux-gnu -o - | opt -passes=verify
|
|
// FIXME: remove the call to "opt" once the tests are running the Clang verifier automatically again.
|
|
|
|
int Bar(int);
|
|
int Baz(int);
|
|
|
|
int Func1(int x) {
|
|
if (x) {
|
|
// CHECK: %call = musttail call noundef i32 @_Z3Bari(i32 noundef %1)
|
|
// CHECK-NEXT: ret i32 %call
|
|
[[clang::musttail]] return Bar(x);
|
|
} else {
|
|
[[clang::musttail]] return Baz(x); // CHECK: %call1 = musttail call noundef i32 @_Z3Bazi(i32 noundef %3)
|
|
}
|
|
}
|
|
|
|
int Func2(int x) {
|
|
{
|
|
[[clang::musttail]] return Bar(Bar(x));
|
|
}
|
|
}
|
|
|
|
// CHECK: %call1 = musttail call noundef i32 @_Z3Bari(i32 noundef %call)
|
|
|
|
class Foo {
|
|
public:
|
|
static int StaticMethod(int x);
|
|
int MemberFunction(int x);
|
|
int TailFrom(int x);
|
|
int TailFrom2(int x);
|
|
int TailFrom3(int x);
|
|
};
|
|
|
|
int Foo::TailFrom(int x) {
|
|
[[clang::musttail]] return MemberFunction(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_ZN3Foo14MemberFunctionEi(ptr noundef nonnull align 1 dereferenceable(1) %this1, i32 noundef %0)
|
|
|
|
int Func3(int x) {
|
|
[[clang::musttail]] return Foo::StaticMethod(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_ZN3Foo12StaticMethodEi(i32 noundef %0)
|
|
|
|
int Func4(int x) {
|
|
Foo foo; // Object with trivial destructor.
|
|
[[clang::musttail]] return foo.StaticMethod(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_ZN3Foo12StaticMethodEi(i32 noundef %0)
|
|
|
|
int (Foo::*pmf)(int);
|
|
|
|
int Foo::TailFrom2(int x) {
|
|
[[clang::musttail]] return ((*this).*pmf)(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 %5(ptr noundef nonnull align 1 dereferenceable(1) %1, i32 noundef %6)
|
|
|
|
int Foo::TailFrom3(int x) {
|
|
[[clang::musttail]] return (this->*pmf)(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 %5(ptr noundef nonnull align 1 dereferenceable(1) %1, i32 noundef %6)
|
|
|
|
void ReturnsVoid();
|
|
|
|
void Func5() {
|
|
[[clang::musttail]] return ReturnsVoid();
|
|
}
|
|
|
|
// CHECK: musttail call void @_Z11ReturnsVoidv()
|
|
|
|
class HasTrivialDestructor {};
|
|
|
|
int ReturnsInt(int x);
|
|
|
|
int Func6(int x) {
|
|
HasTrivialDestructor foo;
|
|
[[clang::musttail]] return ReturnsInt(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_Z10ReturnsInti(i32 noundef %0)
|
|
|
|
struct Data {
|
|
int (*fptr)(Data *);
|
|
};
|
|
|
|
int Func7(Data *data) {
|
|
[[clang::musttail]] return data->fptr(data);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 %1(ptr noundef %2)
|
|
|
|
template <class T>
|
|
T TemplateFunc(T) {
|
|
return 5;
|
|
}
|
|
|
|
int Func9(int x) {
|
|
[[clang::musttail]] return TemplateFunc<int>(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_Z12TemplateFuncIiET_S0_(i32 noundef %0)
|
|
|
|
template <class T>
|
|
int Func10(int x) {
|
|
T t;
|
|
[[clang::musttail]] return Bar(x);
|
|
}
|
|
|
|
int Func11(int x) {
|
|
return Func10<int>(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_Z3Bari(i32 noundef %0)
|
|
|
|
template <class T>
|
|
T Func12(T x) {
|
|
[[clang::musttail]] return ::Bar(x);
|
|
}
|
|
|
|
int Func13(int x) {
|
|
return Func12<int>(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_Z3Bari(i32 noundef %0)
|
|
|
|
int Func14(int x) {
|
|
int vla[x];
|
|
[[clang::musttail]] return Bar(x);
|
|
}
|
|
|
|
// CHECK: %call = musttail call noundef i32 @_Z3Bari(i32 noundef %3)
|
|
|
|
void TrivialDestructorParam(HasTrivialDestructor obj);
|
|
|
|
void Func14(HasTrivialDestructor obj) {
|
|
[[clang::musttail]] return TrivialDestructorParam(obj);
|
|
}
|
|
|
|
// CHECK: musttail call void @_Z22TrivialDestructorParam20HasTrivialDestructor()
|
|
|
|
struct Struct3 {
|
|
void ConstMemberFunction(const int *) const;
|
|
void NonConstMemberFunction(int *i);
|
|
};
|
|
void Struct3::NonConstMemberFunction(int *i) {
|
|
// The parameters are not identical, but they are compatible.
|
|
[[clang::musttail]] return ConstMemberFunction(i);
|
|
}
|
|
|
|
// CHECK: musttail call void @_ZNK7Struct319ConstMemberFunctionEPKi(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef %0)
|
|
|
|
struct HasNonTrivialCopyConstructor {
|
|
HasNonTrivialCopyConstructor(const HasNonTrivialCopyConstructor &);
|
|
};
|
|
HasNonTrivialCopyConstructor ReturnsClassByValue();
|
|
HasNonTrivialCopyConstructor TestNonElidableCopyConstructor() {
|
|
[[clang::musttail]] return (((ReturnsClassByValue())));
|
|
}
|
|
|
|
// CHECK: musttail call void @_Z19ReturnsClassByValuev(ptr dead_on_unwind writable sret(%struct.HasNonTrivialCopyConstructor) align 1 %agg.result)
|
|
|
|
struct HasNonTrivialCopyConstructor2 {
|
|
// Copy constructor works even if it has extra default params.
|
|
HasNonTrivialCopyConstructor2(const HasNonTrivialCopyConstructor &, int DefaultParam = 5);
|
|
};
|
|
HasNonTrivialCopyConstructor2 ReturnsClassByValue2();
|
|
HasNonTrivialCopyConstructor2 TestNonElidableCopyConstructor2() {
|
|
[[clang::musttail]] return (((ReturnsClassByValue2())));
|
|
}
|
|
|
|
// CHECK: musttail call void @_Z20ReturnsClassByValue2v()
|
|
|
|
void TestFunctionPointer(int x) {
|
|
void (*p)(int) = nullptr;
|
|
[[clang::musttail]] return p(x);
|
|
}
|
|
|
|
// CHECK: musttail call void %0(i32 noundef %1)
|
|
|
|
struct LargeWithCopyConstructor {
|
|
LargeWithCopyConstructor(const LargeWithCopyConstructor &);
|
|
char data[32];
|
|
};
|
|
LargeWithCopyConstructor ReturnsLarge();
|
|
LargeWithCopyConstructor TestLargeWithCopyConstructor() {
|
|
[[clang::musttail]] return ReturnsLarge();
|
|
}
|
|
|
|
// CHECK: define dso_local void @_Z28TestLargeWithCopyConstructorv(ptr dead_on_unwind noalias writable sret(%struct.LargeWithCopyConstructor) align 1 %agg.result)
|
|
// CHECK: musttail call void @_Z12ReturnsLargev(ptr dead_on_unwind writable sret(%struct.LargeWithCopyConstructor) align 1 %agg.result)
|
|
|
|
using IntFunctionType = int();
|
|
IntFunctionType *ReturnsIntFunction();
|
|
int TestRValueFunctionPointer() {
|
|
[[clang::musttail]] return ReturnsIntFunction()();
|
|
}
|
|
|
|
// CHECK: musttail call noundef i32 %call()
|
|
|
|
void(FuncWithParens)() {
|
|
[[clang::musttail]] return FuncWithParens();
|
|
}
|
|
|
|
// CHECK: musttail call void @_Z14FuncWithParensv()
|
|
|
|
int TestNonCapturingLambda() {
|
|
auto lambda = []() { return 12; };
|
|
[[clang::musttail]] return (+lambda)();
|
|
}
|
|
|
|
// CHECK: %call = call noundef ptr @"_ZZ22TestNonCapturingLambdavENK3$_0cvPFivEEv"(ptr noundef nonnull align 1 dereferenceable(1) %lambda)
|
|
// CHECK: musttail call noundef i32 %call()
|
|
|
|
class TestVirtual {
|
|
virtual void TailTo();
|
|
virtual void TailFrom();
|
|
};
|
|
|
|
void TestVirtual::TailFrom() {
|
|
[[clang::musttail]] return TailTo();
|
|
}
|
|
|
|
// CHECK: musttail call void %0(ptr noundef nonnull align 8 dereferenceable(8) %this1)
|