| // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s |
| |
| 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 void @basic_finally() |
| // CHECK: invoke void @might_crash() |
| // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] |
| // |
| // CHECK: [[invoke_cont]] |
| // CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] |
| // CHECK: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // CHECK: call void @cleanup() |
| // CHECK: load i8, i8* %[[abnormal]] |
| // CHECK: icmp eq |
| // CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]] |
| // |
| // CHECK: [[finallycont]] |
| // CHECK-NEXT: ret void |
| // |
| // CHECK: [[lpad]] |
| // CHECK-NEXT: landingpad |
| // CHECK-NEXT: cleanup |
| // CHECK: store i8 1, i8* %[[abnormal]] |
| // CHECK: br label %[[finally]] |
| // |
| // CHECK: [[resumecont]] |
| // CHECK: br label %[[ehresume:[^ ]*]] |
| // |
| // CHECK: [[ehresume]] |
| // CHECK: resume { i8*, i32 } |
| |
| // 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 void @label_in_finally() |
| // CHECK: invoke void @might_crash() |
| // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] |
| // |
| // CHECK: [[invoke_cont]] |
| // CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] |
| // CHECK: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // 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 void @use_abnormal_termination() |
| // CHECK: invoke void @might_crash() |
| // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] |
| // |
| // CHECK: [[invoke_cont]] |
| // CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] |
| // CHECK: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // CHECK: load i8, i8* %[[abnormal]] |
| // CHECK: zext i8 %{{.*}} to i32 |
| // CHECK: store i32 %{{.*}}, i32* @crashed |
| // CHECK: load i8, i8* %[[abnormal]] |
| // CHECK: icmp eq |
| // CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]] |
| // |
| // CHECK: [[finallycont]] |
| // CHECK-NEXT: ret void |
| // |
| // CHECK: [[lpad]] |
| // CHECK-NEXT: landingpad |
| // CHECK-NEXT: cleanup |
| // CHECK: store i8 1, i8* %[[abnormal]] |
| // CHECK: br label %[[finally]] |
| // |
| // CHECK: [[resumecont]] |
| // CHECK: br label %[[ehresume:[^ ]*]] |
| // |
| // CHECK: [[ehresume]] |
| // CHECK: resume { i8*, i32 } |
| |
| void noreturn_noop_finally() { |
| __try { |
| __noop(); |
| } __finally { |
| abort(); |
| } |
| } |
| |
| // CHECK-LABEL: define void @noreturn_noop_finally() |
| // CHECK: store i8 0, i8* % |
| // CHECK: br label %[[finally:[^ ]*]] |
| // CHECK: [[finally]] |
| // CHECK: call void @abort() |
| // CHECK-NEXT: unreachable |
| // CHECK-NOT: load |
| |
| void noreturn_finally() { |
| __try { |
| might_crash(); |
| } __finally { |
| abort(); |
| } |
| } |
| |
| // CHECK-LABEL: define void @noreturn_finally() |
| // CHECK: invoke void @might_crash() |
| // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] |
| // |
| // CHECK: [[cont]] |
| // CHECK: store i8 0, i8* % |
| // CHECK: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // CHECK: call void @abort() |
| // CHECK-NEXT: unreachable |
| // |
| // CHECK: [[lpad]] |
| // CHECK: landingpad |
| // CHECK-NEXT: cleanup |
| // CHECK: store i8 1, i8* % |
| // CHECK: br label %[[finally]] |
| |
| int finally_with_return() { |
| __try { |
| return 42; |
| } __finally { |
| } |
| } |
| // CHECK-LABEL: define i32 @finally_with_return() |
| // CHECK: store i8 0, i8* % |
| // CHECK-NEXT: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // CHECK-NEXT: br label %[[finallycont:[^ ]*]] |
| // |
| // CHECK: [[finallycont]] |
| // CHECK-NEXT: ret i32 42 |
| |
| int nested___finally___finally() { |
| __try { |
| __try { |
| } __finally { |
| return 1; |
| } |
| } __finally { |
| // Intentionally no return here. |
| } |
| return 0; |
| } |
| // CHECK-LABEL: define i32 @nested___finally___finally |
| // CHECK: store i8 0, i8* % |
| // CHECK-NEXT: br label %[[finally:[^ ]*]] |
| // |
| // CHECK: [[finally]] |
| // CHECK-NEXT: store i32 1, i32* % |
| // CHECK-NEXT: store i32 1, i32* % |
| // CHECK-NEXT: br label %[[cleanup:[^ ]*]] |
| // |
| // The finally's unreachable continuation block: |
| // CHECK: store i32 0, i32* % |
| // CHECK-NEXT: br label %[[cleanup]] |
| // |
| // CHECK: [[cleanup]] |
| // CHECK-NEXT: store i8 0, i8* % |
| // CHECK-NEXT: br label %[[outerfinally:[^ ]*]] |
| // |
| // CHECK: [[outerfinally]] |
| // CHECK-NEXT: br label %[[finallycont:[^ ]*]] |
| // |
| // CHECK: [[finallycont]] |
| // CHECK-NEXT: %[[dest:[^ ]*]] = load i32, i32* % |
| // CHECK-NEXT: switch i32 %[[dest]] |
| // CHECK-NEXT: i32 0, label %[[cleanupcont:[^ ]*]] |
| // |
| // CHECK: [[cleanupcont]] |
| // CHECK-NEXT: store i32 0, i32* % |
| // CHECK-NEXT: br label %[[return:[^ ]*]] |
| // |
| // CHECK: [[return]] |
| // CHECK-NEXT: %[[reg:[^ ]*]] = load i32, i32* % |
| // CHECK-NEXT: ret i32 %[[reg]] |
| |
| int nested___finally___finally_with_eh_edge() { |
| __try { |
| __try { |
| might_crash(); |
| } __finally { |
| return 899; |
| } |
| } __finally { |
| // Intentionally no return here. |
| } |
| return 912; |
| } |
| // CHECK-LABEL: define i32 @nested___finally___finally_with_eh_edge |
| // CHECK: invoke void @might_crash() #3 |
| // CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad:[^ ]*]] |
| // |
| // CHECK: [[invokecont]] |
| // CHECK-NEXT: store i8 0, i8* %[[abnormal:[^ ]*]] |
| // CHECK-NEXT: br label %[[finally:[^ ]*]] |
| |
| // CHECK: [[finally]] |
| // CHECK-NEXT: store i32 899, i32* % |
| // CHECK-NEXT: store i32 1, i32* % |
| // CHECK-NEXT: br label %[[cleanup:[^ ]*]] |
| // |
| // The inner finally's unreachable continuation block: |
| // CHECK: store i32 0, i32* % |
| // CHECK-NEXT: br label %[[cleanup]] |
| // |
| // CHECK: [[cleanup]] |
| // CHECK-NEXT: store i8 0, i8* % |
| // CHECK-NEXT: br label %[[outerfinally:[^ ]*]] |
| // |
| // CHECK: [[outerfinally]] |
| // CHECK-NEXT: %[[abnormallocal:[^ ]*]] = load i8, i8* %[[abnormal]] |
| // CHECK-NEXT: %[[reg:[^ ]*]] = icmp eq i8 %[[abnormallocal]], 0 |
| // CHECK-NEXT: br i1 %[[reg]], label %[[finallycont:[^ ]*]], label %[[finallyresume:[^ ]*]] |
| // |
| // CHECK: [[finallycont]] |
| // CHECK-NEXT: %[[dest:[^ ]*]] = load i32, i32* % |
| // CHECK-NEXT: switch i32 %[[dest]] |
| // CHECK-NEXT: i32 0, label %[[cleanupcont:[^ ]*]] |
| // |
| // CHECK: [[cleanupcont]] |
| // CHECK-NEXT: store i32 912, i32* % |
| // CHECK-NEXT: br label %[[return:[^ ]*]] |
| // |
| // |
| // CHECK: [[lpad]] |
| // CHECK-NEXT: landingpad |
| // CHECK-NEXT: cleanup |
| // CHECK: store i8 1, i8* %[[abnormal]] |
| // CHECK: br label %[[finally]] |
| // |
| // The inner finally's unreachable resume block: |
| // CHECK: store i8 1, i8* %[[abnormal]] |
| // CHECK-NEXT: br label %[[outerfinally]] |
| // |
| // CHECK: [[finallyresume]] |
| // CHECK-NEXT: br label %[[ehresume:[^ ]*]] |
| // |
| // CHECK: [[return]] |
| // CHECK-NEXT: %[[reg:[^ ]*]] = load i32, i32* % |
| // CHECK-NEXT: ret i32 %[[reg]] |
| // |
| // The ehresume block, not reachable either. |
| // CHECK: [[ehresume]] |
| // CHECK: resume |