[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/annotations.mir b/llvm/test/CodeGen/WebAssembly/annotations.mir
deleted file mode 100644
index 1ae2db8..0000000
--- a/llvm/test/CodeGen/WebAssembly/annotations.mir
+++ /dev/null
@@ -1,94 +0,0 @@
-# RUN: llc -mtriple=wasm32-unknown-unknown -start-after xray-instrumentation -wasm-keep-registers %s -o - | FileCheck %s
-
----
-# Tests if block/loop/try/catch/end instructions are correctly printed with
-# their annotations.
-
-# CHECK: test0:
-# CHECK: block
-# CHECK: try
-# CHECK: br 0 # 0: down to label1
-# CHECK: catch_all # catch0:
-# CHECK: block
-# CHECK: br_if 0, 1 # 0: down to label2
-# CHECK: loop # label3:
-# CHECK: br_if 0, 1 # 0: up to label3
-# CHECK: end_loop
-# CHECK: end_block # label2:
-# CHECK: try
-# CHECK: rethrow 0 # 0: down to catch1
-# CHECK: catch_all # catch1:
-# CHECK: block
-# CHECK: try
-# CHECK: br 0 # 0: down to label6
-# CHECK: catch_all # catch2:
-# CHECK: unreachable
-# CHECK: end_try # label6:
-# CHECK: end_block # label5:
-# CHECK: rethrow 0 # 0: to caller
-# CHECK: end_try # label4:
-# CHECK: end_try # label1:
-# CHECK: end_block # label0:
-
-name: test0
-liveins:
- - { reg: '$arguments', reg: '$value_stack' }
-body: |
- bb.0:
- successors: %bb.7, %bb.1
- BLOCK 64, implicit-def $value_stack, implicit $value_stack
- TRY 64, implicit-def $value_stack, implicit $value_stack
- BR 0, implicit-def $arguments
-
- bb.1 (landing-pad):
- ; predecessors: %bb.0
- successors: %bb.2, %bb.3
-
- CATCH_ALL implicit-def $arguments
- BLOCK 64, implicit-def $value_stack, implicit $value_stack
- BR_IF 0, 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
-
- bb.2:
- ; predecessors: %bb.1, %bb.2
- successors: %bb.2, %bb.3
-
- LOOP 64, implicit-def $value_stack, implicit $value_stack
- BR_IF 0, 1, implicit-def $arguments
-
- bb.3:
- ; predecessors: %bb.1, %bb.2
- successors: %bb.4
-
- END_LOOP implicit-def $value_stack, implicit $value_stack
- END_BLOCK implicit-def $value_stack, implicit $value_stack
- TRY 64, implicit-def $value_stack, implicit $value_stack
- RETHROW 0, implicit-def $arguments
-
- bb.4 (landing-pad):
- ; predecessors: %bb.3
- successors: %bb.6, %bb.5
-
- CATCH_ALL implicit-def $arguments
- BLOCK 64, implicit-def $value_stack, implicit $value_stack
- TRY 64, implicit-def $value_stack, implicit $value_stack
- BR 0, implicit-def $arguments
-
- bb.5 (landing-pad):
- ; predecessors: %bb.4
- CATCH_ALL implicit-def $arguments
- UNREACHABLE implicit-def dead $arguments
-
- bb.6:
- ; predecessors: %bb.4
- END_TRY implicit-def $value_stack, implicit $value_stack
- END_BLOCK implicit-def $value_stack, implicit $value_stack
- RETHROW 0, implicit-def $arguments
-
- bb.7:
- ; predecessors: %bb.0
- END_TRY implicit-def $value_stack, implicit $value_stack
- END_TRY implicit-def $value_stack, implicit $value_stack
- END_BLOCK implicit-def $value_stack, implicit $value_stack
- FALLTHROUGH_RETURN_VOID implicit-def dead $arguments
- END_FUNCTION implicit-def $value_stack, implicit $value_stack
-...
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index e3c34f8..0377301 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -7,21 +7,25 @@
@_ZTId = external constant i8*
; Simple test case with two catch clauses
+; void test0() {
+; try {
+; foo();
+; } catch (int n) {
+; bar();
+; } catch (double d) {
+; }
+; }
; CHECK-LABEL: test0
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB0_1:
-; CHECK: i32.catch
+; 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: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
-; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
-; CHECK: call bar@FUNCTION
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB0_3:
-; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB0_5:
-; CHECK: call __cxa_rethrow@FUNCTION
-; CHECK: .LBB0_6:
+; CHECK: end_try
; CHECK: return
define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
@@ -68,39 +72,45 @@
}
; Nested try-catches within a catch
+; void test1() {
+; try {
+; foo();
+; } catch (int n) {
+; try {
+; foo();
+; } catch (int n) {
+; foo();
+; }
+; }
+; }
; CHECK-LABEL: test1
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB1_1:
-; CHECK: i32.catch $0=, 0
-; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $0
-; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION, $0
+; CHECK: catch
+; CHECK: br_on_exn 0, __cpp_exception@EVENT
+; CHECK: rethrow
+; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB1_3:
-; CHECK: i32.catch $0=, 0
-; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION, $0
-; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION, $0
+; CHECK: catch
+; CHECK: br_on_exn 0, __cpp_exception@EVENT
+; CHECK: rethrow
+; CHECK: i32.call $drop=, _Unwind_CallPersonality@FUNCTION
+; CHECK: try
+; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB1_5:
-; CHECK: catch_all
-; CHECK: call __cxa_end_catch@FUNCTION
+; CHECK: catch $drop=
; CHECK: rethrow
-; CHECK: .LBB1_6:
-; CHECK: call __cxa_rethrow@FUNCTION
+; CHECK: end_try
+; CHECK: catch $drop=
; CHECK: rethrow
-; CHECK: .LBB1_7:
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB1_8:
-; CHECK: catch_all
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB1_9:
-; CHECK: call __cxa_rethrow@FUNCTION
-; CHECK: rethrow
-; CHECK: .LBB1_10:
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB1_11:
+; CHECK: end_try
+; CHECK: end_try
+; CHECK: end_try
; CHECK: return
-define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
to label %try.cont11 unwind label %catch.dispatch
@@ -175,30 +185,38 @@
}
; Nested loop within a catch clause
+; void test2() {
+; try {
+; foo();
+; } catch (...) {
+; for (int i = 0; i < 50; i++)
+; foo();
+; }
+; }
; CHECK-LABEL: test2
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB2_1:
-; CHECK: i32.catch
-; CHECK: i32.call $drop=, __cxa_begin_catch@FUNCTION
-; CHECK: .LBB2_2:
+; CHECK: catch
+; CHECK: br_on_exn 0, __cpp_exception@EVENT
+; CHECK: rethrow
+; CHECK: loop
+; CHECK: try
; CHECK: call foo@FUNCTION
-; CHECK: .LBB2_4:
-; CHECK: catch_all
+; CHECK: catch $drop=
+; CHECK: try
; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB2_5:
-; CHECK: i32.catch
+; CHECK: catch
+; CHECK: br_on_exn 0, __cpp_exception@EVENT
+; CHECK: call __clang_call_terminate@FUNCTION, 0
+; CHECK: unreachable
; CHECK: call __clang_call_terminate@FUNCTION
; CHECK: unreachable
-; CHECK: .LBB2_6:
-; CHECK: catch_all
-; CHECK: call _ZSt9terminatev@FUNCTION
-; CHECK: unreachable
-; CHECK: .LBB2_7:
+; CHECK: end_try
; CHECK: rethrow
-; CHECK: .LBB2_8:
-; CHECK: call __cxa_end_catch@FUNCTION
-; CHECK: .LBB2_10:
+; CHECK: end_try
+; CHECK: end_loop
+; CHECK: end_try
; CHECK: return
define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir
deleted file mode 100644
index 11d9aaf..0000000
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.mir
+++ /dev/null
@@ -1,322 +0,0 @@
-# RUN: llc -mtriple=wasm32-unknown-unknown -exception-model=wasm -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
-
---- |
- target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
- target triple = "wasm32-unknown-unknown"
-
- @__wasm_lpad_context = external global { i32, i8*, i32 }
-
- declare void @may_throw()
- ; Function Attrs: nounwind
- declare void @dont_throw() #0
- declare i8* @__cxa_begin_catch(i8*)
- declare void @__cxa_end_catch()
- declare void @__cxa_rethrow()
- ; Function Attrs: nounwind
- declare i32 @__gxx_wasm_personality_v0(...)
- declare i32 @_Unwind_CallPersonality(i8*) #0
-
- define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
- unreachable
- }
- define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
- unreachable
- }
- define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
- unreachable
- }
- define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
- unreachable
- }
-
- attributes #0 = { nounwind }
-
----
-# Simplest try-catch
-# try {
-# may_throw();
-# } catch (...) {
-# }
-name: test0
-# CHECK-LABEL: name: test0
-liveins:
- - { reg: '$arguments', reg: '$value_stack' }
-body: |
- bb.0:
- successors: %bb.2, %bb.1
-
- CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.2, implicit-def $arguments
- ; CHECK-LABEL: bb.0:
- ; CHECK: TRY
- ; CHECK-NEXT: CALL_VOID @may_throw
-
- bb.1 (landing-pad):
- ; predecessors: %bb.0
- successors: %bb.2
-
- %2:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %3:i32 = CALL_I32 @__cxa_begin_catch, %2:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
- DROP_I32 killed %3:i32, implicit-def $arguments
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-
- bb.2:
- ; predecessors: %bb.0, %bb.1
-
- RETURN_VOID implicit-def dead $arguments
- ; CHECK-LABEL: bb.2:
- ; CHECK-NEXT: END_TRY
- ; CHECK: RETURN_VOID
-...
----
-
-# Nested try-catch inside another catch
-# try {
-# may_throw();
-# } catch (int n) {
-# try {
-# may_throw();
-# } catch (int n) {
-# }
-# }
-name: test1
-# CHECK-LABEL: name: test1
-liveins:
- - { reg: '$arguments', reg: '$value_stack' }
-body: |
- bb.0:
- successors: %bb.9, %bb.1
-
- CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.9, implicit-def $arguments
- ; CHECK-LABEL: bb.0:
- ; CHECK: TRY
- ; CHECK-NEXT: CALL_VOID @may_throw
-
- bb.1 (landing-pad):
- ; predecessors: %bb.0
- successors: %bb.2, %bb.7
-
- %30:i32 = CATCH_I32 0, implicit-def dead $arguments
- LOCAL_SET_I32 0, %30:i32, implicit-def $arguments
- %16:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %27:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- STORE_I32 2, @__wasm_lpad_context + 4, %16:i32, %27:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1)`)
- %26:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %25:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- STORE_I32 2, @__wasm_lpad_context, %26:i32, %25:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
- %32:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %31:i32 = CALL_I32 @_Unwind_CallPersonality, %32:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- DROP_I32 killed %31:i32, implicit-def $arguments
- %24:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %17:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %24:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
- %18:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %19:i32 = NE_I32 %17:i32, %18:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_IF %bb.7, %19:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
-
- bb.2:
- ; predecessors: %bb.1
- successors: %bb.8, %bb.3, %bb.6
-
- %34:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %33:i32 = CALL_I32 @__cxa_begin_catch, %34:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- DROP_I32 killed %33:i32, implicit-def $arguments
- CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.8, implicit-def $arguments
- ; CHECK-LABEL: bb.2:
- ; CHECK: DROP_I32
- ; CHECK-NEXT: TRY
- ; CHECK-NEXT: TRY
- ; CHECK-NEXT: CALL_VOID @may_throw
-
- bb.3 (landing-pad):
- ; predecessors: %bb.2
- successors: %bb.4, %bb.5
-
- %35:i32 = CATCH_I32 0, implicit-def dead $arguments
- LOCAL_SET_I32 0, %35:i32, implicit-def $arguments
- %21:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %20:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- STORE_I32 2, @__wasm_lpad_context, %21:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
- %37:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %36:i32 = CALL_I32 @_Unwind_CallPersonality, %37:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- DROP_I32 killed %36:i32, implicit-def $arguments
- %29:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %22:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %29:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
- %28:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %23:i32 = NE_I32 %22:i32, %28:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_IF %bb.5, %23:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
-
- bb.4:
- ; predecessors: %bb.3
- successors: %bb.8
-
- %39:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %38:i32 = CALL_I32 @__cxa_begin_catch, %39:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- DROP_I32 killed %38:i32, implicit-def $arguments
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.8, implicit-def $arguments
-
- bb.5:
- ; predecessors: %bb.3
- successors: %bb.6
-
- CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- RETHROW %bb.6, implicit-def $arguments
-
- bb.6 (landing-pad):
- ; predecessors: %bb.2, %bb.5
-
- CATCH_ALL implicit-def $arguments
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- RETHROW_TO_CALLER implicit-def $arguments
- ; CHECK-LABEL: bb.6 (landing-pad):
- ; CHECK-NEXT: END_TRY
-
- bb.7:
- ; predecessors: %bb.1
-
- CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- RETHROW_TO_CALLER implicit-def $arguments
- ; CHECK-LABEL: bb.7:
- ; CHECK-NEXT: END_TRY
- ; CHECK: RETHROW 0
-
- bb.8:
- ; predecessors: %bb.2, %bb.4
- successors: %bb.9
-
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-
- bb.9:
- ; predecessors: %bb.0, %bb.8
-
- RETURN_VOID implicit-def dead $arguments
- ; CHECK-LABEL: bb.9:
- ; CHECK-NEXT: END_TRY
-...
----
-
-# A loop within a try.
-# try {
-# for (int i = 0; i < n; ++i)
-# may_throw();
-# } catch (...) {
-# }
-name: test2
-# CHECK-LABEL: name: test2
-liveins:
- - { reg: '$arguments', reg: '$value_stack' }
-body: |
- bb.0:
- successors: %bb.1, %bb.4
-
- %18:i32 = CONST_I32 0, implicit-def dead $arguments
- LOCAL_SET_I32 1, %18:i32, implicit-def $arguments
- %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %19:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %9:i32 = GE_S_I32 %14:i32, %19:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_IF %bb.4, %9:i32, implicit-def $arguments
-
- bb.1:
- ; predecessors: %bb.0, %bb.3
- successors: %bb.3, %bb.2
-
- CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.3, implicit-def $arguments
- ; CHECK-LABEL: bb.1:
- ; CHECK: LOOP
- ; CHECK: TRY
- ; CHECK-NEXT: CALL_VOID @may_throw
-
- bb.2 (landing-pad):
- ; predecessors: %bb.1
- successors: %bb.4
-
- %11:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %22:i32 = CALL_I32 @__cxa_begin_catch, %11:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
- DROP_I32 killed %22:i32, implicit-def $arguments
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.4, implicit-def $arguments
-
- bb.3:
- ; predecessors: %bb.1
- successors: %bb.1, %bb.4
-
- %20:i32 = LOCAL_GET_I32 1, implicit-def $arguments
- %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %16:i32 = ADD_I32 %20:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %15:i32 = LOCAL_TEE_I32 1, %16:i32, implicit-def $arguments
- %21:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %10:i32 = GE_S_I32 %15:i32, %21:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_UNLESS %bb.1, %10:i32, implicit-def $arguments
- ; CHECK-LABEL: bb.3:
- ; CHECK: END_TRY
-
- bb.4:
- ; predecessors: %bb.2, %bb.0, %bb.3
-
- RETURN_VOID implicit-def dead $arguments
-...
----
-
-# A loop within a catch
-# try {
-# may_throw();
-# } catch (...) {
-# for (int i = 0; i < n; ++i)
-# dont_throw();
-# }
-name: test3
-# CHECK-LABEL: name: test3
-liveins:
- - { reg: '$arguments', reg: '$value_stack' }
-body: |
- bb.0:
- successors: %bb.4, %bb.1
-
- CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- BR %bb.4, implicit-def $arguments
- ; CHECK-LABEL: bb.0:
- ; CHECK: TRY
- ; CHECK-NEXT: CALL_VOID @may_throw
-
- bb.1 (landing-pad):
- ; predecessors: %bb.0
- successors: %bb.2, %bb.3
-
- %9:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %18:i32 = CALL_I32 @__cxa_begin_catch, %9:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
- DROP_I32 killed %18:i32, implicit-def $arguments
- %19:i32 = CONST_I32 0, implicit-def dead $arguments
- LOCAL_SET_I32 1, %19:i32, implicit-def $arguments
- %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %20:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %10:i32 = GE_S_I32 %14:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_IF %bb.3, %10:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
-
- bb.2:
- ; predecessors: %bb.1, %bb.2
- successors: %bb.2, %bb.3
-
- CALL_VOID @dont_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
- %21:i32 = LOCAL_GET_I32 1, implicit-def $arguments
- %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %16:i32 = ADD_I32 %21:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- %15:i32 = LOCAL_TEE_I32 1, %16:i32, implicit-def $arguments
- %22:i32 = LOCAL_GET_I32 0, implicit-def $arguments
- %11:i32 = GE_S_I32 %15:i32, %22:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
- BR_UNLESS %bb.2, %11:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
-
- bb.3:
- ; predecessors: %bb.1, %bb.2
- successors: %bb.4
-
- CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
-
- bb.4:
- ; predecessors: %bb.0, %bb.3
-
- RETURN_VOID implicit-def dead $arguments
- ; CHECK-LABEL: bb.4:
- ; CHECK: END_TRY
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:
diff --git a/llvm/test/CodeGen/WebAssembly/wasmehprepare.ll b/llvm/test/CodeGen/WebAssembly/wasmehprepare.ll
index 6df7175..09013a0 100644
--- a/llvm/test/CodeGen/WebAssembly/wasmehprepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasmehprepare.ll
@@ -29,7 +29,7 @@
br i1 %matches, label %catch, label %rethrow
; CHECK: catch.start:
; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad
-; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0)
+; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.extract.exception()
; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0)
; CHECK-NEXT: store i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)
; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.lsda()
@@ -76,7 +76,6 @@
catchret from %1 to label %try.cont
; CHECK: catch.start:
; CHECK-NEXT: catchpad within %0 [i8* null]
-; CHECK-NEXT: call i8* @llvm.wasm.catch(i32 0)
; CHECK-NOT: call void @llvm.wasm.landingpad.index
; CHECK-NOT: store {{.*}} @__wasm_lpad_context
; CHECK-NOT: call i8* @llvm.wasm.lsda()
@@ -178,7 +177,6 @@
cleanupret from %12 unwind to caller
; CHECK: ehcleanup:
; CHECK-NEXT: cleanuppad
-; CHECK-NOT: call i8* @llvm.wasm.catch(i32 0)
; CHECK-NOT: call void @llvm.wasm.landingpad.index
; CHECK-NOT: store {{.*}} @__wasm_lpad_context
; CHECK-NOT: call i8* @llvm.wasm.lsda()
@@ -191,7 +189,7 @@
; A cleanuppad with a call to __clang_call_terminate().
; A call to wasm.catch() should be generated after the cleanuppad.
-define hidden void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
; CHECK-LABEL: @test3
entry:
invoke void @foo()
@@ -230,14 +228,14 @@
unreachable
; CHECK: terminate:
; CHECK-NEXT: cleanuppad
-; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0)
+; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.extract.exception
; CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]])
}
; PHI demotion test. Only the phi before catchswitch should be demoted; the phi
; before cleanuppad should NOT.
-define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
-; CHECK-LABEL: @test5
+define void @test4() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+; CHECK-LABEL: @test4
entry:
%c = alloca %struct.Cleanup, align 1
invoke void @foo()
@@ -301,8 +299,8 @@
; Tests if instructions after a call to @llvm.wasm.throw are deleted and the
; BB's dead children are deleted.
-; CHECK-LABEL: @test6
-define i32 @test6(i1 %b, i8* %p) {
+; CHECK-LABEL: @test5
+define i32 @test5(i1 %b, i8* %p) {
entry:
br i1 %b, label %bb.true, label %bb.false
@@ -326,6 +324,34 @@
ret i32 0
}
+; Tests if instructions after a call to @llvm.wasm.rethrow are deleted and the
+; BB's dead children are deleted.
+
+; CHECK-LABEL: @test6
+define i32 @test6(i1 %b, i8* %p) {
+entry:
+ br i1 %b, label %bb.true, label %bb.false
+
+; CHECK: bb.true:
+; CHECK-NEXT: call void @llvm.wasm.rethrow()
+; CHECK-NEXT: unreachable
+bb.true: ; preds = %entry
+ call void @llvm.wasm.rethrow()
+ br label %bb.true.0
+
+; CHECK-NOT: bb.true.0
+bb.true.0: ; preds = %bb.true
+ br label %merge
+
+; CHECK: bb.false
+bb.false: ; preds = %entry
+ br label %merge
+
+; CHECK: merge
+merge: ; preds = %bb.true.0, %bb.false
+ ret i32 0
+}
+
declare void @foo()
declare void @func(i32)
declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
@@ -334,12 +360,12 @@
declare i32 @llvm.wasm.get.ehselector(token)
declare i32 @llvm.eh.typeid.for(i8*)
declare void @llvm.wasm.throw(i32, i8*)
+declare void @llvm.wasm.rethrow()
declare i8* @__cxa_begin_catch(i8*)
declare void @__cxa_end_catch()
declare void @__cxa_rethrow()
declare void @__clang_call_terminate(i8*)
-; CHECK-DAG: declare i8* @llvm.wasm.catch(i32)
; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32)
; CHECK-DAG: declare i8* @llvm.wasm.lsda()
; CHECK-DAG: declare i32 @_Unwind_CallPersonality(i8*)