[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