diff --git a/clang/test/CodeGen/exceptions-seh-leave-new.c b/clang/test/CodeGen/exceptions-seh-leave-new.c new file mode 100644 index 000000000000..69c1a56bca92 --- /dev/null +++ b/clang/test/CodeGen/exceptions-seh-leave-new.c @@ -0,0 +1,347 @@ +// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -fnew-ms-eh -emit-llvm -o - | FileCheck %s + +void g(void); + +////////////////////////////////////////////////////////////////////////////// +// __leave with __except + +// Nothing in the __try block can trap, so __try.cont isn't created. +int __leave_with___except_simple() { + int myres = 0; + __try { + myres = 15; + __leave; + myres = 23; + } __except (1) { + return 0; + } + return 1; +} +// CHECK-LABEL: define i32 @__leave_with___except_simple() +// CHECK: store i32 15, i32* %myres +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 +// CHECK: [[tryleave]] +// CHECK-NEXT: ret i32 1 + + +// The "normal" case. +int __leave_with___except() { + int myres = 0; + __try { + g(); + __leave; + myres = 23; + } __except (1) { + return 0; + } + return 1; +} +// CHECK-LABEL: define i32 @__leave_with___except() +// CHECK: invoke void @g() +// CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}} +// For __excepts, instead of an explicit __try.__leave label, we could use +// use invoke.cont as __leave jump target instead. However, not doing this +// keeps the CodeGen code simpler, __leave is very rare, and SimplifyCFG will +// simplify this anyways. +// CHECK: [[cont]] +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 +// CHECK: [[tryleave]] +// CHECK-NEXT: br label % + + +////////////////////////////////////////////////////////////////////////////// +// __leave with __finally + +void abort(void) __attribute__((noreturn)); + +// Nothing in the __try block can trap, so __finally.cont and friends aren't +// created. +int __leave_with___finally_simple() { + int myres = 0; + __try { + myres = 15; + __leave; + myres = 23; + } __finally { + return 0; + } + return 1; +} +// CHECK-LABEL: define i32 @__leave_with___finally_simple() +// CHECK: store i32 15, i32* %myres +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 +// CHECK: [[tryleave]] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally_simple@@"(i8 0, i8* %[[fp]]) + +// __finally block doesn't return, __finally.cont doesn't exist. +int __leave_with___finally_noreturn() { + int myres = 0; + __try { + myres = 15; + __leave; + myres = 23; + } __finally { + abort(); + } + return 1; +} +// CHECK-LABEL: define i32 @__leave_with___finally_noreturn() +// CHECK: store i32 15, i32* %myres +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 +// CHECK: [[tryleave]] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally_noreturn@@"(i8 0, i8* %[[fp]]) + +// The "normal" case. +int __leave_with___finally() { + int myres = 0; + __try { + g(); + __leave; + myres = 23; + } __finally { + return 0; + } + return 1; +} +// CHECK-LABEL: define i32 @__leave_with___finally() +// CHECK: invoke void @g() +// CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}} +// For __finally, there needs to be an explicit __try.__leave, because +// abnormal.termination.slot needs to be set there. +// CHECK: [[cont]] +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 +// CHECK: [[tryleave]] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally@@"(i8 0, i8* %[[fp]]) + + +////////////////////////////////////////////////////////////////////////////// +// Mixed, nested cases. + +int nested___except___finally() { + int myres = 0; + __try { + __try { + g(); + } __finally { + g(); + __leave; // Refers to the outer __try, not the __finally! + myres = 23; + return 0; + } + + myres = 51; + } __except (1) { + } + return 1; +} +// CHECK-LABEL: define i32 @nested___except___finally() + +// CHECK-LABEL: invoke void @g() +// CHECK-NEXT: to label %[[g1_cont1:.*]] unwind label %[[g1_lpad:.*]] + +// CHECK: [[g1_cont1]] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$0@0@nested___except___finally@@"(i8 0, i8* %[[fp]]) +// CHECK-NEXT: to label %[[fin_cont:.*]] unwind label %[[g2_lpad:.*]] + +// CHECK: [[fin_cont]] +// CHECK: store i32 51, i32* % +// CHECK-NEXT: br label %[[trycont:[^ ]*]] + +// CHECK: [[g1_lpad]] +// CHECK-NEXT: cleanuppad +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$0@0@nested___except___finally@@"(i8 1, i8* %[[fp]]) +// CHECK-NEXT: to label %[[g1_resume:.*]] unwind label %[[cleanupend:[^ ]*]] +// CHECK: cleanupret {{.*}} unwind label %[[g2_lpad]] + +// CHECK: [[g2_lpad]] +// CHECK: catchpad [i8* null] +// CHECK: catchret +// CHECK: br label %[[trycont]] + +// CHECK: [[trycont]] +// CHECK-NEXT: ret i32 1 + +// CHECK: [[cleanupend]] +// CHECK-NEXT: cleanupendpad {{.*}} unwind label %[[g2_lpad]] + +// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___except___finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: call void @g() +// CHECK: unreachable + +int nested___except___except() { + int myres = 0; + __try { + __try { + g(); + myres = 16; + } __except (1) { + g(); + __leave; // Refers to the outer __try, not the __except we're in! + myres = 23; + return 0; + } + + myres = 51; + } __except (1) { + } + return 1; +} +// The order of basic blocks in the below doesn't matter. +// CHECK-LABEL: define i32 @nested___except___except() + +// CHECK-LABEL: invoke void @g() +// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] + +// CHECK: [[g1_lpad]] +// CHECK: catchpad [i8* null] +// CHECK: catchret {{.*}} to label %[[except:[^ ]*]] +// CHECK: [[except]] +// CHECK: invoke void @g() +// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] + +// CHECK: [[g2_lpad]] +// CHECK: catchpad [i8* null] +// CHECK: catchret +// CHECK: br label %[[trycont4:[^ ]*]] + +// CHECK: [[trycont4]] +// CHECK-NEXT: ret i32 1 + +// CHECK: [[g2_cont]] +// CHECK-NEXT: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: store i32 23 + +// CHECK: [[g1_cont]] +// CHECK: store i32 16, i32* %myres +// CHECK-NEXT: br label %[[trycont:[^ ]*]] + +// CHECK: [[trycont]] +// CHECK-NEXT: store i32 51, i32* %myres +// CHECK-NEXT: br label %[[tryleave]] + +// CHECK: [[tryleave]] +// CHECK-NEXT: br label %[[trycont4]] + +int nested___finally___except() { + int myres = 0; + __try { + __try { + g(); + } __except (1) { + g(); + __leave; // Refers to the outer __try, not the __except! + myres = 23; + return 0; + } + + myres = 51; + } __finally { + } + return 1; +} +// The order of basic blocks in the below doesn't matter. +// CHECK-LABEL: define i32 @nested___finally___except() + +// CHECK-LABEL: invoke void @g() +// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] + +// CHECK: [[g1_lpad]] +// CHECK: catchpad +// CHECK: catchret +// CHECK: invoke void @g() +// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] + +// CHECK: [[g2_cont]] +// CHECK: br label %[[tryleave:[^ ]*]] +// CHECK-NOT: 23 + +// CHECK: [[g1_cont]] +// CHECK-NEXT: br label %[[trycont:[^ ]*]] + +// CHECK: [[trycont]] +// CHECK: store i32 51, i32* % +// CHECK-NEXT: br label %[[tryleave]] + +// CHECK: [[tryleave]] +// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___except@@"(i8 0, i8* %[[fp]]) +// CHECK-NEXT: ret i32 1 + +// CHECK: [[g2_lpad]] +// CHECK: cleanuppad +// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$0@0@nested___finally___except@@"(i8 1, i8* %[[fp]]) +// CHECK: cleanupret {{.*}} unwind to caller + +// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___except@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: ret void + +int nested___finally___finally() { + int myres = 0; + __try { + __try { + g(); + myres = 16; + } __finally { + g(); + __leave; // Refers to the outer __try, not the __finally we're in! + myres = 23; + return 0; + } + + myres = 51; + } __finally { + } + return 1; +} +// The order of basic blocks in the below doesn't matter. +// CHECK-LABEL: define i32 @nested___finally___finally() + +// CHECK: invoke void @g() +// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] + +// CHECK: [[g1_cont]] +// CHECK: store i32 16, i32* %[[myres:[^ ]*]], +// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i8 0, i8* %[[fp]]) +// CHECK-NEXT: to label %[[finally_cont:.*]] unwind label %[[g2_lpad:.*]] + +// CHECK: [[finally_cont]] +// CHECK: store i32 51, i32* %[[myres]] +// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i8 0, i8* %[[fp]]) +// CHECK-NEXT: ret i32 1 + +// CHECK: [[g1_lpad]] +// CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad [] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i8 1, i8* %[[fp]]) +// CHECK-NEXT: to label %[[finally_cont2:.*]] unwind label %[[endcleanup:[^ ]*]] +// CHECK: [[finally_cont2]] +// CHECK: cleanupret %[[padtoken]] unwind label %[[g2_lpad]] + +// CHECK: [[endcleanup]] +// CHECK-NEXT: cleanupendpad %[[padtoken]] unwind label %[[g2_lpad]] + +// CHECK: [[g2_lpad]] +// CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad [] +// CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress() +// CHECK-NEXT: invoke void @"\01?fin$0@0@nested___finally___finally@@"(i8 1, i8* %[[fp]]) +// CHECK: cleanupret %[[padtoken]] unwind to caller + +// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: ret void + +// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer) +// CHECK: call void @g() +// CHECK: unreachable