llvm-project/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp
Heejin Ahn 9261ee32dc [WebAssembly] Make EH work with dynamic linking
This makes Wasm EH work with dynamic linking. So far we were only able
to handle destructors, which do not use any tags or LSDA info.

1. This uses `TargetExternalSymbol` for `GCC_except_tableN` symbols,
   which points to the address of per-function LSDA info. It is more
   convenient to use than `MCSymbol` because it can take additional
   target flags.

2. When lowering `wasm_lsda` intrinsic, if PIC is enabled, make the
   symbol relative to `__memory_base` and generate the `add` node. If
   PIC is disabled, continue to use the absolute address.

3. Make tag symbols (`__cpp_exception` and `__c_longjmp`) undefined in
   the backend, because it is hard to make it work with dynamic
   linking's loading order. Instead, we make all tag symbols undefined
   in the LLVM backend and import it from JS.

4. Add support for undefined tags to the linker.

Companion patches:
- https://github.com/WebAssembly/binaryen/pull/4223
- https://github.com/emscripten-core/emscripten/pull/15266

Reviewed By: sbc100

Differential Revision: https://reviews.llvm.org/D111388
2021-10-12 23:28:27 -07:00

107 lines
4.6 KiB
C++

//===-- CodeGen/AsmPrinter/WasmException.cpp - Wasm Exception Impl --------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains support for writing WebAssembly exception info into asm
// files.
//
//===----------------------------------------------------------------------===//
#include "WasmException.h"
#include "llvm/IR/Mangler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCStreamer.h"
using namespace llvm;
void WasmException::endModule() {
// These are symbols used to throw/catch C++ exceptions and C longjmps. These
// symbols have to be emitted somewhere once in the module. Check if each of
// the symbols has already been created, i.e., we have at least one 'throw' or
// 'catch' instruction with the symbol in the module, and emit the symbol only
// if so.
//
// But in dynamic linking, it is in general not possible to come up with a
// module instantiating order in which tag-defining modules are loaded before
// the importing modules. So we make them undefined symbols here, define tags
// in the JS side, and feed them to each importing module.
if (!Asm->isPositionIndependent()) {
for (const char *SymName : {"__cpp_exception", "__c_longjmp"}) {
SmallString<60> NameStr;
Mangler::getNameWithPrefix(NameStr, SymName, Asm->getDataLayout());
if (Asm->OutContext.lookupSymbol(NameStr)) {
MCSymbol *ExceptionSym = Asm->GetExternalSymbolSymbol(SymName);
Asm->OutStreamer->emitLabel(ExceptionSym);
}
}
}
}
void WasmException::markFunctionEnd() {
// Get rid of any dead landing pads.
if (!Asm->MF->getLandingPads().empty()) {
auto *NonConstMF = const_cast<MachineFunction *>(Asm->MF);
// Wasm does not set BeginLabel and EndLabel information for landing pads,
// so we should set the second argument false.
NonConstMF->tidyLandingPads(nullptr, /* TidyIfNoBeginLabels */ false);
}
}
void WasmException::endFunction(const MachineFunction *MF) {
bool ShouldEmitExceptionTable = false;
for (const LandingPadInfo &Info : MF->getLandingPads()) {
if (MF->hasWasmLandingPadIndex(Info.LandingPadBlock)) {
ShouldEmitExceptionTable = true;
break;
}
}
if (!ShouldEmitExceptionTable)
return;
MCSymbol *LSDALabel = emitExceptionTable();
assert(LSDALabel && ".GCC_exception_table has not been emitted!");
// Wasm requires every data section symbol to have a .size set. So we emit an
// end marker and set the size as the difference between the start end the end
// marker.
MCSymbol *LSDAEndLabel = Asm->createTempSymbol("GCC_except_table_end");
Asm->OutStreamer->emitLabel(LSDAEndLabel);
MCContext &OutContext = Asm->OutStreamer->getContext();
const MCExpr *SizeExp = MCBinaryExpr::createSub(
MCSymbolRefExpr::create(LSDAEndLabel, OutContext),
MCSymbolRefExpr::create(LSDALabel, OutContext), OutContext);
Asm->OutStreamer->emitELFSize(LSDALabel, SizeExp);
}
// Compute the call-site table for wasm EH. Even though we use the same function
// name to share the common routines, a call site entry in the table corresponds
// to not a call site for possibly-throwing functions but a landing pad. In wasm
// EH the VM is responsible for stack unwinding. After an exception occurs and
// the stack is unwound, the control flow is transferred to wasm 'catch'
// instruction by the VM, after which the personality function is called from
// the compiler-generated code. Refer to WasmEHPrepare pass for more
// information.
void WasmException::computeCallSiteTable(
SmallVectorImpl<CallSiteEntry> &CallSites,
SmallVectorImpl<CallSiteRange> &CallSiteRanges,
const SmallVectorImpl<const LandingPadInfo *> &LandingPads,
const SmallVectorImpl<unsigned> &FirstActions) {
MachineFunction &MF = *Asm->MF;
for (unsigned I = 0, N = LandingPads.size(); I < N; ++I) {
const LandingPadInfo *Info = LandingPads[I];
MachineBasicBlock *LPad = Info->LandingPadBlock;
// We don't emit LSDA for single catch (...).
if (!MF.hasWasmLandingPadIndex(LPad))
continue;
// Wasm EH must maintain the EH pads in the order assigned to them by the
// WasmEHPrepare pass.
unsigned LPadIndex = MF.getWasmLandingPadIndex(LPad);
CallSiteEntry Site = {nullptr, nullptr, Info, FirstActions[I]};
if (CallSites.size() < LPadIndex + 1)
CallSites.resize(LPadIndex + 1);
CallSites[LPadIndex] = Site;
}
}