forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			342 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
| // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | opt -instnamer -S | 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 dso_local 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 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() {
 | |
|   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, 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 @"?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 dso_local 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 @"?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 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 i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: call void @"?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 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 i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: invoke void @"?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 @"?fin$0@0@nested___except___finally@@"(i8 1, i8* %[[fp]])
 | |
| // CHECK-NEXT:       to label %[[g1_resume:.*]] unwind label %[[g2_lpad]]
 | |
| // 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-LABEL: define internal void @"?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 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 {{.*}} [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 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, i32* %
 | |
| // CHECK-NEXT: br label %[[tryleave]]
 | |
| 
 | |
| // CHECK: [[tryleave]]
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: call void @"?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: call void @"?fin$0@0@nested___finally___except@@"(i8 1, i8* %[[fp]])
 | |
| // CHECK: cleanupret {{.*}} unwind to caller
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?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 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, i32* %[[myres:[^ ]*]],
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: invoke void @"?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 @"?fin$0@0@nested___finally___finally@@"(i8 0, i8* %[[fp]])
 | |
| // CHECK-NEXT: ret i32 1
 | |
| 
 | |
| // CHECK: [[g1_lpad]]
 | |
| // CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad within none []
 | |
| // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: invoke void @"?fin$1@0@nested___finally___finally@@"(i8 1, i8* %[[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 i8* @llvm.localaddress()
 | |
| // CHECK-NEXT: call void @"?fin$0@0@nested___finally___finally@@"(i8 1, i8* %[[fp]])
 | |
| // CHECK: cleanupret from %[[padtoken]] unwind to caller
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer)
 | |
| // CHECK: ret void
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally@@"(i8 %abnormal_termination, i8* %frame_pointer)
 | |
| // CHECK: call void @g()
 | |
| // CHECK: unreachable
 |