// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fcxx-exceptions -fexceptions -o - %s | FileCheck --check-prefix=CHECK-EH %s // Test code generation for the named return value optimization. class X { public: X(); X(const X&); ~X(); }; template<typename T> struct Y { Y(); static Y f() { Y y; return y; } }; // CHECK-LABEL: define void @_Z5test0v // CHECK-EH-LABEL: define void @_Z5test0v X test0() { X x; // CHECK: call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret void // CHECK-EH: call {{.*}} @_ZN1XC1Ev // CHECK-EH-NEXT: ret void return x; } // CHECK-LABEL: define void @_Z5test1b( // CHECK-EH-LABEL: define void @_Z5test1b( X test1(bool B) { // CHECK: tail call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret void X x; if (B) return (x); return x; // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev // CHECK-EH-NEXT: ret void } // CHECK-LABEL: define void @_Z5test2b // CHECK-EH-LABEL: define void @_Z5test2b // CHECK-EH-SAME: personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) X test2(bool B) { // No NRVO. X x; X y; if (B) return y; return x; // CHECK: call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: {{.*}} getelementptr inbounds %class.X, %class.X* %y, i32 0, i32 0 // CHECK-NEXT: call void @llvm.lifetime.start // CHECK-NEXT: call {{.*}} @_ZN1XC1Ev // CHECK: call {{.*}} @_ZN1XC1ERKS_ // CHECK: call {{.*}} @_ZN1XC1ERKS_ // CHECK: call {{.*}} @_ZN1XD1Ev // CHECK-NEXT: call void @llvm.lifetime.end // CHECK: call {{.*}} @_ZN1XD1Ev // CHECK-NEXT: call void @llvm.lifetime.end // CHECK: ret void // The block ordering in the -fexceptions IR is unfortunate. // CHECK-EH: call void @llvm.lifetime.start // CHECK-EH-NEXT: call {{.*}} @_ZN1XC1Ev // CHECK-EH: call void @llvm.lifetime.start // CHECK-EH-NEXT: invoke {{.*}} @_ZN1XC1Ev // -> %invoke.cont, %lpad // %invoke.cont: // CHECK-EH: br i1 // -> %if.then, %if.end // %if.then: returning 'x' // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // -> %cleanup, %lpad1 // %lpad: landing pad for ctor of 'y', dtor of 'y' // CHECK-EH: [[CAUGHTVAL:%.*]] = landingpad { i8*, i32 } // CHECK-EH-NEXT: cleanup // CHECK-EH-NEXT: extractvalue { i8*, i32 } [[CAUGHTVAL]], 0 // CHECK-EH-NEXT: extractvalue { i8*, i32 } [[CAUGHTVAL]], 1 // CHECK-EH-NEXT: br label // -> %eh.cleanup // %lpad1: landing pad for return copy ctors, EH cleanup for 'y' // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %eh.cleanup, %terminate.lpad // %if.end: returning 'y' // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // -> %cleanup, %lpad1 // %cleanup: normal cleanup for 'y' // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %invoke.cont11, %lpad // %invoke.cont11: normal cleanup for 'x' // CHECK-EH: call void @llvm.lifetime.end // CHECK-EH-NEXT: call {{.*}} @_ZN1XD1Ev // CHECK-EH-NEXT: call void @llvm.lifetime.end // CHECK-EH-NEXT: ret void // %eh.cleanup: EH cleanup for 'x' // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %invoke.cont17, %terminate.lpad // %invoke.cont17: rethrow block for %eh.cleanup. // This really should be elsewhere in the function. // CHECK-EH: resume { i8*, i32 } // %terminate.lpad: terminate landing pad. // CHECK-EH: [[T0:%.*]] = landingpad { i8*, i32 } // CHECK-EH-NEXT: catch i8* null // CHECK-EH-NEXT: [[T1:%.*]] = extractvalue { i8*, i32 } [[T0]], 0 // CHECK-EH-NEXT: call void @__clang_call_terminate(i8* [[T1]]) [[NR_NUW:#[0-9]+]] // CHECK-EH-NEXT: unreachable } // CHECK-LABEL: define void @_Z5test3b X test3(bool B) { // CHECK: tail call {{.*}} @_ZN1XC1Ev // CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_ // CHECK: call {{.*}} @_ZN1XC1Ev // CHECK: call {{.*}} @_ZN1XC1ERKS_ if (B) { X y; return y; } // FIXME: we should NRVO this variable too. X x; return x; } extern "C" void exit(int) throw(); // CHECK-LABEL: define void @_Z5test4b X test4(bool B) { { // CHECK: tail call {{.*}} @_ZN1XC1Ev X x; // CHECK: br i1 if (B) return x; } // CHECK: tail call {{.*}} @_ZN1XD1Ev // CHECK: tail call void @exit(i32 1) exit(1); } #ifdef __EXCEPTIONS // CHECK-EH-LABEL: define void @_Z5test5 void may_throw(); X test5() { try { may_throw(); } catch (X x) { // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // CHECK-EH: call void @__cxa_end_catch() // CHECK-EH: ret void return x; } } #endif // rdar://problem/10430868 // CHECK-LABEL: define void @_Z5test6v X test6() { X a __attribute__((aligned(8))); return a; // CHECK: [[A:%.*]] = alloca [[X:%.*]], align 8 // CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds %class.X, %class.X* [[A]], i32 0, i32 0 // CHECK-NEXT: call void @llvm.lifetime.start(i64 1, i8* [[PTR]]) // CHECK-NEXT: call {{.*}} @_ZN1XC1Ev([[X]]* nonnull [[A]]) // CHECK-NEXT: call {{.*}} @_ZN1XC1ERKS_([[X]]* {{%.*}}, [[X]]* nonnull dereferenceable({{[0-9]+}}) [[A]]) // CHECK-NEXT: call {{.*}} @_ZN1XD1Ev([[X]]* nonnull [[A]]) // CHECK-NEXT: call void @llvm.lifetime.end(i64 1, i8* [[PTR]]) // CHECK-NEXT: ret void } // CHECK-LABEL: define void @_Z5test7b X test7(bool b) { // CHECK: tail call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret if (b) { X x; return x; } return X(); } // CHECK-LABEL: define void @_Z5test8b X test8(bool b) { // CHECK: tail call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret if (b) { X x; return x; } else { X y; return y; } } Y<int> test9() { Y<int>::f(); } // CHECK-LABEL: define linkonce_odr void @_ZN1YIiE1fEv // CHECK: tail call {{.*}} @_ZN1YIiEC1Ev // CHECK-EH: attributes [[NR_NUW]] = { noreturn nounwind }