Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
//===-- CodeGen/AsmPrinter/WasmException.cpp - Wasm Exception Impl --------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file contains support for writing WebAssembly exception info into asm
|
|
|
|
// files.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "WasmException.h"
|
2018-11-14 10:46:21 +08:00
|
|
|
#include "llvm/IR/Mangler.h"
|
|
|
|
#include "llvm/MC/MCContext.h"
|
Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
#include "llvm/MC/MCStreamer.h"
|
|
|
|
using namespace llvm;
|
|
|
|
|
2018-11-14 10:46:21 +08:00
|
|
|
void WasmException::endModule() {
|
2020-12-26 12:23:33 +08:00
|
|
|
// This is the symbol used in 'throw' and 'catch' instruction to denote this
|
|
|
|
// is a C++ exception. This symbol has to be emitted somewhere once in the
|
|
|
|
// module. Check if the symbol has already been created, i.e., we have at
|
|
|
|
// least one 'throw' or 'catch' instruction in the module, and emit the symbol
|
|
|
|
// only if so.
|
2018-11-14 10:46:21 +08:00
|
|
|
SmallString<60> NameStr;
|
|
|
|
Mangler::getNameWithPrefix(NameStr, "__cpp_exception", Asm->getDataLayout());
|
|
|
|
if (Asm->OutContext.lookupSymbol(NameStr)) {
|
|
|
|
MCSymbol *ExceptionSym = Asm->GetExternalSymbolSymbol("__cpp_exception");
|
2020-02-15 11:21:58 +08:00
|
|
|
Asm->OutStreamer->emitLabel(ExceptionSym);
|
2018-11-14 10:46:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
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");
|
2020-02-15 11:21:58 +08:00
|
|
|
Asm->OutStreamer->emitLabel(LSDAEndLabel);
|
Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
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,
|
Exception support for basic block sections
This is part of the Propeller framework to do post link code layout optimizations. Please see the RFC here: https://groups.google.com/forum/#!msg/llvm-dev/ef3mKzAdJ7U/1shV64BYBAAJ and the detailed RFC doc here: https://github.com/google/llvm-propeller/blob/plo-dev/Propeller_RFC.pdf
This patch provides exception support for basic block sections by splitting the call-site table into call-site ranges corresponding to different basic block sections. Still all landing pads must reside in the same basic block section (which is guaranteed by the the core basic block section patch D73674 (ExceptionSection) ). Each call-site table will refer to the landing pad fragment by explicitly specifying @LPstart (which is omitted in the normal non-basic-block section case). All these call-site tables will share their action and type tables.
The C++ ABI somehow assumes that no landing pads point directly to LPStart (which works in the normal case since the function begin is never a landing pad), and uses LP.offset = 0 to specify no landing pad. In the case of basic block section where one section contains all the landing pads, the landing pad offset relative to LPStart could actually be zero. Thus, we avoid zero-offset landing pads by inserting a **nop** operation as the first non-CFI instruction in the exception section.
**Background on Exception Handling in C++ ABI**
https://github.com/itanium-cxx-abi/cxx-abi/blob/master/exceptions.pdf
Compiler emits an exception table for every function. When an exception is thrown, the stack unwinding library queries the unwind table (which includes the start and end of each function) to locate the exception table for that function.
The exception table includes a call site table for the function, which is used to guide the exception handling runtime to take the appropriate action upon an exception. Each call site record in this table is structured as follows:
| CallSite | --> Position of the call site (relative to the function entry)
| CallSite length | --> Length of the call site.
| Landing Pad | --> Position of the landing pad (relative to the landing pad fragment’s begin label)
| Action record offset | --> Position of the first action record
The call site records partition a function into different pieces and describe what action must be taken for each callsite. The callsite fields are relative to the start of the function (as captured in the unwind table).
The landing pad entry is a reference into the function and corresponds roughly to the catch block of a try/catch statement. When execution resumes at a landing pad, it receives an exception structure and a selector value corresponding to the type of the exception thrown, and executes similar to a switch-case statement. The landing pad field is relative to the beginning of the procedure fragment which includes all the landing pads (@LPStart). The C++ ABI requires all landing pads to be in the same fragment. Nonetheless, without basic block sections, @LPStart is the same as the function @Start (found in the unwind table) and can be omitted.
The action record offset is an index into the action table which includes information about which exception types are caught.
**C++ Exceptions with Basic Block Sections**
Basic block sections break the contiguity of a function fragment. Therefore, call sites must be specified relative to the beginning of the basic block section. Furthermore, the unwinding library should be able to find the corresponding callsites for each section. To do so, the .cfi_lsda directive for a section must point to the range of call-sites for that section.
This patch introduces a new **CallSiteRange** structure which specifies the range of call-sites which correspond to every section:
`struct CallSiteRange {
// Symbol marking the beginning of the precedure fragment.
MCSymbol *FragmentBeginLabel = nullptr;
// Symbol marking the end of the procedure fragment.
MCSymbol *FragmentEndLabel = nullptr;
// LSDA symbol for this call-site range.
MCSymbol *ExceptionLabel = nullptr;
// Index of the first call-site entry in the call-site table which
// belongs to this range.
size_t CallSiteBeginIdx = 0;
// Index just after the last call-site entry in the call-site table which
// belongs to this range.
size_t CallSiteEndIdx = 0;
// Whether this is the call-site range containing all the landing pads.
bool IsLPRange = false;
};`
With N basic-block-sections, the call-site table is partitioned into N call-site ranges.
Conceptually, we emit the call-site ranges for sections sequentially in the exception table as if each section has its own exception table. In the example below, two sections result in the two call site ranges (denoted by LSDA1 and LSDA2) placed next to each other. However, their call-sites will refer to records in the shared Action Table. We also emit the header fields (@LPStart and CallSite Table Length) for each call site range in order to place the call site ranges in separate LSDAs. We note that with -basic-block-sections, The CallSiteTableLength will not actually represent the length of the call site table, but rather the reference to the action table. Since the only purpose of this field is to locate the action table, correctness is guaranteed.
Finally, every call site range has one @LPStart pointer so the landing pads of each section must all reside in one section (not necessarily the same section). To make this easier, we decide to place all landing pads of the function in one section (hence the `IsLPRange` field in CallSiteRange).
| @LPStart | ---> Landing pad fragment ( LSDA1 points here)
| CallSite Table Length | ---> Used to find the action table.
| CallSites |
| … |
| … |
| @LPStart | ---> Landing pad fragment ( LSDA2 points here)
| CallSite Table Length |
| CallSites |
| … |
| … |
…
…
| Action Table |
| Types Table |
Reviewed By: MaskRay
Differential Revision: https://reviews.llvm.org/D73739
2020-10-01 01:37:00 +08:00
|
|
|
SmallVectorImpl<CallSiteRange> &CallSiteRanges,
|
Reland "[WebAssembly] LSDA info generation"
Summary:
This adds support for LSDA (exception table) generation for wasm EH.
Wasm EH mostly follows the structure of Itanium-style exception tables,
with one exception: a call site table entry in wasm EH corresponds to
not a call site 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 on this part.)
This patch:
- Changes wasm.landingpad.index intrinsic to take a token argument, to
make this 1:1 match with a catchpad instruction
- Stores landingpad index info and catch type info MachineFunction in
before instruction selection
- Lowers wasm.lsda intrinsic to an MCSymbol pointing to the start of an
exception table
- Adds WasmException class with overridden methods for table generation
- Adds support for LSDA section in Wasm object writer
Reviewers: dschuff, sbc100, rnk
Subscribers: mgorny, jgravelle-google, sunfish, llvm-commits
Differential Revision: https://reviews.llvm.org/D52748
llvm-svn: 345345
2018-10-26 07:55:10 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|