summaryrefslogtreecommitdiff
path: root/llvm/lib/Target
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2021-03-02 15:05:30 -0800
committerHeejin Ahn <aheejin@gmail.com>2021-03-04 14:26:35 -0800
commit561abd83ffecc8d4ba8fcbbbcadb31efc55985c2 (patch)
tree1188e466063367d115c7a499545f97ad5fc0f2e3 /llvm/lib/Target
parent2b896e39bfff70852ad709a9babe5d3654531ce6 (diff)
[WebAssembly] Disable uses of __clang_call_terminate
Background: Wasm EH, while using Windows EH (catchpad/cleanuppad based) IR, uses Itanium-based libraries and ABIs with some modifications. `__clang_call_terminate` is a wrapper generated in Clang's Itanium C++ ABI implementation. It contains this code, in C-style pseudocode: ``` void __clang_call_terminate(void *exn) { __cxa_begin_catch(exn); std::terminate(); } ``` So this function is a wrapper to call `__cxa_begin_catch` on the exception pointer before termination. In Itanium ABI, this function is called when another exception is thrown while processing an exception. The pointer for this second, violating exception is passed as the argument of this `__clang_call_terminate`, which calls `__cxa_begin_catch` with that pointer and calls `std::terminate` to terminate the program. The spec (https://libcxxabi.llvm.org/spec.html) for `__cxa_begin_catch` says, ``` When the personality routine encounters a termination condition, it will call __cxa_begin_catch() to mark the exception as handled and then call terminate(), which shall not return to its caller. ``` In wasm EH's Clang implementation, this function is called from cleanuppads that terminates the program, which we also call terminate pads. Cleanuppads normally don't access the thrown exception and the wasm backend converts them to `catch_all` blocks. But because we need the exception pointer in this cleanuppad, we generate `wasm.get.exception` intrinsic (which will eventually be lowered to `catch` instruction) as we do in the catchpads. But because terminate pads are cleanup pads and should run even when a foreign exception is thrown, so what we have been doing is: 1. In `WebAssemblyLateEHPrepare::ensureSingleBBTermPads()`, we make sure terminate pads are in this simple shape: ``` %exn = catch call @__clang_call_terminate(%exn) unreachable ``` 2. In `WebAssemblyHandleEHTerminatePads` pass at the end of the pipeline, we attach a `catch_all` to terminate pads, so they will be in this form: ``` %exn = catch call @__clang_call_terminate(%exn) unreachable catch_all call @std::terminate() unreachable ``` In `catch_all` part, we don't have the exception pointer, so we call `std::terminate()` directly. The reason we ran HandleEHTerminatePads at the end of the pipeline, separate from LateEHPrepare, was it was convenient to assume there was only a single `catch` part per `try` during CFGSort and CFGStackify. --- Problem: While it thinks terminate pads could have been possibly split or calls to `__clang_call_terminate` could have been duplicated, `WebAssemblyLateEHPrepare::ensureSingleBBTermPads()` assumes terminate pads contain no more than calls to `__clang_call_terminate` and `unreachable` instruction. I assumed that because in LLVM very limited forms of transformations are done to catchpads and cleanuppads to maintain the scoping structure. But it turned out to be incorrect; passes can merge cleanuppads into one, including terminate pads, as long as the new code has a correct scoping structure. One pass that does this I observed was `SimplifyCFG`, but there can be more. After this transformation, a single cleanuppad can contain any number of other instructions with the call to `__clang_call_terminate` and can span many BBs. It wouldn't be practical to duplicate all these BBs within the cleanuppad to generate the equivalent `catch_all` blocks, only with calls to `__clang_call_terminate` replaced by calls to `std::terminate`. Unless we do more complicated transformation to split those calls to `__clang_call_terminate` into a separate cleanuppad, it is tricky to solve. --- Solution (?): This CL just disables the generation and use of `__clang_call_terminate` and calls `std::terminate()` directly in its place. The possible downside of this approach can be, because the Itanium ABI intended to "mark" the violating exception handled, we don't do that anymore. What `__cxa_begin_catch` actually does is increment the exception's handler count and decrement the uncaught exception count, which in my opinion do not matter much given that we are about to terminate the program anyway. Also it does not affect info like stack traces that can be possibly shown to developers. And while we use a variant of Itanium EH ABI, we can make some deviations if we choose to; we are already different in that in the current version of the EH spec we don't support two-phase unwinding. We can possibly consider a more complicated transformation later to reenable this, but I don't think that has high priority. Changes in this CL contains: - In Clang, we don't generate a call to `wasm.get.exception()` intrinsic and `__clang_call_terminate` function in terminate pads anymore; we simply generate calls to `std::terminate()`, which is the default implementation of `CGCXXABI::emitTerminateForUnexpectedException`. - Remove `WebAssembly::ensureSingleBBTermPads() function and `WebAssemblyHandleEHTerminatePads` pass, because terminate pads are already `catch_all` now (because they don't need the exception pointer) and we don't need these transformations anymore. - Change tests to use `std::terminate` directly. Also removes tests that tested `LateEHPrepare::ensureSingleBBTermPads` and `HandleEHTerminatePads` pass. - Drive-by fix: Add some function attributes to EH intrinsic declarations Fixes https://github.com/emscripten-core/emscripten/issues/13582. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D97834
Diffstat (limited to 'llvm/lib/Target')
-rw-r--r--llvm/lib/Target/WebAssembly/CMakeLists.txt1
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssembly.h2
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp152
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp76
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp5
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp3
6 files changed, 1 insertions, 238 deletions
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 40fc85d0df24..87920e7a950b 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -22,7 +22,6 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyCFGSort.cpp
WebAssemblyDebugFixup.cpp
WebAssemblyDebugValueManager.cpp
- WebAssemblyHandleEHTerminatePads.cpp
WebAssemblyLateEHPrepare.cpp
WebAssemblyExceptionInfo.cpp
WebAssemblyExplicitLocals.cpp
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 7838e57d82d3..e812d0047961 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -49,7 +49,6 @@ FunctionPass *createWebAssemblyFixIrreducibleControlFlow();
FunctionPass *createWebAssemblyLateEHPrepare();
FunctionPass *createWebAssemblyCFGSort();
FunctionPass *createWebAssemblyCFGStackify();
-FunctionPass *createWebAssemblyHandleEHTerminatePads();
FunctionPass *createWebAssemblyExplicitLocals();
FunctionPass *createWebAssemblyLowerBrUnless();
FunctionPass *createWebAssemblyRegNumbering();
@@ -76,7 +75,6 @@ void initializeWebAssemblyLateEHPreparePass(PassRegistry &);
void initializeWebAssemblyExceptionInfoPass(PassRegistry &);
void initializeWebAssemblyCFGSortPass(PassRegistry &);
void initializeWebAssemblyCFGStackifyPass(PassRegistry &);
-void initializeWebAssemblyHandleEHTerminatePadsPass(PassRegistry &);
void initializeWebAssemblyExplicitLocalsPass(PassRegistry &);
void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp
deleted file mode 100644
index d41947a036dd..000000000000
--- a/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// WebAssemblyHandleEHTerminatePads.cpp - WebAssembly Handle EH TerminatePads //
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Add catch_all blocks to terminate pads.
-///
-/// Terminate pads are cleanup pads with a __clang_call_terminate call. These
-/// are reached when an exception is thrown again in the middle of processing a
-/// thrown exception, to terminate the program. These are cleanup pads that
-/// should run regardless whether the thrown exception is a C++ exception or
-/// not.
-///
-/// Because __clang_call_terminate takes an exception pointer, and
-/// llvm.get.exception intrinsic is selected to 'catch' instruction in
-/// instruction selection, terminate pads have a catch instruction and are in
-/// this form after LateEHPrepare, even though they are cleanup pads:
-/// termpad:
-/// %exn = catch $__cpp_exception
-/// call @__clang_call_terminate(%exn)
-/// unreachable
-///
-/// This pass assumes LateEHPrepare ensured every terminate pad is a single
-/// BB.
-///
-/// __clang_call_terminate is a function generated by clang, in the form of
-/// void __clang_call_terminate(i8* %arg) {
-/// call @__cxa_begin_catch(%arg)
-/// call void @std::terminate()
-/// unreachable
-/// }
-///
-/// To make the terminate pads reachable when a foreign exception is thrown,
-/// this pass attaches an additional catch_all BB after this catch terminate pad
-/// BB, with a call to std::terminate, because foreign exceptions don't have a
-/// valid exception pointer to call __cxa_begin_catch with. So the code example
-/// becomes:
-/// termpad:
-/// %exn = catch $__cpp_exception
-/// call @__clang_call_terminate(%exn)
-/// unreachable
-/// termpad-catchall:
-/// catch_all
-/// call @std::terminate()
-/// unreachable
-///
-/// We do this at the very end of compilation pipeline, even after CFGStackify,
-/// because even though wasm spec allows multiple catch/catch_all blocks per a
-/// try instruction, it has been convenient to maintain the invariant so far
-/// that there has been only a single catch or catch_all attached to a try. This
-/// assumption makes ExceptionInfo generation and CFGStackify simpler, because
-/// we have been always able to assume an EH pad is an end of try block and a
-/// start of catch/catch_all block.
-//===----------------------------------------------------------------------===//
-
-#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
-#include "WebAssembly.h"
-#include "WebAssemblySubtarget.h"
-#include "WebAssemblyUtilities.h"
-#include "llvm/CodeGen/MachineModuleInfo.h"
-#include "llvm/MC/MCAsmInfo.h"
-#include "llvm/Target/TargetMachine.h"
-using namespace llvm;
-
-#define DEBUG_TYPE "wasm-handle-termpads"
-
-namespace {
-class WebAssemblyHandleEHTerminatePads final : public MachineFunctionPass {
- StringRef getPassName() const override {
- return "WebAssembly Handle EH Terminate Pads";
- }
-
- bool runOnMachineFunction(MachineFunction &MF) override;
-
-public:
- static char ID; // Pass identification, replacement for typeid
- WebAssemblyHandleEHTerminatePads() : MachineFunctionPass(ID) {}
-};
-} // end anonymous namespace
-
-char WebAssemblyHandleEHTerminatePads::ID = 0;
-INITIALIZE_PASS(WebAssemblyHandleEHTerminatePads, DEBUG_TYPE,
- "WebAssembly Handle EH Terminate Pads", false, false)
-
-FunctionPass *llvm::createWebAssemblyHandleEHTerminatePads() {
- return new WebAssemblyHandleEHTerminatePads();
-}
-
-bool WebAssemblyHandleEHTerminatePads::runOnMachineFunction(
- MachineFunction &MF) {
- LLVM_DEBUG(dbgs() << "********** Handle EH Terminate Pads **********\n"
- "********** Function: "
- << MF.getName() << '\n');
-
- if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
- ExceptionHandling::Wasm ||
- !MF.getFunction().hasPersonalityFn())
- return false;
-
- const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
-
- // Find calls to __clang_call_terminate()
- SmallVector<MachineInstr *, 8> ClangCallTerminateCalls;
- 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)
- ClangCallTerminateCalls.push_back(&MI);
- }
- }
- }
-
- if (ClangCallTerminateCalls.empty())
- return false;
-
- for (auto *Call : ClangCallTerminateCalls) {
- // This should be an EH pad because LateEHPrepare ensures terminate pads are
- // a single BB.
- MachineBasicBlock *CatchBB = Call->getParent();
- assert(CatchBB->isEHPad());
-
- auto *CatchAllBB = MF.CreateMachineBasicBlock();
- MF.insert(std::next(CatchBB->getIterator()), CatchAllBB);
- CatchAllBB->setIsEHPad(true);
- for (auto *Pred : CatchBB->predecessors())
- Pred->addSuccessor(CatchAllBB);
-
- // If the definition of __clang_call_terminate exists in the module, there
- // should be a declaration of std::terminate within the same module, because
- // __clang_call_terminate calls it.
- const auto *StdTerminateFn =
- MF.getMMI().getModule()->getNamedValue(WebAssembly::StdTerminateFn);
- assert(StdTerminateFn && "std::terminate() does not exist in the module");
-
- // Generate a BB in the form of:
- // catch_all
- // call @std::terminate
- // unreachable
- BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::CATCH_ALL));
- BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::CALL))
- .addGlobalAddress(StdTerminateFn);
- BuildMI(CatchAllBB, Call->getDebugLoc(), TII.get(WebAssembly::UNREACHABLE));
- }
-
- return true;
-}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
index 8cd2b38d8e00..1dcf84a26af0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
@@ -38,7 +38,6 @@ class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
bool addCatchAlls(MachineFunction &MF);
bool replaceFuncletReturns(MachineFunction &MF);
bool removeUnnecessaryUnreachables(MachineFunction &MF);
- bool ensureSingleBBTermPads(MachineFunction &MF);
bool restoreStackPointer(MachineFunction &MF);
MachineBasicBlock *getMatchingEHPad(MachineInstr *MI);
@@ -128,7 +127,6 @@ bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
Changed |= hoistCatches(MF);
Changed |= addCatchAlls(MF);
Changed |= replaceFuncletReturns(MF);
- Changed |= ensureSingleBBTermPads(MF);
}
Changed |= removeUnnecessaryUnreachables(MF);
if (MF.getFunction().hasPersonalityFn())
@@ -288,80 +286,6 @@ bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
return Changed;
}
-// Clang-generated terminate pads are an single-BB EH pad in the form of
-// termpad:
-// %exn = catch $__cpp_exception
-// call @__clang_call_terminate(%exn)
-// unreachable
-// (There can be local.set and local.gets before the call if we didn't run
-// RegStackify)
-// But code transformations can change or add more control flow, so the call to
-// __clang_call_terminate() function may not be in the original EH pad anymore.
-// This ensures every terminate pad is a single BB in the form illustrated
-// above.
-//
-// This is preparation work for the HandleEHTerminatePads pass later, which
-// duplicates terminate pads both for 'catch' and 'catch_all'. Refer to
-// WebAssemblyHandleEHTerminatePads.cpp for details.
-bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
- const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
-
- // Find calls to __clang_call_terminate()
- SmallVector<MachineInstr *, 8> ClangCallTerminateCalls;
- SmallPtrSet<MachineBasicBlock *, 8> TermPads;
- 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) {
- MachineBasicBlock *EHPad = getMatchingEHPad(&MI);
- assert(EHPad && "No matching EH pad for __clang_call_terminate");
- // In case a __clang_call_terminate call is duplicated during code
- // transformation so one terminate pad contains multiple
- // __clang_call_terminate calls, we only count one of them
- if (TermPads.insert(EHPad).second)
- ClangCallTerminateCalls.push_back(&MI);
- }
- }
- }
- }
-
- bool Changed = false;
- for (auto *Call : ClangCallTerminateCalls) {
- MachineBasicBlock *EHPad = getMatchingEHPad(Call);
- assert(EHPad && "No matching EH pad for __clang_call_terminate");
-
- // If it is already the form we want, skip it
- if (Call->getParent() == EHPad &&
- Call->getNextNode()->getOpcode() == WebAssembly::UNREACHABLE)
- continue;
-
- // In case the __clang_call_terminate() call is not in its matching EH pad,
- // move the call to the end of EH pad and add an unreachable instruction
- // after that. Delete all successors and their children if any, because here
- // the program terminates.
- Changed = true;
- // This runs after hoistCatches(), so catch instruction should be at the top
- MachineInstr *Catch = WebAssembly::findCatch(EHPad);
- assert(Catch && "EH pad does not have a catch instruction");
- // Takes the result register of the catch instruction as argument. There may
- // have been some other local.set/local.gets in between, but at this point
- // we don't care.
- Call->getOperand(1).setReg(Catch->getOperand(0).getReg());
- auto InsertPos = std::next(MachineBasicBlock::iterator(Catch));
- EHPad->insert(InsertPos, Call->removeFromParent());
- BuildMI(*EHPad, InsertPos, Call->getDebugLoc(),
- TII.get(WebAssembly::UNREACHABLE));
- EHPad->erase(InsertPos, EHPad->end());
- SmallVector<MachineBasicBlock *, 8> Succs(EHPad->successors());
- for (auto *Succ : Succs)
- EHPad->removeSuccessor(Succ);
- eraseDeadBBsAndChildren(Succs);
- }
- return Changed;
-}
-
// 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.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 0b078504ec7b..135055a43afc 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -82,7 +82,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
initializeWebAssemblyExceptionInfoPass(PR);
initializeWebAssemblyCFGSortPass(PR);
initializeWebAssemblyCFGStackifyPass(PR);
- initializeWebAssemblyHandleEHTerminatePadsPass(PR);
initializeWebAssemblyExplicitLocalsPass(PR);
initializeWebAssemblyLowerBrUnlessPass(PR);
initializeWebAssemblyRegNumberingPass(PR);
@@ -486,10 +485,6 @@ void WebAssemblyPassConfig::addPreEmitPass() {
// Insert BLOCK and LOOP markers.
addPass(createWebAssemblyCFGStackify());
- // Handle terminate pads for cleanups
- if (TM->Options.ExceptionModel == ExceptionHandling::Wasm)
- addPass(createWebAssemblyHandleEHTerminatePads());
-
// Insert explicit local.get and local.set operators.
if (!WasmDisableExplicitLocals)
addPass(createWebAssemblyExplicitLocals());
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 332605fef981..b9344bba8dc9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -18,7 +18,6 @@
#include "llvm/MC/MCContext.h"
using namespace llvm;
-const char *const WebAssembly::ClangCallTerminateFn = "__clang_call_terminate";
const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
@@ -73,7 +72,7 @@ bool WebAssembly::mayThrow(const MachineInstr &MI) {
return false;
// These functions never throw
if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||
- F->getName() == ClangCallTerminateFn || F->getName() == StdTerminateFn)
+ F->getName() == StdTerminateFn)
return false;
// TODO Can we exclude call instructions that are marked as 'nounwind' in the