// 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