When `try_table`'s catch clause's destination has a return type, as in
the case of catch with a concrete tag, catch_ref, and catch_all_ref. For
example:
```wasm
block exnref
try_table (catch_all_ref 0)
...
end_try_table
end_block
... use exnref ...
```
This code is not valid because the block's body type is not exnref. So
we add an unreachable after the 'end_try_table' to make the code valid
here:
```wasm
block exnref
try_table (catch_all_ref 0)
...
end_try_table
unreachable ;; Newly added
end_block
```
Because 'unreachable' is a terminator we also need to split the BB.
---
We need to handle the same thing for unwind mismatch handling. In the
code below, we create a "trampoline BB" that will be the destination for
the nested `try_table`~`end_try_table` added to fix a unwind mismatch:
```wasm
try_table (catch ... )
block exnref
...
try_table (catch_all_ref N)
some code
end_try_table
...
end_block ;; Trampoline BB
throw_ref
end_try_table
```
While the `block` added for the trampoline BB has the return type
`exnref`, its body, which contains the nested `try_table` and other
code, wouldn't have the `exnref` return type. Most times it didn't
become a problem because the block's body ended with something like `br`
or `return`, but that may not always be the case, especially when there
is a loop. So we add an `unreachable` to make the code valid here too:
```wasm
try_table (catch ... )
block exnref
...
try_table (catch_all_ref N)
some code
end_try_table
...
unreachable ;; Newly added
end_block ;; Trampoline BB
throw_ref
end_try_table
```
In this case we just append the `unreachable` at the end of the layout
predecessor BB. (This was tricky to do in the first (non-mismatch) case
because there `end_try_table` and `end_block` were added in the
beginning of an EH pad in `placeTryTableMarker` and moving
`end_try_table` and the new `unreachable` to the previous BB caused
other problems.)
---
This adds many `unreaachable`s to the output, but this adds
`unreachable` to only a few places to see if this is working. The
FileCheck lines in `exception.ll` and `cfg-stackify-eh.ll` are already
heavily redacted to only leave important control-flow instructions, so I
don't think it's worth adding `unreachable`s everywhere.
114 lines
3.1 KiB
YAML
114 lines
3.1 KiB
YAML
# RUN: llc -mtriple=wasm32-unknown-unknown -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling -run-pass wasm-late-eh-prepare -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
|
|
|
|
--- |
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
declare i32 @__gxx_wasm_personality_v0(...)
|
|
declare void @foo()
|
|
define void @eh_label_test() personality ptr @__gxx_wasm_personality_v0 {
|
|
ret void
|
|
}
|
|
define void @unreachable_ehpad_test() personality ptr @__gxx_wasm_personality_v0 {
|
|
ret void
|
|
}
|
|
define void @empty_cleanuppad_test() personality ptr @__gxx_wasm_personality_v0 {
|
|
ret void
|
|
}
|
|
...
|
|
|
|
---
|
|
# This tests 'try' and 'catch' instructions are correctly placed with respect to
|
|
# EH_LABEL instructions.
|
|
# CHECK-LABEL: name: eh_label_test
|
|
name: eh_label_test
|
|
liveins:
|
|
- { reg: '$arguments' }
|
|
frameInfo:
|
|
hasCalls: true
|
|
body: |
|
|
bb.0:
|
|
; TRY should be before EH_LABEL wrappers of throwing calls
|
|
; CHECK: bb.0
|
|
; CHECK: TRY
|
|
; CHECK-NEXT: EH_LABEL
|
|
; CHECK-NEXT: CALL @foo
|
|
; CHECK-NEXT: EH_LABEL
|
|
successors: %bb.1, %bb.2
|
|
EH_LABEL <mcsymbol .Ltmp0>
|
|
CALL @foo, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
EH_LABEL <mcsymbol .Ltmp1>
|
|
BR %bb.2, implicit-def dead $arguments
|
|
|
|
bb.1 (landing-pad):
|
|
; predecessors: %bb.0
|
|
successors: %bb.2
|
|
; CATCH_ALL_LEGACY should be after EH_LABELs in the beginning of an EH pad.
|
|
; (Sometimes there are multiple EH_LABELs in an EH pad. This test tests
|
|
; that.) GLOBAL_SET should follow right after that.
|
|
; CHECK: bb.1
|
|
; CHECK: EH_LABEL
|
|
; CHECK: EH_LABEL
|
|
; CHECK-NEXT: CATCH_ALL_LEGACY
|
|
; CHECK-NEXT: GLOBAL_SET_I32
|
|
EH_LABEL <mcsymbol .Ltmp2>
|
|
EH_LABEL <mcsymbol .Ltmp2>
|
|
CATCHRET %bb.2, %bb.1, implicit-def dead $arguments
|
|
|
|
bb.2:
|
|
; predecessors: %bb.0, %bb.1
|
|
RETURN implicit-def dead $arguments
|
|
...
|
|
|
|
---
|
|
# Unreachable EH pads should be removed by LateEHPrepare.
|
|
# CHECK-LABEL: name: unreachable_ehpad_test
|
|
name: unreachable_ehpad_test
|
|
liveins:
|
|
- { reg: '$arguments' }
|
|
body: |
|
|
; CHECK: bb.0
|
|
bb.0:
|
|
successors: %bb.2
|
|
BR %bb.2, implicit-def dead $arguments
|
|
|
|
; This EH pad is unreachable, so it should be removed by LateEHPrepare
|
|
; CHECK-NOT: bb.1 (landing-pad)
|
|
bb.1 (landing-pad):
|
|
successors: %bb.2
|
|
EH_LABEL <mcsymbol .Ltmp2>
|
|
CATCHRET %bb.2, %bb.1, implicit-def dead $arguments
|
|
|
|
; This BB should remain (it will be renumbered to bb.1)
|
|
; CHECK: bb.1
|
|
bb.2:
|
|
; predecessors: %bb.0, %bb.1
|
|
RETURN implicit-def dead $arguments
|
|
...
|
|
|
|
---
|
|
# Regression test for a bug that LateEHPrepare::addCatchAll didn't handle empty
|
|
# cleanup pads. (It tried to get debug info from end() iterator.)
|
|
name: empty_cleanuppad_test
|
|
liveins:
|
|
- { reg: '$arguments' }
|
|
body: |
|
|
bb.0:
|
|
successors: %bb.1, %bb.3
|
|
EH_LABEL <mcsymbol .Ltmp0>
|
|
CALL @foo, implicit-def dead $arguments, implicit $sp32, implicit $sp64
|
|
EH_LABEL <mcsymbol .Ltmp1>
|
|
BR %bb.3, implicit-def dead $arguments
|
|
|
|
;; Empty cleanuppad
|
|
bb.1 (landing-pad):
|
|
successors: %bb.2
|
|
EH_LABEL <mcsymbol .Ltmp2>
|
|
|
|
bb.2:
|
|
successors: %bb.3
|
|
CLEANUPRET %bb.1, implicit-def dead $arguments
|
|
|
|
bb.3:
|
|
RETURN implicit-def dead $arguments
|
|
...
|