diff options
Diffstat (limited to 'clang/test')
| -rw-r--r-- | clang/test/CodeGenCXX/stack-reuse-miscompile.cpp | 39 | ||||
| -rw-r--r-- | clang/test/CodeGenCXX/stack-reuse.cpp | 146 |
2 files changed, 185 insertions, 0 deletions
diff --git a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp new file mode 100644 index 00000000000..e6b9cbe3365 --- /dev/null +++ b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp @@ -0,0 +1,39 @@ +// RUN: %clang -S -emit-llvm -O1 -mllvm -disable-llvm-optzns -S %s -o - | FileCheck %s + +// This test should not to generate llvm.lifetime.start/llvm.lifetime.end for +// f function because all temporary objects in this function are used for the +// final result + +class S { + char *ptr; + unsigned int len; +}; + +class T { + S left; + S right; + +public: + T(const char s[]); + T(S); + + T concat(const T &Suffix) const; + const char * str() const; +}; + +const char * f(S s) +{ +// CHECK: %1 = alloca %class.T, align 4 +// CHECK: %2 = alloca %class.T, align 4 +// CHECK: %3 = alloca %class.S, align 4 +// CHECK: %4 = alloca %class.T, align 4 +// CHECK: %5 = call x86_thiscallcc %class.T* @"\01??0T@@QAE@QBD@Z" +// CHECK: %6 = bitcast %class.S* %3 to i8* +// CHECK: %7 = bitcast %class.S* %s to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32 +// CHECK: %8 = call x86_thiscallcc %class.T* @"\01??0T@@QAE@VS@@@Z" +// CHECK: call x86_thiscallcc void @"\01?concat@T@@QBE?AV1@ABV1@@Z" +// CHECK: %9 = call x86_thiscallcc i8* @"\01?str@T@@QBEPBDXZ"(%class.T* %4) + + return T("[").concat(T(s)).str(); +} diff --git a/clang/test/CodeGenCXX/stack-reuse.cpp b/clang/test/CodeGenCXX/stack-reuse.cpp new file mode 100644 index 00000000000..ef73d1a5387 --- /dev/null +++ b/clang/test/CodeGenCXX/stack-reuse.cpp @@ -0,0 +1,146 @@ +// RUN: %clang -target armv7l-unknown-linux-gnueabihf -S %s -o - -emit-llvm -O1 -disable-llvm-optzns | FileCheck %s + +// Stack should be reused when possible, no need to allocate two separate slots +// if they have disjoint lifetime. + +// Sizes of objects are related to previously existed threshold of 32. In case +// of S_large stack size is rounded to 40 bytes. + +// 32B +struct S_small { + int a[8]; +}; + +// 36B +struct S_large { + int a[9]; +}; + +// Helper class for lifetime scope absence testing +struct Combiner { + S_large a, b; + + Combiner(S_large); + Combiner f(); +}; + +extern S_small foo_small(); +extern S_large foo_large(); +extern void bar_small(S_small*); +extern void bar_large(S_large*); + +// Prevent mangling of function names. +extern "C" { + +void small_rvoed_unnamed_temporary_object() { +// CHECK-LABEL: define void @small_rvoed_unnamed_temporary_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_smallv +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_smallv +// CHECK: call void @llvm.lifetime.end + + foo_small(); + foo_small(); +} + +void large_rvoed_unnamed_temporary_object() { +// CHECK-LABEL: define void @large_rvoed_unnamed_temporary_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_largev +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_largev +// CHECK: call void @llvm.lifetime.end + + foo_large(); + foo_large(); +} + +void small_rvoed_named_temporary_object() { +// CHECK-LABEL: define void @small_rvoed_named_temporary_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_smallv +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_smallv +// CHECK: call void @llvm.lifetime.end + + { + S_small s = foo_small(); + } + { + S_small s = foo_small(); + } +} + +void large_rvoed_named_temporary_object() { +// CHECK-LABEL: define void @large_rvoed_named_temporary_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_largev +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9foo_largev +// CHECK: call void @llvm.lifetime.end + + { + S_large s = foo_large(); + } + { + S_large s = foo_large(); + } +} + +void small_auto_object() { +// CHECK-LABEL: define void @small_auto_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9bar_smallP7S_small +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9bar_smallP7S_small +// CHECK: call void @llvm.lifetime.end + + { + S_small s; + bar_small(&s); + } + { + S_small s; + bar_small(&s); + } +} + +void large_auto_object() { +// CHECK-LABEL: define void @large_auto_object +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9bar_largeP7S_large +// CHECK: call void @llvm.lifetime.end +// CHECK: call void @llvm.lifetime.start +// CHECK: call void @_Z9bar_largeP7S_large +// CHECK: call void @llvm.lifetime.end + + { + S_large s; + bar_large(&s); + } + { + S_large s; + bar_large(&s); + } +} + +int large_combiner_test(S_large s) { +// CHECK-LABEL: define i32 @large_combiner_test +// CHECK: %1 = alloca %struct.Combiner +// CHECK: %2 = alloca %struct.Combiner +// CHECK: %3 = call %struct.Combiner* @_ZN8CombinerC1E7S_large(%struct.Combiner* %1, [9 x i32] %s.coerce) +// CHECK: call void @_ZN8Combiner1fEv(%struct.Combiner* sret %2, %struct.Combiner* %1) +// CHECK: %4 = getelementptr inbounds %struct.Combiner, %struct.Combiner* %2, i32 0, i32 0, i32 0, i32 0 +// CHECK: %5 = load i32, i32* %4 +// CHECK: ret i32 %5 + + return Combiner(s).f().a.a[0]; +} + +} |

