diff options
Diffstat (limited to 'test/CodeGenCXX/stmtexpr.cpp')
-rw-r--r-- | test/CodeGenCXX/stmtexpr.cpp | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/test/CodeGenCXX/stmtexpr.cpp b/test/CodeGenCXX/stmtexpr.cpp index 7bf19bbfb46d..5885a1663e63 100644 --- a/test/CodeGenCXX/stmtexpr.cpp +++ b/test/CodeGenCXX/stmtexpr.cpp @@ -80,3 +80,85 @@ int foo5(bool b) { y = ({ A a(1); if (b) goto G; a.i; }); G: return y; } + +// When we emit a full expression with cleanups that contains branches out of +// the full expression, the result of the inner expression (the call to +// call_with_cleanups in this case) may not dominate the fallthrough destination +// of the shared cleanup block. +// +// In this case the CFG will be a sequence of two diamonds, but the only +// dynamically possible execution paths are both left hand branches and both +// right hand branches. The first diamond LHS will call bar, and the second +// diamond LHS will assign the result to v, but the call to bar does not +// dominate the assignment. +int bar(A, int); +extern "C" int cleanup_exit_scalar(bool b) { + int v = bar(A(1), ({ if (b) return 42; 13; })); + return v; +} + +// CHECK-LABEL: define{{.*}} i32 @cleanup_exit_scalar({{.*}}) +// CHECK: call {{.*}} @_ZN1AC1Ei +// Spill after bar. +// CHECK: %[[v:[^ ]*]] = call{{.*}} i32 @_Z3bar1Ai({{.*}}) +// CHECK-NEXT: store i32 %[[v]], i32* %[[tmp:[^, ]*]] +// Do cleanup. +// CHECK: call {{.*}} @_ZN1AD1Ev +// CHECK: switch +// Reload before v assignment. +// CHECK: %[[v:[^ ]*]] = load i32, i32* %[[tmp]] +// CHECK-NEXT: store i32 %[[v]], i32* %v + +// No need to spill when the expression result is a constant, constants don't +// have dominance problems. +extern "C" int cleanup_exit_scalar_constant(bool b) { + int v = (A(1), (void)({ if (b) return 42; 0; }), 13); + return v; +} + +// CHECK-LABEL: define{{.*}} i32 @cleanup_exit_scalar_constant({{.*}}) +// CHECK: store i32 13, i32* %v + +// Check for the same bug for lvalue expression evaluation kind. +// FIXME: What about non-reference lvalues, like bitfield lvalues and vector +// lvalues? +int &getref(); +extern "C" int cleanup_exit_lvalue(bool cond) { + int &r = (A(1), ({ if (cond) return 0; (void)0; }), getref()); + return r; +} +// CHECK-LABEL: define{{.*}} i32 @cleanup_exit_lvalue({{.*}}) +// CHECK: call {{.*}} @_ZN1AC1Ei +// Spill after bar. +// CHECK: %[[v:[^ ]*]] = call dereferenceable(4) i32* @_Z6getrefv({{.*}}) +// CHECK-NEXT: store i32* %[[v]], i32** %[[tmp:[^, ]*]] +// Do cleanup. +// CHECK: call {{.*}} @_ZN1AD1Ev +// CHECK: switch +// Reload before v assignment. +// CHECK: %[[v:[^ ]*]] = load i32*, i32** %[[tmp]] +// CHECK-NEXT: store i32* %[[v]], i32** %r + + +// We handle ExprWithCleanups for complex evaluation type separately, and it had +// the same bug. +_Complex float bar_complex(A, int); +extern "C" int cleanup_exit_complex(bool b) { + _Complex float v = bar_complex(A(1), ({ if (b) return 42; 13; })); + return v; +} + +// CHECK-LABEL: define{{.*}} i32 @cleanup_exit_complex({{.*}}) +// CHECK: call {{.*}} @_ZN1AC1Ei +// Spill after bar. +// CHECK: call {{.*}} @_Z11bar_complex1Ai({{.*}}) +// CHECK: store float %{{.*}}, float* %[[tmp1:[^, ]*]] +// CHECK: store float %{{.*}}, float* %[[tmp2:[^, ]*]] +// Do cleanup. +// CHECK: call {{.*}} @_ZN1AD1Ev +// CHECK: switch +// Reload before v assignment. +// CHECK: %[[v1:[^ ]*]] = load float, float* %[[tmp1]] +// CHECK: %[[v2:[^ ]*]] = load float, float* %[[tmp2]] +// CHECK: store float %[[v1]], float* %v.realp +// CHECK: store float %[[v2]], float* %v.imagp |