forked from OSchip/llvm-project
[WebAssembly] Handle EH terminate pads for cleanup
Terminate pads, cleanup pads with `__clang_call_terminate` call, have `catch` instruction in them because `__clang_call_terminate` takes an exception pointer. But these terminate pads should be reached also in case of foreign exception. So this pass attaches an additional `catch_all` BB after every terminate pad BB, with a call to `std::terminate`. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D94050
This commit is contained in:
parent
e117295922
commit
be0efa1f23
|
@ -22,6 +22,7 @@ add_llvm_target(WebAssemblyCodeGen
|
|||
WebAssemblyCFGSort.cpp
|
||||
WebAssemblyDebugFixup.cpp
|
||||
WebAssemblyDebugValueManager.cpp
|
||||
WebAssemblyHandleEHTerminatePads.cpp
|
||||
WebAssemblyLateEHPrepare.cpp
|
||||
WebAssemblyExceptionInfo.cpp
|
||||
WebAssemblyExplicitLocals.cpp
|
||||
|
|
|
@ -49,6 +49,7 @@ FunctionPass *createWebAssemblyFixIrreducibleControlFlow();
|
|||
FunctionPass *createWebAssemblyLateEHPrepare();
|
||||
FunctionPass *createWebAssemblyCFGSort();
|
||||
FunctionPass *createWebAssemblyCFGStackify();
|
||||
FunctionPass *createWebAssemblyHandleEHTerminatePads();
|
||||
FunctionPass *createWebAssemblyExplicitLocals();
|
||||
FunctionPass *createWebAssemblyLowerBrUnless();
|
||||
FunctionPass *createWebAssemblyRegNumbering();
|
||||
|
@ -75,6 +76,7 @@ void initializeWebAssemblyLateEHPreparePass(PassRegistry &);
|
|||
void initializeWebAssemblyExceptionInfoPass(PassRegistry &);
|
||||
void initializeWebAssemblyCFGSortPass(PassRegistry &);
|
||||
void initializeWebAssemblyCFGStackifyPass(PassRegistry &);
|
||||
void initializeWebAssemblyHandleEHTerminatePadsPass(PassRegistry &);
|
||||
void initializeWebAssemblyExplicitLocalsPass(PassRegistry &);
|
||||
void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
|
||||
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
// 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;
|
||||
}
|
|
@ -82,6 +82,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
|
|||
initializeWebAssemblyExceptionInfoPass(PR);
|
||||
initializeWebAssemblyCFGSortPass(PR);
|
||||
initializeWebAssemblyCFGStackifyPass(PR);
|
||||
initializeWebAssemblyHandleEHTerminatePadsPass(PR);
|
||||
initializeWebAssemblyExplicitLocalsPass(PR);
|
||||
initializeWebAssemblyLowerBrUnlessPass(PR);
|
||||
initializeWebAssemblyRegNumberingPass(PR);
|
||||
|
@ -485,6 +486,10 @@ 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());
|
||||
|
|
|
@ -132,6 +132,9 @@ ehcleanup: ; preds = %entry
|
|||
; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception
|
||||
; CHECK: call __clang_call_terminate, $[[EXN]]
|
||||
; CHECK: unreachable
|
||||
; CHECK: catch_all
|
||||
; CHECK: call _ZSt9terminatev
|
||||
; CHECK: unreachable
|
||||
; CHECK: end_try
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_try
|
||||
|
@ -429,6 +432,7 @@ declare i32 @llvm.eh.typeid.for(i8*)
|
|||
declare i8* @__cxa_begin_catch(i8*)
|
||||
declare void @__cxa_end_catch()
|
||||
declare void @__clang_call_terminate(i8*)
|
||||
declare void @_ZSt9terminatev()
|
||||
declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
|
||||
|
||||
; CHECK: __cpp_exception:
|
||||
|
|
Loading…
Reference in New Issue