[llvm] [WebAssembly] Add assembly support for final EH proposal (PR #107917)
Derek Schuff via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 10 13:34:08 PDT 2024
================
@@ -657,7 +668,251 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
updateScopeTops(Header, End);
}
+void WebAssemblyCFGStackify::placeTryTableMarker(MachineBasicBlock &MBB) {
+ assert(MBB.isEHPad());
+ MachineFunction &MF = *MBB.getParent();
+ auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+ const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
+ const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>();
+ SortRegionInfo SRI(MLI, WEI);
+ const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+
+ // Compute the nearest common dominator of all unwind predecessors
+ MachineBasicBlock *Header = nullptr;
+ int MBBNumber = MBB.getNumber();
+ for (auto *Pred : MBB.predecessors()) {
+ if (Pred->getNumber() < MBBNumber) {
+ Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred;
+ assert(!explicitlyBranchesTo(Pred, &MBB) &&
+ "Explicit branch to an EH pad!");
+ }
+ }
+ if (!Header)
+ return;
+
+ assert(&MBB != &MF.front() && "Header blocks shouldn't have predecessors");
+ MachineBasicBlock *LayoutPred = MBB.getPrevNode();
+
+ // If the nearest common dominator is inside a more deeply nested context,
+ // walk out to the nearest scope which isn't more deeply nested.
+ for (MachineFunction::iterator I(LayoutPred), E(Header); I != E; --I) {
+ if (MachineBasicBlock *ScopeTop = ScopeTops[I->getNumber()]) {
+ if (ScopeTop->getNumber() > Header->getNumber()) {
+ // Skip over an intervening scope.
+ I = std::next(ScopeTop->getIterator());
+ } else {
+ // We found a scope level at an appropriate depth.
+ Header = ScopeTop;
+ break;
+ }
+ }
+ }
+
+ // Decide where in Header to put the TRY_TABLE.
+
+ // Instructions that should go before the TRY_TABLE.
+ SmallPtrSet<const MachineInstr *, 4> BeforeSet;
+ // Instructions that should go after the TRY_TABLE.
+ SmallPtrSet<const MachineInstr *, 4> AfterSet;
+ for (const auto &MI : *Header) {
+ // If there is a previously placed LOOP marker and the bottom block of the
+ // loop is above MBB, it should be after the TRY_TABLE, because the loop is
+ // nested in this TRY_TABLE. Otherwise it should be before the TRY_TABLE.
+ if (MI.getOpcode() == WebAssembly::LOOP) {
+ auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode();
+ if (MBB.getNumber() > LoopBottom->getNumber())
+ AfterSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ BeforeSet.insert(&MI);
+#endif
+ }
+
+ // All previously inserted BLOCK/TRY_TABLE markers should be after the
+ // TRY_TABLE because they are all nested blocks/try_tables.
+ if (MI.getOpcode() == WebAssembly::BLOCK ||
+ MI.getOpcode() == WebAssembly::TRY_TABLE)
+ AfterSet.insert(&MI);
+
+#ifndef NDEBUG
+ // All END_(BLOCK/LOOP/TRY_TABLE) markers should be before the TRY_TABLE.
+ if (MI.getOpcode() == WebAssembly::END_BLOCK ||
+ MI.getOpcode() == WebAssembly::END_LOOP ||
+ MI.getOpcode() == WebAssembly::END_TRY_TABLE)
+ BeforeSet.insert(&MI);
+#endif
+
+ // Terminators should go after the TRY_TABLE.
+ if (MI.isTerminator())
+ AfterSet.insert(&MI);
+ }
+
+ // If Header unwinds to MBB (= Header contains 'invoke'), the try_table block
+ // should contain the call within it. So the call should go after the
+ // TRY_TABLE. The exception is when the header's terminator is a rethrow
+ // instruction, in which case that instruction, not a call instruction before
+ // it, is gonna throw.
+ MachineInstr *ThrowingCall = nullptr;
+ if (MBB.isPredecessor(Header)) {
+ auto TermPos = Header->getFirstTerminator();
+ if (TermPos == Header->end() ||
+ TermPos->getOpcode() != WebAssembly::RETHROW) {
+ for (auto &MI : reverse(*Header)) {
+ if (MI.isCall()) {
+ AfterSet.insert(&MI);
+ ThrowingCall = &MI;
+ // Possibly throwing calls are usually wrapped by EH_LABEL
+ // instructions. We don't want to split them and the call.
+ if (MI.getIterator() != Header->begin() &&
+ std::prev(MI.getIterator())->isEHLabel()) {
+ AfterSet.insert(&*std::prev(MI.getIterator()));
+ ThrowingCall = &*std::prev(MI.getIterator());
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Local expression tree should go after the TRY_TABLE.
+ // For BLOCK placement, we start the search from the previous instruction of a
+ // BB's terminator, but in TRY_TABLE's case, we should start from the previous
+ // instruction of a call that can throw, or a EH_LABEL that precedes the call,
----------------
dschuff wrote:
I thought that throwing calls were also terminators. Is this not the case?
https://github.com/llvm/llvm-project/pull/107917
More information about the llvm-commits
mailing list