| // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -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(void) { | |
| int myres = 0; | |
| __try { | |
| myres = 15; | |
| __leave; | |
| myres = 23; | |
| } __except (1) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| // CHECK-LABEL: define dso_local i32 @__leave_with___except_simple() | |
| // CHECK: store i32 15, ptr %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(void) { | |
| int myres = 0; | |
| __try { | |
| g(); | |
| __leave; | |
| myres = 23; | |
| } __except (1) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| // CHECK-LABEL: define dso_local 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(void) { | |
| int myres = 0; | |
| __try { | |
| myres = 15; | |
| __leave; | |
| myres = 23; | |
| } __finally { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| // CHECK-LABEL: define dso_local i32 @__leave_with___finally_simple() | |
| // CHECK: store i32 15, ptr %myres | |
| // CHECK-NEXT: br label %[[tryleave:[^ ]*]] | |
| // CHECK-NOT: store i32 23 | |
| // CHECK: [[tryleave]] | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally_simple@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // __finally block doesn't return, __finally.cont doesn't exist. | |
| int __leave_with___finally_noreturn(void) { | |
| int myres = 0; | |
| __try { | |
| myres = 15; | |
| __leave; | |
| myres = 23; | |
| } __finally { | |
| abort(); | |
| } | |
| return 1; | |
| } | |
| // CHECK-LABEL: define dso_local i32 @__leave_with___finally_noreturn() | |
| // CHECK: store i32 15, ptr %myres | |
| // CHECK-NEXT: br label %[[tryleave:[^ ]*]] | |
| // CHECK-NOT: store i32 23 | |
| // CHECK: [[tryleave]] | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally_noreturn@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // The "normal" case. | |
| int __leave_with___finally(void) { | |
| int myres = 0; | |
| __try { | |
| g(); | |
| __leave; | |
| myres = 23; | |
| } __finally { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| // CHECK-LABEL: define dso_local 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 ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| ////////////////////////////////////////////////////////////////////////////// | |
| // Mixed, nested cases. | |
| int nested___except___finally(void) { | |
| 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 dso_local 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 ptr @llvm.localaddress() | |
| // CHECK-NEXT: invoke void @"?fin$0@0@nested___except___finally@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: to label %[[fin_cont:.*]] unwind label %[[g2_lpad:.*]] | |
| // CHECK: [[fin_cont]] | |
| // CHECK: store i32 51, ptr % | |
| // CHECK-NEXT: br label %[[trycont:[^ ]*]] | |
| // CHECK: [[g1_lpad]] | |
| // CHECK-NEXT: cleanuppad | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: invoke void @"?fin$0@0@nested___except___finally@@"(i8 noundef 1, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: to label %[[g1_resume:.*]] unwind label %[[g2_lpad]] | |
| // CHECK: cleanupret {{.*}} unwind label %[[g2_lpad]] | |
| // CHECK: [[g2_lpad]] | |
| // CHECK: catchpad {{.*}} [ptr null] | |
| // CHECK: catchret | |
| // CHECK: br label %[[trycont]] | |
| // CHECK: [[trycont]] | |
| // CHECK-NEXT: ret i32 1 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___except___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer) | |
| // CHECK: call void @g() | |
| // CHECK: unreachable | |
| int nested___except___except(void) { | |
| 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 dso_local i32 @nested___except___except() | |
| // CHECK-LABEL: invoke void @g() | |
| // CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] | |
| // CHECK: [[g1_lpad]] | |
| // CHECK: catchpad {{.*}} [ptr 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 {{.*}} [ptr 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, ptr %myres | |
| // CHECK-NEXT: br label %[[trycont:[^ ]*]] | |
| // CHECK: [[trycont]] | |
| // CHECK-NEXT: store i32 51, ptr %myres | |
| // CHECK-NEXT: br label %[[tryleave]] | |
| // CHECK: [[tryleave]] | |
| // CHECK-NEXT: br label %[[trycont4]] | |
| int nested___finally___except(void) { | |
| 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 dso_local 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, ptr % | |
| // CHECK-NEXT: br label %[[tryleave]] | |
| // CHECK: [[tryleave]] | |
| // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@nested___finally___except@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: ret i32 1 | |
| // CHECK: [[g2_lpad]] | |
| // CHECK: cleanuppad | |
| // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@nested___finally___except@@"(i8 noundef 1, ptr noundef %[[fp]]) | |
| // CHECK: cleanupret {{.*}} unwind to caller | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___except@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer) | |
| // CHECK: ret void | |
| int nested___finally___finally(void) { | |
| 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 dso_local 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, ptr %[[myres:[^ ]*]], | |
| // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: invoke void @"?fin$1@0@nested___finally___finally@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: to label %[[finally_cont:.*]] unwind label %[[g2_lpad:.*]] | |
| // CHECK: [[finally_cont]] | |
| // CHECK: store i32 51, ptr %[[myres]] | |
| // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@nested___finally___finally@@"(i8 noundef 0, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: ret i32 1 | |
| // CHECK: [[g1_lpad]] | |
| // CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad within none [] | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: invoke void @"?fin$1@0@nested___finally___finally@@"(i8 noundef 1, ptr noundef %[[fp]]) | |
| // CHECK-NEXT: to label %[[finally_cont2:.*]] unwind label %[[g2_lpad]] | |
| // CHECK: [[finally_cont2]] | |
| // CHECK: cleanupret from %[[padtoken]] unwind label %[[g2_lpad]] | |
| // CHECK: [[g2_lpad]] | |
| // CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad within none [] | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress() | |
| // CHECK-NEXT: call void @"?fin$0@0@nested___finally___finally@@"(i8 noundef 1, ptr noundef %[[fp]]) | |
| // CHECK: cleanupret from %[[padtoken]] unwind to caller | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer) | |
| // CHECK: ret void | |
| // CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer) | |
| // CHECK: call void @g() | |
| // CHECK: unreachable |