Index: llvm/trunk/test/CodeGen/WebAssembly/cfg-stackify-eh.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ llvm/trunk/test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s +; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @@ -7,26 +7,37 @@ @_ZTId = external constant i8* ; Simple test case with two catch clauses +; +; void foo(); ; void test0() { ; try { ; foo(); -; } catch (int n) { -; bar(); -; } catch (double d) { +; } catch (int) { +; } catch (double) { ; } ; } ; CHECK-LABEL: test0 +; CHECK: block ; CHECK: try -; CHECK: call foo -; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= -; CHECK: block i32 -; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]] -; CHECK: rethrow -; CHECK: end_block -; CHECK: i32.call $drop=, _Unwind_CallPersonality -; CHECK: end_try -; CHECK: return +; CHECK: call foo +; CHECK: br 0 # 0: down to label1 +; CHECK: catch +; CHECK: block +; CHECK: br_if 0, {{.*}} # 0: down to label3 +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: call __cxa_end_catch +; CHECK: br 1 # 1: down to label1 +; CHECK: end_block # label3: +; CHECK: block +; CHECK: br_if 0, {{.*}} # 0: down to label4 +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: call __cxa_end_catch +; CHECK: br 1 # 1: down to label1 +; CHECK: end_block # label4: +; CHECK: call __cxa_rethrow +; CHECK: end_try # label1: +; CHECK: end_block define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() @@ -45,21 +56,16 @@ catch2: ; preds = %catch.start %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] - %6 = bitcast i8* %5 to i32* - %7 = load i32, i32* %6, align 4 - call void @bar() [ "funclet"(token %1) ] call void @__cxa_end_catch() [ "funclet"(token %1) ] catchret from %1 to label %try.cont catch.fallthrough: ; preds = %catch.start - %8 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) - %matches1 = icmp eq i32 %3, %8 + %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) + %matches1 = icmp eq i32 %3, %6 br i1 %matches1, label %catch, label %rethrow catch: ; preds = %catch.fallthrough - %9 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] - %10 = bitcast i8* %9 to double* - %11 = load double, double* %10, align 8 + %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] call void @__cxa_end_catch() [ "funclet"(token %1) ] catchret from %1 to label %try.cont @@ -75,41 +81,17 @@ ; void test1() { ; try { ; foo(); -; } catch (int n) { +; } catch (int) { ; try { ; foo(); -; } catch (int n) { +; } catch (int) { ; foo(); ; } ; } ; } ; CHECK-LABEL: test1 -; CHECK: try -; CHECK: call foo -; CHECK: catch -; CHECK: br_on_exn 0, __cpp_exception -; CHECK: rethrow -; CHECK: i32.call $drop=, _Unwind_CallPersonality -; CHECK: try -; CHECK: call foo -; CHECK: catch -; CHECK: br_on_exn 0, __cpp_exception -; CHECK: rethrow -; CHECK: i32.call $drop=, _Unwind_CallPersonality -; CHECK: try -; CHECK: i32.call $drop=, __cxa_begin_catch -; CHECK: try -; CHECK: call foo -; CHECK: catch $drop= -; CHECK: rethrow -; CHECK: end_try -; CHECK: catch $drop= -; CHECK: rethrow -; CHECK: end_try -; CHECK: end_try -; CHECK: end_try -; CHECK: return +; TODO Fill in CHECK lines once we fix ScopeTops info bug in D58605 define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() @@ -195,29 +177,39 @@ ; } ; CHECK-LABEL: test2 +; CHECK: block ; CHECK: try -; CHECK: call foo -; CHECK: catch -; CHECK: br_on_exn 0, __cpp_exception -; CHECK: rethrow -; CHECK: loop -; CHECK: try -; CHECK: call foo -; CHECK: catch $drop= -; CHECK: try -; CHECK: call __cxa_end_catch +; CHECK: call foo +; CHECK: br 0 # 0: down to label17 ; CHECK: catch -; CHECK: br_on_exn 0, __cpp_exception -; CHECK: call __clang_call_terminate, 0 -; CHECK: unreachable -; CHECK: call __clang_call_terminate -; CHECK: unreachable -; CHECK: end_try -; CHECK: rethrow -; CHECK: end_try -; CHECK: end_loop -; CHECK: end_try -; CHECK: return +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: loop # label19: +; CHECK: block +; CHECK: block +; CHECK: br_if 0, {{.*}} # 0: down to label21 +; CHECK: try +; CHECK: call foo +; CHECK: br 2 # 2: down to label20 +; CHECK: catch +; CHECK: block +; CHECK: try +; CHECK: call __cxa_end_catch +; CHECK: br 0 # 0: down to label24 +; CHECK: catch +; CHECK: call __clang_call_terminate +; CHECK: unreachable +; CHECK: end_try # label24: +; CHECK: end_block +; CHECK: rethrow # to caller +; CHECK: end_try +; CHECK: end_block # label21: +; CHECK: call __cxa_end_catch +; CHECK: br 2 # 2: down to label17 +; CHECK: end_block # label20: +; CHECK: br 0 # 0: up to label19 +; CHECK: end_loop +; CHECK: end_try # label17: +; CHECK: end_block define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() Index: llvm/trunk/test/CodeGen/WebAssembly/exception.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/exception.ll +++ llvm/trunk/test/CodeGen/WebAssembly/exception.ll @@ -5,45 +5,62 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" -%struct.Cleanup = type { i8 } +%struct.Temp = type { i8 } @_ZTIi = external constant i8* ; CHECK-LABEL: test_throw: -; CHECK: throw __cpp_exception, $0 -; CHECK-NOT: unreachable +; CHECK: throw __cpp_exception, $0 +; CHECK-NOT: unreachable define void @test_throw(i8* %p) { call void @llvm.wasm.throw(i32 0, i8* %p) ret void } ; CHECK-LABEL: test_rethrow: -; CHECK: rethrow -; CHECK-NOT: unreachable +; CHECK: rethrow +; CHECK-NOT: unreachable define void @test_rethrow(i8* %p) { call void @llvm.wasm.rethrow() ret void } -; CHECK-LABEL: test_catch_rethrow: -; CHECK: global.get ${{.+}}=, __stack_pointer -; CHECK: try -; CHECK: call foo -; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= -; CHECK: global.set __stack_pointer -; CHECK: block i32 -; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]] -; CHECK: rethrow -; CHECK: end_block -; CHECK: extract_exception $[[EXN:[0-9]+]]= -; CHECK-DAG: i32.store __wasm_lpad_context -; CHECK-DAG: i32.store __wasm_lpad_context+4 -; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]] -; CHECK: i32.call $drop=, __cxa_begin_catch -; CHECK: call __cxa_end_catch -; CHECK: call __cxa_rethrow -; CHECK: end_try -define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +; Simple test with a try-catch +; +; void foo(); +; void test_catch() { +; try { +; foo(); +; } catch (int) { +; } +; } + +; CHECK-LABEL: test_catch: +; CHECK: global.get ${{.+}}=, __stack_pointer +; CHECK: block +; CHECK: try +; CHECK: call foo +; CHECK: br 0 +; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= +; CHECK: global.set __stack_pointer +; CHECK: block i32 +; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]] +; CHECK: rethrow +; CHECK: end_block +; CHECK: extract_exception $[[EXN:[0-9]+]]= +; CHECK-DAG: i32.store __wasm_lpad_context +; CHECK-DAG: i32.store __wasm_lpad_context+4 +; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]] +; CHECK: block +; CHECK: br_if 0 +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: call __cxa_end_catch +; CHECK: br 1 +; CHECK: end_block +; CHECK: call __cxa_rethrow +; CHECK: end_try +; CHECK: end_block +define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() to label %try.cont unwind label %catch.dispatch @@ -72,40 +89,88 @@ ret void } +; Destructor (cleanup) test +; +; void foo(); +; struct Temp { +; ~Temp() {} +; }; +; void test_cleanup() { +; Temp t; +; foo(); +; } + ; CHECK-LABEL: test_cleanup: +; CHECK: block ; CHECK: try -; CHECK: call foo +; CHECK: call foo +; CHECK: br 0 ; CHECK: catch -; CHECK: global.set __stack_pointer -; CHECK: i32.call $drop=, _ZN7CleanupD1Ev -; CHECK: rethrow +; CHECK: global.set __stack_pointer +; CHECK: i32.call $drop=, _ZN4TempD2Ev +; CHECK: rethrow ; CHECK: end_try +; CHECK: end_block define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: - %c = alloca %struct.Cleanup, align 1 + %t = alloca %struct.Temp, align 1 invoke void @foo() to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry - %call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) + %call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) ret void ehcleanup: ; preds = %entry %0 = cleanuppad within none [] - %call1 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ] + %call1 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ] cleanupret from %0 unwind to caller } +; Calling a function that may throw within a 'catch (...)' generates a +; temrinatepad, because __cxa_end_catch() also can throw within 'catch (...)'. +; +; void foo(); +; void test_terminatepad() { +; try { +; foo(); +; } catch (...) { +; foo(); +; } +; } + ; CHECK-LABEL: test_terminatepad +; CHECK: block +; CHECK: try +; CHECK: call foo +; CHECK: br 0 ; CHECK: catch -; CHECK: block i32 -; CHECK: br_on_exn 0, __cpp_exception -; CHECK: call __clang_call_terminate, 0 -; CHECK: unreachable -; CHECK: end_block -; CHECK: extract_exception -; CHECK: call __clang_call_terminate -; CHECK: unreachable +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: block +; CHECK: try +; CHECK: call foo +; CHECK: br 0 +; CHECK: catch +; CHECK: block +; CHECK: try +; CHECK: call __cxa_end_catch +; CHECK: br 0 +; CHECK: catch +; CHECK: block i32 +; CHECK: br_on_exn 0, __cpp_exception +; CHECK: call __clang_call_terminate, 0 +; CHECK: unreachable +; CHECK: end_block +; CHECK: call __clang_call_terminate +; CHECK: unreachable +; CHECK: end_try +; CHECK: end_block +; CHECK: rethrow +; CHECK: end_try +; CHECK: end_block +; CHECK: call __cxa_end_catch +; CHECK: end_try +; CHECK: end_block define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() @@ -149,23 +214,47 @@ ; should not have a prologue, and BBs ending with a catchret/cleanupret should ; not have an epilogue. This is separate from __stack_pointer restoring ; instructions after a catch instruction. +; +; void bar(int) noexcept; +; void test_no_prolog_epilog_in_ehpad() { +; int stack_var = 0; +; bar(stack_var); +; try { +; foo(); +; } catch (int) { +; foo(); +; } +; } ; CHECK-LABEL: test_no_prolog_epilog_in_ehpad -; CHECK: try -; CHECK: call foo -; CHECK: catch -; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer -; CHECK: global.set __stack_pointer -; CHECK: try -; CHECK: call foo -; CHECK: catch -; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer -; CHECK: global.set __stack_pointer -; CHECK: call __cxa_end_catch -; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} -; CHECK: end_try -; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} -; CHECK: end_try +; CHECK: block +; CHECK: try +; CHECK: call foo +; CHECK: br 0 +; CHECK: catch +; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer +; CHECK: global.set __stack_pointer +; CHECK: block +; CHECK: block +; CHECK: br_if 0 +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: try +; CHECK: call foo +; CHECK: br 2 +; CHECK: catch +; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer +; CHECK: global.set __stack_pointer +; CHECK: call __cxa_end_catch +; CHECK: rethrow +; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} +; CHECK: end_try +; CHECK: end_block +; CHECK: call __cxa_rethrow +; CHECK: end_block +; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} +; CHECK: call __cxa_end_catch +; CHECK: end_try +; CHECK: end_block define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: %stack_var = alloca i32, align 4 @@ -210,14 +299,28 @@ ; When a function does not have stack-allocated objects, it does not need to ; store SP back to __stack_pointer global at the epilog. - -; CHECK-LABEL: no_sp_writeback -; CHECK: try -; CHECK: call foo -; CHECK: end_try -; CHECK-NOT: global.set __stack_pointer -; CHECK: return -define void @no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +; +; void foo(); +; void test_no_sp_writeback() { +; try { +; foo(); +; } catch (...) { +; } +; } + +; CHECK-LABEL: test_no_sp_writeback +; CHECK: block +; CHECK: try +; CHECK: call foo +; CHECK: br 0 +; CHECK: catch +; CHECK: i32.call $drop=, __cxa_begin_catch +; CHECK: call __cxa_end_catch +; CHECK: end_try +; CHECK: end_block +; CHECK-NOT: global.set __stack_pointer +; CHECK: return +define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() to label %try.cont unwind label %catch.dispatch @@ -249,7 +352,7 @@ declare void @__cxa_end_catch() declare void @__cxa_rethrow() declare void @__clang_call_terminate(i8*) -declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned) +declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) ; CHECK: __cpp_exception: ; CHECK: .eventtype __cpp_exception i32 Index: llvm/trunk/test/CodeGen/WebAssembly/wasmehprepare.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/wasmehprepare.ll +++ llvm/trunk/test/CodeGen/WebAssembly/wasmehprepare.ll @@ -6,11 +6,19 @@ ; CHECK: @__wasm_lpad_context = external global { i32, i8*, i32 } @_ZTIi = external constant i8* -%struct.Cleanup = type { i8 } +%struct.Temp = type { i8 } ; A single 'catch (int)' clause. ; A wasm.catch() call, wasm.lsda() call, and personality call to generate a ; selector should all be genereated after the catchpad. +; +; void foo(); +; void test0() { +; try { +; foo(); +; } catch (int) { +; } +; } define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK-LABEL: @test0() entry: @@ -53,11 +61,23 @@ ret void } -; Two try-catches, one of them is with a single 'catch (...)' clause. +; Two try-catches. ; For the catchpad with a single 'catch (...)', only a wasm.catch() call should ; be generated after the catchpad; wasm.landingpad.index() and personality call ; should NOT be generated. For the other catchpad, the argument of ; wasm.landingpad.index() should be not 1 but 0. +; +; void foo(); +; void test1() { +; try { +; foo(); +; } catch (...) { +; } +; try { +; foo(); +; } catch (int) { +; } +; } define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK-LABEL: @test1() entry: @@ -112,8 +132,20 @@ ret void } -; A nested try-catch within a catch. Within the nested catchpad, wasm.lsda() -; call should NOT be generated. +; A nested try-catch within a catch. +; +; void foo(); +; void test2() { +; try { +; foo(); +; } catch (int) { +; try { +; foo(); +; } catch (int) { +; } +; } +; } +; Within the nested catchpad, wasm.lsda() call should NOT be generated. define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK-LABEL: @test2() entry: @@ -189,6 +221,15 @@ ; A cleanuppad with a call to __clang_call_terminate(). ; A call to wasm.catch() should be generated after the cleanuppad. +; +; void foo(); +; void test3() { +; try { +; foo(); +; } catch (...) { +; foo(); +; } +; } define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK-LABEL: @test3 entry: @@ -234,10 +275,37 @@ ; PHI demotion test. Only the phi before catchswitch should be demoted; the phi ; before cleanuppad should NOT. +; +; void foo(); +; int bar(int) noexcept; +; struct Temp { +; ~Temp() {} +; }; +; +; void test4() { +; int num; +; try { +; Temp t; +; num = 1; +; foo(); +; num = 2; +; foo(); +; } catch (...) { +; bar(num); +; } +; try { +; foo(); +; num = 1; +; foo(); +; num = 2; +; } catch (...) { +; bar(num); +; } +; } define void @test4() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK-LABEL: @test4 entry: - %c = alloca %struct.Cleanup, align 1 + %t = alloca %struct.Temp, align 1 invoke void @foo() to label %invoke.cont unwind label %ehcleanup @@ -246,13 +314,13 @@ to label %invoke.cont1 unwind label %ehcleanup invoke.cont1: ; preds = %invoke.cont - %call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) + %call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) br label %try.cont ehcleanup: ; preds = %invoke.cont, %entry %num.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] %0 = cleanuppad within none [] - %call2 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ] + %call2 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ] cleanupret from %0 unwind label %catch.dispatch ; CHECK: ehcleanup: ; CHECK-NEXT: = phi @@ -265,7 +333,7 @@ %3 = call i8* @llvm.wasm.get.exception(token %2) %4 = call i32 @llvm.wasm.get.ehselector(token %2) %5 = call i8* @__cxa_begin_catch(i8* %3) [ "funclet"(token %2) ] - call void @func(i32 %num.0) [ "funclet"(token %2) ] + call void @bar(i32 %num.0) [ "funclet"(token %2) ] call void @__cxa_end_catch() [ "funclet"(token %2) ] catchret from %2 to label %try.cont @@ -288,7 +356,7 @@ %8 = call i8* @llvm.wasm.get.exception(token %7) %9 = call i32 @llvm.wasm.get.ehselector(token %7) %10 = call i8* @__cxa_begin_catch(i8* %8) [ "funclet"(token %7) ] - call void @func(i32 %num.1) [ "funclet"(token %7) ] + call void @bar(i32 %num.1) [ "funclet"(token %7) ] call void @__cxa_end_catch() [ "funclet"(token %7) ] catchret from %7 to label %try.cont10 @@ -353,8 +421,8 @@ } declare void @foo() -declare void @func(i32) -declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned) +declare void @bar(i32) +declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) declare i32 @__gxx_wasm_personality_v0(...) declare i8* @llvm.wasm.get.exception(token) declare i32 @llvm.wasm.get.ehselector(token)