[llvm-branch-commits] [llvm] 9e4eade - [WebAssembly] Update basic EH instructions for the new spec
Heejin Ahn via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Jan 9 01:53:14 PST 2021
Author: Heejin Ahn
Date: 2021-01-09T01:48:06-08:00
New Revision: 9e4eadeb135d140b3a9e499354472170017cbe58
URL: https://github.com/llvm/llvm-project/commit/9e4eadeb135d140b3a9e499354472170017cbe58
DIFF: https://github.com/llvm/llvm-project/commit/9e4eadeb135d140b3a9e499354472170017cbe58.diff
LOG: [WebAssembly] Update basic EH instructions for the new spec
This implements basic instructions for the new spec.
- Adds new versions of instructions: `catch`, `catch_all`, and `rethrow`
- Adds support for instruction selection for the new instructions
- `catch` needs a custom routine for the same reason `throw` needs one,
to encode `__cpp_exception` tag symbol.
- Updates `WebAssembly::isCatch` utility function to include `catch_all`
and Change code that compares an instruction's opcode with `catch` to
use that function.
- LateEHPrepare
- Previously in LateEHPrepare we added `catch` instruction to both
`catchpad`s (for user catches) and `cleanuppad`s (for destructors).
In the new version `catch` is generated from `llvm.catch` intrinsic
in instruction selection phase, so we only need to add `catch_all`
to the beginning of cleanup pads.
- `catch` is generated from instruction selection, but we need to
hoist the `catch` instruction to the beginning of every EH pad,
because `catch` can be in the middle of the EH pad or even in a
split BB from it after various code transformations.
- Removes `addExceptionExtraction` function, which was used to
generate `br_on_exn` before.
- CFGStackfiy: Deletes `fixUnwindMismatches` function. Running this
function on the new instruction causes crashes, and the new version
will be added in a later CL, whose contents will be completely
different. So deleting the whole function will make the diff easier to
read.
- Reenables all disabled tests in exception.ll and eh-lsda.ll and a
single basic test in cfg-stackify-eh.ll.
- Updates existing tests to use the new assembly format. And deletes
`br_on_exn` instructions from the tests and FileCheck lines.
Reviewed By: dschuff, tlively
Differential Revision: https://reviews.llvm.org/D94040
Added:
Modified:
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
llvm/lib/Target/WebAssembly/WebAssemblyISD.def
llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td
llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
llvm/test/CodeGen/WebAssembly/eh-labels.mir
llvm/test/CodeGen/WebAssembly/eh-lsda.ll
llvm/test/CodeGen/WebAssembly/exception.ll
llvm/test/MC/WebAssembly/annotations.s
llvm/test/MC/WebAssembly/basic-assembly.s
llvm/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 814c4f5e9623..20e13b361cf8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1637,10 +1637,32 @@ void SelectionDAGBuilder::visitCleanupPad(const CleanupPadInst &CPI) {
}
}
-// For wasm, there's always a single catch pad attached to a catchswitch, and
-// the control flow always stops at the single catch pad, as it does for a
-// cleanup pad. In case the exception caught is not of the types the catch pad
-// catches, it will be rethrown by a rethrow.
+// In wasm EH, even though a catchpad may not catch an exception if a tag does
+// not match, it is OK to add only the first unwind destination catchpad to the
+// successors, because there will be at least one invoke instruction within the
+// catch scope that points to the next unwind destination, if one exists, so
+// CFGSort cannot mess up with BB sorting order.
+// (All catchpads with 'catch (type)' clauses have a 'llvm.rethrow' intrinsic
+// call within them, and catchpads only consisting of 'catch (...)' have a
+// '__cxa_end_catch' call within them, both of which generate invokes in case
+// the next unwind destination exists, i.e., the next unwind destination is not
+// the caller.)
+//
+// Having at most one EH pad successor is also simpler and helps later
+// transformations.
+//
+// For example,
+// current:
+// invoke void @foo to ... unwind label %catch.dispatch
+// catch.dispatch:
+// %0 = catchswitch within ... [label %catch.start] unwind label %next
+// catch.start:
+// ...
+// ... in this BB or some other child BB dominated by this BB there will be an
+// invoke that points to 'next' BB as an unwind destination
+//
+// next: ; We don't need to add this to 'current' BB's successor
+// ...
static void findWasmUnwindDestinations(
FunctionLoweringInfo &FuncInfo, const BasicBlock *EHPadBB,
BranchProbability Prob,
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 518c8db1920f..3538d6c3bb12 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -440,6 +440,18 @@ inline bool isMarker(unsigned Opc) {
}
}
+inline bool isCatch(unsigned Opc) {
+ switch (Opc) {
+ case WebAssembly::CATCH:
+ case WebAssembly::CATCH_S:
+ case WebAssembly::CATCH_ALL:
+ case WebAssembly::CATCH_ALL_S:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // end namespace WebAssembly
} // end namespace llvm
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
index 5eafeb70c02a..0f4379e66ca4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
@@ -183,8 +183,7 @@ static MachineInstr *findCatch(MachineBasicBlock *EHPad, Register &ExnReg) {
assert(EHPad->isEHPad());
MachineInstr *Catch = nullptr;
for (auto &MI : *EHPad) {
- switch (MI.getOpcode()) {
- case WebAssembly::CATCH:
+ if (WebAssembly::isCatch(MI.getOpcode())) {
Catch = &MI;
ExnReg = Catch->getOperand(0).getReg();
break;
@@ -784,11 +783,15 @@ static unsigned getCopyOpcode(const TargetRegisterClass *RC) {
// When MBB is split into MBB and Split, we should unstackify defs in MBB that
// have their uses in Split.
-static void unstackifyVRegsUsedInSplitBB(MachineBasicBlock &MBB,
- MachineBasicBlock &Split,
- WebAssemblyFunctionInfo &MFI,
- MachineRegisterInfo &MRI,
- const WebAssemblyInstrInfo &TII) {
+// FIXME This function will be used when fixing unwind mismatches, but the old
+// version of that function was removed for the moment and the new version has
+// not yet been added. So 'LLVM_ATTRIBUTE_UNUSED' is added to suppress the
+// warning. Remove the attribute after the new functionality is added.
+LLVM_ATTRIBUTE_UNUSED static void
+unstackifyVRegsUsedInSplitBB(MachineBasicBlock &MBB, MachineBasicBlock &Split,
+ WebAssemblyFunctionInfo &MFI,
+ MachineRegisterInfo &MRI,
+ const WebAssemblyInstrInfo &TII) {
for (auto &MI : Split) {
for (auto &MO : MI.explicit_uses()) {
if (!MO.isReg() || Register::isPhysicalRegister(MO.getReg()))
@@ -842,508 +845,8 @@ static void unstackifyVRegsUsedInSplitBB(MachineBasicBlock &MBB,
}
bool WebAssemblyCFGStackify::fixUnwindMismatches(MachineFunction &MF) {
- const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
- MachineRegisterInfo &MRI = MF.getRegInfo();
-
- // Linearizing the control flow by placing TRY / END_TRY markers can create
- // mismatches in unwind destinations. There are two kinds of mismatches we
- // try to solve here.
-
- // 1. When an instruction may throw, but the EH pad it will unwind to can be
- //
diff erent from the original CFG.
- //
- // Example: we have the following CFG:
- // bb0:
- // call @foo (if it throws, unwind to bb2)
- // bb1:
- // call @bar (if it throws, unwind to bb3)
- // bb2 (ehpad):
- // catch
- // ...
- // bb3 (ehpad)
- // catch
- // handler body
- //
- // And the CFG is sorted in this order. Then after placing TRY markers, it
- // will look like: (BB markers are omitted)
- // try $label1
- // try
- // call @foo
- // call @bar (if it throws, unwind to bb3)
- // catch <- ehpad (bb2)
- // ...
- // end_try
- // catch <- ehpad (bb3)
- // handler body
- // end_try
- //
- // Now if bar() throws, it is going to end up ip in bb2, not bb3, where it
- // is supposed to end up. We solve this problem by
- // a. Split the target unwind EH pad (here bb3) so that the handler body is
- // right after 'end_try', which means we extract the handler body out of
- // the catch block. We do this because this handler body should be
- // somewhere branch-eable from the inner scope.
- // b. Wrap the call that has an incorrect unwind destination ('call @bar'
- // here) with a nested try/catch/end_try scope, and within the new catch
- // block, branches to the handler body.
- // c. Place a branch after the newly inserted nested end_try so it can bypass
- // the handler body, which is now outside of a catch block.
- //
- // The result will like as follows. (new: a) means this instruction is newly
- // created in the process of doing 'a' above.
- //
- // block $label0 (new: placeBlockMarker)
- // try $label1
- // try
- // call @foo
- // try (new: b)
- // call @bar
- // catch (new: b)
- // local.set n / drop (new: b)
- // br $label1 (new: b)
- // end_try (new: b)
- // catch <- ehpad (bb2)
- // end_try
- // br $label0 (new: c)
- // catch <- ehpad (bb3)
- // end_try (hoisted: a)
- // handler body
- // end_block (new: placeBlockMarker)
- //
- // Note that the new wrapping block/end_block will be generated later in
- // placeBlockMarker.
- //
- // TODO Currently local.set and local.gets are generated to move exnref value
- // created by catches. That's because we don't support yielding values from a
- // block in LLVM machine IR yet, even though it is supported by wasm. Delete
- // unnecessary local.get/local.sets once yielding values from a block is
- // supported. The full EH spec requires multi-value support to do this, but
- // for C++ we don't yet need it because we only throw a single i32.
- //
- // ---
- // 2. The same as 1, but in this case an instruction unwinds to a caller
- // function and not another EH pad.
- //
- // Example: we have the following CFG:
- // bb0:
- // call @foo (if it throws, unwind to bb2)
- // bb1:
- // call @bar (if it throws, unwind to caller)
- // bb2 (ehpad):
- // catch
- // ...
- //
- // And the CFG is sorted in this order. Then after placing TRY markers, it
- // will look like:
- // try
- // call @foo
- // call @bar (if it throws, unwind to caller)
- // catch <- ehpad (bb2)
- // ...
- // end_try
- //
- // Now if bar() throws, it is going to end up ip in bb2, when it is supposed
- // throw up to the caller.
- // We solve this problem by
- // a. Create a new 'appendix' BB at the end of the function and put a single
- // 'rethrow' instruction (+ local.get) in there.
- // b. Wrap the call that has an incorrect unwind destination ('call @bar'
- // here) with a nested try/catch/end_try scope, and within the new catch
- // block, branches to the new appendix block.
- //
- // block $label0 (new: placeBlockMarker)
- // try
- // call @foo
- // try (new: b)
- // call @bar
- // catch (new: b)
- // local.set n (new: b)
- // br $label0 (new: b)
- // end_try (new: b)
- // catch <- ehpad (bb2)
- // ...
- // end_try
- // ...
- // end_block (new: placeBlockMarker)
- // local.get n (new: a) <- appendix block
- // rethrow (new: a)
- //
- // In case there are multiple calls in a BB that may throw to the caller, they
- // can be wrapped together in one nested try scope. (In 1, this couldn't
- // happen, because may-throwing instruction there had an unwind destination,
- // i.e., it was an invoke before, and there could be only one invoke within a
- // BB.)
-
- SmallVector<const MachineBasicBlock *, 8> EHPadStack;
- // Range of intructions to be wrapped in a new nested try/catch
- using TryRange = std::pair<MachineInstr *, MachineInstr *>;
- // In original CFG, <unwind destination BB, a vector of try ranges>
- DenseMap<MachineBasicBlock *, SmallVector<TryRange, 4>> UnwindDestToTryRanges;
- // In new CFG, <destination to branch to, a vector of try ranges>
- DenseMap<MachineBasicBlock *, SmallVector<TryRange, 4>> BrDestToTryRanges;
- // In new CFG, <destination to branch to, register containing exnref>
- DenseMap<MachineBasicBlock *, unsigned> BrDestToExnReg;
-
- // Destinations for branches that will be newly added, for which a new
- // BLOCK/END_BLOCK markers are necessary.
- SmallVector<MachineBasicBlock *, 8> BrDests;
-
- // Gather possibly throwing calls (i.e., previously invokes) whose current
- // unwind destination is not the same as the original CFG.
- for (auto &MBB : reverse(MF)) {
- bool SeenThrowableInstInBB = false;
- for (auto &MI : reverse(MBB)) {
- if (MI.getOpcode() == WebAssembly::TRY)
- EHPadStack.pop_back();
- else if (MI.getOpcode() == WebAssembly::CATCH)
- EHPadStack.push_back(MI.getParent());
-
- // In this loop we only gather calls that have an EH pad to unwind. So
- // there will be at most 1 such call (= invoke) in a BB, so after we've
- // seen one, we can skip the rest of BB. Also if MBB has no EH pad
- // successor or MI does not throw, this is not an invoke.
- if (SeenThrowableInstInBB || !MBB.hasEHPadSuccessor() ||
- !WebAssembly::mayThrow(MI))
- continue;
- SeenThrowableInstInBB = true;
-
- // If the EH pad on the stack top is where this instruction should unwind
- // next, we're good.
- MachineBasicBlock *UnwindDest = nullptr;
- for (auto *Succ : MBB.successors()) {
- if (Succ->isEHPad()) {
- UnwindDest = Succ;
- break;
- }
- }
- if (EHPadStack.back() == UnwindDest)
- continue;
-
- // If not, record the range.
- UnwindDestToTryRanges[UnwindDest].push_back(TryRange(&MI, &MI));
- }
- }
-
- assert(EHPadStack.empty());
-
- // Gather possibly throwing calls that are supposed to unwind up to the caller
- // if they throw, but currently unwind to an incorrect destination. Unlike the
- // loop above, there can be multiple calls within a BB that unwind to the
- // caller, which we should group together in a range.
- bool NeedAppendixBlock = false;
- for (auto &MBB : reverse(MF)) {
- MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr; // inclusive
- for (auto &MI : reverse(MBB)) {
- if (MI.getOpcode() == WebAssembly::TRY)
- EHPadStack.pop_back();
- else if (MI.getOpcode() == WebAssembly::CATCH)
- EHPadStack.push_back(MI.getParent());
-
- // If MBB has an EH pad successor, this inst does not unwind to caller.
- if (MBB.hasEHPadSuccessor())
- continue;
-
- // We wrap up the current range when we see a marker even if we haven't
- // finished a BB.
- if (RangeEnd && WebAssembly::isMarker(MI.getOpcode())) {
- NeedAppendixBlock = true;
- // Record the range. nullptr here means the unwind destination is the
- // caller.
- UnwindDestToTryRanges[nullptr].push_back(
- TryRange(RangeBegin, RangeEnd));
- RangeBegin = RangeEnd = nullptr; // Reset range pointers
- }
-
- // If EHPadStack is empty, that means it is correctly unwind to caller if
- // it throws, so we're good. If MI does not throw, we're good too.
- if (EHPadStack.empty() || !WebAssembly::mayThrow(MI))
- continue;
-
- // We found an instruction that unwinds to the caller but currently has an
- // incorrect unwind destination. Create a new range or increment the
- // currently existing range.
- if (!RangeEnd)
- RangeBegin = RangeEnd = &MI;
- else
- RangeBegin = &MI;
- }
-
- if (RangeEnd) {
- NeedAppendixBlock = true;
- // Record the range. nullptr here means the unwind destination is the
- // caller.
- UnwindDestToTryRanges[nullptr].push_back(TryRange(RangeBegin, RangeEnd));
- RangeBegin = RangeEnd = nullptr; // Reset range pointers
- }
- }
-
- assert(EHPadStack.empty());
- // We don't have any unwind destination mismatches to resolve.
- if (UnwindDestToTryRanges.empty())
- return false;
-
- // If we found instructions that should unwind to the caller but currently
- // have incorrect unwind destination, we create an appendix block at the end
- // of the function with a local.get and a rethrow instruction.
- if (NeedAppendixBlock) {
- auto *AppendixBB = getAppendixBlock(MF);
- Register ExnReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass);
- BuildMI(AppendixBB, DebugLoc(), TII.get(WebAssembly::RETHROW))
- .addReg(ExnReg);
- // These instruction ranges should branch to this appendix BB.
- for (auto Range : UnwindDestToTryRanges[nullptr])
- BrDestToTryRanges[AppendixBB].push_back(Range);
- BrDestToExnReg[AppendixBB] = ExnReg;
- }
-
- // We loop through unwind destination EH pads that are targeted from some
- // inner scopes. Because these EH pads are destination of more than one scope
- // now, we split them so that the handler body is after 'end_try'.
- // - Before
- // ehpad:
- // catch
- // local.set n / drop
- // handler body
- // ...
- // cont:
- // end_try
- //
- // - After
- // ehpad:
- // catch
- // local.set n / drop
- // brdest: (new)
- // end_try (hoisted from 'cont' BB)
- // handler body (taken from 'ehpad')
- // ...
- // cont:
- for (auto &P : UnwindDestToTryRanges) {
- NumUnwindMismatches += P.second.size();
-
- // This means the destination is the appendix BB, which was separately
- // handled above.
- if (!P.first)
- continue;
-
- MachineBasicBlock *EHPad = P.first;
- Register ExnReg = 0;
- MachineInstr *Catch = findCatch(EHPad, ExnReg);
- auto SplitPos = std::next(Catch->getIterator());
-
- // Create a new BB that's gonna be the destination for branches from the
- // inner mismatched scope.
- MachineInstr *BeginTry = EHPadToTry[EHPad];
- MachineInstr *EndTry = BeginToEnd[BeginTry];
- MachineBasicBlock *Cont = EndTry->getParent();
- auto *BrDest = MF.CreateMachineBasicBlock();
- MF.insert(std::next(EHPad->getIterator()), BrDest);
- // Hoist up the existing 'end_try'.
- BrDest->insert(BrDest->end(), EndTry->removeFromParent());
- // Take out the handler body from EH pad to the new branch destination BB.
- BrDest->splice(BrDest->end(), EHPad, SplitPos, EHPad->end());
- unstackifyVRegsUsedInSplitBB(*EHPad, *BrDest, MFI, MRI, TII);
- // Fix predecessor-successor relationship.
- BrDest->transferSuccessors(EHPad);
- EHPad->addSuccessor(BrDest);
-
- // All try ranges that were supposed to unwind to this EH pad now have to
- // branch to this new branch dest BB.
- for (auto Range : UnwindDestToTryRanges[EHPad])
- BrDestToTryRanges[BrDest].push_back(Range);
- BrDestToExnReg[BrDest] = ExnReg;
-
- // In case we fall through to the continuation BB after the catch block, we
- // now have to add a branch to it.
- // - Before
- // try
- // ...
- // (falls through to 'cont')
- // catch
- // handler body
- // end
- // <-- cont
- //
- // - After
- // try
- // ...
- // br %cont (new)
- // catch
- // end
- // handler body
- // <-- cont
- MachineBasicBlock *EHPadLayoutPred = &*std::prev(EHPad->getIterator());
- MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
- SmallVector<MachineOperand, 4> Cond;
- bool Analyzable = !TII.analyzeBranch(*EHPadLayoutPred, TBB, FBB, Cond);
- if (Analyzable && !TBB && !FBB) {
- DebugLoc DL = EHPadLayoutPred->empty()
- ? DebugLoc()
- : EHPadLayoutPred->rbegin()->getDebugLoc();
- BuildMI(EHPadLayoutPred, DL, TII.get(WebAssembly::BR)).addMBB(Cont);
- BrDests.push_back(Cont);
- }
- }
-
- // For possibly throwing calls whose unwind destinations are currently
- // incorrect because of CFG linearization, we wrap them with a nested
- // try/catch/end_try, and within the new catch block, we branch to the correct
- // handler.
- // - Before
- // mbb:
- // call @foo <- Unwind destination mismatch!
- // ehpad:
- // ...
- //
- // - After
- // mbb:
- // try (new)
- // call @foo
- // nested-ehpad: (new)
- // catch (new)
- // local.set n / drop (new)
- // br %brdest (new)
- // nested-end: (new)
- // end_try (new)
- // ehpad:
- // ...
- for (auto &P : BrDestToTryRanges) {
- MachineBasicBlock *BrDest = P.first;
- auto &TryRanges = P.second;
- unsigned ExnReg = BrDestToExnReg[BrDest];
-
- for (auto Range : TryRanges) {
- MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr;
- std::tie(RangeBegin, RangeEnd) = Range;
- auto *MBB = RangeBegin->getParent();
- // Store the first function call from this range, because RangeBegin can
- // be moved to point EH_LABEL before the call
- MachineInstr *RangeBeginCall = RangeBegin;
-
- // Include possible EH_LABELs in the range
- if (RangeBegin->getIterator() != MBB->begin() &&
- std::prev(RangeBegin->getIterator())->isEHLabel())
- RangeBegin = &*std::prev(RangeBegin->getIterator());
- if (std::next(RangeEnd->getIterator()) != MBB->end() &&
- std::next(RangeEnd->getIterator())->isEHLabel())
- RangeEnd = &*std::next(RangeEnd->getIterator());
-
- MachineBasicBlock *EHPad = nullptr;
- for (auto *Succ : MBB->successors()) {
- if (Succ->isEHPad()) {
- EHPad = Succ;
- break;
- }
- }
-
- // Local expression tree before the first call of this range should go
- // after the nested TRY.
- SmallPtrSet<const MachineInstr *, 4> AfterSet;
- AfterSet.insert(RangeBegin);
- AfterSet.insert(RangeBeginCall);
- for (auto I = MachineBasicBlock::iterator(RangeBeginCall),
- E = MBB->begin();
- I != E; --I) {
- if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition())
- continue;
- if (WebAssembly::isChild(*std::prev(I), MFI))
- AfterSet.insert(&*std::prev(I));
- else
- break;
- }
-
- // Create the nested try instruction.
- auto InsertPos = getLatestInsertPos(
- MBB, SmallPtrSet<const MachineInstr *, 4>(), AfterSet);
- MachineInstr *NestedTry =
- BuildMI(*MBB, InsertPos, RangeBegin->getDebugLoc(),
- TII.get(WebAssembly::TRY))
- .addImm(int64_t(WebAssembly::BlockType::Void));
-
- // Create the nested EH pad and fill instructions in.
- MachineBasicBlock *NestedEHPad = MF.CreateMachineBasicBlock();
- MF.insert(std::next(MBB->getIterator()), NestedEHPad);
- NestedEHPad->setIsEHPad();
- NestedEHPad->setIsEHScopeEntry();
- BuildMI(NestedEHPad, RangeEnd->getDebugLoc(), TII.get(WebAssembly::CATCH),
- ExnReg);
- BuildMI(NestedEHPad, RangeEnd->getDebugLoc(), TII.get(WebAssembly::BR))
- .addMBB(BrDest);
-
- // Create the nested continuation BB and end_try instruction.
- MachineBasicBlock *NestedCont = MF.CreateMachineBasicBlock();
- MF.insert(std::next(NestedEHPad->getIterator()), NestedCont);
- MachineInstr *NestedEndTry =
- BuildMI(*NestedCont, NestedCont->begin(), RangeEnd->getDebugLoc(),
- TII.get(WebAssembly::END_TRY));
- // In case MBB has more instructions after the try range, move them to the
- // new nested continuation BB.
- NestedCont->splice(NestedCont->end(), MBB,
- std::next(RangeEnd->getIterator()), MBB->end());
- unstackifyVRegsUsedInSplitBB(*MBB, *NestedCont, MFI, MRI, TII);
- registerTryScope(NestedTry, NestedEndTry, NestedEHPad);
-
- // Fix predecessor-successor relationship.
- NestedCont->transferSuccessors(MBB);
- if (EHPad) {
- NestedCont->removeSuccessor(EHPad);
- // If EHPad does not have any predecessors left after removing
- // NextedCont predecessor, remove its successor too, because this EHPad
- // is not reachable from the entry BB anyway. We can't remove EHPad BB
- // itself because it can contain 'catch' or 'end', which are necessary
- // for keeping try-catch-end structure.
- if (EHPad->pred_empty())
- EHPad->removeSuccessor(BrDest);
- }
- MBB->addSuccessor(NestedEHPad);
- MBB->addSuccessor(NestedCont);
- NestedEHPad->addSuccessor(BrDest);
- }
- }
-
- // Renumber BBs and recalculate ScopeTop info because new BBs might have been
- // created and inserted above.
- MF.RenumberBlocks();
- ScopeTops.clear();
- ScopeTops.resize(MF.getNumBlockIDs());
- for (auto &MBB : reverse(MF)) {
- for (auto &MI : reverse(MBB)) {
- if (ScopeTops[MBB.getNumber()])
- break;
- switch (MI.getOpcode()) {
- case WebAssembly::END_BLOCK:
- case WebAssembly::END_LOOP:
- case WebAssembly::END_TRY:
- ScopeTops[MBB.getNumber()] = EndToBegin[&MI]->getParent();
- break;
- case WebAssembly::CATCH:
- ScopeTops[MBB.getNumber()] = EHPadToTry[&MBB]->getParent();
- break;
- }
- }
- }
-
- // Recompute the dominator tree.
- getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF);
-
- // Place block markers for newly added branches, if necessary.
-
- // If we've created an appendix BB and a branch to it, place a block/end_block
- // marker for that. For some new branches, those branch destination BBs start
- // with a hoisted end_try marker, so we don't need a new marker there.
- if (AppendixBB)
- BrDests.push_back(AppendixBB);
-
- llvm::sort(BrDests,
- [&](const MachineBasicBlock *A, const MachineBasicBlock *B) {
- auto ANum = A->getNumber();
- auto BNum = B->getNumber();
- return ANum < BNum;
- });
- for (auto *Dest : BrDests)
- placeBlockMarker(*Dest);
-
- return true;
+ // TODO Implement this
+ return false;
}
static unsigned
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
index 5720d3e5afb0..d75afdcefb7d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -34,6 +34,7 @@ HANDLE_NODETYPE(WIDEN_LOW_U)
HANDLE_NODETYPE(WIDEN_HIGH_S)
HANDLE_NODETYPE(WIDEN_HIGH_U)
HANDLE_NODETYPE(THROW)
+HANDLE_NODETYPE(CATCH)
HANDLE_NODETYPE(MEMORY_COPY)
HANDLE_NODETYPE(MEMORY_FILL)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 75f065a92825..c693bef6d325 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -267,6 +267,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
// Exception handling intrinsics
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
+ setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
setMaxAtomicSizeInBitsSupported(64);
@@ -1461,6 +1462,21 @@ SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op,
MachinePointerInfo(SV));
}
+static SDValue getCppExceptionSymNode(SDValue Op, unsigned TagIndex,
+ SelectionDAG &DAG) {
+ // We only support C++ exceptions for now
+ int Tag =
+ cast<ConstantSDNode>(Op.getOperand(TagIndex).getNode())->getZExtValue();
+ if (Tag != WebAssembly::CPP_EXCEPTION)
+ llvm_unreachable("Invalid tag: We only support C++ exceptions for now");
+ auto &MF = DAG.getMachineFunction();
+ const auto &TLI = DAG.getTargetLoweringInfo();
+ MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
+ const char *SymName = MF.createExternalSymbolName("__cpp_exception");
+ return DAG.getNode(WebAssemblyISD::Wrapper, SDLoc(Op), PtrVT,
+ DAG.getTargetExternalSymbol(SymName, PtrVT));
+}
+
SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
@@ -1494,15 +1510,7 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
}
case Intrinsic::wasm_throw: {
- // We only support C++ exceptions for now
- int Tag = cast<ConstantSDNode>(Op.getOperand(2).getNode())->getZExtValue();
- if (Tag != WebAssembly::CPP_EXCEPTION)
- llvm_unreachable("Invalid tag!");
- const TargetLowering &TLI = DAG.getTargetLoweringInfo();
- MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
- const char *SymName = MF.createExternalSymbolName("__cpp_exception");
- SDValue SymNode = DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
- DAG.getTargetExternalSymbol(SymName, PtrVT));
+ SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG);
return DAG.getNode(WebAssemblyISD::THROW, DL,
MVT::Other, // outchain type
{
@@ -1512,6 +1520,19 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
});
}
+ case Intrinsic::wasm_catch: {
+ SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG);
+ return DAG.getNode(WebAssemblyISD::CATCH, DL,
+ {
+ MVT::i32, // outchain type
+ MVT::Other // return value
+ },
+ {
+ Op.getOperand(0), // inchain
+ SymNode // exception symbol
+ });
+ }
+
case Intrinsic::wasm_shuffle: {
// Drop in-chain and replace undefs, but otherwise pass through unchanged
SDValue Ops[18];
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td
index bc2c2d8bbd90..4c5305ecc072 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td
@@ -131,14 +131,11 @@ defm THROW : I<(outs), (ins event_op:$tag, variable_ops),
(outs), (ins event_op:$tag),
[(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag))],
"throw \t$tag", "throw \t$tag", 0x08>;
-defm RETHROW : I<(outs), (ins EXNREF:$exn), (outs), (ins), [],
- "rethrow \t$exn", "rethrow", 0x09>;
-// Pseudo instruction to be the lowering target of int_wasm_rethrow intrinsic.
-// Will be converted to the real rethrow instruction later.
-let isPseudo = 1 in
-defm RETHROW_IN_CATCH : NRI<(outs), (ins), [(int_wasm_rethrow)],
- "rethrow_in_catch", 0>;
+defm RETHROW : NRI<(outs), (ins i32imm:$depth), [], "rethrow \t$depth", 0x09>;
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
+// For C++ support, we only rethrow the latest exception, thus always setting
+// the depth to 0.
+def : Pat<(int_wasm_rethrow), (RETHROW 0)>;
// Region within which an exception is caught: try / end_try
let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in {
@@ -146,10 +143,18 @@ defm TRY : NRI<(outs), (ins Signature:$sig), [], "try \t$sig", 0x06>;
defm END_TRY : NRI<(outs), (ins), [], "end_try", 0x0b>;
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
-// Catching an exception: catch / extract_exception
-let hasCtrlDep = 1, hasSideEffects = 1 in
-defm CATCH : I<(outs EXNREF:$dst), (ins), (outs), (ins), [],
- "catch \t$dst", "catch", 0x07>;
+// Catching an exception: catch / catch_all
+let hasCtrlDep = 1, hasSideEffects = 1 in {
+// TODO Currently 'catch' can only extract an i32, which is sufficient for C++
+// support, but according to the spec 'catch' can extract any number of values
+// based on the event type.
+defm CATCH : I<(outs I32:$dst), (ins event_op:$tag),
+ (outs), (ins event_op:$tag),
+ [(set I32:$dst,
+ (WebAssemblycatch (WebAssemblywrapper texternalsym:$tag)))],
+ "catch \t$dst, $tag", "catch \t$tag", 0x07>;
+defm CATCH_ALL : NRI<(outs), (ins), [], "catch_all", 0x05>;
+}
// Querying / extracing exception: br_on_exn
// br_on_exn queries an exnref to see if it matches the corresponding exception
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 40a6545530be..dc1f3b4d4dcf 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -81,7 +81,8 @@ def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
SDTCisPtrTy<0>]>;
def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
SDTCisPtrTy<0>]>;
-def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, []>;
+def SDT_WebAssemblyCatch : SDTypeProfile<1, 1, [SDTCisPtrTy<0>]>;
//===----------------------------------------------------------------------===//
// WebAssembly-specific DAG Nodes.
@@ -107,6 +108,8 @@ def WebAssemblywrapperPIC : SDNode<"WebAssemblyISD::WrapperPIC",
SDT_WebAssemblyWrapperPIC>;
def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
[SDNPHasChain, SDNPVariadic]>;
+def WebAssemblycatch : SDNode<"WebAssemblyISD::CATCH", SDT_WebAssemblyCatch,
+ [SDNPHasChain, SDNPSideEffect]>;
//===----------------------------------------------------------------------===//
// WebAssembly-specific Operands.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
index d9c2ba6a6537..5501af0b2bbd 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
@@ -33,10 +33,10 @@ class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
bool runOnMachineFunction(MachineFunction &MF) override;
void recordCatchRetBBs(MachineFunction &MF);
- bool addCatches(MachineFunction &MF);
+ bool hoistCatches(MachineFunction &MF);
+ bool addCatchAlls(MachineFunction &MF);
bool replaceFuncletReturns(MachineFunction &MF);
bool removeUnnecessaryUnreachables(MachineFunction &MF);
- bool addExceptionExtraction(MachineFunction &MF);
bool restoreStackPointer(MachineFunction &MF);
MachineBasicBlock *getMatchingEHPad(MachineInstr *MI);
@@ -118,14 +118,13 @@ bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
bool Changed = false;
if (MF.getFunction().hasPersonalityFn()) {
recordCatchRetBBs(MF);
- Changed |= addCatches(MF);
+ Changed |= hoistCatches(MF);
+ Changed |= addCatchAlls(MF);
Changed |= replaceFuncletReturns(MF);
}
Changed |= removeUnnecessaryUnreachables(MF);
- if (MF.getFunction().hasPersonalityFn()) {
- Changed |= addExceptionExtraction(MF);
+ if (MF.getFunction().hasPersonalityFn())
Changed |= restoreStackPointer(MF);
- }
return Changed;
}
@@ -144,20 +143,62 @@ void WebAssemblyLateEHPrepare::recordCatchRetBBs(MachineFunction &MF) {
}
}
-// Add catch instruction to beginning of catchpads and cleanuppads.
-bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
+// Hoist catch instructions to the beginning of their matching EH pad BBs in
+// case,
+// (1) catch instruction is not the first instruction in EH pad.
+// ehpad:
+// some_other_instruction
+// ...
+// %exn = catch 0
+// (2) catch instruction is in a non-EH pad BB. For example,
+// ehpad:
+// br bb0
+// bb0:
+// %exn = catch 0
+bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
+ bool Changed = false;
+ SmallVector<MachineInstr *, 16> Catches;
+ for (auto &MBB : MF)
+ for (auto &MI : MBB)
+ if (WebAssembly::isCatch(MI.getOpcode()))
+ Catches.push_back(&MI);
+
+ for (auto *Catch : Catches) {
+ MachineBasicBlock *EHPad = getMatchingEHPad(Catch);
+ assert(EHPad && "No matching EH pad for catch");
+ auto InsertPos = EHPad->begin();
+ // Skip EH_LABELs in the beginning of an EH pad if present. We don't use
+ // these labels at the moment, but other targets also seem to have an
+ // EH_LABEL instruction in the beginning of an EH pad.
+ while (InsertPos != EHPad->end() && InsertPos->isEHLabel())
+ InsertPos++;
+ if (InsertPos == Catch)
+ continue;
+ Changed = true;
+ EHPad->insert(InsertPos, Catch->removeFromParent());
+ }
+ return Changed;
+}
+
+// Add catch_all to beginning of cleanup pads.
+bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {
bool Changed = false;
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- MachineRegisterInfo &MRI = MF.getRegInfo();
+
for (auto &MBB : MF) {
- if (MBB.isEHPad()) {
+ if (!MBB.isEHPad())
+ continue;
+ auto InsertPos = MBB.begin();
+ // Skip EH_LABELs in the beginning of an EH pad if present.
+ while (InsertPos != MBB.end() && InsertPos->isEHLabel())
+ InsertPos++;
+ // This runs after hoistCatches(), so we assume that if there is a catch,
+ // that should be the non-EH label first instruction in an EH pad.
+ if (InsertPos == MBB.end() ||
+ !WebAssembly::isCatch(InsertPos->getOpcode())) {
Changed = true;
- auto InsertPos = MBB.begin();
- if (InsertPos->isEHLabel()) // EH pad starts with an EH label
- ++InsertPos;
- Register DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass);
- BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(),
- TII.get(WebAssembly::CATCH), DstReg);
+ BuildMI(MBB, InsertPos, InsertPos->getDebugLoc(),
+ TII.get(WebAssembly::CATCH_ALL));
}
}
return Changed;
@@ -184,17 +225,11 @@ bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
Changed = true;
break;
}
- case WebAssembly::CLEANUPRET:
- case WebAssembly::RETHROW_IN_CATCH: {
- // Replace a cleanupret/rethrow_in_catch with a rethrow
- auto *EHPad = getMatchingEHPad(TI);
- auto CatchPos = EHPad->begin();
- if (CatchPos->isEHLabel()) // EH pad starts with an EH label
- ++CatchPos;
- MachineInstr *Catch = &*CatchPos;
- Register ExnReg = Catch->getOperand(0).getReg();
+ case WebAssembly::CLEANUPRET: {
+ // Replace a cleanupret with a rethrow. For C++ support, currently
+ // rethrow's immediate argument is always 0 (= the latest exception).
BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
- .addReg(ExnReg);
+ .addImm(0);
TI->eraseFromParent();
Changed = true;
break;
@@ -230,156 +265,6 @@ bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
return Changed;
}
-// Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
-// exnref type object returned by 'catch', and branches to the destination if it
-// matches a given tag. We currently use __cpp_exception symbol to represent the
-// tag for all C++ exceptions.
-//
-// block $l (result i32)
-// ...
-// ;; exnref $e is on the stack at this point
-// br_on_exn $l $e ;; branch to $l with $e's arguments
-// ...
-// end
-// ;; Here we expect the extracted values are on top of the wasm value stack
-// ... Handle exception using values ...
-//
-// br_on_exn takes an exnref object and branches if it matches the given tag.
-// There can be multiple br_on_exn instructions if we want to match for another
-// tag, but for now we only test for __cpp_exception tag, and if it does not
-// match, i.e., it is a foreign exception, we rethrow it.
-//
-// In the destination BB that's the target of br_on_exn, extracted exception
-// values (in C++'s case a single i32, which represents an exception pointer)
-// are placed on top of the wasm stack. Because we can't model wasm stack in
-// LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve
-// it. The pseudo instruction will be deleted later.
-bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) {
- const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- MachineRegisterInfo &MRI = MF.getRegInfo();
- auto *EHInfo = MF.getWasmEHFuncInfo();
- SmallVector<MachineInstr *, 16> ExtractInstrs;
- SmallVector<MachineInstr *, 8> ToDelete;
- for (auto &MBB : MF) {
- for (auto &MI : MBB) {
- if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
- if (MI.getOperand(0).isDead())
- ToDelete.push_back(&MI);
- else
- ExtractInstrs.push_back(&MI);
- }
- }
- }
- bool Changed = !ToDelete.empty() || !ExtractInstrs.empty();
- for (auto *MI : ToDelete)
- MI->eraseFromParent();
- if (ExtractInstrs.empty())
- return Changed;
-
- // Find terminate pads.
- SmallSet<MachineBasicBlock *, 8> TerminatePads;
- for (auto &MBB : MF) {
- for (auto &MI : MBB) {
- if (MI.isCall()) {
- const MachineOperand &CalleeOp = MI.getOperand(0);
- if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
- WebAssembly::ClangCallTerminateFn)
- TerminatePads.insert(getMatchingEHPad(&MI));
- }
- }
- }
-
- for (auto *Extract : ExtractInstrs) {
- MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
- assert(EHPad && "No matching EH pad for extract_exception");
- auto CatchPos = EHPad->begin();
- if (CatchPos->isEHLabel()) // EH pad starts with an EH label
- ++CatchPos;
- MachineInstr *Catch = &*CatchPos;
-
- if (Catch->getNextNode() != Extract)
- EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
-
- // - Before:
- // ehpad:
- // %exnref:exnref = catch
- // %exn:i32 = extract_exception
- // ... use exn ...
- //
- // - After:
- // ehpad:
- // %exnref:exnref = catch
- // br_on_exn %thenbb, $__cpp_exception, %exnref
- // br %elsebb
- // elsebb:
- // rethrow
- // thenbb:
- // %exn:i32 = extract_exception
- // ... use exn ...
- Register ExnReg = Catch->getOperand(0).getReg();
- auto *ThenMBB = MF.CreateMachineBasicBlock();
- auto *ElseMBB = MF.CreateMachineBasicBlock();
- MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB);
- MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB);
- ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end());
- ThenMBB->transferSuccessors(EHPad);
- EHPad->addSuccessor(ThenMBB);
- EHPad->addSuccessor(ElseMBB);
-
- DebugLoc DL = Extract->getDebugLoc();
- const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
- BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN))
- .addMBB(ThenMBB)
- .addExternalSymbol(CPPExnSymbol)
- .addReg(ExnReg);
- BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB);
-
- // When this is a terminate pad with __clang_call_terminate() call, we don't
- // rethrow it anymore and call __clang_call_terminate() with a nullptr
- // argument, which will call std::terminate().
- //
- // - Before:
- // ehpad:
- // %exnref:exnref = catch
- // %exn:i32 = extract_exception
- // call @__clang_call_terminate(%exn)
- // unreachable
- //
- // - After:
- // ehpad:
- // %exnref:exnref = catch
- // br_on_exn %thenbb, $__cpp_exception, %exnref
- // br %elsebb
- // elsebb:
- // call @__clang_call_terminate(0)
- // unreachable
- // thenbb:
- // %exn:i32 = extract_exception
- // call @__clang_call_terminate(%exn)
- // unreachable
- if (TerminatePads.count(EHPad)) {
- Function *ClangCallTerminateFn =
- MF.getFunction().getParent()->getFunction(
- WebAssembly::ClangCallTerminateFn);
- assert(ClangCallTerminateFn &&
- "There is no __clang_call_terminate() function");
- Register Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
- BuildMI(ElseMBB, DL, TII.get(WebAssembly::CONST_I32), Reg).addImm(0);
- BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL))
- .addGlobalAddress(ClangCallTerminateFn)
- .addReg(Reg);
- BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
-
- } else {
- BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg);
- if (EHInfo->hasEHPadUnwindDest(EHPad))
- ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad));
- }
- }
-
- return true;
-}
-
// After the stack is unwound due to a thrown exception, the __stack_pointer
// global can point to an invalid address. This inserts instructions that
// restore __stack_pointer global.
@@ -404,7 +289,7 @@ bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
auto InsertPos = MBB.begin();
if (InsertPos->isEHLabel()) // EH pad starts with an EH label
++InsertPos;
- if (InsertPos->getOpcode() == WebAssembly::CATCH)
+ if (WebAssembly::isCatch(InsertPos->getOpcode()))
++InsertPos;
FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB,
InsertPos, MBB.begin()->getDebugLoc());
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 3943148f138e..6e3bdbf82bd1 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -359,10 +359,9 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use,
if (NextI == Insert)
return true;
- // 'catch' and 'extract_exception' should be the first instruction of a BB and
- // cannot move.
- if (DefI->getOpcode() == WebAssembly::CATCH ||
- DefI->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32)
+ // 'catch' and 'catch_all' should be the first instruction of a BB and cannot
+ // move.
+ if (WebAssembly::isCatch(DefI->getOpcode()))
return false;
// Check for register dependencies.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index af16d799d1dc..135055a43afc 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -446,7 +446,8 @@ void WebAssemblyPassConfig::addPreEmitPass() {
// Do various transformations for exception handling.
// Every CFG-changing optimizations should come before this.
- addPass(createWebAssemblyLateEHPrepare());
+ if (TM->Options.ExceptionModel == ExceptionHandling::Wasm)
+ addPass(createWebAssemblyLateEHPrepare());
// Now that we have a prologue and epilogue and all frame indices are
// rewritten, eliminate SP and FP. This allows them to be stackified,
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index f455d5bcfd4a..f55e23a4d7c4 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -1,15 +1,12 @@
; REQUIRES: asserts
; TODO Reenable disabled lines after updating the backend to the new spec
; R UN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
-; R UN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling
+; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling
; R UN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT
; R UN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT
; R UN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT-LOCALS
; R UN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT-STAT
-; FIXME A temporary RUN line to make the test pass. Remove this later.
-; RUN: true
-
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
diff --git a/llvm/test/CodeGen/WebAssembly/eh-labels.mir b/llvm/test/CodeGen/WebAssembly/eh-labels.mir
index 673d34869b73..105b21acc4f7 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-labels.mir
+++ b/llvm/test/CodeGen/WebAssembly/eh-labels.mir
@@ -34,11 +34,10 @@ body: |
bb.1 (landing-pad):
; predecessors: %bb.0
successors: %bb.2
- ; CATCH should be after an EH_LABEL at the beginning of an EH pad
+ ; CATCH_ALL should be after an EH_LABEL at the beginning of an EH pad
; CHECK: EH_LABEL
- ; CHECK-NEXT: CATCH
+ ; CHECK-NEXT: CATCH_ALL
EH_LABEL <mcsymbol .Ltmp2>
- dead %0:i32 = EXTRACT_EXCEPTION_I32 implicit-def dead $arguments
CATCHRET %bb.2, %bb.0, implicit-def dead $arguments
bb.2:
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index 38a2fe4a8d67..c34c76b4d644 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -1,8 +1,4 @@
-; TODO Reenable disabled lines after updating the backend to the new spec
-; R UN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling | FileCheck -allow-deprecated-dag-overlap %s
-
-; FIXME A temporary RUN line to make the test pass. Remove this later.
-; RUN: true
+; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling | FileCheck -allow-deprecated-dag-overlap %s
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index f16fe37cd0a1..14eba86c5edd 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -1,9 +1,5 @@
-; TODO Reenable disabled lines after updating the backend to the new spec
-; R UN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
-; R UN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling
-
-; FIXME A temporary RUN line to make the test pass. Remove this later.
-; RUN: true
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
+; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
@@ -34,15 +30,9 @@ define void @test_throw(i8* %p) {
; CHECK: global.get ${{.+}}=, __stack_pointer
; CHECK: try
; CHECK: call foo
-; CHECK: catch $[[EXNREF:[0-9]+]]=
+; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception
; CHECK: global.set __stack_pointer
-; CHECK: block i32
-; CHECK: br_on_exn 0, __cpp_exception, $[[EXNREF]]
-; CHECK: rethrow $[[EXNREF]]
-; CHECK: end_block
-; CHECK: extract_exception $[[EXN:[0-9]+]]=
-; CHECK-DAG: i32.store __wasm_lpad_context
-; CHECK-DAG: i32.store __wasm_lpad_context{{.+}}
+; CHECK: i32.{{store|const}} {{.*}} __wasm_lpad_context
; CHECK: call $drop=, _Unwind_CallPersonality, $[[EXN]]
; CHECK: block
; CHECK: br_if 0
@@ -50,7 +40,7 @@ define void @test_throw(i8* %p) {
; CHECK: call __cxa_end_catch
; CHECK: br 1
; CHECK: end_block
-; CHECK: rethrow $[[EXNREF]]
+; CHECK: rethrow 0
; CHECK: end_try
define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
@@ -95,10 +85,10 @@ try.cont: ; preds = %catch, %entry
; CHECK-LABEL: test_cleanup:
; CHECK: try
; CHECK: call foo
-; CHECK: catch $[[EXNREF:[0-9]+]]=
+; CHECK: catch_all
; CHECK: global.set __stack_pointer
; CHECK: call $drop=, _ZN4TempD2Ev
-; CHECK: rethrow $[[EXNREF]]
+; CHECK: rethrow 0
; CHECK: end_try
define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
@@ -135,17 +125,11 @@ ehcleanup: ; preds = %entry
; CHECK: call $drop=, __cxa_begin_catch
; CHECK: try
; CHECK: call foo
-; CHECK: catch
+; CHECK: catch_all
; CHECK: try
; CHECK: call __cxa_end_catch
-; CHECK: catch
-; CHECK: block i32
-; CHECK: br_on_exn 0, __cpp_exception
-; CHECK: i32.const ${{.*}}=, 0
-; CHECK: call __clang_call_terminate
-; CHECK: unreachable
-; CHECK: end_block
-; CHECK: call __clang_call_terminate
+; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception
+; CHECK: call __clang_call_terminate, $[[EXN]]
; CHECK: unreachable
; CHECK: end_try
; CHECK: rethrow
diff --git a/llvm/test/MC/WebAssembly/annotations.s b/llvm/test/MC/WebAssembly/annotations.s
index c663567f4c7f..f2636ec45bee 100644
--- a/llvm/test/MC/WebAssembly/annotations.s
+++ b/llvm/test/MC/WebAssembly/annotations.s
@@ -11,7 +11,7 @@ test_annotation:
.eventtype __cpp_exception i32
try
br 0
- catch
+ catch __cpp_exception
block
br_if 0
loop
@@ -19,21 +19,16 @@ test_annotation:
end_loop
end_block
try
- rethrow
- catch
+ rethrow 0
+ catch __cpp_exception
block
try
br 0
- catch
+ catch __cpp_exception
local.set 0
- block i32
- local.get 0
- br_on_exn 0, __cpp_exception
- rethrow
- end_block
end_try
end_block
- rethrow
+ rethrow 0
end_try
end_try
end_function
@@ -42,7 +37,7 @@ test_annotation:
# CHECK: test_annotation:
# CHECK: try
# CHECK-NEXT: br 0 # 0: down to label0
-# CHECK-NEXT: catch # catch0:
+# CHECK-NEXT: catch __cpp_exception # catch0:
# CHECK-NEXT: block
# CHECK-NEXT: br_if 0 # 0: down to label1
# CHECK-NEXT: loop # label2:
@@ -50,21 +45,16 @@ test_annotation:
# CHECK-NEXT: end_loop
# CHECK-NEXT: end_block # label1:
# CHECK-NEXT: try
-# CHECK-NEXT: rethrow # down to catch1
-# CHECK-NEXT: catch # catch1:
+# CHECK-NEXT: rethrow 0 # down to catch1
+# CHECK-NEXT: catch __cpp_exception # catch1:
# CHECK-NEXT: block
# CHECK-NEXT: try
# CHECK-NEXT: br 0 # 0: down to label5
-# CHECK-NEXT: catch # catch2:
+# CHECK-NEXT: catch __cpp_exception # catch2:
# CHECK-NEXT: local.set 0
-# CHECK-NEXT: block i32
-# CHECK-NEXT: local.get 0
-# CHECK-NEXT: br_on_exn 0, __cpp_exception # 0: down to label6
-# CHECK-NEXT: rethrow # to caller
-# CHECK-NEXT: end_block # label6:
# CHECK-NEXT: end_try # label5:
# CHECK-NEXT: end_block # label4:
-# CHECK-NEXT: rethrow # to caller
+# CHECK-NEXT: rethrow 0 # to caller
# CHECK-NEXT: end_try # label3:
# CHECK-NEXT: end_try # label0:
# CHECK-NEXT: end_function
diff --git a/llvm/test/MC/WebAssembly/basic-assembly.s b/llvm/test/MC/WebAssembly/basic-assembly.s
index d45b10d45b0f..786100d91e5c 100644
--- a/llvm/test/MC/WebAssembly/basic-assembly.s
+++ b/llvm/test/MC/WebAssembly/basic-assembly.s
@@ -81,24 +81,18 @@ test0:
# TODO: enable once instruction has been added.
#i32x4.trunc_sat_f32x4_s
i32.trunc_f32_s
- try exnref
+ try
i32.atomic.load 0
memory.atomic.notify 0
.LBB0_3:
- catch
+ catch __cpp_exception
local.set 0
- block i32
- local.get 0
- br_on_exn 0, __cpp_exception
- rethrow
-.LBB0_4:
- end_block
end_try
i32.const .L.str
i32.load8_u .L.str+2
i32.load16_u .L.str:p2align=0
throw 0
-.LBB0_5:
+.LBB0_4:
#i32.trunc_sat_f32_s
global.get __stack_pointer
end_function
@@ -199,24 +193,18 @@ empty_fref_table:
# CHECK-NEXT: end_if
# CHECK-NEXT: f32x4.add
# CHECK-NEXT: i32.trunc_f32_s
-# CHECK-NEXT: try exnref
+# CHECK-NEXT: try
# CHECK-NEXT: i32.atomic.load 0
# CHECK-NEXT: memory.atomic.notify 0
# CHECK-NEXT: .LBB0_3:
-# CHECK-NEXT: catch
+# CHECK-NEXT: catch __cpp_exception
# CHECK-NEXT: local.set 0
-# CHECK-NEXT: block i32
-# CHECK-NEXT: local.get 0
-# CHECK-NEXT: br_on_exn 0, __cpp_exception
-# CHECK-NEXT: rethrow
-# CHECK-NEXT: .LBB0_4:
-# CHECK-NEXT: end_block
# CHECK-NEXT: end_try
# CHECK-NEXT: i32.const .L.str
# CHECK-NEXT: i32.load8_u .L.str+2
# CHECK-NEXT: i32.load16_u .L.str:p2align=0
# CHECK-NEXT: throw 0
-# CHECK-NEXT: .LBB0_5:
+# CHECK-NEXT: .LBB0_4:
# CHECK-NEXT: global.get __stack_pointer
# CHECK-NEXT: end_function
diff --git a/llvm/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp b/llvm/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
index a8f6d883028d..c63328a278f7 100644
--- a/llvm/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
+++ b/llvm/unittests/Target/WebAssembly/WebAssemblyExceptionInfoTest.cpp
@@ -100,14 +100,14 @@ body: |
; predecessors: %bb.0
successors: %bb.3, %bb.9
liveins: $value_stack
- %0:exnref = CATCH implicit-def $arguments
- CLEANUPRET implicit-def dead $arguments
+ CATCH_ALL implicit-def $arguments
+ RETHROW 0, implicit-def dead $arguments
bb.3 (landing-pad):
; predecessors: %bb.2
successors: %bb.4, %bb.6
liveins: $value_stack
- %1:exnref = CATCH implicit-def $arguments
+ %1:i32 = CATCH &__cpp_exception, implicit-def $arguments
BR_IF %bb.4, %58:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
BR %bb.6, implicit-def $arguments
@@ -121,7 +121,7 @@ body: |
; predecessors: %bb.4
successors: %bb.7
liveins: $value_stack
- CATCHRET %bb.7, %bb.0, implicit-def dead $arguments
+ BR %bb.7, implicit-def dead $arguments
bb.6:
; predecessors: %bb.3
@@ -138,14 +138,14 @@ body: |
; predecessors: %bb.4
successors: %bb.9
liveins: $value_stack
- %2:exnref = CATCH implicit-def $arguments
- CLEANUPRET implicit-def dead $arguments
+ CATCH_ALL implicit-def $arguments
+ RETHROW 0, implicit-def dead $arguments
bb.9 (landing-pad):
; predecessors: %bb.2, %bb.6, %bb.8
liveins: $value_stack
- %3:exnref = CATCH implicit-def $arguments
- CLEANUPRET implicit-def dead $arguments
+ CATCH_ALL implicit-def $arguments
+ RETHROW 0, implicit-def dead $arguments
bb.10:
; predecessors: %bb.6
@@ -257,7 +257,7 @@ body: |
; predecessors: %bb.0
successors: %bb.2, %bb.8
liveins: $value_stack
- %0:exnref = CATCH implicit-def $arguments
+ %0:i32 = CATCH &__cpp_exception, implicit-def $arguments
BR_IF %bb.2, %32:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
BR %bb.8, implicit-def $arguments
@@ -271,7 +271,7 @@ body: |
; predecessors: %bb.2
successors: %bb.4, %bb.6
liveins: $value_stack
- %1:exnref = CATCH implicit-def $arguments
+ %1:i32 = CATCH &__cpp_exception, implicit-def $arguments
BR_IF %bb.4, %43:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
BR %bb.6, implicit-def $arguments
@@ -285,7 +285,7 @@ body: |
; predecessors: %bb.4
successors: %bb.7(0x80000000); %bb.7(200.00%)
liveins: $value_stack
- CATCHRET %bb.7, %bb.1, implicit-def dead $arguments
+ BR %bb.7, implicit-def dead $arguments
bb.6:
; predecessors: %bb.3
@@ -297,7 +297,7 @@ body: |
; predecessors: %bb.2, %bb.5
successors: %bb.9(0x80000000); %bb.9(200.00%)
liveins: $value_stack
- CATCHRET %bb.9, %bb.0, implicit-def dead $arguments
+ BR %bb.9, implicit-def dead $arguments
bb.8:
; predecessors: %bb.1
@@ -313,14 +313,14 @@ body: |
; predecessors: %bb.4
successors: %bb.11
liveins: $value_stack
- %2:exnref = CATCH implicit-def $arguments
- CLEANUPRET implicit-def dead $arguments
+ CATCH_ALL implicit-def $arguments
+ RETHROW 0, implicit-def dead $arguments
bb.11 (landing-pad):
; predecessors: %bb.2, %bb.6, %bb.10
liveins: $value_stack
- %3:exnref = CATCH implicit-def $arguments
- CLEANUPRET implicit-def dead $arguments
+ CATCH_ALL implicit-def $arguments
+ RETHROW 0, implicit-def dead $arguments
bb.12:
; predecessors: %bb.6
More information about the llvm-branch-commits
mailing list