diff options
Diffstat (limited to 'test/CodeGen/WinEH')
31 files changed, 5230 insertions, 0 deletions
diff --git a/test/CodeGen/WinEH/cppeh-alloca-sink.ll b/test/CodeGen/WinEH/cppeh-alloca-sink.ll new file mode 100644 index 000000000000..d50237fa78a7 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-alloca-sink.ll @@ -0,0 +1,180 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test describes two difficult cases in sinking allocas into child frames. +; We don't currently do this optimization, but we'll need to tweak these tests +; when we do. + +; This test is based on the following code: +; +; // In this case we can sink the alloca from the parent into the catch because +; // the lifetime is limited to the catch. +; extern "C" void may_throw(); +; extern "C" void sink_alloca_to_catch() { +; try { +; may_throw(); +; } catch (int) { +; volatile int only_used_in_catch = 42; +; } +; } +; +; // In this case we cannot. The variable should live as long as the parent +; // frame lives. +; extern "C" void use_catch_var(int *); +; extern "C" void dont_sink_alloca_to_catch(int n) { +; int live_in_out_catch = 0; +; while (n > 0) { +; try { +; may_throw(); +; } catch (int) { +; use_catch_var(&live_in_out_catch); +; } +; n--; +; } +; } + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare void @may_throw() #1 +declare i32 @__CxxFrameHandler3(...) +declare i32 @llvm.eh.typeid.for(i8*) #2 +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 +declare void @llvm.eh.endcatch() #3 + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" + +; Function Attrs: uwtable +define void @sink_alloca_to_catch() #0 { +entry: + %0 = alloca i32 + %only_used_in_catch = alloca i32, align 4 + invoke void @may_throw() + to label %try.cont unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + %2 = extractvalue { i8*, i32 } %1, 1 + %3 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*)) #3 + %matches = icmp eq i32 %2, %3 + br i1 %matches, label %catch, label %eh.resume + +catch: ; preds = %lpad + %4 = extractvalue { i8*, i32 } %1, 0 + call void @llvm.eh.begincatch(i8* %4, i8* null) #3 + store volatile i32 42, i32* %only_used_in_catch, align 4 + tail call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %entry, %catch + ret void + +eh.resume: ; preds = %lpad + resume { i8*, i32 } %1 +} + +; CHECK-LABEL: define void @sink_alloca_to_catch() +; CHECK: call void (...) @llvm.frameescape(i32* %only_used_in_catch) + +declare void @use_catch_var(i32*) #1 + +; Function Attrs: uwtable +define void @dont_sink_alloca_to_catch(i32 %n) #0 { +entry: + %0 = alloca i32 + %n.addr = alloca i32, align 4 + %live_in_out_catch = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 %n, i32* %n.addr, align 4 + br label %while.cond + +while.cond: ; preds = %try.cont, %entry + %1 = load i32, i32* %n.addr, align 4 + %cmp = icmp sgt i32 %1, 0 + br i1 %cmp, label %while.body, label %while.end + +while.body: ; preds = %while.cond + invoke void @may_throw() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %while.body + br label %try.cont + +lpad: ; preds = %while.body + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*) + %3 = extractvalue { i8*, i32 } %2, 0 + store i8* %3, i8** %exn.slot + %4 = extractvalue { i8*, i32 } %2, 1 + store i32 %4, i32* %ehselector.slot + br label %catch.dispatch + +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %5 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*)) #3 + %matches = icmp eq i32 %sel, %5 + br i1 %matches, label %catch, label %eh.resume + +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #3 + invoke void @use_catch_var(i32* %live_in_out_catch) + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %catch + call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %invoke.cont2, %invoke.cont + %6 = load i32, i32* %0 + %7 = load i32, i32* %n.addr, align 4 + %dec = add nsw i32 %7, -1 + store i32 %dec, i32* %n.addr, align 4 + br label %while.cond + +lpad1: ; preds = %catch + %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %9 = extractvalue { i8*, i32 } %8, 0 + store i8* %9, i8** %exn.slot + %10 = extractvalue { i8*, i32 } %8, 1 + store i32 %10, i32* %ehselector.slot + call void @llvm.eh.endcatch() #3 + br label %eh.resume + +while.end: ; preds = %while.cond + ret void + +eh.resume: ; preds = %lpad1, %catch.dispatch + %exn3 = load i8*, i8** %exn.slot + %sel4 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn3, 0 + %lpad.val5 = insertvalue { i8*, i32 } %lpad.val, i32 %sel4, 1 + resume { i8*, i32 } %lpad.val5 +} + +; CHECK-LABEL: define void @dont_sink_alloca_to_catch(i32 %n) +; CHECK: call void (...) @llvm.frameescape(i32* %live_in_out_catch) + +; CHECK-LABEL: define internal i8* @sink_alloca_to_catch.catch(i8*, i8*) +; CHECK: %only_used_in_catch.i8 = call i8* @llvm.framerecover({{.*}}, i32 0) +; CHECK: %only_used_in_catch = bitcast + +; CHECK-LABEL: define internal i8* @dont_sink_alloca_to_catch.catch(i8*, i8*) +; CHECK: %live_in_out_catch.i8 = call i8* @llvm.framerecover({{.*}}, i32 0) +; CHECK: %live_in_out_catch = bitcast + + + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } diff --git a/test/CodeGen/WinEH/cppeh-catch-all.ll b/test/CodeGen/WinEH/cppeh-catch-all.ll new file mode 100644 index 000000000000..a6c94d400797 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-catch-all.ll @@ -0,0 +1,97 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; void test() +; { +; try { +; may_throw(); +; } catch (...) { +; handle_exception(); +; } +; } +; +; Parts of the IR have been hand-edited to simplify the test case. +; The full IR will be restored when Windows C++ EH support is complete. + +; ModuleID = 'catch-all.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; The function entry in this case remains unchanged. +; CHECK: define void @_Z4testv() +; CHECK: entry: +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + invoke void @_Z9may_throwv() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* null +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @_Z4testv.catch) +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont] + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %tmp1 = extractvalue { i8*, i32 } %tmp, 0 + store i8* %tmp1, i8** %exn.slot + %tmp2 = extractvalue { i8*, i32 } %tmp, 1 + store i32 %tmp2, i32* %ehselector.slot + br label %catch + +; CHECK-NOT: catch: + +catch: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #2 + call void @_Z16handle_exceptionv() + br label %invoke.cont2 + +; CHECK-NOT: invoke.cont2: + +invoke.cont2: ; preds = %catch + call void @llvm.eh.endcatch() + br label %try.cont + +try.cont: ; preds = %invoke.cont2, %invoke.cont + ret void + +; CHECK: } +} + +; CHECK: define internal i8* @_Z4testv.catch(i8*, i8*) +; CHECK: entry: +; CHECK: call void @_Z16handle_exceptionv() +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) +; CHECK: } + +declare void @_Z9may_throwv() #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @llvm.eh.begincatch(i8*, i8*) + +declare void @_Z16handle_exceptionv() #1 + +declare void @llvm.eh.endcatch() + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noinline noreturn nounwind } +attributes #3 = { nounwind } +attributes #4 = { noreturn nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 226027)"} diff --git a/test/CodeGen/WinEH/cppeh-catch-and-throw.ll b/test/CodeGen/WinEH/cppeh-catch-and-throw.ll new file mode 100644 index 000000000000..c60a339f6ba2 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-catch-and-throw.ll @@ -0,0 +1,143 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; class Obj { +; public: +; ~Obj(); +; }; +; +; void test(void) +; { +; try { +; Obj o; +; throw 1; +; } catch (...) { +; throw; +; } +; } + +; ModuleID = 'cppeh-catch-and-throw.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } +%class.Obj = type { i8 } + +$"\01??_R0H@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat + +; This is just a minimal check to verify that main was handled by WinEHPrepare. +; CHECK: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape +; CHECK: invoke void @_CxxThrowException +; CHECK: } + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %o = alloca %class.Obj, align 1 + %tmp = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 1, i32* %tmp + %0 = bitcast i32* %tmp to i8* + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #3 + to label %unreachable unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %2 = extractvalue { i8*, i32 } %1, 0 + store i8* %2, i8** %exn.slot + %3 = extractvalue { i8*, i32 } %1, 1 + store i32 %3, i32* %ehselector.slot + call void @"\01??1Obj@@QEAA@XZ"(%class.Obj* %o) #2 + br label %catch + +catch: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #2 + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #3 + to label %unreachable unwind label %lpad1 + +lpad1: ; preds = %catch + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %5 = extractvalue { i8*, i32 } %4, 0 + store i8* %5, i8** %exn.slot + %6 = extractvalue { i8*, i32 } %4, 1 + store i32 %6, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +try.cont: ; No predecessors! + ret void + +eh.resume: ; preds = %lpad1 + %exn2 = load i8*, i8** %exn.slot + %sel = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn2, 0 + %lpad.val3 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val3 + +unreachable: ; preds = %catch, %entry + unreachable +} + +; Verify that we inserted a stub invoke into the outlined cleanup handler. +; +; CHECK-LABEL: define internal void @"\01?test@@YAXXZ.cleanup"(i8*, i8*) +; CHECK: entry: +; CHECK: call i8* @llvm.framerecover +; CHECK: call void @"\01??1Obj@@QEAA@XZ" +; CHECK: invoke void @llvm.donothing() +; CHECK: to label %[[SPLIT_LABEL:.+]] unwind label %[[LPAD_LABEL:.+]] +; +; CHECK: [[SPLIT_LABEL]] +; +; CHECK: [[LPAD_LABEL]] +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: cleanup +; CHECK: unreachable +; CHECK: } + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind +declare void @"\01??1Obj@@QEAA@XZ"(%class.Obj*) #1 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #2 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } +attributes #3 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 235214) (llvm/trunk 235213)"} diff --git a/test/CodeGen/WinEH/cppeh-catch-scalar.ll b/test/CodeGen/WinEH/cppeh-catch-scalar.ll new file mode 100644 index 000000000000..4faef82a75fc --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-catch-scalar.ll @@ -0,0 +1,126 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; void test() +; { +; try { +; may_throw(); +; } catch (int i) { +; handle_int(i); +; } +; } +; +; Parts of the IR have been hand-edited to simplify the test case. +; The full IR will be restored when Windows C++ EH support is complete. + +;ModuleID = 'cppeh-catch-scalar.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +@_ZTIi = external constant i8* + +; The function entry will be rewritten like this. +; CHECK: define void @_Z4testv() +; CHECK: entry: +; CHECK: [[I_PTR:\%.+]] = alloca i32, align 4 +; CHECK: call void (...) @llvm.frameescape(i32* [[I_PTR]]) +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %i = alloca i32, align 4 + invoke void @_Z9may_throwv() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIi to i8*) +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (i8** @_ZTIi to i8*), i32 0, i8* (i8*, i8*)* @_Z4testv.catch) +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont] + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (i8** @_ZTIi to i8*) + %tmp1 = extractvalue { i8*, i32 } %tmp, 0 + store i8* %tmp1, i8** %exn.slot + %tmp2 = extractvalue { i8*, i32 } %tmp, 1 + store i32 %tmp2, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch-dispatch: + +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %tmp3 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #3 + %matches = icmp eq i32 %sel, %tmp3 + br i1 %matches, label %catch, label %eh.resume + +; CHECK-NOT: catch: + +catch: ; preds = %catch.dispatch + %exn11 = load i8*, i8** %exn.slot + %i.i8 = bitcast i32* %i to i8* + call void @llvm.eh.begincatch(i8* %exn11, i8* %i.i8) #3 + %tmp7 = load i32, i32* %i, align 4 + call void @_Z10handle_inti(i32 %tmp7) + br label %invoke.cont2 + +; CHECK-NOT: invoke.cont2: + +invoke.cont2: ; preds = %catch + call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %invoke.cont2, %invoke.cont + ret void + +; CHECK-NOT: eh.resume: + +eh.resume: ; preds = %catch.dispatch + %exn3 = load i8*, i8** %exn.slot + %sel4 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn3, 0 + %lpad.val5 = insertvalue { i8*, i32 } %lpad.val, i32 %sel4, 1 + resume { i8*, i32 } %lpad.val5 + +; CHECK: } +} + +; CHECK: define internal i8* @_Z4testv.catch(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 0) +; CHECK: [[I_PTR1:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[TMP:\%.+]] = load i32, i32* [[I_PTR1]], align 4 +; CHECK: call void @_Z10handle_inti(i32 [[TMP]]) +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) +; CHECK: } + +declare void @_Z9may_throwv() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +declare void @llvm.eh.begincatch(i8*, i8*) + +declare void @llvm.eh.endcatch() + +declare void @_Z10handle_inti(i32) #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 227474) (llvm/trunk 227508)"} diff --git a/test/CodeGen/WinEH/cppeh-catch-unwind.ll b/test/CodeGen/WinEH/cppeh-catch-unwind.ll new file mode 100644 index 000000000000..0fd735be57a1 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-catch-unwind.ll @@ -0,0 +1,240 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test was generated from the following source: +; +; void test() { +; try { +; SomeClass obj; +; may_throw(); +; try { +; may_throw(); +; } catch (int) { +; handle_exception(); +; } +; } catch (int) { +; handle_exception(); +; } +; } +; +; The code above was compiled with the -O2 option. + +; ModuleID = 'catch-unwind.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%class.SomeClass = type { i8 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + + +; CHECK-LABEL: define void @"\01?test@@YAXXZ"() #0 { +; CHECK: entry: +; CHECK: [[OBJ_PTR:\%.+]] = alloca %class.SomeClass +; CHECK: [[TMP0:\%.+]] = alloca i32, align 4 +; CHECK: [[TMP1:\%.+]] = alloca i32, align 4 +; CHECK: call void (...) @llvm.frameescape(i32* [[TMP1]], %class.SomeClass* [[OBJ_PTR]], i32* [[TMP0]]) +; CHECK: %call = invoke %class.SomeClass* @"\01??0SomeClass@@QEAA@XZ"(%class.SomeClass* %obj) +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %obj = alloca %class.SomeClass, align 1 + %0 = alloca i32, align 4 + %1 = alloca i32, align 4 + %call = invoke %class.SomeClass* @"\01??0SomeClass@@QEAA@XZ"(%class.SomeClass* %obj) + to label %invoke.cont unwind label %lpad + +; CHECK: invoke.cont: +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont2 unwind label %[[LPAD1_LABEL:lpad[0-9]*]] + +invoke.cont: ; preds = %entry + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont2 unwind label %lpad1 + +; CHECK: invoke.cont2: +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %try.cont unwind label %[[LPAD3_LABEL:lpad[0-9]*]] + +invoke.cont2: ; preds = %invoke.cont + invoke void @"\01?may_throw@@YAXXZ"() + to label %try.cont unwind label %lpad3 + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: [[LPAD_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont15] + +lpad: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %3 = extractvalue { i8*, i32 } %2, 0 + %4 = extractvalue { i8*, i32 } %2, 1 + br label %catch.dispatch7 + +; CHECK: [[LPAD1_LABEL]]:{{[ ]+}}; preds = %invoke.cont +; CHECK: [[LPAD1_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER1:\%.+]] = call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test@@YAXXZ.cleanup", i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK-NEXT: indirectbr i8* [[RECOVER1]], [label %try.cont15] + +lpad1: ; preds = %invoke.cont + %5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %6 = extractvalue { i8*, i32 } %5, 0 + %7 = extractvalue { i8*, i32 } %5, 1 + br label %ehcleanup + +; CHECK: [[LPAD3_LABEL]]:{{[ ]+}}; preds = %invoke.cont2 +; CHECK: [[LPAD3_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER3:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1", i32 0, void (i8*, i8*)* @"\01?test@@YAXXZ.cleanup") +; CHECK-NEXT: indirectbr i8* [[RECOVER3]], [label %try.cont, label %try.cont15] + +lpad3: ; preds = %invoke.cont2 + %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %9 = extractvalue { i8*, i32 } %8, 0 + %10 = extractvalue { i8*, i32 } %8, 1 + %11 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches = icmp eq i32 %10, %11 + br i1 %matches, label %catch, label %ehcleanup + +; CHECK-NOT: catch: +catch: ; preds = %lpad3 + %12 = bitcast i32* %0 to i8* + call void @llvm.eh.begincatch(i8* %9, i8* %12) #3 + invoke void @"\01?handle_exception@@YAXXZ"() + to label %invoke.cont6 unwind label %lpad5 + +; CHECK-NOT: invoke.cont6: +invoke.cont6: ; preds = %catch + call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %invoke.cont2, %invoke.cont6 + call void @"\01??1SomeClass@@QEAA@XZ"(%class.SomeClass* %obj) #3 + br label %try.cont15 + +; CHECK-NOT: lpad5: +lpad5: ; preds = %catch + %13 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %14 = extractvalue { i8*, i32 } %13, 0 + %15 = extractvalue { i8*, i32 } %13, 1 + call void @llvm.eh.endcatch() #3 + br label %ehcleanup + +; CHECK-NOT: ehcleanup +ehcleanup: ; preds = %lpad5, %lpad3, %lpad1 + %exn.slot.0 = phi i8* [ %14, %lpad5 ], [ %9, %lpad3 ], [ %6, %lpad1 ] + %ehselector.slot.0 = phi i32 [ %15, %lpad5 ], [ %10, %lpad3 ], [ %7, %lpad1 ] + call void @"\01??1SomeClass@@QEAA@XZ"(%class.SomeClass* %obj) #3 + br label %catch.dispatch7 + +; CHECK-NOT: catch.dispatch7: +catch.dispatch7: ; preds = %ehcleanup, %lpad + %exn.slot.1 = phi i8* [ %exn.slot.0, %ehcleanup ], [ %3, %lpad ] + %ehselector.slot.1 = phi i32 [ %ehselector.slot.0, %ehcleanup ], [ %4, %lpad ] + %16 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches9 = icmp eq i32 %ehselector.slot.1, %16 + br i1 %matches9, label %catch10, label %eh.resume + +; CHECK-NOT: catch10: +catch10: ; preds = %catch.dispatch7 + %17 = bitcast i32* %1 to i8* + call void @llvm.eh.begincatch(i8* %exn.slot.1, i8* %17) #3 + call void @"\01?handle_exception@@YAXXZ"() + br label %invoke.cont13 + +; CHECK-NOT: invoke.cont13: +invoke.cont13: ; preds = %catch10 + call void @llvm.eh.endcatch() #3 + br label %try.cont15 + +try.cont15: ; preds = %invoke.cont13, %try.cont + ret void + +; CHECK-NOT: eh.resume +eh.resume: ; preds = %catch.dispatch7 + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn.slot.1, 0 + %lpad.val18 = insertvalue { i8*, i32 } %lpad.val, i32 %ehselector.slot.1, 1 + resume { i8*, i32 } %lpad.val18 + +; CHECK: } +} + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_TMP1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[TMP1_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP1]] to i32* +; CHECK: call void @"\01?handle_exception@@YAXXZ"() +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont15) +; CHECK: } + +; CHECK-LABEL: define internal void @"\01?test@@YAXXZ.cleanup"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_OBJ:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[OBJ_PTR:\%.+]] = bitcast i8* %obj.i8 to %class.SomeClass* +; CHECK: call void @"\01??1SomeClass@@QEAA@XZ"(%class.SomeClass* [[OBJ_PTR]]) +; CHECK: ret void +; CHECK: } + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch.1"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_TMP0:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: [[TMP0_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP0]] to i32* +; CHECK: invoke void @"\01?handle_exception@@YAXXZ"() +; CHECK: to label %invoke.cont6 unwind label %[[LPAD5_LABEL:lpad[0-9]+]] +; +; CHECK: invoke.cont6: ; preds = %entry +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; +; CHECK: [[LPAD5_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: [[LPAD5_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: cleanup +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK: } + +declare %class.SomeClass* @"\01??0SomeClass@@QEAA@XZ"(%class.SomeClass* returned) #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @"\01?may_throw@@YAXXZ"() #1 + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +declare void @"\01?handle_exception@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +; Function Attrs: nounwind +declare void @"\01??1SomeClass@@QEAA@XZ"(%class.SomeClass*) #4 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 232069) (llvm/trunk 232070)"} diff --git a/test/CodeGen/WinEH/cppeh-cleanup-invoke.ll b/test/CodeGen/WinEH/cppeh-cleanup-invoke.ll new file mode 100644 index 000000000000..5a570431510f --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-cleanup-invoke.ll @@ -0,0 +1,91 @@ +; RUN: opt -winehprepare -S < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; Modified based on this code: +; struct HasDtor { +; ~HasDtor(); +; }; +; extern "C" void may_throw(); +; int main() { +; try { +; HasDtor o; +; may_throw(); +; } catch (int) { +; } +; } + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } +%struct.HasDtor = type { i8 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" + +define i32 @main() { +entry: + %o = alloca %struct.HasDtor, align 1 + invoke void @may_throw() + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %invoke.cont + call void @"\01??1HasDtor@@QEAA@XZ"(%struct.HasDtor* %o) + br label %try.cont + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + br label %catch.dispatch + +lpad1: ; preds = %invoke.cont + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + %4 = extractvalue { i8*, i32 } %3, 0 + %5 = extractvalue { i8*, i32 } %3, 1 + invoke void @"\01??1HasDtor@@QEAA@XZ"(%struct.HasDtor* %o) + to label %catch.dispatch unwind label %lpad + +catch.dispatch: ; preds = %lpad1, %lpad + %exn.slot.0 = phi i8* [ %4, %lpad1 ], [ %1, %lpad ] + %ehselector.slot.0 = phi i32 [ %5, %lpad1 ], [ %2, %lpad ] + %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*)) + %matches = icmp eq i32 %ehselector.slot.0, %6 + br i1 %matches, label %catch, label %eh.resume + +catch: ; preds = %catch.dispatch + call void @llvm.eh.begincatch(i8* %exn.slot.0, i8* null) + call void @llvm.eh.endcatch() + br label %try.cont + +try.cont: ; preds = %catch, %invoke.cont2 + ret i32 0 + +eh.resume: ; preds = %catch.dispatch + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn.slot.0, 0 + %lpad.val5 = insertvalue { i8*, i32 } %lpad.val, i32 %ehselector.slot.0, 1 + resume { i8*, i32 } %lpad.val5 +} + +; CHECK-LABEL: define i32 @main() +; CHECK: @llvm.eh.actions(i32 0, void (i8*, i8*)* @main.cleanup, i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 -1, i8* (i8*, i8*)* @main.catch) + +; CHECK-LABEL: define internal void @main.cleanup(i8*, i8*) +; CHECK: call void @"\01??1HasDtor@@QEAA@XZ"(%struct.HasDtor* %{{.*}}) +; CHECK: ret void + +declare void @may_throw() + +declare i32 @__CxxFrameHandler3(...) + +declare void @"\01??1HasDtor@@QEAA@XZ"(%struct.HasDtor*) + +declare i32 @llvm.eh.typeid.for(i8*) +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) +declare void @llvm.eh.endcatch() diff --git a/test/CodeGen/WinEH/cppeh-demote-liveout.ll b/test/CodeGen/WinEH/cppeh-demote-liveout.ll new file mode 100644 index 000000000000..48d9b39ca64a --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-demote-liveout.ll @@ -0,0 +1,72 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S < %s | FileCheck %s + +; Notionally based on this C++ source: +; int liveout_catch(int p) { +; int val = p + 1; +; try { +; might_throw(); +; } catch (int) { +; val++; +; } +; return val; +; } + +declare void @llvm.eh.begincatch(i8*, i8*) +declare void @llvm.eh.endcatch() +declare void @might_throw() +declare i32 @__CxxFrameHandler3(...) +declare i32 @llvm.eh.typeid.for(i8*) + +@typeinfo.int = external global i32 + +define i32 @liveout_catch(i32 %p) { +entry: + %val.entry = add i32 %p, 1 + invoke void @might_throw() + to label %ret unwind label %lpad + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__CxxFrameHandler3 + cleanup + catch i32* @typeinfo.int + %ehptr = extractvalue { i8*, i32 } %ehvals, 0 + %sel = extractvalue { i8*, i32 } %ehvals, 1 + %int_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32* @typeinfo.int to i8*)) + %match = icmp eq i32 %sel, %int_sel + br i1 %match, label %catchit, label %resume + +catchit: + call void @llvm.eh.begincatch(i8* %ehptr, i8* null) + %val.lpad = add i32 %val.entry, 1 + call void @llvm.eh.endcatch() + br label %ret + +ret: + %rv = phi i32 [%val.entry, %entry], [%val.lpad, %catchit] + ret i32 %rv + +resume: + resume {i8*, i32} %ehvals +} + +; CHECK-LABEL: define i32 @liveout_catch(i32 %p) +; CHECK: %val.entry = add i32 %p, 1 +; CHECK-NEXT: store i32 %val.entry, i32* %val.entry.reg2mem +; CHECK: invoke void @might_throw() +; +; CHECK: landingpad +; CHECK: indirectbr i8* {{.*}}, [label %catchit.split] +; +; CHECK: catchit.split: +; CHECK: load i32, i32* %val.lpad.reg2mem +; CHECK: br label %ret +; +; CHECK: ret: +; CHECK: %rv = phi i32 [ {{.*}}, %entry ], [ {{.*}}, %catchit.split ] +; CHECK: ret i32 + +; CHECK-LABEL: define internal i8* @liveout_catch.catch(i8*, i8*) +; CHECK: %[[val:[^ ]*]] = load i32, i32* +; CHECK-NEXT: %[[val_lpad:[^ ]*]] = add i32 %[[val]], 1 +; CHECK-NEXT: store i32 %[[val_lpad]], i32* +; CHECK: ret i8* blockaddress(@liveout_catch, %catchit.split) diff --git a/test/CodeGen/WinEH/cppeh-frame-vars.ll b/test/CodeGen/WinEH/cppeh-frame-vars.ll new file mode 100644 index 000000000000..eeda4319a6e6 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-frame-vars.ll @@ -0,0 +1,272 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; struct SomeData { +; int a; +; int b; +; }; +; +; void may_throw(); +; void does_not_throw(int i); +; void dump(int *, int, SomeData&); +; +; void test() { +; int NumExceptions = 0; +; int ExceptionVal[10]; +; SomeData Data = { 0, 0 }; +; +; for (int i = 0; i < 10; ++i) { +; try { +; may_throw(); +; Data.a += i; +; } +; catch (int e) { +; ExceptionVal[NumExceptions] = e; +; ++NumExceptions; +; if (e == i) +; Data.b += e; +; else +; Data.a += e; +; } +; does_not_throw(NumExceptions); +; } +; dump(ExceptionVal, NumExceptions, Data); +; } + +; ModuleID = 'cppeh-frame-vars.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%struct.SomeData = type { i32, i32 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; The function entry should be rewritten like this. +; CHECK: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: [[NUMEXCEPTIONS_PTR:\%.+]] = alloca i32, align 4 +; CHECK: [[EXCEPTIONVAL_PTR:\%.+]] = alloca [10 x i32], align 16 +; CHECK: [[DATA_PTR:\%.+]] = alloca %struct.SomeData, align 4 +; CHECK: [[I_PTR:\%.+]] = alloca i32, align 4 +; CHECK: [[E_PTR:\%.+]] = alloca i32, align 4 +; CHECK: store i32 0, i32* [[NUMEXCEPTIONS_PTR]], align 4 +; CHECK: [[TMP:\%.+]] = bitcast %struct.SomeData* [[DATA_PTR]] to i8* +; CHECK: call void @llvm.memset(i8* [[TMP]], i8 0, i64 8, i32 4, i1 false) +; CHECK: store i32 0, i32* [[I_PTR]], align 4 +; CHECK: call void (...) @llvm.frameescape(i32* [[E_PTR]], i32* [[NUMEXCEPTIONS_PTR]], [10 x i32]* [[EXCEPTIONVAL_PTR]], i32* [[I_PTR]], %struct.SomeData* [[DATA_PTR]]) +; CHECK: br label %for.cond + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %NumExceptions = alloca i32, align 4 + %ExceptionVal = alloca [10 x i32], align 16 + %Data = alloca %struct.SomeData, align 4 + %i = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %e = alloca i32, align 4 + store i32 0, i32* %NumExceptions, align 4 + %tmp = bitcast %struct.SomeData* %Data to i8* + call void @llvm.memset(i8* %tmp, i8 0, i64 8, i32 4, i1 false) + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %tmp1 = load i32, i32* %i, align 4 + %cmp = icmp slt i32 %tmp1, 10 + br i1 %cmp, label %for.body, label %for.end + +; CHECK: for.body: +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +for.body: ; preds = %for.cond + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %for.body + %tmp2 = load i32, i32* %i, align 4 + %a = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 0 + %tmp3 = load i32, i32* %a, align 4 + %add = add nsw i32 %tmp3, %tmp2 + store i32 %add, i32* %a, align 4 + br label %try.cont + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %for.body +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont] + +lpad: ; preds = %for.body + %tmp4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %tmp5 = extractvalue { i8*, i32 } %tmp4, 0 + store i8* %tmp5, i8** %exn.slot + %tmp6 = extractvalue { i8*, i32 } %tmp4, 1 + store i32 %tmp6, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: + +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %tmp7 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #1 + %matches = icmp eq i32 %sel, %tmp7 + br i1 %matches, label %catch, label %eh.resume + +; CHECK-NOT: catch: + +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + %e.i8 = bitcast i32* %e to i8* + call void @llvm.eh.begincatch(i8* %exn, i8* %e.i8) #1 + %tmp11 = load i32, i32* %e, align 4 + %tmp12 = load i32, i32* %NumExceptions, align 4 + %idxprom = sext i32 %tmp12 to i64 + %arrayidx = getelementptr inbounds [10 x i32], [10 x i32]* %ExceptionVal, i32 0, i64 %idxprom + store i32 %tmp11, i32* %arrayidx, align 4 + %tmp13 = load i32, i32* %NumExceptions, align 4 + %inc = add nsw i32 %tmp13, 1 + store i32 %inc, i32* %NumExceptions, align 4 + %tmp14 = load i32, i32* %e, align 4 + %tmp15 = load i32, i32* %i, align 4 + %cmp1 = icmp eq i32 %tmp14, %tmp15 + br i1 %cmp1, label %if.then, label %if.else + +; CHECK-NOT: if.then: + +if.then: ; preds = %catch + %tmp16 = load i32, i32* %e, align 4 + %b = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 1 + %tmp17 = load i32, i32* %b, align 4 + %add2 = add nsw i32 %tmp17, %tmp16 + store i32 %add2, i32* %b, align 4 + br label %if.end + +; CHECK-NOT: if.else: + +if.else: ; preds = %catch + %tmp18 = load i32, i32* %e, align 4 + %a3 = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 0 + %tmp19 = load i32, i32* %a3, align 4 + %add4 = add nsw i32 %tmp19, %tmp18 + store i32 %add4, i32* %a3, align 4 + br label %if.end + +; CHECK-NOT: if.end: + +if.end: ; preds = %if.else, %if.then + call void @llvm.eh.endcatch() #1 + br label %try.cont + +try.cont: ; preds = %if.end, %invoke.cont + %tmp20 = load i32, i32* %NumExceptions, align 4 + call void @"\01?does_not_throw@@YAXH@Z"(i32 %tmp20) + br label %for.inc + +for.inc: ; preds = %try.cont + %tmp21 = load i32, i32* %i, align 4 + %inc5 = add nsw i32 %tmp21, 1 + store i32 %inc5, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + %tmp22 = load i32, i32* %NumExceptions, align 4 + %arraydecay = getelementptr inbounds [10 x i32], [10 x i32]* %ExceptionVal, i32 0, i32 0 + call void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32* %arraydecay, i32 %tmp22, %struct.SomeData* dereferenceable(8) %Data) + ret void + +; CHECK-NOT: eh.resume: + +eh.resume: ; preds = %catch.dispatch + %exn6 = load i8*, i8** %exn.slot + %sel7 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn6, 0 + %lpad.val8 = insertvalue { i8*, i32 } %lpad.val, i32 %sel7, 1 + resume { i8*, i32 } %lpad.val8 + +; CHECK: } +} + +; The following catch handler should be outlined. +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_E:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[E_PTR1:\%.+]] = bitcast i8* [[RECOVER_E]] to i32* +; CHECK: [[RECOVER_NUMEXCEPTIONS:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[NUMEXCEPTIONS_PTR1:\%.+]] = bitcast i8* [[RECOVER_NUMEXCEPTIONS]] to i32* +; CHECK: [[RECOVER_EXCEPTIONVAL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: [[EXCEPTIONVAL_PTR1:\%.+]] = bitcast i8* [[RECOVER_EXCEPTIONVAL]] to [10 x i32]* +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) +; CHECK: [[I_PTR1:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[RECOVER_DATA:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) +; CHECK: [[DATA_PTR1:\%.+]] = bitcast i8* [[RECOVER_DATA]] to %struct.SomeData* +; CHECK: [[TMP:\%.+]] = load i32, i32* [[E_PTR1]], align 4 +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_PTR]], align 4 +; CHECK: [[IDXPROM:\%.+]] = sext i32 [[TMP1]] to i64 +; CHECK: [[ARRAYIDX:\%.+]] = getelementptr inbounds [10 x i32], [10 x i32]* [[EXCEPTIONVAL_PTR1]], i32 0, i64 [[IDXPROM]] +; CHECK: store i32 [[TMP]], i32* [[ARRAYIDX]], align 4 +; CHECK: [[TMP2:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_PTR1]], align 4 +; CHECK: [[INC:\%.+]] = add nsw i32 [[TMP2]], 1 +; CHECK: store i32 [[INC]], i32* [[NUMEXCEPTIONS_PTR]], align 4 +; CHECK: [[TMP3:\%.+]] = load i32, i32* [[E_PTR1]], align 4 +; CHECK: [[TMP4:\%.+]] = load i32, i32* [[I_PTR1]], align 4 +; CHECK: [[CMP:\%.+]] = icmp eq i32 [[TMP3]], [[TMP4]] +; CHECK: br i1 [[CMP]], label %if.then, label %if.else +; +; CHECK: if.then: ; preds = %entry +; CHECK: [[TMP5:\%.+]] = load i32, i32* [[E_PTR1]], align 4 +; CHECK: [[B_PTR:\%.+]] = getelementptr inbounds %struct.SomeData, %struct.SomeData* [[DATA_PTR1]], i32 0, i32 1 +; CHECK: [[TMP6:\%.+]] = load i32, i32* [[B_PTR]], align 4 +; CHECK: %add2 = add nsw i32 [[TMP6]], [[TMP5]] +; CHECK: store i32 [[ADD:\%.+]], i32* [[B_PTR]], align 4 +; CHECK: br label %if.end +; +; CHECK: if.else: ; preds = %entry +; CHECK: [[TMP7:\%.+]] = load i32, i32* %e, align 4 +; CHECK: [[A3:\%.+]] = getelementptr inbounds %struct.SomeData, %struct.SomeData* %Data, i32 0, i32 0 +; CHECK: [[TMP8:\%.+]] = load i32, i32* %a3, align 4 +; CHECK: [[ADD1:\%.+]] = add nsw i32 [[TMP8]], [[TMP7]] +; CHECK: store i32 [[ADD1]], i32* [[A3]], align 4 +; CHECK: br label %if.end +; +; CHECK: if.end: ; preds = %if.else, %if.then +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; CHECK: } + + +; Function Attrs: nounwind +declare void @llvm.memset(i8* nocapture, i8, i64, i32, i1) #1 + +declare void @"\01?may_throw@@YAXXZ"() #2 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +declare void @llvm.eh.begincatch(i8*, i8*) + +declare void @llvm.eh.endcatch() + +declare void @"\01?does_not_throw@@YAXH@Z"(i32) #2 + +declare void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32*, i32, %struct.SomeData* dereferenceable(8)) #2 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 228868)"} diff --git a/test/CodeGen/WinEH/cppeh-inalloca.ll b/test/CodeGen/WinEH/cppeh-inalloca.ll new file mode 100644 index 000000000000..13471b8661a3 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-inalloca.ll @@ -0,0 +1,194 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is built from the following code: +; struct A { +; A(int a); +; A(const A &o); +; ~A(); +; int a; +; }; +; +; void may_throw(); +; +; int test(A a) { +; try { +; may_throw(); +; } +; catch (int e) { +; return a.a + e; +; } +; return 0; +; } +; +; The test was built for a 32-bit Windows target and then the reference to +; the inalloca instruction was manually sunk into the landingpad. + +; ModuleID = 'cppeh-inalloca.cpp' + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%struct.A = type { i32 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; The function entry should be rewritten like this. +; CHECK: define i32 @"\01?test@@YAHUA@@@Z"(<{ %struct.A }>* inalloca) +; CHECK: entry: +; CHECK: [[TMP_REGMEM:\%.+]] = alloca <{ %struct.A }>* +; CHECK: store <{ %struct.A }>* %0, <{ %struct.A }>** [[TMP_REGMEM]] +; CHECK: [[RETVAL:\%.+]] = alloca i32, align 4 +; CHECK: [[E_PTR:\%.+]] = alloca i32, align 4 +; CHECK: [[CLEANUP_SLOT:\%.+]] = alloca i32 +; CHECK: call void (...) @llvm.frameescape(i32* %e, <{ %struct.A }>** [[TMP_REGMEM]], i32* [[RETVAL]], i32* [[CLEANUP_SLOT]]) +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +define i32 @"\01?test@@YAHUA@@@Z"(<{ %struct.A }>* inalloca) #0 { +entry: + %retval = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %e = alloca i32, align 4 + %cleanup.dest.slot = alloca i32 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER:\%recover.*]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAHUA@@@Z.catch", i32 0, void (i8*, i8*)* @"\01?test@@YAHUA@@@Z.cleanup") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %cleanup] + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %2 = extractvalue { i8*, i32 } %1, 0 + store i8* %2, i8** %exn.slot + %3 = extractvalue { i8*, i32 } %1, 1 + store i32 %3, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: + +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches = icmp eq i32 %sel, %4 + br i1 %matches, label %catch, label %ehcleanup + +; CHECK-NOT: catch: + +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + %e.i8 = bitcast i32* %e to i8* + call void @llvm.eh.begincatch(i8* %exn, i8* %e.i8) #3 + %a = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 + %a1 = getelementptr inbounds %struct.A, %struct.A* %a, i32 0, i32 0 + %tmp8 = load i32, i32* %a1, align 4 + %tmp9 = load i32, i32* %e, align 4 + %add = add nsw i32 %tmp8, %tmp9 + store i32 %add, i32* %retval + store i32 1, i32* %cleanup.dest.slot + call void @llvm.eh.endcatch() #3 + br label %cleanup + +try.cont: ; preds = %invoke.cont + store i32 0, i32* %retval + store i32 1, i32* %cleanup.dest.slot + br label %cleanup + +; The cleanup block should be re-written like this. +; CHECK: cleanup:{{[ ]+}}; preds = %[[LPAD_LABEL]], %try.cont +; CHECK: %a2 = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 +; CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %a2) +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[RETVAL]] +; CHECK: ret i32 [[TMP1]] + +cleanup: ; preds = %try.cont, %catch + %a2 = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 + call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %a2) #3 + %tmp10 = load i32, i32* %retval + ret i32 %tmp10 + +; CHECK-NOT: ehcleanup: + +ehcleanup: ; preds = %catch.dispatch + %a3 = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* %0, i32 0, i32 0 + call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* %a3) #3 + br label %eh.resume + +; CHECK-NOT: eh.resume: + +eh.resume: ; preds = %ehcleanup + %exn2 = load i8*, i8** %exn.slot + %sel3 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn2, 0 + %lpad.val4 = insertvalue { i8*, i32 } %lpad.val, i32 %sel3, 1 + resume { i8*, i32 } %lpad.val4 + +; CHECK: } +} + +; The following catch handler should be outlined. +; CHECK: define internal i8* @"\01?test@@YAHUA@@@Z.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_E:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 0) +; CHECK: [[E_PTR:\%.+]] = bitcast i8* [[RECOVER_E]] to i32* +; CHECK: [[RECOVER_EH_TEMP:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 1) +; CHECK: [[EH_TEMP:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP]] to <{ %struct.A }>** +; CHECK: [[RECOVER_RETVAL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 2) +; CHECK: [[RETVAL1:\%.+]] = bitcast i8* [[RECOVER_RETVAL]] to i32* +; CHECK: [[RECOVER_CLEANUPSLOT:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 3) +; CHECK: [[CLEANUPSLOT1:\%.+]] = bitcast i8* [[RECOVER_CLEANUPSLOT]] to i32* +; CHECK: [[E_I8PTR:\%.+]] = bitcast i32* [[E_PTR]] to i8* +; CHECK: [[TMP_RELOAD:\%.+]] = load <{ %struct.A }>*, <{ %struct.A }>** [[EH_TEMP]] +; CHECK: [[RECOVER_A:\%.+]] = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* [[TMP_RELOAD]], i32 0, i32 0 +; CHECK: [[A1:\%.+]] = getelementptr inbounds %struct.A, %struct.A* [[RECOVER_A]], i32 0, i32 0 +; CHECK: [[TMP2:\%.+]] = load i32, i32* [[A1]], align 4 +; CHECK: [[TMP3:\%.+]] = load i32, i32* [[E_PTR]], align 4 +; CHECK: [[ADD:\%.+]] = add nsw i32 [[TMP2]], [[TMP3]] +; CHECK: store i32 [[ADD]], i32* [[RETVAL1]] +; CHECK: store i32 1, i32* [[CLEANUPSLOT1]] +; CHECK: ret i8* blockaddress(@"\01?test@@YAHUA@@@Z", %cleanup) +; CHECK: } + +; The following cleanup handler should be outlined. +; CHECK: define internal void @"\01?test@@YAHUA@@@Z.cleanup"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_EH_TEMP1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (i32 (<{ %struct.A }>*)* @"\01?test@@YAHUA@@@Z" to i8*), i8* %1, i32 1) +; CHECK: [[EH_TEMP1:\%.+]] = bitcast i8* [[RECOVER_EH_TEMP]] to <{ %struct.A }>** +; CHECK: [[TMP_RELOAD1:\%.+]] = load <{ %struct.A }>*, <{ %struct.A }>** [[EH_TEMP1]] +; CHECK: [[A3:\%.+]] = getelementptr inbounds <{ %struct.A }>, <{ %struct.A }>* [[TMP_RELOAD1]], i32 0, i32 0 +; CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A* [[A3]]) +; CHECK: ret void +; CHECK: } + +declare void @"\01?may_throw@@YAXXZ"() #0 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #1 + +declare void @llvm.eh.begincatch(i8*, i8*) + +declare void @llvm.eh.endcatch() + +; Function Attrs: nounwind +declare x86_thiscallcc void @"\01??1A@@QAE@XZ"(%struct.A*) #2 + +attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 228868)"} diff --git a/test/CodeGen/WinEH/cppeh-min-unwind.ll b/test/CodeGen/WinEH/cppeh-min-unwind.ll new file mode 100644 index 000000000000..3fffa47a081b --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-min-unwind.ll @@ -0,0 +1,99 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test was generated from the following source: +; +; class SomeClass { +; public: +; SomeClass(); +; ~SomeClass(); +; }; +; +; void test() { +; SomeClass obj; +; may_throw(); +; } + + +; ModuleID = 'min-unwind.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%class.SomeClass = type { [28 x i32] } + +; The function entry should be rewritten like this. +; CHECK: define void @_Z4testv() +; CHECK: entry: +; CHECK: [[OBJ_PTR:\%.+]] = alloca %class.SomeClass, align 4 +; CHECK: call void @_ZN9SomeClassC1Ev(%class.SomeClass* [[OBJ_PTR]]) +; CHECK: call void (...) @llvm.frameescape(%class.SomeClass* [[OBJ_PTR]]) +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %obj = alloca %class.SomeClass, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + call void @_ZN9SomeClassC1Ev(%class.SomeClass* %obj) + invoke void @_Z9may_throwv() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) + ret void + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @_Z4testv.cleanup) +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [] + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %tmp1 = extractvalue { i8*, i32 } %tmp, 0 + store i8* %tmp1, i8** %exn.slot + %tmp2 = extractvalue { i8*, i32 } %tmp, 1 + store i32 %tmp2, i32* %ehselector.slot + call void @_ZN9SomeClassD1Ev(%class.SomeClass* %obj) + br label %eh.resume + +; CHECK-NOT: eh.resume: + +eh.resume: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + %sel = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0 + %lpad.val2 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val2 + +; CHECK: } +} + +; This cleanup handler should be outlined. +; CHECK: define internal void @_Z4testv.cleanup(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_OBJ:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 0) +; CHECK: [[OBJ_PTR1:\%.+]] = bitcast i8* [[RECOVER_OBJ]] to %class.SomeClass* +; CHECK: call void @_ZN9SomeClassD1Ev(%class.SomeClass* [[OBJ_PTR1]]) +; CHECK: ret void +; CHECK: } + +declare void @_ZN9SomeClassC1Ev(%class.SomeClass*) #1 + +declare void @_Z9may_throwv() #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @_ZN9SomeClassD1Ev(%class.SomeClass*) #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noinline noreturn nounwind } +attributes #3 = { noreturn nounwind } +attributes #4 = { nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 226027)"} diff --git a/test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll b/test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll new file mode 100644 index 000000000000..52f613276d54 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-mixed-catch-and-cleanup.ll @@ -0,0 +1,106 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; void test() +; { +; try { +; Obj o; +; may_throw(); +; } catch (...) { +; } +; } +; +; The purpose of this test is to verify that we create separate catch and +; cleanup handlers. When compiling for the C++ 11 standard, this isn't +; strictly necessary, since calling the destructor from the catch handler +; would be logically equivalent to calling it from a cleanup handler. +; However, if the -std=c++98 option is used, an exception in the cleanup +; code should terminate the process (the MSVCRT runtime will do that) but +; if the destructor is called from the catch handler, it wouldn't terminate +; the process + + +; ModuleID = 'cppeh-mixed-catch-and-cleanup.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%class.Obj = type { i8 } + +; This just verifies that the function was processed by WinEHPrepare. +; +; CHECK-LABEL: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape +; CHECK: } + +; Function Attrs: nounwind uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %o = alloca %class.Obj, align 1 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + call void @"\01??1Obj@@QEAA@XZ"(%class.Obj* %o) #3 + br label %try.cont + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + call void @"\01??1Obj@@QEAA@XZ"(%class.Obj* %o) #3 + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #3 + call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %catch, %invoke.cont + ret void +} + +; Verify that a cleanup handler was created and that it calls ~Obj(). +; CHECK-LABEL: define internal void @"\01?test@@YAXXZ.cleanup"(i8*, i8*) +; CHECK: entry: +; CHECK: @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: call void @"\01??1Obj@@QEAA@XZ" +; CHECK: ret void +; CHECK: } + +; Verify that a catch handler was created and that it does not call ~Obj(). +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK-NOT: call void @"\01??1Obj@@QEAA@XZ" +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; CHECK: } + + + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind +declare void @"\01??1Obj@@QEAA@XZ"(%class.Obj*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 235779) (llvm/trunk 235769)"} diff --git a/test/CodeGen/WinEH/cppeh-multi-catch.ll b/test/CodeGen/WinEH/cppeh-multi-catch.ll new file mode 100644 index 000000000000..28340c60ad1e --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-multi-catch.ll @@ -0,0 +1,226 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; void test() +; { +; try { +; may_throw(); +; } catch (int i) { +; handle_int(i); +; } catch (long long ll) { +; handle_long_long(ll); +; } catch (SomeClass &obj) { +; handle_obj(&obj); +; } catch (...) { +; handle_exception(); +; } +; } +; +; The catch handlers were edited to insert 'ret void' after the endcatch call. + +; ModuleID = 'catch-with-type.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.HandlerMapEntry = type { i32, i32 } +%rtti.TypeDescriptor3 = type { i8**, i8*, [4 x i8] } +%rtti.TypeDescriptor15 = type { i8**, i8*, [16 x i8] } +%class.SomeClass = type { i8 } + +$"\01??_R0H@8" = comdat any + +$"\01??_R0_J@8" = comdat any + +$"\01??_R0?AVSomeClass@@@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external constant i8 +@llvm.eh.handlermapentry.H = private unnamed_addr constant %eh.HandlerMapEntry { i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section "llvm.metadata" +@"\01??_R0_J@8" = linkonce_odr global %rtti.TypeDescriptor3 { i8** @"\01??_7type_info@@6B@", i8* null, [4 x i8] c"._J\00" }, comdat +@llvm.eh.handlermapentry._J = private unnamed_addr constant %eh.HandlerMapEntry { i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor3* @"\01??_R0_J@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section "llvm.metadata" +@"\01??_R0?AVSomeClass@@@8" = linkonce_odr global %rtti.TypeDescriptor15 { i8** @"\01??_7type_info@@6B@", i8* null, [16 x i8] c".?AVSomeClass@@\00" }, comdat +@"llvm.eh.handlermapentry.reference.?AVSomeClass@@" = private unnamed_addr constant %eh.HandlerMapEntry { i32 8, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor15* @"\01??_R0?AVSomeClass@@@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section "llvm.metadata" + + +; CHECK: define void @"\01?test@@YAXXZ"() #0 { +; CHECK: entry: +; CHECK: [[OBJ_PTR:\%.+]] = alloca %class.SomeClass*, align 8 +; CHECK: [[LL_PTR:\%.+]] = alloca i64, align 8 +; CHECK: [[I_PTR:\%.+]] = alloca i32, align 4 +; CHECK: call void (...) @llvm.frameescape(i32* [[I_PTR]], i64* [[LL_PTR]], %class.SomeClass** [[OBJ_PTR]]) +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %obj = alloca %class.SomeClass*, align 8 + %ll = alloca i64, align 8 + %i = alloca i32, align 4 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont + +; CHECK: [[LPAD_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry.H +; CHECK-NEXT: catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry._J +; CHECK-NEXT: catch %eh.HandlerMapEntry* @"llvm.eh.handlermapentry.reference.?AVSomeClass@@" +; CHECK-NEXT: catch i8* null +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions( +; CHECK-SAME: i32 1, i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry.H to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch", +; CHECK-SAME: i32 1, i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry._J to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1", +; CHECK-SAME: i32 1, i8* bitcast (%eh.HandlerMapEntry* @"llvm.eh.handlermapentry.reference.?AVSomeClass@@" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.2", +; CHECK-SAME: i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.3") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %ret] + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry.H + catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry._J + catch %eh.HandlerMapEntry* @"llvm.eh.handlermapentry.reference.?AVSomeClass@@" + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry.H to i8*)) #3 + %matches = icmp eq i32 %sel, %3 + br i1 %matches, label %catch14, label %catch.fallthrough + +ret: + ret void + +; CHECK-NOT: catch14: +; CHECK: ret: +; CHECK-NEXT: ret void +catch14: ; preds = %catch.dispatch + %exn15 = load i8*, i8** %exn.slot + %4 = bitcast i32* %i to i8* + call void @llvm.eh.begincatch(i8* %exn15, i8* %4) #3 + %5 = load i32, i32* %i, align 4 + call void @"\01?handle_int@@YAXH@Z"(i32 %5) + call void @llvm.eh.endcatch() #3 + br label %ret + +try.cont: ; preds = %invoke.cont + br label %ret + +; CHECK-NOT: catch.fallthrough: +catch.fallthrough: ; preds = %catch.dispatch + %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry._J to i8*)) #3 + %matches1 = icmp eq i32 %sel, %6 + br i1 %matches1, label %catch10, label %catch.fallthrough2 + +; CHECK-NOT: catch10: +catch10: ; preds = %catch.fallthrough + %exn11 = load i8*, i8** %exn.slot + %7 = bitcast i64* %ll to i8* + call void @llvm.eh.begincatch(i8* %exn11, i8* %7) #3 + %8 = load i64, i64* %ll, align 8 + call void @"\01?handle_long_long@@YAX_J@Z"(i64 %8) + call void @llvm.eh.endcatch() #3 + br label %ret + +; CHECK-NOT: catch.fallthrough2: +catch.fallthrough2: ; preds = %catch.fallthrough + %9 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.HandlerMapEntry* @"llvm.eh.handlermapentry.reference.?AVSomeClass@@" to i8*)) #3 + %matches3 = icmp eq i32 %sel, %9 + br i1 %matches3, label %catch6, label %catch + +; CHECK-NOT: catch6: +catch6: ; preds = %catch.fallthrough2 + %exn7 = load i8*, i8** %exn.slot + %10 = bitcast %class.SomeClass** %obj to i8* + call void @llvm.eh.begincatch(i8* %exn7, i8* %10) #3 + %11 = load %class.SomeClass*, %class.SomeClass** %obj, align 8 + call void @"\01?handle_obj@@YAXPEAVSomeClass@@@Z"(%class.SomeClass* %11) + call void @llvm.eh.endcatch() #3 + br label %ret + +; CHECK-NOT: catch: +catch: ; preds = %catch.fallthrough2 + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #3 + call void @"\01?handle_exception@@YAXXZ"() call void @llvm.eh.endcatch() #3 + br label %ret +; CHECK: } +} + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[I_PTR:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[I_PTR]], align 4 +; CHECK: call void @"\01?handle_int@@YAXH@Z"(i32 [[TMP1]]) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) +; CHECK: } + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch.1"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_LL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[LL_PTR:\%.+]] = bitcast i8* [[RECOVER_LL]] to i64* +; CHECK: [[TMP2:\%.+]] = load i64, i64* [[LL_PTR]], align 8 +; CHECK: call void @"\01?handle_long_long@@YAX_J@Z"(i64 [[TMP2]]) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) +; CHECK: } + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch.2"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_OBJ:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: [[OBJ_PTR:\%.+]] = bitcast i8* [[RECOVER_OBJ]] to %class.SomeClass** +; CHECK: [[TMP3:\%.+]] = load %class.SomeClass*, %class.SomeClass** [[OBJ_PTR]], align 8 +; CHECK: call void @"\01?handle_obj@@YAXPEAVSomeClass@@@Z"(%class.SomeClass* [[TMP3]]) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) +; CHECK: } + +; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch.3"(i8*, i8*) +; CHECK: entry: +; CHECK: call void @"\01?handle_exception@@YAXXZ"() +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %ret) +; CHECK: } + + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +declare void @"\01?handle_exception@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +declare void @"\01?handle_obj@@YAXPEAVSomeClass@@@Z"(%class.SomeClass*) #1 + +declare void @"\01?handle_long_long@@YAX_J@Z"(i64) #1 + +declare void @"\01?handle_int@@YAXH@Z"(i32) #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 233155) (llvm/trunk 233153)"} diff --git a/test/CodeGen/WinEH/cppeh-nested-1.ll b/test/CodeGen/WinEH/cppeh-nested-1.ll new file mode 100644 index 000000000000..2b13510c5745 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-nested-1.ll @@ -0,0 +1,194 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +;void test() +;{ +; try { +; try { +; may_throw(); +; } catch (int i) { +; handle_int(i); +; } +; } catch (float f) { +; handle_float(f); +; } +; done(); +;} + +; ModuleID = 'cppeh-nested-1.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } + +$"\01??_R0M@8" = comdat any + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0M@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".M\00" }, comdat +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; CHECK: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: %i = alloca i32, align 4 +; CHECK: %f = alloca float, align 4 +; CHECK: call void (...) @llvm.frameescape(float* %f, i32* %i) +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %i = alloca i32, align 4 + %f = alloca float, align 4 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont + +; CHECK: [[LPAD_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) +; CHECK: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1", i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK: indirectbr i8* [[RECOVER]], [label %try.cont, label %try.cont10] + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches = icmp eq i32 %sel, %3 + br i1 %matches, label %catch, label %catch.dispatch3 + +; CHECK-NOT: catch: +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + %4 = bitcast i32* %i to i8* + call void @llvm.eh.begincatch(i8* %exn, i8* %4) #3 + %5 = load i32, i32* %i, align 4 + invoke void @"\01?handle_int@@YAXH@Z"(i32 %5) + to label %invoke.cont2 unwind label %lpad1 + +; CHECK-NOT: invoke.cont2: +invoke.cont2: ; preds = %catch + call void @llvm.eh.endcatch() #3 + br label %try.cont + +try.cont: ; preds = %invoke.cont2, %invoke.cont + br label %try.cont10 + +; CHECK-NOT: lpad1: +lpad1: ; preds = %catch + %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) + %7 = extractvalue { i8*, i32 } %6, 0 + store i8* %7, i8** %exn.slot + %8 = extractvalue { i8*, i32 } %6, 1 + store i32 %8, i32* %ehselector.slot + call void @llvm.eh.endcatch() #3 + br label %catch.dispatch3 + +; CHECK-NOT: catch.dispatch3: +catch.dispatch3: ; preds = %lpad1, %catch.dispatch + %sel4 = load i32, i32* %ehselector.slot + %9 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*)) #3 + %matches5 = icmp eq i32 %sel4, %9 + br i1 %matches5, label %catch6, label %eh.resume + +; CHECK-NOT: catch6: +catch6: ; preds = %catch.dispatch3 + %exn7 = load i8*, i8** %exn.slot + %10 = bitcast float* %f to i8* + call void @llvm.eh.begincatch(i8* %exn7, i8* %10) #3 + %11 = load float, float* %f, align 4 + call void @"\01?handle_float@@YAXM@Z"(float %11) + call void @llvm.eh.endcatch() #3 + br label %try.cont10 + +try.cont10: ; preds = %catch6, %try.cont + call void @"\01?done@@YAXXZ"() + ret void + +; CHECK-NOT: eh.resume: +eh.resume: ; %catch.dispatch3 + %exn11 = load i8*, i8** %exn.slot + %sel12 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn11, 0 + %lpad.val13 = insertvalue { i8*, i32 } %lpad.val, i32 %sel12, 1 + resume { i8*, i32 } %lpad.val13 +; CHECK: } +} + +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_F1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[F_PTR1:\%.+]] = bitcast i8* [[RECOVER_F1]] to float* +; CHECK: [[TMP2:\%.+]] = load float, float* [[F_PTR1]], align 4 +; CHECK: call void @"\01?handle_float@@YAXM@Z"(float [[TMP2]]) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont10) +; CHECK: } + +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch.1"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[I_PTR:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[I_PTR]], align 4 +; CHECK: invoke void @"\01?handle_int@@YAXH@Z"(i32 [[TMP1]]) +; CHECK: to label %invoke.cont2 unwind label %[[LPAD1_LABEL:lpad[0-9]*]] +; +; CHECK: invoke.cont2: +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) +; +; CHECK: [[LPAD1_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: [[LPAD1_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) +; CHECK: [[RECOVER1:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK: indirectbr i8* [[RECOVER1]], [] +; +; CHECK: } + + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +declare void @"\01?handle_int@@YAXH@Z"(i32) #1 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +declare void @"\01?handle_float@@YAXM@Z"(float) #1 + +declare void @"\01?done@@YAXXZ"() #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 232069) (llvm/trunk 232070)"} diff --git a/test/CodeGen/WinEH/cppeh-nested-2.ll b/test/CodeGen/WinEH/cppeh-nested-2.ll new file mode 100644 index 000000000000..f12f3dbed085 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-nested-2.ll @@ -0,0 +1,324 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; class Inner { +; public: +; Inner(); +; ~Inner(); +; }; +; class Outer { +; public: +; Outer(); +; ~Outer(); +; }; +; void test() { +; try { +; Outer outer; +; try { +; Inner inner; +; may_throw(); +; } catch (int i) { +; handle_int(i); +; } +; } catch (float f) { +; handle_float(f); +; } +; done(); +; } + +; ModuleID = 'nested-2.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%class.Outer = type { i8 } +%class.Inner = type { i8 } + +@_ZTIf = external constant i8* +@_ZTIi = external constant i8* + +; The function entry should be rewritten like this. +; CHECK: define void @_Z4testv() +; CHECK: entry: +; CHECK: %outer = alloca %class.Outer, align 1 +; CHECK: %inner = alloca %class.Inner, align 1 +; CHECK: %i = alloca i32, align 4 +; CHECK: %f = alloca float, align 4 +; CHECK: call void (...) @llvm.frameescape(float* %f, i32* %i, %class.Outer* %outer, %class.Inner* %inner) +; CHECK: invoke void @_ZN5OuterC1Ev(%class.Outer* %outer) +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @_Z4testv() #0 { +entry: + %outer = alloca %class.Outer, align 1 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %inner = alloca %class.Inner, align 1 + %i = alloca i32, align 4 + %f = alloca float, align 4 + invoke void @_ZN5OuterC1Ev(%class.Outer* %outer) + to label %invoke.cont unwind label %lpad + +; CHECK: invoke.cont: +; CHECK: invoke void @_ZN5InnerC1Ev(%class.Inner* %inner) +; CHECK: to label %invoke.cont2 unwind label %[[LPAD1_LABEL:lpad[0-9]*]] + +invoke.cont: ; preds = %entry + invoke void @_ZN5InnerC1Ev(%class.Inner* %inner) + to label %invoke.cont2 unwind label %lpad1 + +; CHECK: invoke.cont2: +; CHECK: invoke void @_Z9may_throwv() +; CHECK: to label %invoke.cont4 unwind label %[[LPAD3_LABEL:lpad[0-9]*]] + +invoke.cont2: ; preds = %invoke.cont + invoke void @_Z9may_throwv() + to label %invoke.cont4 unwind label %lpad3 + +; CHECK: invoke.cont4: +; CHECK: invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) +; CHECK: to label %invoke.cont5 unwind label %[[LPAD1_LABEL]] + +invoke.cont4: ; preds = %invoke.cont2 + invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) + to label %invoke.cont5 unwind label %lpad1 + +; CHECK: invoke.cont5: +; CHECK: br label %try.cont + +invoke.cont5: ; preds = %invoke.cont4 + br label %try.cont + +; CHECK: [[LPAD_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (i8** @_ZTIf to i8*), i32 0, i8* (i8*, i8*)* @_Z4testv.catch) +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont19] + +lpad: ; preds = %try.cont, %entry + %tmp = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %tmp1 = extractvalue { i8*, i32 } %tmp, 0 + store i8* %tmp1, i8** %exn.slot + %tmp2 = extractvalue { i8*, i32 } %tmp, 1 + store i32 %tmp2, i32* %ehselector.slot + br label %catch.dispatch11 + +; CHECK: [[LPAD1_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIi to i8*) +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK-NEXT: [[RECOVER1:\%.+]] = call i8* (...) @llvm.eh.actions( +; CHECK-SAME: i32 1, i8* bitcast (i8** @_ZTIi to i8*), i32 1, i8* (i8*, i8*)* @_Z4testv.catch.1, +; CHECK-SAME: i32 0, void (i8*, i8*)* @_Z4testv.cleanup, +; CHECK-SAME: i32 1, i8* bitcast (i8** @_ZTIf to i8*), i32 0, i8* (i8*, i8*)* @_Z4testv.catch) +; CHECK-NEXT: indirectbr i8* [[RECOVER1]], [label %try.cont, label %try.cont19] + +lpad1: ; preds = %invoke.cont4, %invoke.cont + %tmp3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %tmp4 = extractvalue { i8*, i32 } %tmp3, 0 + store i8* %tmp4, i8** %exn.slot + %tmp5 = extractvalue { i8*, i32 } %tmp3, 1 + store i32 %tmp5, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK: [[LPAD3_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIi to i8*) +; CHECK-NEXT: catch i8* bitcast (i8** @_ZTIf to i8*) +; CHECK-NEXT: [[RECOVER3:\%.+]] = call i8* (...) @llvm.eh.actions( +; CHECK-SAME: i32 0, void (i8*, i8*)* @_Z4testv.cleanup.2, +; CHECK-SAME: i32 1, i8* bitcast (i8** @_ZTIi to i8*), i32 1, i8* (i8*, i8*)* @_Z4testv.catch.1, +; CHECK-SAME: i32 0, void (i8*, i8*)* @_Z4testv.cleanup, +; CHECK-SAME: i32 1, i8* bitcast (i8** @_ZTIf to i8*), i32 0, i8* (i8*, i8*)* @_Z4testv.catch) +; CHECK-NEXT: indirectbr i8* [[RECOVER3]], [label %try.cont, label %try.cont19] + +lpad3: ; preds = %invoke.cont2 + %tmp6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIi to i8*) + catch i8* bitcast (i8** @_ZTIf to i8*) + %tmp7 = extractvalue { i8*, i32 } %tmp6, 0 + store i8* %tmp7, i8** %exn.slot + %tmp8 = extractvalue { i8*, i32 } %tmp6, 1 + store i32 %tmp8, i32* %ehselector.slot + call void @_ZN5InnerD1Ev(%class.Inner* %inner) + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: + +catch.dispatch: ; preds = %lpad3, %lpad1 + %sel = load i32, i32* %ehselector.slot + %tmp9 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #4 + %matches = icmp eq i32 %sel, %tmp9 + br i1 %matches, label %catch, label %ehcleanup + +; CHECK-NOT: catch: + +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + %i.i8 = bitcast i32* %i to i8* + call void @llvm.eh.begincatch(i8* %exn, i8* %i.i8) #4 + %tmp13 = load i32, i32* %i, align 4 + invoke void @_Z10handle_inti(i32 %tmp13) + to label %invoke.cont8 unwind label %lpad7 + +; CHECK-NOT: invoke.cont8: + +invoke.cont8: ; preds = %catch + call void @llvm.eh.endcatch() #4 + br label %try.cont + +; CHECK: try.cont: +; CHECK: invoke void @_ZN5OuterD1Ev(%class.Outer* %outer) +; CHECK: to label %invoke.cont9 unwind label %[[LPAD_LABEL]] + +try.cont: ; preds = %invoke.cont8, %invoke.cont5 + invoke void @_ZN5OuterD1Ev(%class.Outer* %outer) + to label %invoke.cont9 unwind label %lpad + +invoke.cont9: ; preds = %try.cont + br label %try.cont19 + +; CHECK-NOT: lpad7: + +lpad7: ; preds = %catch + %tmp14 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch i8* bitcast (i8** @_ZTIf to i8*) + %tmp15 = extractvalue { i8*, i32 } %tmp14, 0 + store i8* %tmp15, i8** %exn.slot + %tmp16 = extractvalue { i8*, i32 } %tmp14, 1 + store i32 %tmp16, i32* %ehselector.slot + call void @llvm.eh.endcatch() #4 + br label %ehcleanup + +; CHECK-NOT: ehcleanup: ; preds = %lpad7, %catch.dispatch + +ehcleanup: ; preds = %lpad7, %catch.dispatch + call void @_ZN5OuterD1Ev(%class.Outer* %outer) + br label %catch.dispatch11 + +; CHECK-NOT: catch.dispatch11: + +catch.dispatch11: ; preds = %ehcleanup, %lpad + %sel12 = load i32, i32* %ehselector.slot + %tmp17 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) #4 + %matches13 = icmp eq i32 %sel12, %tmp17 + br i1 %matches13, label %catch14, label %eh.resume + +; CHECK-NOT: catch14: + +catch14: ; preds = %catch.dispatch11 + %exn15 = load i8*, i8** %exn.slot + %f.i8 = bitcast float* %f to i8* + call void @llvm.eh.begincatch(i8* %exn15, i8* %f.i8) #4 + %tmp21 = load float, float* %f, align 4 + call void @_Z12handle_floatf(float %tmp21) + call void @llvm.eh.endcatch() #4 + br label %try.cont19 + +try.cont19: ; preds = %catch14, %invoke.cont9 + call void @_Z4donev() + ret void + +; CHECK-NOT: eh.resume: + +eh.resume: ; preds = %catch.dispatch11 + %exn20 = load i8*, i8** %exn.slot + %sel21 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn20, 0 + %lpad.val22 = insertvalue { i8*, i32 } %lpad.val, i32 %sel21, 1 + resume { i8*, i32 } %lpad.val22 + +; CHECK: } +} + +; This catch handler should be outlined. +; CHECK: define internal i8* @_Z4testv.catch(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_F:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 0) +; CHECK: [[F_PTR:\%.+]] = bitcast i8* [[RECOVER_F]] to float* +; CHECK: [[TMP:\%.+]] = load float, float* [[F_PTR]], align 4 +; CHECK: call void @_Z12handle_floatf(float [[TMP]]) +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont19) +; CHECK: } + +; This catch handler should be outlined. +; CHECK: define internal i8* @_Z4testv.catch.1(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 1) +; CHECK: [[I_PTR:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[I_PTR]], align 4 +; CHECK: invoke void @_Z10handle_inti(i32 [[TMP1]]) +; CHECK: to label %invoke.cont8 unwind label %[[LPAD7_LABEL:lpad[0-9]*]] +; +; CHECK: invoke.cont8: ; preds = %entry +; CHECK: ret i8* blockaddress(@_Z4testv, %try.cont) +; +; CHECK: [[LPAD7_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: [[LPAD7_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; (FIXME) The nested handler body isn't being populated yet. +; CHECK: } + +; This cleanup handler should be outlined. +; CHECK: define internal void @_Z4testv.cleanup(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_OUTER:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 2) +; CHECK: [[OUTER_PTR:\%.+]] = bitcast i8* [[RECOVER_OUTER]] to %class.Outer* +; CHECK: call void @_ZN5OuterD1Ev(%class.Outer* [[OUTER_PTR]]) +; CHECK: ret void +; CHECK: } + +; This cleanup handler should be outlined. +; CHECK: define internal void @_Z4testv.cleanup.2(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_INNER:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @_Z4testv to i8*), i8* %1, i32 3) +; CHECK: [[INNER_PTR:\%.+]] = bitcast i8* [[RECOVER_INNER]] to %class.Inner* +; CHECK: call void @_ZN5InnerD1Ev(%class.Inner* [[INNER_PTR]]) +; CHECK: ret void +; CHECK: } + + + +declare void @_ZN5OuterC1Ev(%class.Outer*) #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @_ZN5InnerC1Ev(%class.Inner*) #1 + +declare void @_Z9may_throwv() #1 + +declare void @_ZN5InnerD1Ev(%class.Inner*) #1 + +declare void @llvm.eh.begincatch(i8*, i8*) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +declare void @_Z10handle_inti(i32) #1 + +declare void @llvm.eh.endcatch() + +declare void @_ZN5OuterD1Ev(%class.Outer*) #1 + +declare void @_Z12handle_floatf(float) #1 + +declare void @_Z4donev() #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noinline noreturn nounwind } +attributes #3 = { nounwind readnone } +attributes #4 = { nounwind } +attributes #5 = { noreturn nounwind } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 226027)"} diff --git a/test/CodeGen/WinEH/cppeh-nested-3.ll b/test/CodeGen/WinEH/cppeh-nested-3.ll new file mode 100644 index 000000000000..c96abcc6e81c --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-nested-3.ll @@ -0,0 +1,260 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +;void test() +;{ +; try { +; try { +; may_throw(); +; } catch (int i) { +; try { +; may_throw(); +; } +; catch (int j) { +; i = j; +; } +; handle_int(i); +; } +; } catch (float f) { +; handle_float(f); +; } +; done(); +;} + +; ModuleID = 'cppeh-nested-3.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } + +$"\01??_R0M@8" = comdat any + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0M@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".M\00" }, comdat +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; CHECK: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: %i = alloca i32, align 4 +; CHECK: %j = alloca i32, align 4 +; CHECK: %f = alloca float, align 4 +; CHECK: call void (...) @llvm.frameescape(i32* %j, i32* %i, float* %f) +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]*]] + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %i = alloca i32, align 4 + %j = alloca i32, align 4 + %f = alloca float, align 4 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %try.cont10 + +; CHECK: [[LPAD_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) +; CHECK: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.2", i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1") +; CHECK: indirectbr i8* [[RECOVER]], [label %try.cont10, label %try.cont19] + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %catch.dispatch + +; CHECK-NOT: catch.dispatch: +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches = icmp eq i32 %sel, %3 + br i1 %matches, label %catch, label %catch.dispatch11 + +; CHECK-NOT: catch: +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + %4 = bitcast i32* %i to i8* + call void @llvm.eh.begincatch(i8* %exn, i8* %4) #3 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont2 unwind label %lpad1 + +; CHECK-NOT: invoke.cont2: +invoke.cont2: ; preds = %catch + br label %try.cont + +; CHECK-NOT: lpad1: +lpad1: ; preds = %catch + %5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) + %6 = extractvalue { i8*, i32 } %5, 0 + store i8* %6, i8** %exn.slot + %7 = extractvalue { i8*, i32 } %5, 1 + store i32 %7, i32* %ehselector.slot + br label %catch.dispatch3 + +; CHECK-NOT: catch.dispatch3: +catch.dispatch3: ; preds = %lpad1 + %sel4 = load i32, i32* %ehselector.slot + %8 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #3 + %matches5 = icmp eq i32 %sel4, %8 + br i1 %matches5, label %catch6, label %catch.dispatch11 + +; CHECK-NOT: catch6: +catch6: ; preds = %catch.dispatch3 + %exn7 = load i8*, i8** %exn.slot + %9 = bitcast i32* %j to i8* + call void @llvm.eh.begincatch(i8* %exn7, i8* %9) #3 + %10 = load i32, i32* %j, align 4 + store i32 %10, i32* %i, align 4 + call void @llvm.eh.endcatch() #3 + br label %try.cont + +; CHECK-NOT: try.cont: +try.cont: ; preds = %catch6, %invoke.cont2 + %11 = load i32, i32* %i, align 4 + invoke void @"\01?handle_int@@YAXH@Z"(i32 %11) + to label %invoke.cont9 unwind label %lpad8 + +; CHECK-NOT: invoke.cont9: +invoke.cont9: ; preds = %try.cont + call void @llvm.eh.endcatch() #3 + br label %try.cont10 + +try.cont10: ; preds = %invoke.cont9, %invoke.cont + br label %try.cont19 + +; CHECK-NOT: lpad8: +lpad8: ; preds = %try.cont + %12 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) + %13 = extractvalue { i8*, i32 } %12, 0 + store i8* %13, i8** %exn.slot + %14 = extractvalue { i8*, i32 } %12, 1 + store i32 %14, i32* %ehselector.slot + call void @llvm.eh.endcatch() #3 + br label %catch.dispatch11 + +; CHECK-NOT: catch.dispatch11: +catch.dispatch11: ; preds = %lpad8, %catch.dispatch3, %catch.dispatch + %sel12 = load i32, i32* %ehselector.slot + %15 = call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*)) #3 + %matches13 = icmp eq i32 %sel12, %15 + br i1 %matches13, label %catch14, label %eh.resume + +; CHECK-NOT: catch14: +catch14: ; preds = %catch.dispatch11 + %exn15 = load i8*, i8** %exn.slot + %16 = bitcast float* %f to i8* + call void @llvm.eh.begincatch(i8* %exn15, i8* %16) #3 + %17 = load float, float* %f, align 4 + call void @"\01?handle_float@@YAXM@Z"(float %17) + call void @llvm.eh.endcatch() #3 + br label %try.cont19 + +try.cont19: ; preds = %catch14, %try.cont10 + call void @"\01?done@@YAXXZ"() + ret void + +; CHECK-NOT: eh.resume: +eh.resume: ; preds = %lpad16, %catch.dispatch11 + %exn20 = load i8*, i8** %exn.slot + %sel21 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn20, 0 + %lpad.val22 = insertvalue { i8*, i32 } %lpad.val, i32 %sel21, 1 + resume { i8*, i32 } %lpad.val22 +; CHECK: } +} + +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_J:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[J_PTR:\%.+]] = bitcast i8* [[RECOVER_J]] to i32* +; CHECK: [[RECOVER_I1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[I_PTR1:\%.+]] = bitcast i8* [[RECOVER_I1]] to i32* +; CHECK: [[TMP3:\%.+]] = load i32, i32* [[J_PTR]], align 4 +; CHECK: store i32 [[TMP3]], i32* [[I_PTR1]] +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ.catch.2", %invoke.cont2) +; CHECK: } + +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch.1"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_F:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: [[F_PTR:\%.+]] = bitcast i8* [[RECOVER_F]] to float* +; CHECK: [[TMP2:\%.+]] = load float, float* [[F_PTR]], align 4 +; CHECK: call void @"\01?handle_float@@YAXM@Z"(float [[TMP2]]) +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont19) +; CHECK: } + +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch.2"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[I_PTR:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +; CHECK: to label %invoke.cont2 unwind label %[[LPAD1_LABEL:lpad[0-9]*]] +; +; CHECK: invoke.cont2: ; preds = %[[LPAD1_LABEL]], %entry +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[I_PTR]], align 4 +; CHECK: invoke void @"\01?handle_int@@YAXH@Z"(i32 [[TMP1]]) +; CHECK: to label %invoke.cont9 unwind label %[[LPAD8_LABEL:lpad[0-9]*]] +; +; CHECK: [[LPAD1_LABEL]]:{{[ ]+}}; preds = %entry +; CHECK: [[LPAD1_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) +; CHECK: [[RECOVER1:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch", i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1") +; CHECK: indirectbr i8* [[RECOVER1]], [label %invoke.cont2] +; +; CHECK: invoke.cont9: +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont10) +; +; CHECK: [[LPAD8_LABEL]]:{{[ ]+}}; preds = %invoke.cont2 +; CHECK: [[LPAD8_VAL:\%.+]] = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*) +; CHECK: [[RECOVER2:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0M@8" to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch.1") +; CHECK: indirectbr i8* [[RECOVER2]], [] +; +; CHECK: } + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +declare void @"\01?handle_int@@YAXH@Z"(i32) #1 + +declare void @"\01?handle_float@@YAXM@Z"(float) #1 + +declare void @"\01?done@@YAXXZ"() #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 232069) (llvm/trunk 232070)"} diff --git a/test/CodeGen/WinEH/cppeh-nested-rethrow.ll b/test/CodeGen/WinEH/cppeh-nested-rethrow.ll new file mode 100644 index 000000000000..60b404113345 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-nested-rethrow.ll @@ -0,0 +1,212 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test was generated from the following code. +; +; void test1() { +; try { +; try { +; throw 1; +; } catch(...) { throw; } +; } catch (...) { } +; } +; void test2() { +; try { +; throw 1; +; } catch(...) { +; try { +; throw; +; } catch (...) {} +; } +; } +; +; These two functions result in functionally equivalent code, but the last +; catch block contains a call to llvm.eh.endcatch that tripped up processing +; during development. +; +; The main purpose of this test is to verify that we can correctly +; handle the case of nested landing pads that return directly to a block in +; the parent function. + +; ModuleID = 'cppeh-nested-rethrow.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +$"\01??_R0H@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat + +; CHECK-LABEL: define void @"\01?test1@@YAXXZ"() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape + +; Function Attrs: nounwind uwtable +define void @"\01?test1@@YAXXZ"() #0 { +entry: + %tmp = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 1, i32* %tmp + %0 = bitcast i32* %tmp to i8* + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #2 + to label %unreachable unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %2 = extractvalue { i8*, i32 } %1, 0 + store i8* %2, i8** %exn.slot + %3 = extractvalue { i8*, i32 } %1, 1 + store i32 %3, i32* %ehselector.slot + br label %catch + +catch: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #1 + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #2 + to label %unreachable unwind label %lpad1 + +lpad1: ; preds = %catch + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %5 = extractvalue { i8*, i32 } %4, 0 + store i8* %5, i8** %exn.slot + %6 = extractvalue { i8*, i32 } %4, 1 + store i32 %6, i32* %ehselector.slot + br label %catch2 + +catch2: ; preds = %lpad1 + %exn3 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn3, i8* null) #1 + call void @llvm.eh.endcatch() #1 + br label %try.cont.4 + +; This block should not be eliminated. +; CHECK: try.cont.4: +try.cont.4: ; preds = %catch2, %try.cont + ret void + +try.cont: ; No predecessors! + br label %try.cont.4 + +unreachable: ; preds = %catch, %entry + unreachable +; CHECK: } +} + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #1 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #1 + +; CHECK-LABEL: define void @"\01?test2@@YAXXZ"() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape + +; Function Attrs: nounwind uwtable +define void @"\01?test2@@YAXXZ"() #0 { +entry: + %tmp = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 1, i32* %tmp + %0 = bitcast i32* %tmp to i8* + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #2 + to label %unreachable unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %2 = extractvalue { i8*, i32 } %1, 0 + store i8* %2, i8** %exn.slot + %3 = extractvalue { i8*, i32 } %1, 1 + store i32 %3, i32* %ehselector.slot + br label %catch + +catch: ; preds = %lpad + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* null) #1 + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #2 + to label %unreachable unwind label %lpad1 + +lpad1: ; preds = %catch + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %5 = extractvalue { i8*, i32 } %4, 0 + store i8* %5, i8** %exn.slot + %6 = extractvalue { i8*, i32 } %4, 1 + store i32 %6, i32* %ehselector.slot + br label %catch2 + +catch2: ; preds = %lpad1 + %exn3 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn3, i8* null) #1 + call void @llvm.eh.endcatch() #1 + br label %try.cont + +; This block should not be eliminated. +; CHECK: try.cont: +; The endcatch call should be eliminated. +; CHECK-NOT: call void @llvm.eh.endcatch() +try.cont: ; preds = %catch2 + call void @llvm.eh.endcatch() #1 + br label %try.cont.4 + +try.cont.4: ; preds = %try.cont + ret void + +unreachable: ; preds = %catch, %entry + unreachable +; CHECK: } +} + +; The outlined test1.catch handler should return to a valid block address. +; CHECK-LABEL: define internal i8* @"\01?test1@@YAXXZ.catch"(i8*, i8*) +; CHECK-NOT: ret i8* inttoptr (i32 1 to i8*) +; CHECK: } + +; The outlined test1.catch1 handler should not contain a return instruction. +; CHECK-LABEL: define internal i8* @"\01?test1@@YAXXZ.catch.1"(i8*, i8*) +; CHECK-NOT: ret +; CHECK: } + +; The outlined test2.catch handler should return to a valid block address. +; CHECK-LABEL: define internal i8* @"\01?test2@@YAXXZ.catch"(i8*, i8*) +; CHECK-NOT: ret i8* inttoptr (i32 1 to i8*) +; CHECK: } + +; The outlined test2.catch2 handler should not contain a return instruction. +; CHECK-LABEL: define internal i8* @"\01?test2@@YAXXZ.catch.2"(i8*, i8*) +; CHECK-NOT: ret +; CHECK: } + + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 236059)"} diff --git a/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll b/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll new file mode 100644 index 000000000000..15f6bfb4680d --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-nonalloca-frame-values.ll @@ -0,0 +1,278 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; struct SomeData { +; int a; +; int b; +; }; +; +; void may_throw(); +; void does_not_throw(int i); +; void dump(int *, int, SomeData&); +; +; void test() { +; int NumExceptions = 0; +; int ExceptionVal[10]; +; SomeData Data = { 0, 0 }; +; +; for (int i = 0; i < 10; ++i) { +; try { +; may_throw(); +; Data.a += i; +; } +; catch (int e) { +; ExceptionVal[NumExceptions] = e; +; ++NumExceptions; +; if (e == i) +; Data.b += e; +; else +; Data.a += e; +; } +; does_not_throw(NumExceptions); +; } +; dump(ExceptionVal, NumExceptions, Data); +; } +; +; Unlike the cppeh-frame-vars.ll test, this test was generated using -O2 +; optimization, which results in non-alloca values being used in the +; catch handler. + +; ModuleID = 'cppeh-frame-vars.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%struct.SomeData = type { i32, i32 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat + +; The function entry should be rewritten like this. +; CHECK: define void @"\01?test@@YAXXZ"() +; CHECK: entry: +; CHECK: [[NUMEXCEPTIONS_REGMEM:\%.+]] = alloca i32 +; CHECK: [[I_REGMEM:\%.+]] = alloca i32 +; CHECK: [[B_REGMEM:\%.+]] = alloca i32* +; CHECK: [[A_REGMEM:\%.+]] = alloca i32* +; CHECK: [[E_PTR:\%.+]] = alloca i32, align 4 +; CHECK: [[EXCEPTIONVAL:\%.+]] = alloca [10 x i32], align 16 +; CHECK: [[DATA_PTR:\%.+]] = alloca i64, align 8 +; CHECK: [[TMPCAST:\%.+]] = bitcast i64* [[DATA_PTR]] to %struct.SomeData* +; CHECK: [[TMP:\%.+]] = bitcast [10 x i32]* [[EXCEPTIONVAL]] to i8* +; CHECK: call void @llvm.lifetime.start(i64 40, i8* [[TMP]]) +; CHECK: store i64 0, i64* [[DATA_PTR]], align 8 +; CHECK: [[A_PTR:\%.+]] = bitcast i64* [[DATA_PTR]] to i32* +; CHECK: store i32* [[A_PTR]], i32** [[A_REGMEM]] +; CHECK: [[B_PTR:\%.+]] = getelementptr inbounds %struct.SomeData, %struct.SomeData* [[TMPCAST]], i64 0, i32 1 +; CHECK: store i32* [[B_PTR]], i32** [[B_REGMEM]] +; CHECK: call void (...) @llvm.frameescape(i32* %e, i32* %NumExceptions.020.reg2mem, [10 x i32]* [[EXCEPTIONVAL]], i32* %inc.reg2mem, i32* [[I_REGMEM]], i32** [[A_REGMEM]], i32** [[B_REGMEM]]) +; CHECK: br label %for.body + +; Function Attrs: uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %e = alloca i32, align 4 + %ExceptionVal = alloca [10 x i32], align 16 + %Data = alloca i64, align 8 + %tmpcast = bitcast i64* %Data to %struct.SomeData* + %0 = bitcast [10 x i32]* %ExceptionVal to i8* + call void @llvm.lifetime.start(i64 40, i8* %0) #1 + store i64 0, i64* %Data, align 8 + %a = bitcast i64* %Data to i32* + %b = getelementptr inbounds %struct.SomeData, %struct.SomeData* %tmpcast, i64 0, i32 1 + br label %for.body + +; CHECK: for.body: +; CHECK: [[NUMEXCEPTIONS_PHI:\%.*]] = phi i32 [ 0, %entry ], [ {{\%NumExceptions.*}}, %try.cont ] +; CHECK: [[I_PHI:\%.*]] = phi i32 [ 0, %entry ], [ {{\%inc.*}}, %try.cont ] +; CHECK: store i32 [[I_PHI]], i32* [[I_REGMEM]] +; CHECK: store i32 [[NUMEXCEPTIONS_PHI]], i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: invoke void @"\01?may_throw@@YAXXZ"() +for.body: ; preds = %entry, %try.cont + %NumExceptions.020 = phi i32 [ 0, %entry ], [ %NumExceptions.1, %try.cont ] + %i.019 = phi i32 [ 0, %entry ], [ %inc5, %try.cont ] + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +; CHECK: invoke.cont: ; preds = %for.body +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[A_RELOAD]], align 8 +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] +; CHECK: [[ADD:\%.+]] = add nsw i32 [[TMP1]], [[I_RELOAD]] +; CHECK: [[A_RELOAD1:\%.+]] = load i32*, i32** [[A_REGMEM]] +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: br label %try.cont +invoke.cont: ; preds = %for.body + %1 = load i32, i32* %a, align 8, !tbaa !2 + %add = add nsw i32 %1, %i.019 + store i32 %add, i32* %a, align 8, !tbaa !2 + br label %try.cont + +; CHECK: [[LPAD_LABEL:lpad[0-9]*]]:{{[ ]+}}; preds = %for.body +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %[[SPLIT_RECOVER_BB:.*]]] + +lpad: ; preds = %for.body + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) + %3 = extractvalue { i8*, i32 } %2, 1 + %4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #1 + %matches = icmp eq i32 %3, %4 + br i1 %matches, label %catch, label %eh.resume + +; CHECK-NOT: catch: + +catch: ; preds = %lpad + %5 = extractvalue { i8*, i32 } %2, 0 + %e.i8 = bitcast i32* %e to i8* + call void @llvm.eh.begincatch(i8* %5, i8* %e.i8) #1 + %tmp8 = load i32, i32* %e, align 4, !tbaa !7 + %idxprom = sext i32 %NumExceptions.020 to i64 + %arrayidx = getelementptr inbounds [10 x i32], [10 x i32]* %ExceptionVal, i64 0, i64 %idxprom + store i32 %tmp8, i32* %arrayidx, align 4, !tbaa !7 + %inc = add nsw i32 %NumExceptions.020, 1 + %cmp1 = icmp eq i32 %tmp8, %i.019 + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %catch + %tmp9 = load i32, i32* %b, align 4, !tbaa !8 + %add2 = add nsw i32 %tmp9, %i.019 + store i32 %add2, i32* %b, align 4, !tbaa !8 + br label %if.end + +; CHECK-NOT: if.else: + +if.else: ; preds = %catch + %tmp10 = load i32, i32* %a, align 8, !tbaa !2 + %add4 = add nsw i32 %tmp10, %tmp8 + store i32 %add4, i32* %a, align 8, !tbaa !2 + br label %if.end + +; CHECK-NOT: if.end: +; CHECK: [[SPLIT_RECOVER_BB]]: +; CHECK: [[INC_RELOAD:\%.*]] = load i32, i32* +; CHECK: br label %try.cont + +if.end: ; preds = %if.else, %if.then + tail call void @llvm.eh.endcatch() #1 + br label %try.cont + +; CHECK: try.cont:{{[ ]+}}; preds = %[[SPLIT_RECOVER_BB]], %invoke.cont +; CHECK: [[NUMEXCEPTIONS_PHI:\%.*]] = phi i32 [ [[NUMEXCEPTIONS_RELOAD]], %invoke.cont ], [ [[INC_RELOAD]], %[[SPLIT_RECOVER_BB]] ] +; CHECK: tail call void @"\01?does_not_throw@@YAXH@Z"(i32 [[NUMEXCEPTIONS_PHI]]) +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] +; CHECK: [[INC:\%.+]] = add nuw nsw i32 [[I_RELOAD]], 1 +; CHECK: [[CMP:\%.+]] = icmp slt i32 [[INC]], 10 +; CHECK: br i1 [[CMP]], label %for.body, label %for.end + +try.cont: ; preds = %if.end, %invoke.cont + %NumExceptions.1 = phi i32 [ %NumExceptions.020, %invoke.cont ], [ %inc, %if.end ] + tail call void @"\01?does_not_throw@@YAXH@Z"(i32 %NumExceptions.1) + %inc5 = add nuw nsw i32 %i.019, 1 + %cmp = icmp slt i32 %inc5, 10 + br i1 %cmp, label %for.body, label %for.end + +for.end: ; preds = %try.cont + %NumExceptions.1.lcssa = phi i32 [ %NumExceptions.1, %try.cont ] + %arraydecay = getelementptr inbounds [10 x i32], [10 x i32]* %ExceptionVal, i64 0, i64 0 + call void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32* %arraydecay, i32 %NumExceptions.1.lcssa, %struct.SomeData* dereferenceable(8) %tmpcast) + call void @llvm.lifetime.end(i64 40, i8* %0) #1 + ret void + +eh.resume: ; preds = %lpad + %.lcssa = phi { i8*, i32 } [ %2, %lpad ] + resume { i8*, i32 } %.lcssa +} + +; The following catch handler should be outlined. +; CHECK: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) +; CHECK: entry: +; CHECK: [[RECOVER_E:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) +; CHECK: [[E_PTR:\%.+]] = bitcast i8* [[RECOVER_E]] to i32* +; CHECK: [[RECOVER_NUMEXCEPTIONS:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 1) +; CHECK: [[NUMEXCEPTIONS_REGMEM:\%.+]] = bitcast i8* [[RECOVER_NUMEXCEPTIONS]] to i32* +; CHECK: [[RECOVER_EXCEPTIONVAL:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) +; CHECK: [[EXCEPTIONVAL:\%.+]] = bitcast i8* [[RECOVER_EXCEPTIONVAL]] to [10 x i32]* +; CHECK: [[RECOVER_INC:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 3) +; CHECK: [[INC_REGMEM:\%.+]] = bitcast i8* [[RECOVER_INC]] to i32* +; CHECK: [[RECOVER_I:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 4) +; CHECK: [[I_REGMEM:\%.+]] = bitcast i8* [[RECOVER_I]] to i32* +; CHECK: [[RECOVER_A:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 5) +; CHECK: [[A_REGMEM:\%.+]] = bitcast i8* [[RECOVER_A]] to i32** +; CHECK: [[RECOVER_B:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 6) +; CHECK: [[B_REGMEM:\%.+]] = bitcast i8* [[RECOVER_B]] to i32** +; CHECK: [[E_I8PTR:\%.+]] = bitcast i32* [[E_PTR]] to i8* +; CHECK: [[TMP:\%.+]] = load i32, i32* [[E_PTR]], align 4 +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: [[IDXPROM:\%.+]] = sext i32 [[NUMEXCEPTIONS_RELOAD]] to i64 +; CHECK: [[ARRAYIDX:\%.+]] = getelementptr inbounds [10 x i32], [10 x i32]* [[EXCEPTIONVAL]], i64 0, i64 [[IDXPROM]] +; CHECK: store i32 [[TMP]], i32* [[ARRAYIDX]], align 4 +; CHECK: [[NUMEXCEPTIONS_RELOAD:\%.+]] = load i32, i32* [[NUMEXCEPTIONS_REGMEM]] +; CHECK: [[INC:\%.+]] = add nsw i32 [[NUMEXCEPTIONS_RELOAD]], 1 +; CHECK: [[CMP:\%.+]] = icmp eq i32 [[TMP]], [[I_RELOAD]] +; CHECK: br i1 [[CMP]], label %if.then, label %if.else +; +; CHECK: if.then:{{[ ]+}}; preds = %entry +; CHECK: [[B_RELOAD:\%.+]] = load i32*, i32** [[B_REGMEM]] +; CHECK: [[TMP1:\%.+]] = load i32, i32* [[B_RELOAD]], align 4 +; CHECK: [[I_RELOAD:\%.+]] = load i32, i32* [[I_REGMEM]] +; CHECK: [[ADD:\%.+]] = add nsw i32 [[TMP1]], [[I_RELOAD]] +; CHECK: [[B_RELOAD:\%.+]] = load i32*, i32** [[B_REGMEM]] +; CHECK: store i32 [[ADD]], i32* [[B_RELOAD]], align 4 +; CHECK: br label %if.end +; +; CHECK: if.else:{{[ ]+}}; preds = %entry +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] +; CHECK: [[TMP2:\%.+]] = load i32, i32* [[A_RELOAD]], align 8 +; CHECK: [[ADD2:\%.+]] = add nsw i32 [[TMP2]], [[TMP]] +; CHECK: [[A_RELOAD:\%.+]] = load i32*, i32** [[A_REGMEM]] +; CHECK: store i32 [[ADD2]], i32* [[A_RELOAD]], align 8 +; CHECK: br label %if.end +; +; CHECK: if.end:{{[ ]+}}; preds = %if.else, %if.then +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %[[SPLIT_RECOVER_BB]]) +; CHECK: } + +; Function Attrs: nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +declare void @"\01?may_throw@@YAXXZ"() #2 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +declare void @llvm.eh.begincatch(i8*, i8*) + +declare void @llvm.eh.endcatch() + +declare void @"\01?does_not_throw@@YAXH@Z"(i32) #2 + +declare void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32*, i32, %struct.SomeData* dereferenceable(8)) #2 + +; Function Attrs: nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 228868)"} +!2 = !{!3, !4, i64 0} +!3 = !{!"?AUSomeData@@", !4, i64 0, !4, i64 4} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} +!7 = !{!4, !4, i64 0} +!8 = !{!3, !4, i64 4} diff --git a/test/CodeGen/WinEH/cppeh-prepared-catch-all.ll b/test/CodeGen/WinEH/cppeh-prepared-catch-all.ll new file mode 100644 index 000000000000..f395d64c7b5e --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-prepared-catch-all.ll @@ -0,0 +1,47 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; This test case is equivalent to: +; extern "C" void may_throw(); +; extern "C" void test_catch_all() { +; try { +; may_throw(); +; } catch (...) { +; } +; } + +declare void @may_throw() #1 +declare i32 @__CxxFrameHandler3(...) +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #2 +declare void @llvm.eh.endcatch() #2 + +; Function Attrs: nounwind uwtable +define void @test_catch_all() #0 { +entry: + invoke void @may_throw() + to label %try.cont unwind label %lpad + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + tail call void @llvm.eh.begincatch(i8* %1, i8* null) #2 + tail call void @llvm.eh.endcatch() #2 + br label %try.cont + +try.cont: ; preds = %entry, %lpad + ret void +} + +; CHECK-LABEL: $handlerMap$0$test_catch_all: +; CHECK: .long {{[0-9]+}} +; CHECK: .long 0 +; CHECK: .long 0 +; CHECK: .long test_catch_all.catch@IMGREL +; CHECK: .long .Ltest_catch_all.catch$parent_frame_offset + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } diff --git a/test/CodeGen/WinEH/cppeh-prepared-catch-reordered.ll b/test/CodeGen/WinEH/cppeh-prepared-catch-reordered.ll new file mode 100644 index 000000000000..6383ca7f1883 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-prepared-catch-reordered.ll @@ -0,0 +1,164 @@ +; RUN: llc < %s | FileCheck %s + +; Verify that we get the right frame escape label when the catch comes after the +; parent function. + +; This test case is equivalent to: +; int main() { +; try { +; throw 42; +; } catch (int e) { +; printf("e: %d\n", e); +; } +; } + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } +%eh.CatchHandlerType = type { i32, i8* } + +$"\01??_R0H@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +$"\01??_C@_06PNOAJMHG@e?3?5?$CFd?6?$AA@" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" +@"\01??_C@_06PNOAJMHG@e?3?5?$CFd?6?$AA@" = linkonce_odr unnamed_addr constant [7 x i8] c"e: %d\0A\00", comdat, align 1 + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +; Function Attrs: uwtable +define i32 @main() #1 { +entry: + %tmp.i = alloca i32, align 4 + %e = alloca i32, align 4 + %0 = bitcast i32* %tmp.i to i8* + store i32 42, i32* %tmp.i, align 4, !tbaa !2 + call void (...) @llvm.frameescape(i32* %e) + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #6 + to label %.noexc unwind label %lpad1 + +.noexc: ; preds = %entry + unreachable + +lpad1: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + %recover = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 0, i8* (i8*, i8*)* @main.catch) + indirectbr i8* %recover, [label %try.cont.split] + +try.cont.split: ; preds = %lpad1 + ret i32 0 +} + +; CHECK-LABEL: main: +; CHECK: .seh_handlerdata +; CHECK: .long ($cppxdata$main)@IMGREL + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare i32 @printf(i8* nocapture readonly, ...) #4 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +; Function Attrs: nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #3 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #3 + +define internal i8* @main.catch(i8*, i8*) #5 { +entry: + %e.i8 = call i8* @llvm.framerecover(i8* bitcast (i32 ()* @main to i8*), i8* %1, i32 0) + %e = bitcast i8* %e.i8 to i32* + %2 = bitcast i32* %e to i8* + %3 = load i32, i32* %e, align 4, !tbaa !2 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"\01??_C@_06PNOAJMHG@e?3?5?$CFd?6?$AA@", i64 0, i64 0), i32 %3) + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret i8* blockaddress(@main, %try.cont.split) + +stub: ; preds = %entry + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +; CHECK-LABEL: main.catch: +; CHECK: .seh_handlerdata +; CHECK: .long ($cppxdata$main)@IMGREL + +; CHECK-NEXT: $cppxdata$main: +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long ($stateUnwindMap$main)@IMGREL +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ($tryMap$main)@IMGREL +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long ($ip2state$main)@IMGREL +; CHECK-NEXT: .long 40 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 + +; Make sure we get the right frame escape label. + +; CHECK: $handlerMap$0$main: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "??_R0H@8"@IMGREL +; CHECK-NEXT: .long .Lmain$frame_escape_0 +; CHECK-NEXT: .long main.catch@IMGREL +; CHECK-NEXT: .long .Lmain.catch$parent_frame_offset + +; Function Attrs: nounwind readnone +declare void @llvm.donothing() #2 + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #3 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #2 + +attributes #0 = { noreturn uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="main" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #5 = { "wineh-parent"="main" } +attributes #6 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 "} +!2 = !{!3, !3, i64 0} +!3 = !{!"int", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} + diff --git a/test/CodeGen/WinEH/cppeh-prepared-catch.ll b/test/CodeGen/WinEH/cppeh-prepared-catch.ll new file mode 100644 index 000000000000..e7aaca86a882 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-prepared-catch.ll @@ -0,0 +1,210 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; This test case is equivalent to: +; void f() { +; try { +; try { +; may_throw(); +; } catch (int &) { +; may_throw(); +; } +; may_throw(); +; } catch (double) { +; } +; } + + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } + +$"\01??_R0N@8" = comdat any + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0N@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".N\00" }, comdat +@llvm.eh.handlertype.N.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0N@8" to i8*) }, section "llvm.metadata" +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.8 = private unnamed_addr constant %eh.CatchHandlerType { i32 8, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" + +define internal i8* @"\01?f@@YAXXZ.catch"(i8*, i8*) #4 { +entry: + %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 0) + %bc2 = bitcast i8* %.i8 to i32** + %bc3 = bitcast i32** %bc2 to i8* + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %entry + ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont) + +lpad1: ; preds = %entry + %lp4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), i32 1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover, [label %invoke.cont2] +} + +; CHECK-LABEL: "?f@@YAXXZ.catch": +; No code should be generated for the indirectbr. +; CHECK-NOT: jmpq * +; CHECK: .seh_handlerdata +; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL + + +define internal i8* @"\01?f@@YAXXZ.catch1"(i8*, i8*) #4 { +entry: + %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 1) + %2 = bitcast i8* %.i8 to double* + %3 = bitcast double* %2 to i8* + invoke void (...) @llvm.donothing() + to label %done unwind label %lpad + +done: + ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont8) + +lpad: ; preds = %entry + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +; CHECK-LABEL: "?f@@YAXXZ.catch1": +; No code should be generated for the indirectbr. +; CHECK-NOT: jmpq * +; CHECK: ".L?f@@YAXXZ.catch1$parent_frame_offset" = 16 +; CHECK: movq %rdx, 16(%rsp) +; CHECK: .seh_handlerdata +; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL + +define void @"\01?f@@YAXXZ"() #0 { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %0 = alloca i32*, align 8 + %1 = alloca double, align 8 + call void (...) @llvm.frameescape(i32** %0, double* %1) + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad2 + +invoke.cont: ; preds = %entry + br label %try.cont + +lpad2: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.8 + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.8 to i8*), i32 0, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), i32 1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover, [label %try.cont, label %try.cont8] + +try.cont: ; preds = %lpad2, %invoke.cont + invoke void @"\01?may_throw@@YAXXZ"() + to label %try.cont8 unwind label %lpad1 + +lpad1: + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover2 = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), i32 1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover2, [label %try.cont8] + +try.cont8: ; preds = %lpad2, %try.cont + ret void +} + +; CHECK-LABEL: "?f@@YAXXZ": +; No code should be generated for the indirectbr. +; CHECK-NOT: jmpq * +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long ("$cppxdata$?f@@YAXXZ")@IMGREL +; CHECK-NEXT:"$cppxdata$?f@@YAXXZ": +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long ("$stateUnwindMap$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long ("$tryMap$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long ("$ip2state$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 32 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK-NEXT:"$stateUnwindMap$?f@@YAXXZ": +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long 0 +; CHECK-NEXT:"$tryMap$?f@@YAXXZ": +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ("$handlerMap$0$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ("$handlerMap$1$?f@@YAXXZ")@IMGREL +; CHECK-NEXT:"$handlerMap$0$?f@@YAXXZ": +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long "??_R0H@8"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ$frame_escape_0" +; CHECK-NEXT: .long "?f@@YAXXZ.catch"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ.catch$parent_frame_offset" +; CHECK-NEXT:"$handlerMap$1$?f@@YAXXZ": +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "??_R0N@8"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ$frame_escape_1" +; CHECK-NEXT: .long "?f@@YAXXZ.catch1"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ.catch1$parent_frame_offset" +; CHECK-NEXT:"$ip2state$?f@@YAXXZ": +; CHECK-NEXT: .long .Lfunc_begin0@IMGREL +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long .Lfunc_begin1@IMGREL +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long .Lfunc_begin2@IMGREL +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long .Ltmp13@IMGREL +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp16@IMGREL +; CHECK-NEXT: .long 0 + + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #3 + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #3 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #2 + +declare void @llvm.donothing(...) + +attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="?f@@YAXXZ" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { "wineh-parent"="?f@@YAXXZ" } diff --git a/test/CodeGen/WinEH/cppeh-prepared-cleanups.ll b/test/CodeGen/WinEH/cppeh-prepared-cleanups.ll new file mode 100644 index 000000000000..876cb53baba1 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-prepared-cleanups.ll @@ -0,0 +1,243 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } +%struct.S = type { i8 } + +$"\01??_DS@@QEAA@XZ" = comdat any + +$"\01??_R0H@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat + + +; CHECK-LABEL: "?test1@@YAXXZ": +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long ("$cppxdata$?test1@@YAXXZ")@IMGREL +; CHECK-NEXT:"$cppxdata$?test1@@YAXXZ": +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ("$stateUnwindMap$?test1@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long ("$ip2state$?test1@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 32 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK-NEXT:"$stateUnwindMap$?test1@@YAXXZ": +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long "?test1@@YAXXZ.cleanup"@IMGREL +; CHECK-NEXT:"$ip2state$?test1@@YAXXZ": +; CHECK-NEXT: .long .Lfunc_begin0@IMGREL +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long 0 + +define void @"\01?test1@@YAXXZ"() #0 { +entry: + %unwindhelp = alloca i64 + %tmp = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 0, i32* %tmp + %0 = bitcast i32* %tmp to i8* + call void (...) @llvm.frameescape() + store volatile i64 -2, i64* %unwindhelp + %1 = bitcast i64* %unwindhelp to i8* + call void @llvm.eh.unwindhelp(i8* %1) + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #8 + to label %unreachable unwind label %lpad1 + +lpad1: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test1@@YAXXZ.cleanup") + indirectbr i8* %recover, [] + +unreachable: ; preds = %entry + unreachable +} + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind +define linkonce_odr void @"\01??_DS@@QEAA@XZ"(%struct.S* %this) unnamed_addr #1 comdat align 2 { +entry: + %this.addr = alloca %struct.S*, align 8 + store %struct.S* %this, %struct.S** %this.addr, align 8 + %this1 = load %struct.S*, %struct.S** %this.addr + call void @"\01??1S@@QEAA@XZ"(%struct.S* %this1) #4 + ret void +} + +; CHECK-LABEL: "?test2@@YAX_N@Z": +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long ("$cppxdata$?test2@@YAX_N@Z")@IMGREL +; CHECK-NEXT:"$cppxdata$?test2@@YAX_N@Z": +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long ("$stateUnwindMap$?test2@@YAX_N@Z")@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long ("$ip2state$?test2@@YAX_N@Z")@IMGREL +; CHECK-NEXT: .long 40 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK-NEXT:"$stateUnwindMap$?test2@@YAX_N@Z": +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup"@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup1"@IMGREL +; CHECK-NEXT:"$ip2state$?test2@@YAX_N@Z": +; CHECK-NEXT: .long .Lfunc_begin1@IMGREL +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long .Ltmp7@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long .Ltmp9@IMGREL +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp12@IMGREL +; CHECK-NEXT: .long 0 + +define void @"\01?test2@@YAX_N@Z"(i1 zeroext %b) #2 { + %b.addr = alloca i8, align 1 + %s = alloca %struct.S, align 1 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %s1 = alloca %struct.S, align 1 + %frombool = zext i1 %b to i8 + store i8 %frombool, i8* %b.addr, align 1 + call void (...) @llvm.frameescape(%struct.S* %s, %struct.S* %s1) + call void @"\01?may_throw@@YAXXZ"() + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad1 + +invoke.cont: ; preds = %entry + %1 = load i8, i8* %b.addr, align 1 + %tobool = trunc i8 %1 to i1 + br i1 %tobool, label %if.then, label %if.else + +if.then: ; preds = %invoke.cont + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont3 unwind label %lpad3 + +invoke.cont3: ; preds = %if.then + call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4 + br label %if.end + +lpad1: ; preds = %entry, %if.end + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup") + indirectbr i8* %recover, [] + +lpad3: ; preds = %if.then + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover4 = call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup1", i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup") + indirectbr i8* %recover4, [] + +if.else: ; preds = %invoke.cont + call void @"\01?dont_throw@@YAXXZ"() #4 + br label %if.end + +if.end: ; preds = %if.else, %invoke.cont3 + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont4 unwind label %lpad1 + +invoke.cont4: ; preds = %if.end + call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4 + ret void +} + +declare void @"\01?may_throw@@YAXXZ"() #3 + +; Function Attrs: nounwind +declare void @"\01?dont_throw@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @"\01??1S@@QEAA@XZ"(%struct.S*) #1 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #4 + +define internal void @"\01?test1@@YAXXZ.cleanup"(i8*, i8*) #5 { +entry: + %s = alloca %struct.S, align 1 + call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4 + ret void +} + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #4 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #6 + +; Function Attrs: nounwind +declare void @llvm.eh.unwindhelp(i8*) #4 + +define internal void @"\01?test2@@YAX_N@Z.cleanup"(i8*, i8*) #7 { +entry: + %s.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 0) + %s = bitcast i8* %s.i8 to %struct.S* + call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret void + +stub: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + unreachable +} + +define internal void @"\01?test2@@YAX_N@Z.cleanup1"(i8*, i8*) #7 { +entry: + %s1.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 1) + %s1 = bitcast i8* %s1.i8 to %struct.S* + call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret void + +stub: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + unreachable +} + +declare void @llvm.donothing() + +attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="?test1@@YAXXZ" } +attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="?test2@@YAX_N@Z" } +attributes #3 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nounwind } +attributes #5 = { "wineh-parent"="?test1@@YAXXZ" } +attributes #6 = { nounwind readnone } +attributes #7 = { "wineh-parent"="?test2@@YAX_N@Z" } +attributes #8 = { noreturn } diff --git a/test/CodeGen/WinEH/cppeh-shared-empty-catch.ll b/test/CodeGen/WinEH/cppeh-shared-empty-catch.ll new file mode 100644 index 000000000000..dd99a092b201 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-shared-empty-catch.ll @@ -0,0 +1,110 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following source, built with -O2 +; +; void f() { +; try { +; g(); +; try { +; throw; +; } catch (int) { +; } +; } catch (...) { +; } +; } +; + +; ModuleID = '<stdin>' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" + +; CHECK-LABEL: define void @"\01?f@@YAXXZ"() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape() +; CHECK: invoke void @"\01?g@@YAXXZ"() + +; Function Attrs: nounwind +define void @"\01?f@@YAXXZ"() #0 { +entry: + invoke void @"\01?g@@YAXXZ"() + to label %invoke.cont unwind label %lpad + +; CHECK-LABEL: invoke.cont: +; CHECK: invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) +; CHECK: to label %unreachable unwind label %[[LPAD1_LABEL:lpad[0-9]+]] + +invoke.cont: ; preds = %entry + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #4 + to label %unreachable unwind label %lpad1 + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + br label %catch2 + +; Note: Even though this landing pad has two catch clauses, it only has one action because both +; handlers do the same thing. +; CHECK: [[LPAD1_LABEL]]: +; CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +; CHECK-NEXT: catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 +; CHECK-NEXT: catch i8* null +; CHECK-NEXT: [[RECOVER:\%.+]] = call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch") +; CHECK-NEXT: indirectbr i8* [[RECOVER]], [label %try.cont4] + +lpad1: ; preds = %invoke.cont + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %3 = extractvalue { i8*, i32 } %2, 0 + br label %catch2 + +catch2: ; preds = %lpad1, %lpad + %exn.slot.0 = phi i8* [ %3, %lpad1 ], [ %1, %lpad ] + tail call void @llvm.eh.begincatch(i8* %exn.slot.0, i8* null) #3 + tail call void @llvm.eh.endcatch() #3 + br label %try.cont4 + +try.cont4: ; preds = %catch, %catch2 + ret void + +unreachable: ; preds = %invoke.cont + unreachable + +; CHECK: } +} + +declare void @"\01?g@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { noreturn } + +!llvm.ident = !{!0} + +!0 = !{!"clang version 3.7.0 (trunk 235112) (llvm/trunk 235121)"} diff --git a/test/CodeGen/WinEH/cppeh-similar-catch-blocks.ll b/test/CodeGen/WinEH/cppeh-similar-catch-blocks.ll new file mode 100644 index 000000000000..81ee4542062d --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-similar-catch-blocks.ll @@ -0,0 +1,394 @@ +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s + +; This test is based on the following code: +; +; int main(void) { +; try { +; try { +; throw 'a'; +; } catch (char c) { +; printf("%c\n", c); +; } +; throw 1; +; } catch(int x) { +; printf("%d\n", x); +; } catch(...) { +; printf("...\n"); +; } +; try { +; try { +; throw 'b'; +; } catch (char c) { +; printf("%c\n", c); +; } +; throw 2; +; } catch(int x) { +; printf("%d\n", x); +; } catch (char c) { +; printf("%c\n", c); +; } catch(...) { +; printf("...\n"); +; } +; return 0; +; } + +; This test is just checking for failures in processing the IR. +; Extensive handler matching is not required. + +; ModuleID = 'cppeh-similar-catch-blocks.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +$"\01??_R0H@8" = comdat any + +$"\01??_R0D@8" = comdat any + +$"_CT??_R0D@81" = comdat any + +$_CTA1D = comdat any + +$_TI1D = comdat any + +$"\01??_C@_03PJCJOCBM@?$CFc?6?$AA@" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +$"\01??_C@_04MPPNMCOK@?4?4?4?6?$AA@" = comdat any + +$"\01??_C@_03PMGGPEJJ@?$CFd?6?$AA@" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" +@"\01??_R0D@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".D\00" }, comdat +@llvm.eh.handlertype.D.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0D@8" to i8*) }, section "llvm.metadata" +@__ImageBase = external constant i8 +@"_CT??_R0D@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0D@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 1, i32 0 }, section ".xdata", comdat +@_CTA1D = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0D@81" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1D = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1D to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat +@"\01??_C@_03PJCJOCBM@?$CFc?6?$AA@" = linkonce_odr unnamed_addr constant [4 x i8] c"%c\0A\00", comdat, align 1 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat +@"\01??_C@_04MPPNMCOK@?4?4?4?6?$AA@" = linkonce_odr unnamed_addr constant [5 x i8] c"...\0A\00", comdat, align 1 +@"\01??_C@_03PMGGPEJJ@?$CFd?6?$AA@" = linkonce_odr unnamed_addr constant [4 x i8] c"%d\0A\00", comdat, align 1 + +; This is just a minimal check to verify that main was handled by WinEHPrepare. +; CHECK: define i32 @main() +; CHECK: entry: +; CHECK: call void (...) @llvm.frameescape(i32* [[X_PTR:\%.+]], i32* [[X2_PTR:\%.+]], i8* [[C2_PTR:\%.+]], i8* [[C3_PTR:\%.+]], i8* [[C_PTR:\%.+]]) +; CHECK: invoke void @_CxxThrowException +; CHECK: } + +; Function Attrs: uwtable +define i32 @main() #0 { +entry: + %retval = alloca i32, align 4 + %tmp = alloca i8, align 1 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %c = alloca i8, align 1 + %tmp3 = alloca i32, align 4 + %x = alloca i32, align 4 + %tmp20 = alloca i8, align 1 + %c28 = alloca i8, align 1 + %tmp34 = alloca i32, align 4 + %c48 = alloca i8, align 1 + %x56 = alloca i32, align 4 + store i32 0, i32* %retval + store i8 97, i8* %tmp + invoke void @_CxxThrowException(i8* %tmp, %eh.ThrowInfo* @_TI1D) #4 + to label %unreachable unwind label %lpad + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.D.0 + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %catch.dispatch + +catch.dispatch: ; preds = %lpad + %sel = load i32, i32* %ehselector.slot + %3 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*)) #2 + %matches = icmp eq i32 %sel, %3 + br i1 %matches, label %catch, label %catch.dispatch5 + +catch: ; preds = %catch.dispatch + %exn = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn, i8* %c) #2 + %4 = load i8, i8* %c, align 1 + %conv = sext i8 %4 to i32 + %call = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01??_C@_03PJCJOCBM@?$CFc?6?$AA@", i32 0, i32 0), i32 %conv) + to label %invoke.cont unwind label %lpad2 + +invoke.cont: ; preds = %catch + call void @llvm.eh.endcatch() #2 + br label %try.cont + +try.cont: ; preds = %invoke.cont + store i32 1, i32* %tmp3 + %5 = bitcast i32* %tmp3 to i8* + invoke void @_CxxThrowException(i8* %5, %eh.ThrowInfo* @_TI1H) #4 + to label %unreachable unwind label %lpad4 + +lpad2: ; preds = %catch + %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %7 = extractvalue { i8*, i32 } %6, 0 + store i8* %7, i8** %exn.slot + %8 = extractvalue { i8*, i32 } %6, 1 + store i32 %8, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %catch.dispatch5 + +lpad4: ; preds = %try.cont + %9 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %10 = extractvalue { i8*, i32 } %9, 0 + store i8* %10, i8** %exn.slot + %11 = extractvalue { i8*, i32 } %9, 1 + store i32 %11, i32* %ehselector.slot + br label %catch.dispatch5 + +catch.dispatch5: ; preds = %lpad4, %lpad2, %catch.dispatch + %sel6 = load i32, i32* %ehselector.slot + %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*)) #2 + %matches7 = icmp eq i32 %sel6, %12 + br i1 %matches7, label %catch13, label %catch8 + +catch13: ; preds = %catch.dispatch5 + %exn14 = load i8*, i8** %exn.slot + %13 = bitcast i32* %x to i8* + call void @llvm.eh.begincatch(i8* %exn14, i8* %13) #2 + %14 = load i32, i32* %x, align 4 + %call18 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01??_C@_03PMGGPEJJ@?$CFd?6?$AA@", i32 0, i32 0), i32 %14) + to label %invoke.cont17 unwind label %lpad16 + +invoke.cont17: ; preds = %catch13 + call void @llvm.eh.endcatch() #2 + br label %try.cont19 + +try.cont19: ; preds = %invoke.cont17, %invoke.cont11 + store i8 98, i8* %tmp20 + invoke void @_CxxThrowException(i8* %tmp20, %eh.ThrowInfo* @_TI1D) #4 + to label %unreachable unwind label %lpad21 + +catch8: ; preds = %catch.dispatch5 + %exn9 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn9, i8* null) #2 + %call12 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01??_C@_04MPPNMCOK@?4?4?4?6?$AA@", i32 0, i32 0)) + to label %invoke.cont11 unwind label %lpad10 + +invoke.cont11: ; preds = %catch8 + call void @llvm.eh.endcatch() #2 + br label %try.cont19 + +lpad10: ; preds = %catch8 + %15 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %16 = extractvalue { i8*, i32 } %15, 0 + store i8* %16, i8** %exn.slot + %17 = extractvalue { i8*, i32 } %15, 1 + store i32 %17, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +lpad16: ; preds = %catch13 + %18 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %19 = extractvalue { i8*, i32 } %18, 0 + store i8* %19, i8** %exn.slot + %20 = extractvalue { i8*, i32 } %18, 1 + store i32 %20, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +lpad21: ; preds = %try.cont19 + %21 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*) + catch i8* null + %22 = extractvalue { i8*, i32 } %21, 0 + store i8* %22, i8** %exn.slot + %23 = extractvalue { i8*, i32 } %21, 1 + store i32 %23, i32* %ehselector.slot + br label %catch.dispatch22 + +catch.dispatch22: ; preds = %lpad21 + %sel23 = load i32, i32* %ehselector.slot + %24 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*)) #2 + %matches24 = icmp eq i32 %sel23, %24 + br i1 %matches24, label %catch25, label %catch.dispatch36 + +catch25: ; preds = %catch.dispatch22 + %exn26 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn26, i8* %c28) #2 + %25 = load i8, i8* %c28, align 1 + %conv29 = sext i8 %25 to i32 + %call32 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01??_C@_03PJCJOCBM@?$CFc?6?$AA@", i32 0, i32 0), i32 %conv29) + to label %invoke.cont31 unwind label %lpad30 + +invoke.cont31: ; preds = %catch25 + call void @llvm.eh.endcatch() #2 + br label %try.cont33 + +try.cont33: ; preds = %invoke.cont31 + store i32 2, i32* %tmp34 + %26 = bitcast i32* %tmp34 to i8* + invoke void @_CxxThrowException(i8* %26, %eh.ThrowInfo* @_TI1H) #4 + to label %unreachable unwind label %lpad35 + +lpad30: ; preds = %catch25 + %27 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*) + catch i8* null + %28 = extractvalue { i8*, i32 } %27, 0 + store i8* %28, i8** %exn.slot + %29 = extractvalue { i8*, i32 } %27, 1 + store i32 %29, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %catch.dispatch36 + +lpad35: ; preds = %try.cont33 + %30 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*) + catch i8* null + %31 = extractvalue { i8*, i32 } %30, 0 + store i8* %31, i8** %exn.slot + %32 = extractvalue { i8*, i32 } %30, 1 + store i32 %32, i32* %ehselector.slot + br label %catch.dispatch36 + +catch.dispatch36: ; preds = %lpad35, %lpad30, %catch.dispatch22 + %sel37 = load i32, i32* %ehselector.slot + %33 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*)) #2 + %matches38 = icmp eq i32 %sel37, %33 + br i1 %matches38, label %catch53, label %catch.fallthrough + +catch53: ; preds = %catch.dispatch36 + %exn54 = load i8*, i8** %exn.slot + %34 = bitcast i32* %x56 to i8* + call void @llvm.eh.begincatch(i8* %exn54, i8* %34) #2 + %35 = load i32, i32* %x56, align 4 + %call59 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01??_C@_03PMGGPEJJ@?$CFd?6?$AA@", i32 0, i32 0), i32 %35) + to label %invoke.cont58 unwind label %lpad57 + +invoke.cont58: ; preds = %catch53 + call void @llvm.eh.endcatch() #2 + br label %try.cont60 + +try.cont60: ; preds = %invoke.cont58, %invoke.cont51, %invoke.cont43 + ret i32 0 + +catch.fallthrough: ; preds = %catch.dispatch36 + %36 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*)) #2 + %matches39 = icmp eq i32 %sel37, %36 + br i1 %matches39, label %catch45, label %catch40 + +catch45: ; preds = %catch.fallthrough + %exn46 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn46, i8* %c48) #2 + %37 = load i8, i8* %c48, align 1 + %conv49 = sext i8 %37 to i32 + %call52 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01??_C@_03PJCJOCBM@?$CFc?6?$AA@", i32 0, i32 0), i32 %conv49) + to label %invoke.cont51 unwind label %lpad50 + +invoke.cont51: ; preds = %catch45 + call void @llvm.eh.endcatch() #2 + br label %try.cont60 + +catch40: ; preds = %catch.fallthrough + %exn41 = load i8*, i8** %exn.slot + call void @llvm.eh.begincatch(i8* %exn41, i8* null) #2 + %call44 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01??_C@_04MPPNMCOK@?4?4?4?6?$AA@", i32 0, i32 0)) + to label %invoke.cont43 unwind label %lpad42 + +invoke.cont43: ; preds = %catch40 + call void @llvm.eh.endcatch() #2 + br label %try.cont60 + +lpad42: ; preds = %catch40 + %38 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %39 = extractvalue { i8*, i32 } %38, 0 + store i8* %39, i8** %exn.slot + %40 = extractvalue { i8*, i32 } %38, 1 + store i32 %40, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +lpad50: ; preds = %catch45 + %41 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %42 = extractvalue { i8*, i32 } %41, 0 + store i8* %42, i8** %exn.slot + %43 = extractvalue { i8*, i32 } %41, 1 + store i32 %43, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +lpad57: ; preds = %catch53 + %44 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %45 = extractvalue { i8*, i32 } %44, 0 + store i8* %45, i8** %exn.slot + %46 = extractvalue { i8*, i32 } %44, 1 + store i32 %46, i32* %ehselector.slot + call void @llvm.eh.endcatch() #2 + br label %eh.resume + +eh.resume: ; preds = %lpad57, %lpad50, %lpad42, %lpad16, %lpad10 + %exn61 = load i8*, i8** %exn.slot + %sel62 = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn61, 0 + %lpad.val63 = insertvalue { i8*, i32 } %lpad.val, i32 %sel62, 1 + resume { i8*, i32 } %lpad.val63 + +unreachable: ; preds = %try.cont33, %try.cont19, %try.cont, %entry + unreachable +} + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #1 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #2 + +declare i32 @printf(i8*, ...) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #2 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind } +attributes #3 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 235214) (llvm/trunk 235213)"} diff --git a/test/CodeGen/WinEH/cppeh-state-calc-1.ll b/test/CodeGen/WinEH/cppeh-state-calc-1.ll new file mode 100644 index 000000000000..3549b1d51dee --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-state-calc-1.ll @@ -0,0 +1,289 @@ +; RUN: llc < %s | FileCheck %s + +; This test was generated from the following code. +; +; void test() { +; try { +; try { +; try { +; two(); +; throw 2; +; } catch (int x) { +; catch_two(); +; } +; a(); +; throw 'a'; +; } catch (char c) { +; catch_a(); +; } +; one(); +; throw 1; +; } catch(int x) { +; catch_one(); +; } catch(...) { +; catch_all(); +; } +; } +; +; The function calls before the throws were declared as 'noexcept' and are +; just here to make blocks easier to identify in the IR. + +; ModuleID = '<stdin>' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +$"\01??_R0H@8" = comdat any + +$"\01??_R0D@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +$"_CT??_R0D@81" = comdat any + +$_CTA1D = comdat any + +$_TI1D = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" +@"\01??_R0D@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".D\00" }, comdat +@llvm.eh.handlertype.D.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0D@8" to i8*) }, section "llvm.metadata" +@__ImageBase = external constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat +@"_CT??_R0D@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0D@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 1, i32 0 }, section ".xdata", comdat +@_CTA1D = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0D@81" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1D = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1D to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat + +; Function Attrs: nounwind uwtable +define void @"\01?test@@YAXXZ"() #0 { +entry: + %tmp = alloca i32, align 4 + %x = alloca i32, align 4 + %tmp2 = alloca i8, align 1 + %c = alloca i8, align 1 + %tmp11 = alloca i32, align 4 + %x21 = alloca i32, align 4 + call void @"\01?two@@YAXXZ"() #3 + store i32 2, i32* %tmp + %0 = bitcast i32* %tmp to i8* + call void (...) @llvm.frameescape(i32* %x, i8* %c, i32* %x21) + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #5 + to label %unreachable unwind label %lpad + +lpad: ; preds = %entry + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.D.0 + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %recover = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 0, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch1", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch2", i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch3") + indirectbr i8* %recover, [label %try.cont, label %try.cont10, label %try.cont22] + +try.cont: ; preds = %lpad + call void @"\01?a@@YAXXZ"() #3 + store i8 97, i8* %tmp2 + invoke void @_CxxThrowException(i8* %tmp2, %eh.ThrowInfo* @_TI1D) #5 + to label %unreachable unwind label %lpad3 + +lpad3: ; preds = %try.cont + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.D.0 + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %recover1 = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.D.0 to i8*), i32 1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch1", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch2", i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch3") + indirectbr i8* %recover1, [label %try.cont10, label %try.cont22] + +try.cont10: ; preds = %lpad3, %lpad + call void @"\01?one@@YAXXZ"() #3 + store i32 1, i32* %tmp11 + %3 = bitcast i32* %tmp11 to i8* + invoke void @_CxxThrowException(i8* %3, %eh.ThrowInfo* @_TI1H) #5 + to label %unreachable unwind label %lpad12 + +lpad12: ; preds = %try.cont10 + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.0 + catch i8* null + %recover2 = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.0 to i8*), i32 2, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch2", i32 1, i8* null, i32 -1, i8* (i8*, i8*)* @"\01?test@@YAXXZ.catch3") + indirectbr i8* %recover2, [label %try.cont22] + +try.cont22: ; preds = %lpad12, %lpad3, %lpad + ret void + +unreachable: ; preds = %try.cont10, %try.cont, %entry + unreachable +} + +; Function Attrs: nounwind +declare void @"\01?two@@YAXXZ"() #1 + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @"\01?catch_two@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +; Function Attrs: nounwind +declare void @"\01?a@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @"\01?catch_a@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @"\01?one@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @"\01?catch_all@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare void @"\01?catch_one@@YAXXZ"() #1 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #3 + +define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) #4 { +entry: + %x.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) + %x = bitcast i8* %x.i8 to i32* + %2 = bitcast i32* %x to i8* + call void @"\01?catch_two@@YAXXZ"() #3 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) + +stub: ; preds = %entry + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +; Function Attrs: nounwind readnone +declare void @llvm.donothing() #2 + +define internal i8* @"\01?test@@YAXXZ.catch1"(i8*, i8*) #4 { +entry: + call void @"\01?catch_a@@YAXXZ"() #3 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont10) + +stub: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +define internal i8* @"\01?test@@YAXXZ.catch2"(i8*, i8*) #4 { +entry: + %x21.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) + %x21 = bitcast i8* %x21.i8 to i32* + %2 = bitcast i32* %x21 to i8* + call void @"\01?catch_one@@YAXXZ"() #3 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont22) + +stub: ; preds = %entry + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +define internal i8* @"\01?test@@YAXXZ.catch3"(i8*, i8*) #4 { +entry: + call void @"\01?catch_all@@YAXXZ"() #3 + invoke void @llvm.donothing() + to label %entry.split unwind label %stub + +entry.split: ; preds = %entry + ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont22) + +stub: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + %recover = call i8* (...) @llvm.eh.actions() + unreachable +} + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #3 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #2 + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="?test@@YAXXZ" } +attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { "wineh-parent"="?test@@YAXXZ" } +attributes #5 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 (trunk 236059)"} + +; CHECK-LABEL: "$cppxdata$?test@@YAXXZ": +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long +; CHECK-NEXT: .long ("$stateUnwindMap$?test@@YAXXZ")@IMGREL +; CHECK-NEXT: .long +; CHECK-NEXT: .long ("$tryMap$?test@@YAXXZ")@IMGREL +; CHECK-NEXT: .long +; CHECK-NEXT: .long ("$ip2state$?test@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 40 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK: "$stateUnwindMap$?test@@YAXXZ": +; CHECK: "$tryMap$?test@@YAXXZ": +; CHECK: "$handlerMap$0$?test@@YAXXZ": +; CHECK: "$ip2state$?test@@YAXXZ": +; CHECK-NEXT: .long .Lfunc_begin0@IMGREL +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long .Ltmp3@IMGREL +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp6@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long .Lfunc_begin1@IMGREL +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long .Lfunc_begin2@IMGREL +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long .Lfunc_begin3@IMGREL +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .long .Lfunc_begin4@IMGREL +; CHECK-NEXT: .long 6 diff --git a/test/CodeGen/WinEH/lit.local.cfg b/test/CodeGen/WinEH/lit.local.cfg new file mode 100644 index 000000000000..67905d7e06e6 --- /dev/null +++ b/test/CodeGen/WinEH/lit.local.cfg @@ -0,0 +1,12 @@ +# FIXME: For now, override suffixes to exclude any .s tests, because some of the +# buildbots have a stray misched-copy.s output file lying around that causes +# failures. See misched-copy.s where we try and clean up that file. +# +# It should be possible to remove this override once all the bots have cycled +# cleanly. +config.suffixes = ['.ll', '.test', '.txt'] + +# FIXME: Add Windows on ARM support to these tests. +if not 'X86' in config.root.targets: + config.unsupported = True + diff --git a/test/CodeGen/WinEH/seh-catch-all.ll b/test/CodeGen/WinEH/seh-catch-all.ll new file mode 100644 index 000000000000..c2a652b80990 --- /dev/null +++ b/test/CodeGen/WinEH/seh-catch-all.ll @@ -0,0 +1,59 @@ +; RUN: opt -S -winehprepare < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +@str.__except = internal unnamed_addr constant [9 x i8] c"__except\00", align 1 + +; Function Attrs: uwtable + +declare i32 @puts(i8*) + +define void @may_crash() { +entry: + store volatile i32 42, i32* null, align 4 + ret void +} + +declare i32 @__C_specific_handler(...) + +; Function Attrs: nounwind readnone +declare i8* @llvm.frameaddress(i32) + +; Function Attrs: uwtable +define void @seh_catch_all() { +entry: + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + invoke void @may_crash() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + br label %__try.cont + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + store i8* %1, i8** %exn.slot + %2 = extractvalue { i8*, i32 } %0, 1 + store i32 %2, i32* %ehselector.slot + br label %__except + +__except: ; preds = %lpad + %call = call i32 @puts(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @str.__except, i32 0, i32 0)) + br label %__try.cont + +__try.cont: ; preds = %__except, %invoke.cont + ret void +} + +; CHECK-LABEL: define void @seh_catch_all() +; CHECK: landingpad +; CHECK-NEXT: catch i8* null +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@seh_catch_all, %lpad.split)) +; CHECK-NEXT: indirectbr +; +; CHECK: lpad.split: +; CHECK-NOT: extractvalue +; CHECK: call i32 @puts diff --git a/test/CodeGen/WinEH/seh-inlined-finally.ll b/test/CodeGen/WinEH/seh-inlined-finally.ll new file mode 100644 index 000000000000..d2080cff79d4 --- /dev/null +++ b/test/CodeGen/WinEH/seh-inlined-finally.ll @@ -0,0 +1,83 @@ +; RUN: opt -S -winehprepare < %s | FileCheck %s + +; Check that things work when the mid-level optimizer inlines the finally +; block. + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +%struct._RTL_CRITICAL_SECTION = type { %struct._RTL_CRITICAL_SECTION_DEBUG*, i32, i32, i8*, i8*, i64 } +%struct._RTL_CRITICAL_SECTION_DEBUG = type { i16, i16, %struct._RTL_CRITICAL_SECTION*, %struct._LIST_ENTRY, i32, i32, i32, i16, i16 } +%struct._LIST_ENTRY = type { %struct._LIST_ENTRY*, %struct._LIST_ENTRY* } + +declare i32 @puts(i8*) +declare void @may_crash() +declare i32 @__C_specific_handler(...) +declare i8* @llvm.framerecover(i8*, i8*, i32) #1 +declare i8* @llvm.frameaddress(i32) +declare void @llvm.frameescape(...) +declare dllimport void @EnterCriticalSection(%struct._RTL_CRITICAL_SECTION*) +declare dllimport void @LeaveCriticalSection(%struct._RTL_CRITICAL_SECTION*) + +define void @use_finally() { +entry: + invoke void @may_crash() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + %call.i = tail call i32 @puts(i8* null) + ret void + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %call.i2 = tail call i32 @puts(i8* null) + resume { i8*, i32 } %0 +} + +; CHECK-LABEL: define void @use_finally() +; CHECK: invoke void @may_crash() +; +; CHECK: landingpad +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @use_finally.cleanup) +; CHECK-NEXT: indirectbr i8* %recover, [] + +; Function Attrs: nounwind uwtable +define i32 @call_may_crash_locked() { +entry: + %p = alloca %struct._RTL_CRITICAL_SECTION, align 8 + call void (...) @llvm.frameescape(%struct._RTL_CRITICAL_SECTION* %p) + call void @EnterCriticalSection(%struct._RTL_CRITICAL_SECTION* %p) + invoke void @may_crash() + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + %tmp2 = call i8* @llvm.frameaddress(i32 0) + %tmp3 = call i8* @llvm.framerecover(i8* bitcast (i32 ()* @call_may_crash_locked to i8*), i8* %tmp2, i32 0) #2 + %tmp6 = bitcast i8* %tmp3 to %struct._RTL_CRITICAL_SECTION* + call void @LeaveCriticalSection(%struct._RTL_CRITICAL_SECTION* %tmp6) + ret i32 42 + +lpad: ; preds = %entry + %tmp7 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %tmp8 = call i8* @llvm.frameaddress(i32 0) + %tmp9 = call i8* @llvm.framerecover(i8* bitcast (i32 ()* @call_may_crash_locked to i8*), i8* %tmp8, i32 0) + %tmp12 = bitcast i8* %tmp9 to %struct._RTL_CRITICAL_SECTION* + call void @LeaveCriticalSection(%struct._RTL_CRITICAL_SECTION* %tmp12) + resume { i8*, i32 } %tmp7 +} + +; CHECK-LABEL: define i32 @call_may_crash_locked() +; CHECK: invoke void @may_crash() +; +; CHECK: landingpad +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @call_may_crash_locked.cleanup) +; CHECK-NEXT: indirectbr i8* %recover, [] + +; CHECK-LABEL: define internal void @call_may_crash_locked.cleanup(i8*, i8*) +; CHECK: %tmp9 = call i8* @llvm.framerecover(i8* bitcast (i32 ()* @call_may_crash_locked to i8*), i8* %1, i32 0) +; CHECK: %tmp12 = bitcast i8* %tmp9 to %struct._RTL_CRITICAL_SECTION* +; CHECK: call void @LeaveCriticalSection(%struct._RTL_CRITICAL_SECTION* %tmp12) diff --git a/test/CodeGen/WinEH/seh-outlined-finally.ll b/test/CodeGen/WinEH/seh-outlined-finally.ll new file mode 100644 index 000000000000..19558b705308 --- /dev/null +++ b/test/CodeGen/WinEH/seh-outlined-finally.ll @@ -0,0 +1,155 @@ +; RUN: opt -S -winehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s + +; Test case based on this code: +; +; extern "C" int _abnormal_termination(); +; #pragma intrinsic(_abnormal_termination) +; extern "C" int printf(const char *, ...); +; extern "C" void may_crash() { +; *(volatile int *)0 = 42; +; } +; int main() { +; int myres = 0; +; __try { +; __try { +; may_crash(); +; } __finally { +; printf("inner finally %d\n", _abnormal_termination()); +; may_crash(); +; } +; } __finally { +; printf("outer finally %d\n", _abnormal_termination()); +; } +; } +; +; Note that if the inner finally crashes, the outer finally still runs. There +; is nothing like a std::terminate call in this situation. + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +@str_outer_finally = linkonce_odr unnamed_addr constant [18 x i8] c"outer finally %d\0A\00", align 1 +@str_inner_finally = linkonce_odr unnamed_addr constant [18 x i8] c"inner finally %d\0A\00", align 1 + +; Function Attrs: nounwind uwtable +define void @may_crash() #0 { +entry: + store volatile i32 42, i32* null, align 4 + ret void +} + +; Function Attrs: uwtable +define i32 @main() #1 { +entry: + %myres = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 0, i32* %myres, align 4 + invoke void @may_crash() #4 + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + %0 = call i8* @llvm.frameaddress(i32 0) + invoke void @"\01?fin$1@0@main@@"(i1 zeroext false, i8* %0) #4 + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %invoke.cont + %1 = call i8* @llvm.frameaddress(i32 0) + call void @"\01?fin$0@0@main@@"(i1 zeroext false, i8* %1) + ret i32 0 + +lpad: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %3 = extractvalue { i8*, i32 } %2, 0 + store i8* %3, i8** %exn.slot + %4 = extractvalue { i8*, i32 } %2, 1 + store i32 %4, i32* %ehselector.slot + %5 = call i8* @llvm.frameaddress(i32 0) + invoke void @"\01?fin$1@0@main@@"(i1 zeroext true, i8* %5) #4 + to label %invoke.cont3 unwind label %lpad1 + +lpad1: ; preds = %lpad, %invoke.cont + %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %7 = extractvalue { i8*, i32 } %6, 0 + store i8* %7, i8** %exn.slot + %8 = extractvalue { i8*, i32 } %6, 1 + store i32 %8, i32* %ehselector.slot + br label %ehcleanup + +invoke.cont3: ; preds = %lpad + br label %ehcleanup + +ehcleanup: ; preds = %invoke.cont3, %lpad1 + %9 = call i8* @llvm.frameaddress(i32 0) + call void @"\01?fin$0@0@main@@"(i1 zeroext true, i8* %9) + br label %eh.resume + +eh.resume: ; preds = %ehcleanup + %exn = load i8*, i8** %exn.slot + %sel = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0 + %lpad.val4 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val4 +} + +; CHECK-NOT: define internal void @ + +; CHECK-LABEL: define i32 @main() +; CHECK: invoke void @may_crash() +; +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$1@0@main@@", i32 0, void (i1, i8*)* @"\01?fin$0@0@main@@") +; CHECK-NEXT: indirectbr +; +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$0@0@main@@") +; CHECK-NEXT: indirectbr + +; There should not be any *new* cleanup helpers, just the existing ones. +; CHECK-NOT: define internal void @ +; CHECK: define internal void @"\01?fin$0@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) +; CHECK-NOT: define internal void @ +; CHECK: define internal void @"\01?fin$1@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) +; CHECK-NOT: define internal void @ + +define internal void @"\01?fin$0@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 { +entry: + %frame_pointer.addr = alloca i8*, align 8 + %abnormal_termination.addr = alloca i8, align 1 + store i8* %frame_pointer, i8** %frame_pointer.addr, align 8 + %frombool = zext i1 %abnormal_termination to i8 + store i8 %frombool, i8* %abnormal_termination.addr, align 1 + %0 = zext i1 %abnormal_termination to i32 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_outer_finally, i32 0, i32 0), i32 %0) + ret void +} + +declare i32 @printf(i8*, ...) #2 + +define internal void @"\01?fin$1@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 { +entry: + %frame_pointer.addr = alloca i8*, align 8 + %abnormal_termination.addr = alloca i8, align 1 + store i8* %frame_pointer, i8** %frame_pointer.addr, align 8 + %frombool = zext i1 %abnormal_termination to i8 + store i8 %frombool, i8* %abnormal_termination.addr, align 1 + %0 = zext i1 %abnormal_termination to i32 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_inner_finally, i32 0, i32 0), i32 %0) + call void @may_crash() + ret void +} + +declare i32 @__C_specific_handler(...) + +; Function Attrs: nounwind readnone +declare i8* @llvm.frameaddress(i32) #3 + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } +attributes #4 = { noinline } diff --git a/test/CodeGen/WinEH/seh-prepared-basic.ll b/test/CodeGen/WinEH/seh-prepared-basic.ll new file mode 100644 index 000000000000..880bb3c33a8d --- /dev/null +++ b/test/CodeGen/WinEH/seh-prepared-basic.ll @@ -0,0 +1,83 @@ +; RUN: llc < %s | FileCheck %s + +; Test case based on this code: +; extern "C" unsigned long _exception_code(); +; extern "C" int filt(unsigned long); +; extern "C" void g(); +; extern "C" void do_except() { +; __try { +; g(); +; } __except(filt(_exception_code())) { +; } +; } + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; Function Attrs: uwtable +define void @do_except() #0 { +entry: + call void (...) @llvm.frameescape() + invoke void @g() #5 + to label %__try.cont unwind label %lpad1 + +lpad1: ; preds = %entry + %ehvals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@do_except@@" to i8*) + %recover = call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@do_except@@" to i8*), i32 -1, i8* blockaddress(@do_except, %__try.cont)) + indirectbr i8* %recover, [label %__try.cont] + +__try.cont: ; preds = %lpad1, %entry + ret void +} + +; CHECK-LABEL: do_except: +; CHECK: .seh_handler __C_specific_handler +; CHECK-NOT: jmpq * +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp{{.*}} +; CHECK-NEXT: .long .Ltmp{{.*}} +; CHECK-NEXT: .long "?filt$0@0@do_except@@"@IMGREL +; CHECK-NEXT: .long .Ltmp{{.*}}@IMGREL + +; Function Attrs: noinline nounwind +define internal i32 @"\01?filt$0@0@do_except@@"(i8* nocapture readonly %exception_pointers, i8* nocapture readnone %frame_pointer) #1 { +entry: + %0 = bitcast i8* %exception_pointers to i32** + %1 = load i32*, i32** %0, align 8 + %2 = load i32, i32* %1, align 4 + %call = tail call i32 @filt(i32 %2) #4 + ret i32 %call +} + +declare i32 @filt(i32) #2 + +declare void @g() #2 + +declare i32 @__C_specific_handler(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #3 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #4 + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #4 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #3 + +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="do_except" } +attributes #1 = { noinline nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } +attributes #4 = { nounwind } +attributes #5 = { noinline } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.7.0 "} diff --git a/test/CodeGen/WinEH/seh-resume-phi.ll b/test/CodeGen/WinEH/seh-resume-phi.ll new file mode 100644 index 000000000000..256dd852d287 --- /dev/null +++ b/test/CodeGen/WinEH/seh-resume-phi.ll @@ -0,0 +1,66 @@ +; RUN: opt -S -winehprepare < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare void @might_crash(i8* %ehptr) +declare i32 @filt() +declare void @cleanup() +declare i32 @__C_specific_handler(...) +declare i32 @llvm.eh.typeid.for(i8*) + +define void @resume_phi() { +entry: + invoke void @might_crash(i8* null) + to label %return unwind label %lpad1 + +lpad1: + %ehvals1 = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + catch i32 ()* @filt + %ehptr1 = extractvalue { i8*, i32 } %ehvals1, 0 + %ehsel1 = extractvalue { i8*, i32 } %ehvals1, 1 + %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*)) + %matches = icmp eq i32 %ehsel1, %filt_sel + br i1 %matches, label %__except, label %eh.resume + +__except: + invoke void @might_crash(i8* %ehptr1) + to label %return unwind label %lpad2 + +lpad2: + %ehvals2 = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + cleanup + %ehptr2 = extractvalue { i8*, i32 } %ehvals2, 0 + %ehsel2 = extractvalue { i8*, i32 } %ehvals2, 1 + call void @cleanup() + br label %eh.resume + +return: + ret void + +eh.resume: + %ehptr.phi = phi i8* [ %ehptr1, %lpad1 ], [ %ehptr2, %lpad2 ] + %ehsel.phi = phi i32 [ %ehsel1, %lpad1 ], [ %ehsel2, %lpad2 ] + %ehval.phi1 = insertvalue { i8*, i32 } undef, i8* %ehptr.phi, 0 + %ehval.phi2 = insertvalue { i8*, i32 } %ehval.phi1, i32 %ehsel.phi, 1 + resume { i8*, i32 } %ehval.phi2 +} + +; CHECK-LABEL: define void @resume_phi() +; CHECK: invoke void @might_crash(i8* null) +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: catch i32 ()* @filt +; CHECK-NEXT: call i8* (...) @llvm.eh.actions( +; CHECK-SAME: i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@resume_phi, %__except)) +; CHECK-NEXT: indirectbr {{.*}} [label %__except] +; +; CHECK: __except: +; CHECK: call i32 @llvm.eh.exceptioncode() +; CHECK: invoke void @might_crash(i8* %{{.*}}) +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @resume_phi.cleanup) +; CHECK-NEXT: indirectbr {{.*}} [] + +; CHECK-LABEL: define internal void @resume_phi.cleanup(i8*, i8*) +; CHECK: call void @cleanup() diff --git a/test/CodeGen/WinEH/seh-simple.ll b/test/CodeGen/WinEH/seh-simple.ll new file mode 100644 index 000000000000..9a451874d587 --- /dev/null +++ b/test/CodeGen/WinEH/seh-simple.ll @@ -0,0 +1,201 @@ +; RUN: opt -S -winehprepare -mtriple=x86_64-windows-msvc < %s \ +; RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X64 + +; This test should also pass in 32-bit using _except_handler3. +; RUN: sed -e 's/__C_specific_handler/_except_handler3/' %s \ +; RUN: | opt -S -winehprepare -mtriple=i686-windows-msvc \ +; RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X86 + +declare void @cleanup() +declare i32 @filt() +declare void @might_crash() +declare i32 @__C_specific_handler(...) +declare i32 @llvm.eh.typeid.for(i8*) + +define i32 @simple_except_store() { +entry: + %retval = alloca i32 + store i32 0, i32* %retval + invoke void @might_crash() + to label %return unwind label %lpad + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + catch i32 ()* @filt + %sel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*)) + %matches = icmp eq i32 %sel, %filt_sel + br i1 %matches, label %__except, label %eh.resume + +__except: + store i32 1, i32* %retval + br label %return + +return: + %r = load i32, i32* %retval + ret i32 %r + +eh.resume: + resume { i8*, i32 } %ehvals +} + +; CHECK-LABEL: define i32 @simple_except_store() +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: catch i32 ()* @filt +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@simple_except_store, %__except)) +; CHECK-NEXT: indirectbr {{.*}} [label %__except] + +define i32 @catch_all() { +entry: + %retval = alloca i32 + store i32 0, i32* %retval + invoke void @might_crash() + to label %return unwind label %lpad + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + catch i8* null + store i32 1, i32* %retval + br label %return + +return: + %r = load i32, i32* %retval + ret i32 %r +} + +; CHECK-LABEL: define i32 @catch_all() +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: catch i8* null +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* null, i32 -1, i8* blockaddress(@catch_all, %lpad.split)) +; CHECK-NEXT: indirectbr {{.*}} [label %lpad.split] +; +; CHECK: lpad.split: +; CHECK: store i32 1, i32* %retval + + +define i32 @except_phi() { +entry: + invoke void @might_crash() + to label %return unwind label %lpad + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + catch i32 ()* @filt + %sel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*)) + %matches = icmp eq i32 %sel, %filt_sel + br i1 %matches, label %return, label %eh.resume + +return: + %r = phi i32 [0, %entry], [1, %lpad] + ret i32 %r + +eh.resume: + resume { i8*, i32 } %ehvals +} + +; CHECK-LABEL: define i32 @except_phi() +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: catch i32 ()* @filt +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@except_phi, %lpad.return_crit_edge)) +; CHECK-NEXT: indirectbr {{.*}} [label %lpad.return_crit_edge] +; +; CHECK: lpad.return_crit_edge: +; CHECK: br label %return +; +; CHECK: return: +; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad.return_crit_edge ] +; CHECK-NEXT: ret i32 %r + +define i32 @lpad_phi() { +entry: + invoke void @might_crash() + to label %cont unwind label %lpad + +cont: + invoke void @might_crash() + to label %return unwind label %lpad + +lpad: + %ncalls.1 = phi i32 [ 0, %entry ], [ 1, %cont ] + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + catch i32 ()* @filt + %sel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*)) + %matches = icmp eq i32 %sel, %filt_sel + br i1 %matches, label %return, label %eh.resume + +return: + %r = phi i32 [2, %cont], [%ncalls.1, %lpad] + ret i32 %r + +eh.resume: + resume { i8*, i32 } %ehvals +} + +; CHECK-LABEL: define i32 @lpad_phi() +; CHECK: alloca i32 +; CHECK: store i32 0, i32* +; CHECK: invoke void @might_crash() +; CHECK: store i32 1, i32* +; CHECK: invoke void @might_crash() +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i32 ()* @filt +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void ({{.*}})* @lpad_phi.cleanup, i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@lpad_phi, %lpad.return_crit_edge)) +; CHECK-NEXT: indirectbr {{.*}} [label %lpad.return_crit_edge] +; +; CHECK: lpad.return_crit_edge: +; CHECK: load i32, i32* +; CHECK: br label %return +; +; CHECK: return: +; CHECK-NEXT: %r = phi i32 [ 2, %cont ], [ %{{.*}}, %lpad.return_crit_edge ] +; CHECK-NEXT: ret i32 %r + +define i32 @cleanup_and_except() { +entry: + invoke void @might_crash() + to label %return unwind label %lpad + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + cleanup + catch i32 ()* @filt + call void @cleanup() + %sel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*)) + %matches = icmp eq i32 %sel, %filt_sel + br i1 %matches, label %return, label %eh.resume + +return: + %r = phi i32 [0, %entry], [1, %lpad] + ret i32 %r + +eh.resume: + resume { i8*, i32 } %ehvals +} + +; CHECK-LABEL: define i32 @cleanup_and_except() +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: catch i32 ()* @filt +; CHECK-NEXT: call i8* (...) @llvm.eh.actions( +; CHECK: i32 0, void ({{.*}})* @cleanup_and_except.cleanup, +; CHECK: i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@cleanup_and_except, %lpad.return_crit_edge)) +; CHECK-NEXT: indirectbr {{.*}} [label %lpad.return_crit_edge] +; +; CHECK: lpad.return_crit_edge: +; CHECK: br label %return +; +; CHECK: return: +; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad.return_crit_edge ] +; CHECK-NEXT: ret i32 %r + +; FIXME: This cleanup is an artifact of bad demotion. +; X64-LABEL: define internal void @lpad_phi.cleanup(i8*, i8*) +; X86-LABEL: define internal void @lpad_phi.cleanup() +; X86: call i8* @llvm.frameaddress(i32 1) +; CHECK: call i8* @llvm.framerecover({{.*}}) +; CHECK: load i32 +; CHECK: store i32 %{{.*}}, i32* |
