; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S -hoist-common-insts=true | FileCheck %s ; ModuleID = 'cppeh-simplify.cpp' target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc18.0.0" ; This case arises when two objects with empty destructors are cleaned up. ; ; void f1() { ; S a; ; S b; ; g(); ; } ; ; In this case, both cleanup pads can be eliminated and the invoke can be ; converted to a call. ; define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f1( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @g() ; CHECK-NEXT: ret void ; entry: invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry ret void ehcleanup: ; preds = %entry %0 = cleanuppad within none [] cleanupret from %0 unwind label %ehcleanup.1 ehcleanup.1: ; preds = %ehcleanup %1 = cleanuppad within none [] cleanupret from %1 unwind to caller } ; This case arises when an object with an empty destructor must be cleaned up ; outside of a try-block and an object with a non-empty destructor must be ; cleaned up within the try-block. ; ; void f2() { ; S a; ; try { ; S2 b; ; g(); ; } catch (...) {} ; } ; ; In this case, the outermost cleanup pad can be eliminated and the catch block ; should unwind to the caller (that is, exception handling continues with the ; parent frame of the caller). ; define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_S2:%.*]], align 1 ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[EHCLEANUP:%.*]] ; CHECK: ehcleanup: ; CHECK-NEXT: [[TMP0:%.*]] = cleanuppad within none [] ; CHECK-NEXT: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* [[B]]) ; CHECK-NEXT: cleanupret from [[TMP0]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP1:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[TMP1]] to label [[TRY_CONT]] ; CHECK: try.cont: ; CHECK-NEXT: ret void ; entry: %b = alloca %struct.S2, align 1 invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry br label %try.cont ehcleanup: ; preds = %entry %0 = cleanuppad within none [] call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 catch: ; preds = %catch.dispatch %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %1 to label %catchret.dest catchret.dest: ; preds = %catch br label %try.cont try.cont: ; preds = %catchret.dest, %invoke.cont ret void ehcleanup.1: %2 = cleanuppad within none [] cleanupret from %2 unwind to caller } ; This case arises when an object with a non-empty destructor must be cleaned up ; outside of a try-block and an object with an empty destructor must be cleaned ; within the try-block. ; ; void f3() { ; S2 a; ; try { ; S b; ; g(); ; } catch (...) {} ; } ; ; In this case the inner cleanup pad should be eliminated and the invoke of g() ; should unwind directly to the catchpad. define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f3( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_S2:%.*]], align 1 ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[EHCLEANUP_1:%.*]] ; CHECK: catch: ; CHECK-NEXT: [[CP2:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[CP2]] to label [[TRY_CONT]] ; CHECK: try.cont: ; CHECK-NEXT: ret void ; CHECK: ehcleanup.1: ; CHECK-NEXT: [[CP3:%.*]] = cleanuppad within none [] ; CHECK-NEXT: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* [[A]]) ; CHECK-NEXT: cleanupret from [[CP3]] unwind to caller ; entry: %a = alloca %struct.S2, align 1 invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry br label %try.cont ehcleanup: ; preds = %entry %0 = cleanuppad within none [] cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 catch: ; preds = %catch.dispatch %cp2 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %cp2 to label %catchret.dest catchret.dest: ; preds = %catch br label %try.cont try.cont: ; preds = %catchret.dest, %invoke.cont ret void ehcleanup.1: %cp3 = cleanuppad within none [] call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) cleanupret from %cp3 unwind to caller } ; This case arises when an object with an empty destructor may require cleanup ; from either inside or outside of a try-block. ; ; void f4() { ; S a; ; g(); ; try { ; g(); ; } catch (...) {} ; } ; ; In this case, the cleanuppad should be eliminated, the invoke outside of the ; catch block should be converted to a call (that is, that is, exception ; handling continues with the parent frame of the caller).) ; ; Note: The cleanuppad simplification will insert an unconditional branch here ; but it will be eliminated, placing the following invoke in the entry BB. ; define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f4( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @g() ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[TMP0]] to label [[TRY_CONT]] ; CHECK: try.cont: ; CHECK-NEXT: ret void ; entry: invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry invoke void @g() to label %try.cont unwind label %catch.dispatch catch.dispatch: ; preds = %invoke.cont %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup catch: ; preds = %catch.dispatch %0 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %0 to label %try.cont try.cont: ; preds = %catch, %invoke.cont ret void ehcleanup: %cp2 = cleanuppad within none [] cleanupret from %cp2 unwind to caller } ; This case tests simplification of an otherwise empty cleanup pad that contains ; a PHI node. ; ; int f6() { ; int state = 1; ; try { ; S a; ; g(); ; state = 2; ; g(); ; } catch (...) { ; return state; ; } ; return 0; ; } ; ; In this case, the cleanup pad should be eliminated and the PHI node in the ; cleanup pad should be sunk into the catch dispatch block. ; define i32 @f6() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f6( ; CHECK-NEXT: entry: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: invoke.cont: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[STATE_0:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ] ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] ; CHECK: return: ; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[STATE_0]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT]] ] ; CHECK-NEXT: ret i32 [[RETVAL_0]] ; entry: invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry invoke void @g() to label %return unwind label %ehcleanup ehcleanup: ; preds = %invoke.cont, %entry %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] %0 = cleanuppad within none [] cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup %cs1 = catchswitch within none [label %catch] unwind to caller catch: ; preds = %catch.dispatch %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %1 to label %return return: ; preds = %invoke.cont, %catch %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ] ret i32 %retval.0 } ; This case tests another variation of simplification of an otherwise empty ; cleanup pad that contains a PHI node. ; ; int f7() { ; int state = 1; ; try { ; g(); ; state = 2; ; S a; ; g(); ; state = 3; ; g(); ; } catch (...) { ; return state; ; } ; return 0; ; } ; ; In this case, the cleanup pad should be eliminated and the PHI node in the ; cleanup pad should be merged with the PHI node in the catch dispatch block. ; define i32 @f7() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f7( ; CHECK-NEXT: entry: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: invoke.cont: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT_1:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: invoke.cont.1: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[STATE_1:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 3, [[INVOKE_CONT_1]] ], [ 2, [[INVOKE_CONT]] ] ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] ; CHECK: return: ; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[STATE_1]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT_1]] ] ; CHECK-NEXT: ret i32 [[RETVAL_0]] ; entry: invoke void @g() to label %invoke.cont unwind label %catch.dispatch invoke.cont: ; preds = %entry invoke void @g() to label %invoke.cont.1 unwind label %ehcleanup invoke.cont.1: ; preds = %invoke.cont invoke void @g() to label %return unwind label %ehcleanup ehcleanup: ; preds = %invoke.cont.1, %invoke.cont %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] %0 = cleanuppad within none [] cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup, %entry %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ] %cs1 = catchswitch within none [label %catch] unwind to caller catch: ; preds = %catch.dispatch %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %1 to label %return return: ; preds = %invoke.cont.1, %catch %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ] ret i32 %retval.0 } ; This case tests a scenario where an empty cleanup pad is not dominated by all ; of the predecessors of its successor, but the successor references a PHI node ; in the empty cleanup pad. ; ; Conceptually, the case being modeled is something like this: ; ; int f8() { ; int x = 1; ; try { ; S a; ; g(); ; x = 2; ; retry: ; g(); ; return ; } catch (...) { ; use_x(x); ; } ; goto retry; ; } ; ; While that C++ syntax isn't legal, the IR below is. ; ; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch ; should have an incoming value entry for path from 'foo' that references the ; PHI node itself. ; define void @f8() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f8( ; CHECK-NEXT: entry: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: invoke.cont: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[X:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ], [ [[X]], [[CATCH_CONT:%.*]] ] ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: call void @use_x(i32 [[X]]) ; CHECK-NEXT: catchret from [[TMP0]] to label [[CATCH_CONT]] ; CHECK: catch.cont: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[RETURN]] unwind label [[CATCH_DISPATCH]] ; CHECK: return: ; CHECK-NEXT: ret void ; entry: invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry invoke void @g() to label %return unwind label %ehcleanup ehcleanup: ; preds = %invoke.cont, %entry %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] %0 = cleanuppad within none [] cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup, %catch.cont %cs1 = catchswitch within none [label %catch] unwind to caller catch: ; preds = %catch.dispatch %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] call void @use_x(i32 %x) catchret from %1 to label %catch.cont catch.cont: ; preds = %catch invoke void @g() to label %return unwind label %catch.dispatch return: ; preds = %invoke.cont, %catch.cont ret void } define i32 @f9() personality i32 (...)* @__CxxFrameHandler3 { ; CHECK-LABEL: @f9( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[S:%.*]] = alloca i8, align 1 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[S]]) ; CHECK-NEXT: [[BC:%.*]] = bitcast i8* [[S]] to %struct.S2* ; CHECK-NEXT: invoke void @"\01??1S2@@QEAA@XZ"(%struct.S2* [[BC]]) ; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[CATCH_SWITCH:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [i8* null, i32 0, i8* null] ; CHECK-NEXT: catchret from [[CATCH_PAD]] to label [[TRY_CONT]] ; CHECK: try.cont: ; CHECK-NEXT: ret i32 0 ; entry: %s = alloca i8, align 1 call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %s) %bc = bitcast i8* %s to %struct.S2* invoke void @"\01??1S2@@QEAA@XZ"(%struct.S2* %bc) to label %try.cont unwind label %ehcleanup ehcleanup: %cleanup.pad = cleanuppad within none [] call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %s) cleanupret from %cleanup.pad unwind label %catch.dispatch catch.dispatch: %catch.switch = catchswitch within none [label %catch] unwind to caller catch: %catch.pad = catchpad within %catch.switch [i8* null, i32 0, i8* null] catchret from %catch.pad to label %try.cont try.cont: ret i32 0 } define void @f10(i32 %V) personality i32 (...)* @__CxxFrameHandler3 { ; CHECK-LABEL: @f10( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @g() ; CHECK-NEXT: unreachable ; entry: invoke void @g() to label %unreachable unwind label %cleanup unreachable: unreachable cleanup: %cp = cleanuppad within none [] switch i32 %V, label %cleanupret1 [ i32 0, label %cleanupret2 ] cleanupret1: cleanupret from %cp unwind to caller cleanupret2: cleanupret from %cp unwind to caller } ; This case tests the handling of an empty cleanup pad that ; contains a lifetime_end intrinsic and does not dominate its ; successor. define void @f11() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { ; CHECK-LABEL: @f11( ; CHECK-NEXT: entry: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] ; CHECK: invoke.cont: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: invoke.cont2: ; CHECK-NEXT: invoke void @g() ; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] ; CHECK: catch.dispatch: ; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller ; CHECK: catch: ; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [i8* null, i32 64, i8* null] ; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] ; CHECK: return: ; CHECK-NEXT: ret void ; entry: invoke void @g() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry invoke void @g() to label %invoke.cont2 unwind label %ehcleanup invoke.cont2: ; preds = %invoke.cont invoke void @g() to label %return unwind label %catch.dispatch ehcleanup: ; preds = %invoke.cont, %entry %x = phi i8* [ undef, %invoke.cont ], [ undef, %entry ] %0 = cleanuppad within none [] call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %x) cleanupret from %0 unwind label %catch.dispatch catch.dispatch: ; preds = %ehcleanup, %invoke.cont %cs1 = catchswitch within none [label %catch] unwind to caller catch: ; preds = %catch.dispatch %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] catchret from %1 to label %return return: ; preds = %invoke.cont, %catch.cont ret void } %struct.S = type { i8 } %struct.S2 = type { i8 } declare void @"\01??1S2@@QEAA@XZ"(%struct.S2*) declare void @g() declare void @use_x(i32 %x) declare i32 @__CxxFrameHandler3(...) declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)