| // REQUIRES: webassembly-registered-target |
| // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s |
| // RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s |
| // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -S -o - -std=c++11 | FileCheck %s --check-prefix=ASSEMBLY |
| |
| void may_throw(); |
| void dont_throw() noexcept; |
| |
| struct Cleanup { |
| ~Cleanup() { dont_throw(); } |
| }; |
| |
| // Multiple catch clauses w/o catch-all |
| void test0() { |
| try { |
| may_throw(); |
| } catch (int) { |
| dont_throw(); |
| } catch (double) { |
| dont_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: define void @_Z5test0v() {{.*}} personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) |
| |
| // CHECK: %[[INT_ALLOCA:.*]] = alloca i32 |
| // CHECK: invoke void @_Z9may_throwv() |
| // CHECK-NEXT: to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]] |
| |
| // CHECK: [[CATCHDISPATCH_BB]]: |
| // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] |
| // CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CATCHPAD]]) |
| // CHECK-NEXT: store i8* %[[EXN]], i8** %exn.slot |
| // CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]]) |
| // CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2 |
| // CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] |
| // CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]] |
| |
| // CHECK: [[CATCH_INT_BB]]: |
| // CHECK-NEXT: %[[EXN:.*]] = load i8*, i8** %exn.slot |
| // CHECK-NEXT: %[[ADDR:.*]] = call i8* @__cxa_begin_catch(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: %[[ADDR_CAST:.*]] = bitcast i8* %[[ADDR]] to i32* |
| // CHECK-NEXT: %[[INT_VAL:.*]] = load i32, i32* %[[ADDR_CAST]] |
| // CHECK-NEXT: store i32 %[[INT_VAL]], i32* %[[INT_ALLOCA]] |
| // CHECK-NEXT: call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]] |
| |
| // CHECK: [[CATCHRET_DEST_BB0]]: |
| // CHECK-NEXT: br label %[[TRY_CONT_BB:.*]] |
| |
| // CHECK: [[CATCH_FALLTHROUGH_BB]] |
| // CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) #2 |
| // CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] |
| // CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]] |
| |
| // CHECK: [[CATCH_FLOAT_BB]]: |
| // CHECK: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]] |
| |
| // CHECK: [[CATCHRET_DEST_BB1]]: |
| // CHECK-NEXT: br label %[[TRY_CONT_BB]] |
| |
| // CHECK: [[RETHROW_BB]]: |
| // CHECK-NEXT: call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: unreachable |
| |
| // Single catch-all |
| void test1() { |
| try { |
| may_throw(); |
| } catch (...) { |
| dont_throw(); |
| } |
| } |
| |
| // CATCH-LABEL: @_Z5test1v() |
| |
| // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] |
| // CHECK: br label %[[CATCH_ALL_BB:.*]] |
| |
| // CHECK: [[CATCH_ALL_BB]]: |
| // CHECK: catchret from %[[CATCHPAD]] to label |
| |
| // Multiple catch clauses w/ catch-all |
| void test2() { |
| try { |
| may_throw(); |
| } catch (int) { |
| dont_throw(); |
| } catch (...) { |
| dont_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test2v() |
| |
| // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] |
| // CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]] |
| |
| // CHECK: [[CATCH_INT_BB]]: |
| // CHECK: catchret from %[[CATCHPAD]] to label |
| |
| // CHECK: [[CATCH_ALL_BB]]: |
| // CHECK: catchret from %[[CATCHPAD]] to label |
| |
| // Cleanup |
| void test3() { |
| Cleanup c; |
| may_throw(); |
| } |
| |
| // CHECK-LABEL: @_Z5test3v() |
| |
| // CHECK: invoke void @_Z9may_throwv() |
| // CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]] |
| |
| // CHECK: [[EHCLEANUP_BB]]: |
| // CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within none [] |
| // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller |
| |
| // Possibly throwing function call within a catch |
| void test4() { |
| try { |
| may_throw(); |
| } catch (int) { |
| may_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test4v() |
| |
| // CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]] |
| |
| // CHECK: [[INVOKE_CONT_BB]]: |
| // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: catchret from %[[CATCHPAD]] to label |
| |
| // CHECK: [[EHCLEANUP_BB]]: |
| // CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] [] |
| // CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller |
| |
| // Possibly throwing function call within a catch-all |
| void test5() { |
| try { |
| may_throw(); |
| } catch (...) { |
| may_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test5v() |
| |
| // CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]] |
| |
| // CHECK: [[INVOKE_CONT_BB0]]: |
| // CHECK-NEXT: call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: catchret from %[[CATCHPAD]] to label |
| |
| // CHECK: [[EHCLEANUP_BB]]: |
| // CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] [] |
| // CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ] |
| // CHECK-NEXT: to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]] |
| |
| // CHECK: [[INVOKE_CONT_BB1]]: |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind to caller |
| |
| // CHECK: [[TERMINATE_BB]]: |
| // CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] [] |
| // CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CLEANUPPAD1]]) |
| // CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] |
| // CHECK-NEXT: unreachable |
| |
| // CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8* %0) |
| // CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %{{.*}}) |
| // CHECK-NEXT: call void @_ZSt9terminatev() |
| // CHECK-NEXT: unreachable |
| |
| // Try-catch with cleanups |
| void test6() { |
| Cleanup c1; |
| try { |
| Cleanup c2; |
| may_throw(); |
| } catch (int) { |
| Cleanup c3; |
| may_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test6v() |
| // CHECK: invoke void @_Z9may_throwv() |
| // CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]] |
| |
| // CHECK: [[EHCLEANUP_BB0]]: |
| // CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] |
| // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ] |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]] |
| |
| // CHECK: [[CATCH_DISPATCH_BB]]: |
| // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]] |
| |
| // CHECK: [[CATCHSTART_BB]]: |
| // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] |
| // CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]] |
| |
| // CHECK: [[CATCH_INT_BB]]: |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]] |
| |
| // CHECK: [[INVOKE_CONT_BB]]: |
| // CHECK: catchret from %[[CATCHPAD]] to label %{{.*}} |
| |
| // CHECK: [[RETHROW_BB]]: |
| // CHECK-NEXT: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] |
| // CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]] |
| |
| // CHECK: [[EHCLEANUP_BB2]]: |
| // CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] [] |
| // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ] |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]] |
| |
| // CHECK: [[EHCLEANUP_BB3]]: |
| // CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]] |
| |
| // CHECK: [[EHCLEANUP_BB1]]: |
| // CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none [] |
| // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] |
| // CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller |
| |
| // CHECK: [[UNREACHABLE_BB]]: |
| // CHECK-NEXT: unreachable |
| |
| // Nested try-catches within a try with cleanups |
| void test7() { |
| Cleanup c1; |
| may_throw(); |
| try { |
| Cleanup c2; |
| may_throw(); |
| try { |
| Cleanup c3; |
| may_throw(); |
| } catch (int) { |
| may_throw(); |
| } catch (double) { |
| may_throw(); |
| } |
| } catch (int) { |
| may_throw(); |
| } catch (...) { |
| may_throw(); |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test7v() |
| // CHECK: invoke void @_Z9may_throwv() |
| |
| // CHECK: invoke void @_Z9may_throwv() |
| |
| // CHECK: invoke void @_Z9may_throwv() |
| |
| // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] |
| // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label |
| |
| // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none |
| |
| // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD0]] to label |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD0]] to label |
| |
| // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] |
| |
| // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label |
| |
| // CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label |
| |
| // CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none [] |
| // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label |
| |
| // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none |
| |
| // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD1]] to label |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] |
| |
| // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD1]] to label |
| |
| // CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] [] |
| // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ] |
| |
| // CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label |
| |
| // CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label |
| |
| // CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none [] |
| // CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller |
| |
| // CHECK: unreachable |
| |
| // CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] [] |
| // CHECK: call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ] |
| // CHECK: unreachable |
| |
| // Nested try-catches within a catch |
| void test8() { |
| try { |
| may_throw(); |
| } catch (int) { |
| try { |
| may_throw(); |
| } catch (int) { |
| may_throw(); |
| } |
| } |
| } |
| |
| // CHECK-LABEL: @_Z5test8v() |
| // CHECK: invoke void @_Z9may_throwv() |
| |
| // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none |
| |
| // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] |
| |
| // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]] |
| |
| // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)] |
| |
| // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD1]] to label |
| |
| // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ] |
| |
| // CHECK: catchret from %[[CATCHPAD0]] to label |
| |
| // CHECK: call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] |
| // CHECK: unreachable |
| |
| // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label |
| |
| // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] |
| // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller |
| |
| // CHECK: unreachable |
| |
| // Here we only check if the command enables wasm exception handling in the |
| // backend so that exception handling instructions can be generated in .s file. |
| |
| // ASSEMBLY: try |
| // ASSEMBLY: catch |
| // ASSEMBLY: rethrow |
| // ASSEMBLY: end_try |