forked from OSchip/llvm-project
471 lines
14 KiB
LLVM
471 lines
14 KiB
LLVM
|
; 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)
|