forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			288 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
| // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
 | |
| // RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
 | |
| // RUN: %clang_cc1 %s -triple aarch64-windows -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
 | |
| // NOTE: we're passing "-O1 -disable-llvm-passes" to avoid adding optnone and noinline everywhere.
 | |
| 
 | |
| void abort(void) __attribute__((noreturn));
 | |
| void might_crash(void);
 | |
| void cleanup(void);
 | |
| int check_condition(void);
 | |
| void basic_finally(void) {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     cleanup();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @basic_finally()
 | |
| // CHECK: invoke void @might_crash()
 | |
| // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[invoke_cont]]
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK: call void @"?fin$0@0@basic_finally@@"({{i8( zeroext)?}} 0, i8* %[[fp]])
 | |
| // CHECK-NEXT: ret void
 | |
| //
 | |
| // CHECK: [[lpad]]
 | |
| // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK: call void @"?fin$0@0@basic_finally@@"({{i8( zeroext)?}} 1, i8* %[[fp]])
 | |
| // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@basic_finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs:#[0-9]+]]
 | |
| // CHECK: call void @cleanup()
 | |
| 
 | |
| // Mostly check that we don't double emit 'r' which would crash.
 | |
| void decl_in_finally(void) {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     int r;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Ditto, don't crash double emitting 'l'.
 | |
| void label_in_finally(void) {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
| l:
 | |
|     cleanup();
 | |
|     if (check_condition())
 | |
|       goto l;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @label_in_finally()
 | |
| // CHECK: invoke void @might_crash()
 | |
| // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[invoke_cont]]
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK: call void @"?fin$0@0@label_in_finally@@"({{i8( zeroext)?}} 0, i8* %[[fp]])
 | |
| // CHECK: ret void
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@label_in_finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: br label %[[l:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[l]]
 | |
| // CHECK: call void @cleanup()
 | |
| // CHECK: call i32 @check_condition()
 | |
| // CHECK: br i1 {{.*}}, label
 | |
| // CHECK: br label %[[l]]
 | |
| 
 | |
| int crashed;
 | |
| void use_abnormal_termination(void) {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     crashed = __abnormal_termination();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @use_abnormal_termination()
 | |
| // CHECK: invoke void @might_crash()
 | |
| // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[invoke_cont]]
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8( zeroext)?}} 0, i8* %[[fp]])
 | |
| // CHECK: ret void
 | |
| //
 | |
| // CHECK: [[lpad]]
 | |
| // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 | |
| // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.localaddress()
 | |
| // CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8( zeroext)?}} 1, i8* %[[fp]])
 | |
| // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@use_abnormal_termination@@"({{i8( zeroext)?}} %[[abnormal:abnormal_termination]], i8* %frame_pointer)
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: %[[abnormal_zext:[^ ]*]] = zext i8 %[[abnormal]] to i32
 | |
| // CHECK: store i32 %[[abnormal_zext]], i32* @crashed
 | |
| // CHECK-NEXT: ret void
 | |
| 
 | |
| void noreturn_noop_finally() {
 | |
|   __try {
 | |
|     __noop();
 | |
|   } __finally {
 | |
|     abort();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @noreturn_noop_finally()
 | |
| // CHECK: call void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
 | |
| // CHECK: ret void
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: call void @abort()
 | |
| // CHECK: unreachable
 | |
| 
 | |
| void noreturn_finally() {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     abort();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @noreturn_finally()
 | |
| // CHECK: invoke void @might_crash()
 | |
| // CHECK:     to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[cont]]
 | |
| // CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
 | |
| // CHECK: ret void
 | |
| //
 | |
| // CHECK: [[lpad]]
 | |
| // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 | |
| // CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
 | |
| // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@noreturn_finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: call void @abort()
 | |
| // CHECK: unreachable
 | |
| 
 | |
| int finally_with_return() {
 | |
|   __try {
 | |
|     return 42;
 | |
|   } __finally {
 | |
|   }
 | |
| }
 | |
| // CHECK-LABEL: define dso_local i32 @finally_with_return()
 | |
| // CHECK: call void @"?fin$0@0@finally_with_return@@"({{.*}})
 | |
| // CHECK-NEXT: ret i32 42
 | |
| 
 | |
| // CHECK: define internal void @"?fin$0@0@finally_with_return@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK-NOT: br i1
 | |
| // CHECK-NOT: br label
 | |
| // CHECK: ret void
 | |
| 
 | |
| int nested___finally___finally() {
 | |
|   __try {
 | |
|     __try {
 | |
|     } __finally {
 | |
|       return 1;
 | |
|     }
 | |
|   } __finally {
 | |
|     // Intentionally no return here.
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local i32 @nested___finally___finally
 | |
| // CHECK: invoke void @"?fin$1@0@nested___finally___finally@@"({{.*}})
 | |
| // CHECK:          to label %[[outercont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[outercont]]
 | |
| // CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
 | |
| // CHECK-NEXT: ret i32 0
 | |
| //
 | |
| // CHECK: [[lpad]]
 | |
| // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 | |
| // CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
 | |
| // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: ret void
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: unreachable
 | |
| 
 | |
| // FIXME: Our behavior seems suspiciously different.
 | |
| 
 | |
| int nested___finally___finally_with_eh_edge() {
 | |
|   __try {
 | |
|     __try {
 | |
|       might_crash();
 | |
|     } __finally {
 | |
|       return 899;
 | |
|     }
 | |
|   } __finally {
 | |
|     // Intentionally no return here.
 | |
|   }
 | |
|   return 912;
 | |
| }
 | |
| // CHECK-LABEL: define dso_local i32 @nested___finally___finally_with_eh_edge
 | |
| // CHECK: invoke void @might_crash()
 | |
| // CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad1:[^ ]*]]
 | |
| //
 | |
| // [[invokecont]]
 | |
| // CHECK: invoke void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-NEXT:       to label %[[outercont:[^ ]*]] unwind label %[[lpad2:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[outercont]]
 | |
| // CHECK: call void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-NEXT: ret i32 912
 | |
| //
 | |
| // CHECK: [[lpad1]]
 | |
| // CHECK-NEXT: %[[innerpad:[^ ]*]] = cleanuppad
 | |
| // CHECK: invoke void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-NEXT:    label %[[innercleanupretbb:[^ ]*]] unwind label %[[lpad2:[^ ]*]]
 | |
| //
 | |
| // CHECK: [[innercleanupretbb]]
 | |
| // CHECK-NEXT: cleanupret from %[[innerpad]] unwind label %[[lpad2]]
 | |
| //
 | |
| // CHECK: [[lpad2]]
 | |
| // CHECK-NEXT: %[[outerpad:[^ ]*]] = cleanuppad
 | |
| // CHECK: call void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-NEXT: cleanupret from %[[outerpad]] unwind to caller
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: ret void
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: unreachable
 | |
| 
 | |
| void finally_within_finally() {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     __try {
 | |
|       might_crash();
 | |
|     } __finally {
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define dso_local void @finally_within_finally(
 | |
| // CHECK: invoke void @might_crash(
 | |
| 
 | |
| // CHECK: call void @"?fin$0@0@finally_within_finally@@"(
 | |
| // CHECK: call void @"?fin$0@0@finally_within_finally@@"({{.*}}) [ "funclet"(
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@finally_within_finally@@"({{[^)]*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| // CHECK: invoke void @might_crash(
 | |
| 
 | |
| // CHECK: call void @"?fin$1@0@finally_within_finally@@"(
 | |
| // CHECK: call void @"?fin$1@0@finally_within_finally@@"({{.*}}) [ "funclet"(
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$1@0@finally_within_finally@@"({{[^)]*}})
 | |
| // CHECK-SAME: [[finally_attrs]]
 | |
| 
 | |
| void cleanup_with_func(const char *);
 | |
| void finally_with_func() {
 | |
|   __try {
 | |
|     might_crash();
 | |
|   } __finally {
 | |
|     cleanup_with_func(__func__);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // CHECK-LABEL: define internal void @"?fin$0@0@finally_with_func@@"({{[^)]*}})
 | |
| // CHECK: call void @cleanup_with_func(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@COAGBPGM@finally_with_func?$AA@", i{{32|64}} 0, i{{32|64}} 0))
 | |
| 
 | |
| // Look for the absence of noinline.  nounwind is expected; any further
 | |
| // attributes should be string attributes.
 | |
| // CHECK: attributes [[finally_attrs]] = { nounwind "{{.*}}" }
 |