Skip to content

Commit 380fd09

Browse files
authored
[WebAssembly] Fix unwind mismatches in new EH (#114361)
This fixes unwind mismatches for the new EH spec. The main flow is similar to that of the legacy EH's unwind mismatch fixing. The new EH shared `fixCallUnwindMismatches` and `fixCatchUnwindMismatches` functions, which gather the range of instructions we need to fix their unwind destination for, with the legacy EH. But unlike the legacy EH that uses `try`-`delegate`s to fix them, the new EH wrap those instructions with nested `try_table`-`end_try_table`s that jump to a "trampoline" BB, where we rethrow (using a `throw_ref`) the exception to the correct `try_table`. For a simple example of a call unwind mismatch, suppose if `call foo` should unwind to the outer `try_table` but is wrapped in another `try_table` (not shown here): ```wast try_table ... call foo ;; Unwind mismatch. Should unwind to the outer try_table ... end_try_table ``` Then we wrap the call with a new nested `try_table`-`end_try_table`, add a `block` / `end_block` right inside the target `try_table`, and make the nested `try_table` jump to it using a `catch_all_ref` clause, and rethrow the exception using a `throw_ref`: ```wast try_table block $l0 exnref ... try_table (catch_all_ref $l0) call foo end_try_table ... end_block ;; Trampoline BB throw_ref end_try_table ``` --- This fixes two existing bugs. These are not easy to test independently without the unwind mismatch fixing. The first one is how we calculate `ScopeTops`. Turns out, we should do it in the same way as in the legacy EH even though there is no `end_try` at the end of `catch` block anymore. `nested_try` in `cfg-stackify-eh.ll` tests this case. The second bug is in `rewriteDepthImmediates`. `try_table`'s immediates should be computed without the `try_table` itself, meaning ```wast block try_table (catch ... 0) end_try_table end_block ``` Here 0 should target not `end_try_table` but `end_block`. This bug didn't crash the program because `placeTryTableMarker` generated only the simple form of `try_table` that has a single catch clause and an `end_block` follows right after the `end_try_table` in the same BB, so jumping to an `end_try_table` is the same as jumping to the `end_block`. But now we generate `catch` clauses with depths greater than 0 with when fixing unwind mismatches, which uncovered this bug. --- One case that needs a special treatment was when `end_loop` precedes an `end_try_table` within a BB and this BB is a (true) unwind destination when fixing unwind mismatches. In this case we need to split this `end_loop` into a predecessor BB. This case is tested in `unwind_mismatches_with_loop` in `cfg-stackify-eh.ll`. --- `cfg-stackify-eh.ll` contains mostly the same set of tests with the existing `cfg-stackify-eh-legacy.ll` with the updated FileCheck expectations. As in `cfg-stackify-eh-legacy.ll`, the FileCheck lines mostly only contain control flow instructions and calls for readability. - `nested_try` and `unwind_mismatches_with_loop` are added to test newly found bugs in the new EH. - Some tests in `cfg-stackify-eh-legacy.ll` about the legacy-EH-specific asepcts have not been added to `cfg-stackify-eh.ll`. (`remove_unnecessary_instrs`, `remove_unnecessary_br`, `fix_function_end_return_type_with_try_catch`, and `branch_remapping_after_fixing_unwind_mismatches_0/1`)
1 parent e28d440 commit 380fd09

File tree

3 files changed

+2205
-33
lines changed

3 files changed

+2205
-33
lines changed

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,22 @@ inline bool isMarker(unsigned Opc) {
478478
}
479479
}
480480

481+
inline bool isEndMarker(unsigned Opc) {
482+
switch (Opc) {
483+
case WebAssembly::END_BLOCK:
484+
case WebAssembly::END_BLOCK_S:
485+
case WebAssembly::END_LOOP:
486+
case WebAssembly::END_LOOP_S:
487+
case WebAssembly::END_TRY:
488+
case WebAssembly::END_TRY_S:
489+
case WebAssembly::END_TRY_TABLE:
490+
case WebAssembly::END_TRY_TABLE_S:
491+
return true;
492+
default:
493+
return false;
494+
}
495+
}
496+
481497
inline bool isTry(unsigned Opc) {
482498
switch (Opc) {
483499
case WebAssembly::TRY:
@@ -510,6 +526,20 @@ inline bool isCatch(unsigned Opc) {
510526
}
511527
}
512528

529+
inline bool isCatchAll(unsigned Opc) {
530+
switch (Opc) {
531+
case WebAssembly::CATCH_ALL_LEGACY:
532+
case WebAssembly::CATCH_ALL_LEGACY_S:
533+
case WebAssembly::CATCH_ALL:
534+
case WebAssembly::CATCH_ALL_S:
535+
case WebAssembly::CATCH_ALL_REF:
536+
case WebAssembly::CATCH_ALL_REF_S:
537+
return true;
538+
default:
539+
return false;
540+
}
541+
}
542+
513543
inline bool isLocalGet(unsigned Opc) {
514544
switch (Opc) {
515545
case WebAssembly::LOCAL_GET_I32:

0 commit comments

Comments
 (0)