| ; RUN: opt < %s -simplifycfg -S | 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. |
| ; |
| ; CHECK: define void @f1() |
| ; CHECK: entry: |
| ; CHECK: call void @g() |
| ; CHECK: ret void |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: } |
| ; |
| define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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). |
| ; |
| ; CHECK: define void @f2() |
| ; CHECK: entry: |
| ; CHECK: invoke void @g() |
| ; CHECK: ehcleanup: |
| ; CHECK: cleanuppad within none |
| ; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) |
| ; CHECK: cleanupret from %0 unwind label %catch.dispatch |
| ; CHECK: catch.dispatch: |
| ; CHECK: catchswitch within none [label %catch] unwind to caller |
| ; CHECK: catch: |
| ; CHECK: catchpad |
| ; CHECK: catchret |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: } |
| ; |
| define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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. |
| ; |
| ; CHECK-LABEL: define void @f3() |
| ; CHECK: entry: |
| ; CHECK: invoke void @g() |
| ; CHECK: to label %try.cont unwind label %catch.dispatch |
| ; CHECK: catch.dispatch: |
| ; CHECK-NEXT: catchswitch within none [label %catch] unwind label %ehcleanup.1 |
| ; CHECK: catch: |
| ; CHECK: catchpad within %cs1 [i8* null, i32 64, i8* null] |
| ; CHECK: catchret |
| ; CHECK: ehcleanup.1: |
| ; CHECK: cleanuppad |
| ; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) |
| ; CHECK: cleanupret from %cp3 unwind to caller |
| ; CHECK: } |
| ; |
| define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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).) |
| ; |
| ; CHECK-LABEL: define void @f4() |
| ; CHECK: entry: |
| ; CHECK: call void @g |
| ; Note: The cleanuppad simplification will insert an unconditional branch here |
| ; but it will be eliminated, placing the following invoke in the entry BB. |
| ; CHECK: invoke void @g() |
| ; CHECK: to label %try.cont unwind label %catch.dispatch |
| ; CHECK: catch.dispatch: |
| ; CHECK: catchswitch within none [label %catch] unwind to caller |
| ; CHECK: catch: |
| ; CHECK: catchpad |
| ; CHECK: catchret |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: } |
| ; |
| define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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. |
| ; |
| ; CHECK-LABEL: define i32 @f6() |
| ; CHECK: entry: |
| ; CHECK: invoke void @g() |
| ; CHECK: invoke.cont: |
| ; CHECK: invoke void @g() |
| ; CHECK-NOT: ehcleanup: |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: catch.dispatch: |
| ; CHECK: %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] |
| ; CHECK: } |
| define i32 @f6() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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. |
| ; |
| ; CHECK-LABEL: define i32 @f7() |
| ; CHECK: entry: |
| ; CHECK: invoke void @g() |
| ; CHECK: invoke.cont: |
| ; CHECK: invoke void @g() |
| ; CHECK: invoke.cont.1: |
| ; CHECK: invoke void @g() |
| ; CHECK-NOT: ehcleanup: |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: catch.dispatch: |
| ; CHECK: %state.1 = phi i32 [ 1, %entry ], [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] |
| ; CHECK: } |
| define i32 @f7() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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. |
| ; |
| ; CHECK-LABEL: define void @f8() |
| ; CHECK: entry: |
| ; CHECK: invoke void @g() |
| ; CHECK: invoke.cont: |
| ; CHECK: invoke void @g() |
| ; CHECK-NOT: ehcleanup: |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: catch.dispatch: |
| ; CHECK: %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ], [ %x, %catch.cont ] |
| ; CHECK: } |
| define void @f8() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { |
| 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 |
| } |
| ; CHECK-LABEL: define i32 @f9() |
| ; CHECK: entry: |
| ; CHECK: invoke void @"\01??1S2@@QEAA@XZ"( |
| ; CHECK-NOT: cleanuppad |
| ; CHECK: catch.dispatch: |
| ; CHECK: } |
| define i32 @f9() personality i32 (...)* @__CxxFrameHandler3 { |
| 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 |
| } |
| |
| ; CHECK-LABEL: define void @f10( |
| define void @f10(i32 %V) personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| invoke void @g() |
| to label %unreachable unwind label %cleanup |
| ; CHECK: call void @g() |
| ; CHECK-NEXT: unreachable |
| |
| 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 |
| } |
| |
| %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) |