Files
clang-p2996/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
Heejin Ahn c3dfd34e54 [WebAssembly] Add unreachable before catch destinations (#123915)
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.
2025-01-22 22:39:43 -08:00

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
...