[WebAssembly] Exception handling: Switch to the new proposal
Summary:
This switches the EH implementation to the new proposal:
https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md
(The previous proposal was
https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md)
- Instruction changes
- Now we have one single `catch` instruction that returns a except_ref
value
- `throw` now can take variable number of operations
- `rethrow` does not have 'depth' argument anymore
- `br_on_exn` queries an except_ref to see if it matches the tag and
branches to the given label if true.
- `extract_exception` is a pseudo instruction that simulates popping
values from wasm stack. This is to make `br_on_exn`, a very special
instruction, work: `br_on_exn` puts values onto the stack only if it
is taken, and the # of values can vay depending on the tag.
- Now there's only one `catch` per `try`, this patch removes all special
handling for terminate pad with a call to `__clang_call_terminate`.
Before it was the only case there are two catch clauses (a normal
`catch` and `catch_all` per `try`).
- Make `rethrow` act as a terminator like `throw`. This splits BB after
`rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable`
after `rethrow` in LateEHPrepare.
- Now we stop at all catchpads (because we add wasm `catch` instruction
that catches all exceptions), this creates new
`findWasmUnwindDestinations` function in SelectionDAGBuilder.
- Now we use `br_on_exn` instrution to figure out if an except_ref
matches the current tag or not, LateEHPrepare generates this sequence
for catch pads:
```
catch
block i32
br_on_exn $__cpp_exception
end_block
extract_exception
```
- Branch analysis for `br_on_exn` in WebAssemblyInstrInfo
- Other various misc. changes to switch to the new proposal.
Reviewers: dschuff
Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D57134
llvm-svn: 352598
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index de2d061..46ec468 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -1,5 +1,5 @@
; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm
-; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
@@ -9,30 +9,39 @@
@_ZTIi = external constant i8*
-declare void @llvm.wasm.throw(i32, i8*)
-
; CHECK-LABEL: test_throw:
-; CHECK: local.get $push0=, 0
-; CHECK-NEXT: throw __cpp_exception@EVENT, $pop0
+; CHECK: throw __cpp_exception@EVENT, $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
+define void @test_rethrow(i8* %p) {
+ call void @llvm.wasm.rethrow()
+ ret void
+}
+
; CHECK-LABEL: test_catch_rethrow:
-; CHECK: global.get $push{{.+}}=, __stack_pointer@GLOBAL
+; CHECK: global.get ${{.+}}=, __stack_pointer@GLOBAL
; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: i32.catch $push{{.+}}=, 0
+; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
+; CHECK: block i32
+; CHECK: br_on_exn 0, __cpp_exception@EVENT, $[[EXCEPT_REF]]
+; CHECK: rethrow
+; CHECK: end_block
+; CHECK: extract_exception $[[EXN:[0-9]+]]=
; CHECK: global.set __stack_pointer@GLOBAL
; CHECK-DAG: i32.store __wasm_lpad_context
; CHECK-DAG: i32.store __wasm_lpad_context+4
-; CHECK: i32.call $push{{.+}}=, _Unwind_CallPersonality@FUNCTION
-; CHECK: i32.call $push{{.+}}=, __cxa_begin_catch@FUNCTION
+; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $[[EXN]]
+; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
; CHECK: call __cxa_end_catch@FUNCTION
; CHECK: call __cxa_rethrow@FUNCTION
-; CHECK-NEXT: rethrow
; CHECK: end_try
define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
@@ -66,9 +75,9 @@
; CHECK-LABEL: test_cleanup:
; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: catch_all
+; CHECK: catch
; CHECK: global.set __stack_pointer@GLOBAL
-; CHECK: i32.call $push{{.+}}=, _ZN7CleanupD1Ev@FUNCTION
+; CHECK: i32.call $drop=, _ZN7CleanupD1Ev@FUNCTION
; CHECK: rethrow
; CHECK: end_try
define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
@@ -87,71 +96,51 @@
cleanupret from %0 unwind to caller
}
-; - Tests multple terminate pads are merged into one
-; - Tests a catch_all terminate pad is created after a catch terminate pad
-
; CHECK-LABEL: test_terminatepad
-; CHECK: i32.catch
-; CHECK: call __clang_call_terminate@FUNCTION
-; CHECK: unreachable
-; CHECK: catch_all
-; CHECK: call _ZSt9terminatev@FUNCTION
-; CHECK-NOT: call __clang_call_terminate@FUNCTION
-define hidden i32 @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+; CHECK: catch
+; CHECK: block i32
+; CHECK: br_on_exn 0, __cpp_exception@EVENT
+; CHECK: call __clang_call_terminate@FUNCTION, 0
+; CHECK: unreachable
+; CHECK: end_block
+; CHECK: extract_exception
+; CHECK: call __clang_call_terminate@FUNCTION
+; CHECK: unreachable
+define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
- %c = alloca %struct.Cleanup, align 1
- %c1 = alloca %struct.Cleanup, align 1
invoke void @foo()
- to label %invoke.cont unwind label %ehcleanup
-
-invoke.cont: ; preds = %entry
- %call = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1)
to label %try.cont unwind label %catch.dispatch
-ehcleanup: ; preds = %entry
- %0 = cleanuppad within none []
- %call4 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1) [ "funclet"(token %0) ]
- to label %invoke.cont3 unwind label %terminate
-
-invoke.cont3: ; preds = %ehcleanup
- cleanupret from %0 unwind label %catch.dispatch
-
-catch.dispatch: ; preds = %invoke.cont3, %invoke.cont
- %1 = catchswitch within none [label %catch.start] unwind label %ehcleanup7
+catch.dispatch: ; preds = %entry
+ %0 = catchswitch within none [label %catch.start] unwind to caller
catch.start: ; preds = %catch.dispatch
- %2 = catchpad within %1 [i8* null]
- %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) ]
- invoke void @__cxa_end_catch() [ "funclet"(token %2) ]
- to label %invoke.cont5 unwind label %ehcleanup7
+ %1 = catchpad within %0 [i8* null]
+ %2 = call i8* @llvm.wasm.get.exception(token %1)
+ %3 = call i32 @llvm.wasm.get.ehselector(token %1)
+ %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
+ invoke void @foo() [ "funclet"(token %1) ]
+ to label %invoke.cont1 unwind label %ehcleanup
-invoke.cont5: ; preds = %catch.start
- catchret from %2 to label %try.cont
+invoke.cont1: ; preds = %catch.start
+ call void @__cxa_end_catch() [ "funclet"(token %1) ]
+ catchret from %1 to label %try.cont
-try.cont: ; preds = %invoke.cont5, %invoke.cont
- %call6 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
- ret i32 0
+try.cont: ; preds = %entry, %invoke.cont1
+ ret void
-ehcleanup7: ; preds = %catch.start, %catch.dispatch
- %6 = cleanuppad within none []
- %call9 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %6) ]
- to label %invoke.cont8 unwind label %terminate10
+ehcleanup: ; preds = %catch.start
+ %5 = cleanuppad within %1 []
+ invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
+ to label %invoke.cont2 unwind label %terminate
-invoke.cont8: ; preds = %ehcleanup7
- cleanupret from %6 unwind to caller
+invoke.cont2: ; preds = %ehcleanup
+ cleanupret from %5 unwind to caller
terminate: ; preds = %ehcleanup
- %7 = cleanuppad within %0 []
- %8 = call i8* @llvm.wasm.get.exception(token %7)
- call void @__clang_call_terminate(i8* %8) [ "funclet"(token %7) ]
- unreachable
-
-terminate10: ; preds = %ehcleanup7
- %9 = cleanuppad within %6 []
- %10 = call i8* @llvm.wasm.get.exception(token %9)
- call void @__clang_call_terminate(i8* %10) [ "funclet"(token %9) ]
+ %6 = cleanuppad within %5 []
+ %7 = call i8* @llvm.wasm.get.exception(token %6)
+ call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
unreachable
}
@@ -164,12 +153,12 @@
; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: i32.catch
+; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL
; CHECK: global.set __stack_pointer@GLOBAL
; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: catch_all
+; CHECK: catch
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer@GLOBAL
; CHECK: global.set __stack_pointer@GLOBAL
; CHECK: call __cxa_end_catch@FUNCTION
@@ -251,6 +240,8 @@
declare void @foo()
declare void @bar(i32*)
declare i32 @__gxx_wasm_personality_v0(...)
+declare void @llvm.wasm.throw(i32, i8*)
+declare void @llvm.wasm.rethrow()
declare i8* @llvm.wasm.get.exception(token)
declare i32 @llvm.wasm.get.ehselector(token)
declare i32 @llvm.eh.typeid.for(i8*)
@@ -258,7 +249,6 @@
declare void @__cxa_end_catch()
declare void @__cxa_rethrow()
declare void @__clang_call_terminate(i8*)
-declare void @_ZSt9terminatev()
declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
; CHECK: __cpp_exception: