diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 6cae0e766dbc0..bdc1cc6d652ac 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -1297,6 +1297,7 @@ void WebAssemblyCFGStackify::addNestedTryDelegate( // some code // end_try_table // ... +// unreachable // end_block ;; Trampoline BB // throw_ref // end_try_table @@ -1358,6 +1359,13 @@ WebAssemblyCFGStackify::getTrampolineBlock(MachineBasicBlock *UnwindDest) { BuildMI(TrampolineBB, EndDebugLoc, TII.get(WebAssembly::THROW_REF)) .addReg(ExnReg); + // The trampoline BB's return type is exnref because it is a target of + // catch_all_ref. But the body type of the block we just created is not. We + // add an 'unreachable' right before the 'end_block' to make the code valid. + MachineBasicBlock *TrampolineLayoutPred = TrampolineBB->getPrevNode(); + BuildMI(TrampolineLayoutPred, TrampolineLayoutPred->findBranchDebugLoc(), + TII.get(WebAssembly::UNREACHABLE)); + registerScope(Block, EndBlock); UnwindDestToTrampoline[UnwindDest] = TrampolineBB; return TrampolineBB; @@ -1465,7 +1473,7 @@ void WebAssemblyCFGStackify::addNestedTryTable(MachineInstr *RangeBegin, // - After: // pre_bb: (new) // range_end - // end_try_table: (new) + // end_try_table_bb: (new) // end_try_table // post_bb: (previous 'ehpad') // catch @@ -1523,9 +1531,9 @@ void WebAssemblyCFGStackify::addNestedTryTable(MachineInstr *RangeBegin, // end_loop // end_try_table // -// So if the unwind dest BB has a end_loop before an end_try_table, we split the -// BB with the end_loop as a separate BB before the end_try_table BB, so that -// after we fix the unwind mismatch, the code will be like: +// So if an end_try_table BB has an end_loop before the end_try_table, we split +// the BB with the end_loop as a separate BB before the end_try_table BB, so +// that after we fix the unwind mismatch, the code will be like: // bb0: // try_table // block exnref @@ -1538,10 +1546,10 @@ void WebAssemblyCFGStackify::addNestedTryTable(MachineInstr *RangeBegin, // end_block // end_try_table_bb: // end_try_table -static void splitEndLoopBB(MachineBasicBlock *UnwindDest) { - auto &MF = *UnwindDest->getParent(); +static void splitEndLoopBB(MachineBasicBlock *EndTryTableBB) { + auto &MF = *EndTryTableBB->getParent(); MachineInstr *EndTryTable = nullptr, *EndLoop = nullptr; - for (auto &MI : reverse(*UnwindDest)) { + for (auto &MI : reverse(*EndTryTableBB)) { if (MI.getOpcode() == WebAssembly::END_TRY_TABLE) { EndTryTable = &MI; continue; @@ -1555,11 +1563,11 @@ static void splitEndLoopBB(MachineBasicBlock *UnwindDest) { return; auto *EndLoopBB = MF.CreateMachineBasicBlock(); - MF.insert(UnwindDest->getIterator(), EndLoopBB); + MF.insert(EndTryTableBB->getIterator(), EndLoopBB); auto SplitPos = std::next(EndLoop->getIterator()); - EndLoopBB->splice(EndLoopBB->end(), UnwindDest, UnwindDest->begin(), + EndLoopBB->splice(EndLoopBB->end(), EndTryTableBB, EndTryTableBB->begin(), SplitPos); - EndLoopBB->addSuccessor(UnwindDest); + EndLoopBB->addSuccessor(EndTryTableBB); } bool WebAssemblyCFGStackify::fixCallUnwindMismatches(MachineFunction &MF) { @@ -1943,8 +1951,16 @@ bool WebAssemblyCFGStackify::fixCallUnwindMismatches(MachineFunction &MF) { // When end_loop is before end_try_table within the same BB in unwind // destinations, we should split the end_loop into another BB. if (!WebAssembly::WasmUseLegacyEH) - for (auto &[UnwindDest, _] : UnwindDestToTryRanges) - splitEndLoopBB(UnwindDest); + for (auto &[UnwindDest, _] : UnwindDestToTryRanges) { + auto It = EHPadToTry.find(UnwindDest); + // If UnwindDest is the fake caller block, it will not be in EHPadToTry + // map + if (It != EHPadToTry.end()) { + auto *TryTable = It->second; + auto *EndTryTable = BeginToEnd[TryTable]; + splitEndLoopBB(EndTryTable->getParent()); + } + } // Now we fix the mismatches by wrapping calls with inner try-delegates. for (auto &P : UnwindDestToTryRanges) { @@ -2179,8 +2195,15 @@ bool WebAssemblyCFGStackify::fixCatchUnwindMismatches(MachineFunction &MF) { // When end_loop is before end_try_table within the same BB in unwind // destinations, we should split the end_loop into another BB. - for (auto &[_, UnwindDest] : EHPadToUnwindDest) - splitEndLoopBB(UnwindDest); + for (auto &[_, UnwindDest] : EHPadToUnwindDest) { + auto It = EHPadToTry.find(UnwindDest); + // If UnwindDest is the fake caller block, it will not be in EHPadToTry map + if (It != EHPadToTry.end()) { + auto *TryTable = It->second; + auto *EndTryTable = BeginToEnd[TryTable]; + splitEndLoopBB(EndTryTable->getParent()); + } + } NumCatchUnwindMismatches += EHPadToUnwindDest.size(); SmallPtrSet NewEndTryBBs; @@ -2372,6 +2395,48 @@ static void appendEndToFunction(MachineFunction &MF, TII.get(WebAssembly::END_FUNCTION)); } +// We added block~end_block and try_table~end_try_table markers in +// placeTryTableMarker. But When 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: +// 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: +// block exnref +// try_table (catch_all_ref 0) +// ... +// end_try_table +// unreachable (new) +// end_block +// +// Because 'unreachable' is a terminator we also need to split the BB. +static void addUnreachableAfterTryTables(MachineFunction &MF, + const WebAssemblyInstrInfo &TII) { + std::vector EndTryTables; + for (auto &MBB : MF) + for (auto &MI : MBB) + if (MI.getOpcode() == WebAssembly::END_TRY_TABLE) + EndTryTables.push_back(&MI); + + for (auto *EndTryTable : EndTryTables) { + auto *MBB = EndTryTable->getParent(); + auto *NewEndTryTableBB = MF.CreateMachineBasicBlock(); + MF.insert(MBB->getIterator(), NewEndTryTableBB); + auto SplitPos = std::next(EndTryTable->getIterator()); + NewEndTryTableBB->splice(NewEndTryTableBB->end(), MBB, MBB->begin(), + SplitPos); + NewEndTryTableBB->addSuccessor(MBB); + BuildMI(NewEndTryTableBB, EndTryTable->getDebugLoc(), + TII.get(WebAssembly::UNREACHABLE)); + } +} + /// Insert BLOCK/LOOP/TRY/TRY_TABLE markers at appropriate places. void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) { // We allocate one more than the number of blocks in the function to @@ -2398,13 +2463,17 @@ void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) { } } - // Fix mismatches in unwind destinations induced by linearizing the code. if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && MF.getFunction().hasPersonalityFn()) { - bool MismatchFixed = fixCallUnwindMismatches(MF); - MismatchFixed |= fixCatchUnwindMismatches(MF); - if (MismatchFixed) - recalculateScopeTops(MF); + const auto &TII = *MF.getSubtarget().getInstrInfo(); + // Add an 'unreachable' after 'end_try_table's. + addUnreachableAfterTryTables(MF, TII); + // Fix mismatches in unwind destinations induced by linearizing the code. + fixCallUnwindMismatches(MF); + fixCatchUnwindMismatches(MF); + // addUnreachableAfterTryTables and fixUnwindMismatches create new BBs, so + // we need to recalculate ScopeTops. + recalculateScopeTops(MF); } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td index b68dd8809bb92..ed6aec1ab33e3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -144,7 +144,7 @@ defm THROW_REF : I<(outs), (ins EXNREF:$exn), (outs), (ins), [], "throw_ref \t$exn", "throw_ref", 0x0a>; } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 -// Region within which an exception is caught: try / end_try +// Region within which an exception is caught: try_table / end_try_table let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in { defm TRY_TABLE : I<(outs), (ins Signature:$sig, variable_ops), (outs), (ins Signature:$sig, catch_list:$cal), [], diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll index 683b03d16d57b..98de9a267b95a 100644 --- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -857,6 +857,7 @@ invoke.cont: ; preds = %entry ; NOSORT: loop ; NOSORT: call foo ; NOSORT: end_loop +; NOSORT: unreachable ; NOSORT: end_block # label[[L3]]: ; NOSORT: throw_ref ; NOSORT: end_try_table diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir index d6f734c64acd6..9273ceeadd0e7 100644 --- a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir +++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir @@ -78,7 +78,8 @@ body: | EH_LABEL CATCHRET %bb.2, %bb.1, implicit-def dead $arguments - ; CHECK: bb.2 + ; 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 diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll index d6f3ffc8c33cb..304664b622e80 100644 --- a/llvm/test/CodeGen/WebAssembly/exception.ll +++ b/llvm/test/CodeGen/WebAssembly/exception.ll @@ -38,6 +38,7 @@ define void @throw(ptr %p) { ; CHECK: call foo ; CHECK: br 2 ; CHECK: end_try_table +; CHECK: unreachable ; CHECK: end_block ; CHECK: local.set 2 ; CHECK: local.get 0