[llvm] be0efa1 - [WebAssembly] Handle EH terminate pads for cleanup
Heejin Ahn via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 6 08:40:56 PST 2021
Author: Heejin Ahn
Date: 2021-02-06T08:40:30-08:00
New Revision: be0efa1f2368c007b9ecb533db42102166ce9a17
URL: https://github.com/llvm/llvm-project/commit/be0efa1f2368c007b9ecb533db42102166ce9a17
DIFF: https://github.com/llvm/llvm-project/commit/be0efa1f2368c007b9ecb533db42102166ce9a17.diff
LOG: [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
Added:
llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp
Modified:
llvm/lib/Target/WebAssembly/CMakeLists.txt
llvm/lib/Target/WebAssembly/WebAssembly.h
llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
llvm/test/CodeGen/WebAssembly/exception.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 87920e7a950b..40fc85d0df24 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -22,6 +22,7 @@ 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 e812d0047961..7838e57d82d3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -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 &);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp
new file mode 100644
index 000000000000..d41947a036dd
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyHandleEHTerminatePads.cpp
@@ -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;
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 135055a43afc..0b078504ec7b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -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());
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index 3fca3071db63..41573a393b72 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -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:
More information about the llvm-commits
mailing list