llvm-project/llvm/lib/CodeGen/WasmEHPrepare.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

372 lines
14 KiB
C++
Raw Normal View History

//===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
//
// 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 transformation is designed for use by code generators which use
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// WebAssembly exception handling scheme. This currently supports C++
// exceptions.
//
// WebAssembly exception handling uses Windows exception IR for the middle level
// representation. This pass does the following transformation for every
// catchpad block:
// (In C-style pseudocode)
//
// - Before:
// catchpad ...
// exn = wasm.get.exception();
// selector = wasm.get.selector();
// ...
//
// - After:
// catchpad ...
// exn = wasm.catch(WebAssembly::CPP_EXCEPTION);
// // Only add below in case it's not a single catch (...)
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// wasm.landingpad.index(index);
// __wasm_lpad_context.lpad_index = index;
// __wasm_lpad_context.lsda = wasm.lsda();
// _Unwind_CallPersonality(exn);
// selector = __wasm_lpad_context.selector;
// ...
//
//
// * Background: Direct personality function call
// In WebAssembly EH, the VM is responsible for unwinding the stack once an
// exception is thrown. After the stack is unwound, the control flow is
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// transfered to WebAssembly 'catch' instruction.
//
// Unwinding the stack is not done by libunwind but the VM, so the personality
// function in libcxxabi cannot be called from libunwind during the unwinding
// process. So after a catch instruction, we insert a call to a wrapper function
// in libunwind that in turn calls the real personality function.
//
// In Itanium EH, if the personality function decides there is no matching catch
// clause in a call frame and no cleanup action to perform, the unwinder doesn't
// stop there and continues unwinding. But in Wasm EH, the unwinder stops at
// every call frame with a catch intruction, after which the personality
// function is called from the compiler-generated user code here.
//
// In libunwind, we have this struct that serves as a communincation channel
// between the compiler-generated user code and the personality function in
// libcxxabi.
//
// struct _Unwind_LandingPadContext {
// uintptr_t lpad_index;
// uintptr_t lsda;
// uintptr_t selector;
// };
// struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
//
// And this wrapper in libunwind calls the personality function.
//
// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
// struct _Unwind_Exception *exception_obj =
// (struct _Unwind_Exception *)exception_ptr;
// _Unwind_Reason_Code ret = __gxx_personality_v0(
// 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
// (struct _Unwind_Context *)__wasm_lpad_context);
// return ret;
// }
//
// We pass a landing pad index, and the address of LSDA for the current function
// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
// the selector after it returns.
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WasmEHFuncInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/InitializePasses.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
using namespace llvm;
#define DEBUG_TYPE "wasmehprepare"
namespace {
class WasmEHPrepare : public FunctionPass {
Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
// Field addresses of struct _Unwind_LandingPadContext
Value *LPadIndexField = nullptr; // lpad_index field
Value *LSDAField = nullptr; // lsda field
Value *SelectorField = nullptr; // selector
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 llvm-svn: 356316
2019-03-16 13:38:57 +08:00
Function *ThrowF = nullptr; // wasm.throw() intrinsic
Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
Function *LSDAF = nullptr; // wasm.lsda() intrinsic
Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
Function *CatchF = nullptr; // wasm.catch() intrinsic
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 llvm-svn: 356316
2019-03-16 13:38:57 +08:00
Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
[opaque pointer types] Add a FunctionCallee wrapper type, and use it. Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc doesn't choke on it, hopefully. Original Message: The FunctionCallee type is effectively a {FunctionType*,Value*} pair, and is a useful convenience to enable code to continue passing the result of getOrInsertFunction() through to EmitCall, even once pointer types lose their pointee-type. Then: - update the CallInst/InvokeInst instruction creation functions to take a Callee, - modify getOrInsertFunction to return FunctionCallee, and - update all callers appropriately. One area of particular note is the change to the sanitizer code. Previously, they had been casting the result of `getOrInsertFunction` to a `Function*` via `checkSanitizerInterfaceFunction`, and storing that. That would report an error if someone had already inserted a function declaraction with a mismatching signature. However, in general, LLVM allows for such mismatches, as `getOrInsertFunction` will automatically insert a bitcast if needed. As part of this cleanup, cause the sanitizer code to do the same. (It will call its functions using the expected signature, however they may have been declared.) Finally, in a small number of locations, callers of `getOrInsertFunction` actually were expecting/requiring that a brand new function was being created. In such cases, I've switched them to Function::Create instead. Differential Revision: https://reviews.llvm.org/D57315 llvm-svn: 352827
2019-02-01 10:28:03 +08:00
FunctionCallee CallPersonalityF =
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 llvm-svn: 356316
2019-03-16 13:38:57 +08:00
nullptr; // _Unwind_CallPersonality() wrapper
bool prepareThrows(Function &F);
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
bool prepareEHPads(Function &F);
void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
public:
static char ID; // Pass identification, replacement for typeid
WasmEHPrepare() : FunctionPass(ID) {}
bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
StringRef getPassName() const override {
return "WebAssembly Exception handling preparation";
}
};
} // end anonymous namespace
char WasmEHPrepare::ID = 0;
[WebAssembly] Fix wasm.lsda() optimization in WasmEHPrepare Summary: When we insert a call to the personality function wrapper (`_Unwind_CallPersonality`) for a catch pad, we store some necessary info in `__wasm_lpad_context` struct and pass it. One of the info is the LSDA address for the function. For this, we insert a call to `wasm.lsda()`, which will be lowered down to the address of LSDA, and store it in a field in `__wasm_lpad_context`. There are exceptions to this personality call insertion: catchpads for `catch (...)` and cleanuppads (for destructors) don't need personality function calls, because we don't need to figure out whether the current exception should be caught or not. (They always should.) There was a little optimization to `wasm.lsda()` call insertion. Because the LSDA address is the same throughout a function, we don't need to insert a store of `wasm.lsda()` return value in every catchpad. For example: ``` try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } } ``` So the previous algorithm checked if the current catch has a parent EH pad, we didn't insert a call to `wasm.lsda()` and its store. But this was incorrect, because what if the outer catch is `catch (...)` or a cleanuppad? ``` try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } } ``` In this case we need to insert `wasm.lsda()` in the inner catchpad, because the outer catchpad does not have one. To minimize the number of inserted `wasm.lsda()` calls and stores, we need a way to figure out whether we have encountered `wasm.lsda()` call in any of EH pads that dominates the current EH pad. To figure that out, we now visit EH pads in BFS order in the dominator tree so that we visit parent BBs first before visiting its child BBs in the domtree. We keep a set named `ExecutedLSDA`, which basically means "Do we have `wasm.lsda()` either in the current EH pad or any of its parent EH pads in the dominator tree?". This is to prevent scanning the domtree up to the root in the worst case every time we examine an EH pad: each EH pad only needs to examine its immediate parent EH pad. - If any of its parent EH pads in the domtree has `wasm.lsda()`, this means we don't need `wasm.lsda()` in the current EH pad. We also insert the current EH pad in `ExecutedLSDA` set. - If none of its parent EH pad has `wasm.lsda()` - If the current EH pad is a `catch (...)` or a cleanuppad, done. - If the current EH pad is neither a `catch (...)` nor a cleanuppad, add `wasm.lsda()` and the store in the current EH pad, and add the current EH pad to `ExecutedLSDA` set. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77423
2020-04-01 07:08:01 +08:00
INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
"Prepare WebAssembly exceptions", false, false)
INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
false, false)
FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
bool WasmEHPrepare::doInitialization(Module &M) {
IRBuilder<> IRB(M.getContext());
LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
IRB.getInt8PtrTy(), // lsda
IRB.getInt32Ty() // selector
);
return false;
}
// Erase the specified BBs if the BB does not have any remaining predecessors,
// and also all its dead children.
template <typename Container>
static void eraseDeadBBsAndChildren(const Container &BBs) {
SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
while (!WL.empty()) {
auto *BB = WL.pop_back_val();
2020-11-23 14:16:12 +08:00
if (!pred_empty(BB))
continue;
WL.append(succ_begin(BB), succ_end(BB));
DeleteDeadBlock(BB);
}
}
bool WasmEHPrepare::runOnFunction(Function &F) {
bool Changed = false;
Changed |= prepareThrows(F);
Changed |= prepareEHPads(F);
return Changed;
}
bool WasmEHPrepare::prepareThrows(Function &F) {
Module &M = *F.getParent();
IRBuilder<> IRB(F.getContext());
bool Changed = false;
// wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 llvm-svn: 356316
2019-03-16 13:38:57 +08:00
// Insert an unreachable instruction after a call to @llvm.wasm.throw and
// delete all following instructions within the BB, and delete all the dead
// children of the BB as well.
for (User *U : ThrowF->users()) {
// A call to @llvm.wasm.throw() is only generated from __cxa_throw()
// builtin call within libcxxabi, and cannot be an InvokeInst.
auto *ThrowI = cast<CallInst>(U);
if (ThrowI->getFunction() != &F)
continue;
Changed = true;
auto *BB = ThrowI->getParent();
SmallVector<BasicBlock *, 4> Succs(successors(BB));
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 llvm-svn: 356316
2019-03-16 13:38:57 +08:00
auto &InstList = BB->getInstList();
InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
IRB.SetInsertPoint(BB);
IRB.CreateUnreachable();
eraseDeadBBsAndChildren(Succs);
}
return Changed;
}
bool WasmEHPrepare::prepareEHPads(Function &F) {
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
Module &M = *F.getParent();
IRBuilder<> IRB(F.getContext());
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
SmallVector<BasicBlock *, 16> CatchPads;
SmallVector<BasicBlock *, 16> CleanupPads;
for (BasicBlock &BB : F) {
if (!BB.isEHPad())
continue;
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
auto *Pad = BB.getFirstNonPHI();
if (isa<CatchPadInst>(Pad))
CatchPads.push_back(&BB);
else if (isa<CleanupPadInst>(Pad))
CleanupPads.push_back(&BB);
}
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
if (CatchPads.empty() && CleanupPads.empty())
return false;
assert(F.hasPersonalityFn() && "Personality function not found");
// __wasm_lpad_context global variable.
[WebAssembly] Make EH/SjLj vars unconditionally thread local This makes three thread local variables (`__THREW__`, `__threwValue`, and `__wasm_lpad_context`) unconditionally thread local. If the target doesn't support TLS, they will be downgraded to normal variables in `stripThreadLocals`. This makes the object not linkable with other objects using shared memory, which is what we intend here; these variables should be thread local when used with shared memory. This is what we initially tried in D88262. But D88323 changed this: It only created these variables when threads were supported, because `__THREW__` and `__threwValue` were always generated even if Emscripten EH/SjLj was not used, making all objects built without threads not linkable with shared memory, which was too restrictive. But sometimes this is not safe. If we build an object using variables such as `__THREW__` without threads, it can be linked to other objects using shared memory, because the original object's `__THREW__` was not created thread local to begin with. So this CL basically reverts D88323 with some additional improvements: - This checks each of the functions and global variables created within `LowerEmscriptenEHSjLj` pass and removes it if it's not used at the end of the pass. So only modules using those variables will be affected. - Moves `CoalesceFeaturesAndStripAtomics` and `AtomicExpand` passes after all other IR pasess that can create thread local variables. It is not sufficient to move them to the end of `addIRPasses`, because `__wasm_lpad_context` is created in `WasmEHPrepare`, which runs inside `addPassesToHandleExceptions`, which runs before `addISelPrepare`. So we override `addISelPrepare` and move atomic/TLS stripping and expanding passes there. This also removes merges `TLS` and `NO-TLS` FileCheck lines into one `CHECK` line, because in the bitcode level we always create them as thread local. Also some function declarations are deleted `CHECK` lines because they are unused. Reviewed By: tlively, sbc100 Differential Revision: https://reviews.llvm.org/D120013
2022-02-17 08:11:29 +08:00
// This variable should be thread local. If the target does not support TLS,
// we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
// non-thread-local ones, in which case we don't allow this object to be
// linked with other objects using shared memory.
LPadContextGV = cast<GlobalVariable>(
M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
[WebAssembly] Make EH/SjLj vars unconditionally thread local This makes three thread local variables (`__THREW__`, `__threwValue`, and `__wasm_lpad_context`) unconditionally thread local. If the target doesn't support TLS, they will be downgraded to normal variables in `stripThreadLocals`. This makes the object not linkable with other objects using shared memory, which is what we intend here; these variables should be thread local when used with shared memory. This is what we initially tried in D88262. But D88323 changed this: It only created these variables when threads were supported, because `__THREW__` and `__threwValue` were always generated even if Emscripten EH/SjLj was not used, making all objects built without threads not linkable with shared memory, which was too restrictive. But sometimes this is not safe. If we build an object using variables such as `__THREW__` without threads, it can be linked to other objects using shared memory, because the original object's `__THREW__` was not created thread local to begin with. So this CL basically reverts D88323 with some additional improvements: - This checks each of the functions and global variables created within `LowerEmscriptenEHSjLj` pass and removes it if it's not used at the end of the pass. So only modules using those variables will be affected. - Moves `CoalesceFeaturesAndStripAtomics` and `AtomicExpand` passes after all other IR pasess that can create thread local variables. It is not sufficient to move them to the end of `addIRPasses`, because `__wasm_lpad_context` is created in `WasmEHPrepare`, which runs inside `addPassesToHandleExceptions`, which runs before `addISelPrepare`. So we override `addISelPrepare` and move atomic/TLS stripping and expanding passes there. This also removes merges `TLS` and `NO-TLS` FileCheck lines into one `CHECK` line, because in the bitcode level we always create them as thread local. Also some function declarations are deleted `CHECK` lines because they are unused. Reviewed By: tlively, sbc100 Differential Revision: https://reviews.llvm.org/D120013
2022-02-17 08:11:29 +08:00
LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
"lpad_index_gep");
LSDAField =
IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
"selector_gep");
// wasm.landingpad.index() intrinsic, which is to specify landingpad index
LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
// wasm.lsda() intrinsic. Returns the address of LSDA table for the current
// function.
LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
// wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
// are generated in clang.
GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
// wasm.catch() will be lowered down to wasm 'catch' instruction in
[WebAssembly] Update WasmEHPrepare for the new spec Clang generates `wasm.get.exception` and `wasm.get.ehselector` intrinsics, which respectively return a caught exception value (a pointer to some C++ exception struct) and a selector (an integer value that tells which C++ `catch` clause the current exception matches, or does not match any). WasmEHPrepare is a pass that does some IR-level preparation before instruction selection. Previously one of things we did in this pass was to convert `wasm.get.exception` intrinsic calls to `wasm.extract.exception` intrinsics. Their semantics were the same except `wasm.extract.exception` did not have a token argument. We maintained these two separate intrinsics with the same semantics because instruction selection couldn't handle token arguments. This `wasm.extract.exception` intrinsic was later converted to `extract_exception` instruction in instruction selection, which was a pseudo instruction to implement `br_on_exn`. Because `br_on_exn` pushed an extracted value onto the value stack after the `end` instruction of a `block`, but LLVM does not have a way of modeling that kind of behavior, so this pseudo instruction was used to pull an extracted value out of thin air, like this: ``` block $l0 ... br_on_exn $cpp_exception $l0 ... end extract_exception ;; pushes values onto the stack ``` In the new spec, we don't need this pseudo instruction anymore because `catch` itself returns a value and we don't have `br_on_exn` anymore. In the spec `catch` returns multiple values (like `br_on_exn`), but here we assume it only returns a single i32, which is sufficient to support C++. So this renames `wasm.get.exception` intrinsic to `wasm.catch`. Because this CL does not yet contain instruction selection for `wasm.catch` intrinsic, all `RUN` lines in exception.ll, eh-lsda.ll, and cfg-stackify-eh.ll, and a single `RUN` line in wasm-eh.cpp (which is an end-to-end test from C++ source to assembly) fail. So this CL temporarily disables those `RUN` lines, and for those test files without any valid remaining `RUN` lines, adds a dummy `RUN` line to make them pass. These tests will be reenabled in later CLs. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D94039
2020-12-26 10:38:59 +08:00
// instruction selection.
CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// _Unwind_CallPersonality() wrapper function, which calls the personality
[opaque pointer types] Add a FunctionCallee wrapper type, and use it. Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc doesn't choke on it, hopefully. Original Message: The FunctionCallee type is effectively a {FunctionType*,Value*} pair, and is a useful convenience to enable code to continue passing the result of getOrInsertFunction() through to EmitCall, even once pointer types lose their pointee-type. Then: - update the CallInst/InvokeInst instruction creation functions to take a Callee, - modify getOrInsertFunction to return FunctionCallee, and - update all callers appropriately. One area of particular note is the change to the sanitizer code. Previously, they had been casting the result of `getOrInsertFunction` to a `Function*` via `checkSanitizerInterfaceFunction`, and storing that. That would report an error if someone had already inserted a function declaraction with a mismatching signature. However, in general, LLVM allows for such mismatches, as `getOrInsertFunction` will automatically insert a bitcast if needed. As part of this cleanup, cause the sanitizer code to do the same. (It will call its functions using the expected signature, however they may have been declared.) Finally, in a small number of locations, callers of `getOrInsertFunction` actually were expecting/requiring that a brand new function was being created. In such cases, I've switched them to Function::Create instead. Differential Revision: https://reviews.llvm.org/D57315 llvm-svn: 352827
2019-02-01 10:28:03 +08:00
CallPersonalityF = M.getOrInsertFunction(
"_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
F->setDoesNotThrow();
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
unsigned Index = 0;
for (auto *BB : CatchPads) {
auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
// In case of a single catch (...), we don't need to emit a personalify
// function call
if (CPI->getNumArgOperands() == 1 &&
cast<Constant>(CPI->getArgOperand(0))->isNullValue())
prepareEHPad(BB, false);
else
prepareEHPad(BB, true, Index++);
}
// Cleanup pads don't need a personality function call.
for (auto *BB : CleanupPads)
prepareEHPad(BB, false);
return true;
}
[WebAssembly] Fix wasm.lsda() optimization in WasmEHPrepare Summary: When we insert a call to the personality function wrapper (`_Unwind_CallPersonality`) for a catch pad, we store some necessary info in `__wasm_lpad_context` struct and pass it. One of the info is the LSDA address for the function. For this, we insert a call to `wasm.lsda()`, which will be lowered down to the address of LSDA, and store it in a field in `__wasm_lpad_context`. There are exceptions to this personality call insertion: catchpads for `catch (...)` and cleanuppads (for destructors) don't need personality function calls, because we don't need to figure out whether the current exception should be caught or not. (They always should.) There was a little optimization to `wasm.lsda()` call insertion. Because the LSDA address is the same throughout a function, we don't need to insert a store of `wasm.lsda()` return value in every catchpad. For example: ``` try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } } ``` So the previous algorithm checked if the current catch has a parent EH pad, we didn't insert a call to `wasm.lsda()` and its store. But this was incorrect, because what if the outer catch is `catch (...)` or a cleanuppad? ``` try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } } ``` In this case we need to insert `wasm.lsda()` in the inner catchpad, because the outer catchpad does not have one. To minimize the number of inserted `wasm.lsda()` calls and stores, we need a way to figure out whether we have encountered `wasm.lsda()` call in any of EH pads that dominates the current EH pad. To figure that out, we now visit EH pads in BFS order in the dominator tree so that we visit parent BBs first before visiting its child BBs in the domtree. We keep a set named `ExecutedLSDA`, which basically means "Do we have `wasm.lsda()` either in the current EH pad or any of its parent EH pads in the dominator tree?". This is to prevent scanning the domtree up to the root in the worst case every time we examine an EH pad: each EH pad only needs to examine its immediate parent EH pad. - If any of its parent EH pads in the domtree has `wasm.lsda()`, this means we don't need `wasm.lsda()` in the current EH pad. We also insert the current EH pad in `ExecutedLSDA` set. - If none of its parent EH pad has `wasm.lsda()` - If the current EH pad is a `catch (...)` or a cleanuppad, done. - If the current EH pad is neither a `catch (...)` nor a cleanuppad, add `wasm.lsda()` and the store in the current EH pad, and add the current EH pad to `ExecutedLSDA` set. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77423
2020-04-01 07:08:01 +08:00
// Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// ignored.
[WebAssembly] Fix wasm.lsda() optimization in WasmEHPrepare Summary: When we insert a call to the personality function wrapper (`_Unwind_CallPersonality`) for a catch pad, we store some necessary info in `__wasm_lpad_context` struct and pass it. One of the info is the LSDA address for the function. For this, we insert a call to `wasm.lsda()`, which will be lowered down to the address of LSDA, and store it in a field in `__wasm_lpad_context`. There are exceptions to this personality call insertion: catchpads for `catch (...)` and cleanuppads (for destructors) don't need personality function calls, because we don't need to figure out whether the current exception should be caught or not. (They always should.) There was a little optimization to `wasm.lsda()` call insertion. Because the LSDA address is the same throughout a function, we don't need to insert a store of `wasm.lsda()` return value in every catchpad. For example: ``` try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } } ``` So the previous algorithm checked if the current catch has a parent EH pad, we didn't insert a call to `wasm.lsda()` and its store. But this was incorrect, because what if the outer catch is `catch (...)` or a cleanuppad? ``` try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } } ``` In this case we need to insert `wasm.lsda()` in the inner catchpad, because the outer catchpad does not have one. To minimize the number of inserted `wasm.lsda()` calls and stores, we need a way to figure out whether we have encountered `wasm.lsda()` call in any of EH pads that dominates the current EH pad. To figure that out, we now visit EH pads in BFS order in the dominator tree so that we visit parent BBs first before visiting its child BBs in the domtree. We keep a set named `ExecutedLSDA`, which basically means "Do we have `wasm.lsda()` either in the current EH pad or any of its parent EH pads in the dominator tree?". This is to prevent scanning the domtree up to the root in the worst case every time we examine an EH pad: each EH pad only needs to examine its immediate parent EH pad. - If any of its parent EH pads in the domtree has `wasm.lsda()`, this means we don't need `wasm.lsda()` in the current EH pad. We also insert the current EH pad in `ExecutedLSDA` set. - If none of its parent EH pad has `wasm.lsda()` - If the current EH pad is a `catch (...)` or a cleanuppad, done. - If the current EH pad is neither a `catch (...)` nor a cleanuppad, add `wasm.lsda()` and the store in the current EH pad, and add the current EH pad to `ExecutedLSDA` set. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77423
2020-04-01 07:08:01 +08:00
void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
unsigned Index) {
assert(BB->isEHPad() && "BB is not an EHPad!");
IRBuilder<> IRB(BB->getContext());
IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
for (auto &U : FPI->uses()) {
if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
if (CI->getCalledOperand() == GetExnF)
GetExnCI = CI;
if (CI->getCalledOperand() == GetSelectorF)
GetSelectorCI = CI;
}
}
[WebAssembly] Disable uses of __clang_call_terminate Background: Wasm EH, while using Windows EH (catchpad/cleanuppad based) IR, uses Itanium-based libraries and ABIs with some modifications. `__clang_call_terminate` is a wrapper generated in Clang's Itanium C++ ABI implementation. It contains this code, in C-style pseudocode: ``` void __clang_call_terminate(void *exn) { __cxa_begin_catch(exn); std::terminate(); } ``` So this function is a wrapper to call `__cxa_begin_catch` on the exception pointer before termination. In Itanium ABI, this function is called when another exception is thrown while processing an exception. The pointer for this second, violating exception is passed as the argument of this `__clang_call_terminate`, which calls `__cxa_begin_catch` with that pointer and calls `std::terminate` to terminate the program. The spec (https://libcxxabi.llvm.org/spec.html) for `__cxa_begin_catch` says, ``` When the personality routine encounters a termination condition, it will call __cxa_begin_catch() to mark the exception as handled and then call terminate(), which shall not return to its caller. ``` In wasm EH's Clang implementation, this function is called from cleanuppads that terminates the program, which we also call terminate pads. Cleanuppads normally don't access the thrown exception and the wasm backend converts them to `catch_all` blocks. But because we need the exception pointer in this cleanuppad, we generate `wasm.get.exception` intrinsic (which will eventually be lowered to `catch` instruction) as we do in the catchpads. But because terminate pads are cleanup pads and should run even when a foreign exception is thrown, so what we have been doing is: 1. In `WebAssemblyLateEHPrepare::ensureSingleBBTermPads()`, we make sure terminate pads are in this simple shape: ``` %exn = catch call @__clang_call_terminate(%exn) unreachable ``` 2. In `WebAssemblyHandleEHTerminatePads` pass at the end of the pipeline, we attach a `catch_all` to terminate pads, so they will be in this form: ``` %exn = catch call @__clang_call_terminate(%exn) unreachable catch_all call @std::terminate() unreachable ``` In `catch_all` part, we don't have the exception pointer, so we call `std::terminate()` directly. The reason we ran HandleEHTerminatePads at the end of the pipeline, separate from LateEHPrepare, was it was convenient to assume there was only a single `catch` part per `try` during CFGSort and CFGStackify. --- Problem: While it thinks terminate pads could have been possibly split or calls to `__clang_call_terminate` could have been duplicated, `WebAssemblyLateEHPrepare::ensureSingleBBTermPads()` assumes terminate pads contain no more than calls to `__clang_call_terminate` and `unreachable` instruction. I assumed that because in LLVM very limited forms of transformations are done to catchpads and cleanuppads to maintain the scoping structure. But it turned out to be incorrect; passes can merge cleanuppads into one, including terminate pads, as long as the new code has a correct scoping structure. One pass that does this I observed was `SimplifyCFG`, but there can be more. After this transformation, a single cleanuppad can contain any number of other instructions with the call to `__clang_call_terminate` and can span many BBs. It wouldn't be practical to duplicate all these BBs within the cleanuppad to generate the equivalent `catch_all` blocks, only with calls to `__clang_call_terminate` replaced by calls to `std::terminate`. Unless we do more complicated transformation to split those calls to `__clang_call_terminate` into a separate cleanuppad, it is tricky to solve. --- Solution (?): This CL just disables the generation and use of `__clang_call_terminate` and calls `std::terminate()` directly in its place. The possible downside of this approach can be, because the Itanium ABI intended to "mark" the violating exception handled, we don't do that anymore. What `__cxa_begin_catch` actually does is increment the exception's handler count and decrement the uncaught exception count, which in my opinion do not matter much given that we are about to terminate the program anyway. Also it does not affect info like stack traces that can be possibly shown to developers. And while we use a variant of Itanium EH ABI, we can make some deviations if we choose to; we are already different in that in the current version of the EH spec we don't support two-phase unwinding. We can possibly consider a more complicated transformation later to reenable this, but I don't think that has high priority. Changes in this CL contains: - In Clang, we don't generate a call to `wasm.get.exception()` intrinsic and `__clang_call_terminate` function in terminate pads anymore; we simply generate calls to `std::terminate()`, which is the default implementation of `CGCXXABI::emitTerminateForUnexpectedException`. - Remove `WebAssembly::ensureSingleBBTermPads() function and `WebAssemblyHandleEHTerminatePads` pass, because terminate pads are already `catch_all` now (because they don't need the exception pointer) and we don't need these transformations anymore. - Change tests to use `std::terminate` directly. Also removes tests that tested `LateEHPrepare::ensureSingleBBTermPads` and `HandleEHTerminatePads` pass. - Drive-by fix: Add some function attributes to EH intrinsic declarations Fixes https://github.com/emscripten-core/emscripten/issues/13582. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D97834
2021-03-03 07:05:30 +08:00
// Cleanup pads do not have any of wasm.get.exception() or
// wasm.get.ehselector() calls. We need to do nothing.
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
if (!GetExnCI) {
assert(!GetSelectorCI &&
"wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
return;
}
// Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
// be lowered to wasm 'catch' instruction. We do this mainly because
[WebAssembly] Update WasmEHPrepare for the new spec Clang generates `wasm.get.exception` and `wasm.get.ehselector` intrinsics, which respectively return a caught exception value (a pointer to some C++ exception struct) and a selector (an integer value that tells which C++ `catch` clause the current exception matches, or does not match any). WasmEHPrepare is a pass that does some IR-level preparation before instruction selection. Previously one of things we did in this pass was to convert `wasm.get.exception` intrinsic calls to `wasm.extract.exception` intrinsics. Their semantics were the same except `wasm.extract.exception` did not have a token argument. We maintained these two separate intrinsics with the same semantics because instruction selection couldn't handle token arguments. This `wasm.extract.exception` intrinsic was later converted to `extract_exception` instruction in instruction selection, which was a pseudo instruction to implement `br_on_exn`. Because `br_on_exn` pushed an extracted value onto the value stack after the `end` instruction of a `block`, but LLVM does not have a way of modeling that kind of behavior, so this pseudo instruction was used to pull an extracted value out of thin air, like this: ``` block $l0 ... br_on_exn $cpp_exception $l0 ... end extract_exception ;; pushes values onto the stack ``` In the new spec, we don't need this pseudo instruction anymore because `catch` itself returns a value and we don't have `br_on_exn` anymore. In the spec `catch` returns multiple values (like `br_on_exn`), but here we assume it only returns a single i32, which is sufficient to support C++. So this renames `wasm.get.exception` intrinsic to `wasm.catch`. Because this CL does not yet contain instruction selection for `wasm.catch` intrinsic, all `RUN` lines in exception.ll, eh-lsda.ll, and cfg-stackify-eh.ll, and a single `RUN` line in wasm-eh.cpp (which is an end-to-end test from C++ source to assembly) fail. So this CL temporarily disables those `RUN` lines, and for those test files without any valid remaining `RUN` lines, adds a dummy `RUN` line to make them pass. These tests will be reenabled in later CLs. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D94039
2020-12-26 10:38:59 +08:00
// instruction selection cannot handle wasm.get.exception intrinsic's token
// argument.
Instruction *CatchCI =
IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
GetExnCI->replaceAllUsesWith(CatchCI);
GetExnCI->eraseFromParent();
// In case it is a catchpad with single catch (...) or a cleanuppad, we don't
// need to call personality function because we don't need a selector.
[WebAssembly] Fix wasm.lsda() optimization in WasmEHPrepare Summary: When we insert a call to the personality function wrapper (`_Unwind_CallPersonality`) for a catch pad, we store some necessary info in `__wasm_lpad_context` struct and pass it. One of the info is the LSDA address for the function. For this, we insert a call to `wasm.lsda()`, which will be lowered down to the address of LSDA, and store it in a field in `__wasm_lpad_context`. There are exceptions to this personality call insertion: catchpads for `catch (...)` and cleanuppads (for destructors) don't need personality function calls, because we don't need to figure out whether the current exception should be caught or not. (They always should.) There was a little optimization to `wasm.lsda()` call insertion. Because the LSDA address is the same throughout a function, we don't need to insert a store of `wasm.lsda()` return value in every catchpad. For example: ``` try { foo(); } catch (int) { // wasm.lsda() call and a store are inserted here, like, in // pseudocode, // %lsda = wasm.lsda(); // store %lsda to a field in __wasm_lpad_context try { foo(); } catch (int) { // We don't need to insert the wasm.lsda() and store again, because // to arrive here, we have already stored the LSDA address to // __wasm_lpad_context in the outer catch. } } ``` So the previous algorithm checked if the current catch has a parent EH pad, we didn't insert a call to `wasm.lsda()` and its store. But this was incorrect, because what if the outer catch is `catch (...)` or a cleanuppad? ``` try { foo(); } catch (...) { // wasm.lsda() call and a store are NOT inserted here try { foo(); } catch (int) { // We need wasm.lsda() here! } } ``` In this case we need to insert `wasm.lsda()` in the inner catchpad, because the outer catchpad does not have one. To minimize the number of inserted `wasm.lsda()` calls and stores, we need a way to figure out whether we have encountered `wasm.lsda()` call in any of EH pads that dominates the current EH pad. To figure that out, we now visit EH pads in BFS order in the dominator tree so that we visit parent BBs first before visiting its child BBs in the domtree. We keep a set named `ExecutedLSDA`, which basically means "Do we have `wasm.lsda()` either in the current EH pad or any of its parent EH pads in the dominator tree?". This is to prevent scanning the domtree up to the root in the worst case every time we examine an EH pad: each EH pad only needs to examine its immediate parent EH pad. - If any of its parent EH pads in the domtree has `wasm.lsda()`, this means we don't need `wasm.lsda()` in the current EH pad. We also insert the current EH pad in `ExecutedLSDA` set. - If none of its parent EH pad has `wasm.lsda()` - If the current EH pad is a `catch (...)` or a cleanuppad, done. - If the current EH pad is neither a `catch (...)` nor a cleanuppad, add `wasm.lsda()` and the store in the current EH pad, and add the current EH pad to `ExecutedLSDA` set. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77423
2020-04-01 07:08:01 +08:00
if (!NeedPersonality) {
if (GetSelectorCI) {
assert(GetSelectorCI->use_empty() &&
"wasm.get.ehselector() still has uses!");
GetSelectorCI->eraseFromParent();
}
return;
}
[WebAssembly] Update WasmEHPrepare for the new spec Clang generates `wasm.get.exception` and `wasm.get.ehselector` intrinsics, which respectively return a caught exception value (a pointer to some C++ exception struct) and a selector (an integer value that tells which C++ `catch` clause the current exception matches, or does not match any). WasmEHPrepare is a pass that does some IR-level preparation before instruction selection. Previously one of things we did in this pass was to convert `wasm.get.exception` intrinsic calls to `wasm.extract.exception` intrinsics. Their semantics were the same except `wasm.extract.exception` did not have a token argument. We maintained these two separate intrinsics with the same semantics because instruction selection couldn't handle token arguments. This `wasm.extract.exception` intrinsic was later converted to `extract_exception` instruction in instruction selection, which was a pseudo instruction to implement `br_on_exn`. Because `br_on_exn` pushed an extracted value onto the value stack after the `end` instruction of a `block`, but LLVM does not have a way of modeling that kind of behavior, so this pseudo instruction was used to pull an extracted value out of thin air, like this: ``` block $l0 ... br_on_exn $cpp_exception $l0 ... end extract_exception ;; pushes values onto the stack ``` In the new spec, we don't need this pseudo instruction anymore because `catch` itself returns a value and we don't have `br_on_exn` anymore. In the spec `catch` returns multiple values (like `br_on_exn`), but here we assume it only returns a single i32, which is sufficient to support C++. So this renames `wasm.get.exception` intrinsic to `wasm.catch`. Because this CL does not yet contain instruction selection for `wasm.catch` intrinsic, all `RUN` lines in exception.ll, eh-lsda.ll, and cfg-stackify-eh.ll, and a single `RUN` line in wasm-eh.cpp (which is an end-to-end test from C++ source to assembly) fail. So this CL temporarily disables those `RUN` lines, and for those test files without any valid remaining `RUN` lines, adds a dummy `RUN` line to make them pass. These tests will be reenabled in later CLs. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D94039
2020-12-26 10:38:59 +08:00
IRB.SetInsertPoint(CatchCI->getNextNode());
// This is to create a map of <landingpad EH label, landingpad index> in
// SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
// Pseudocode: wasm.landingpad.index(Index);
IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
// Pseudocode: __wasm_lpad_context.lpad_index = index;
IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
auto *CPI = cast<CatchPadInst>(FPI);
[WebAssembly] Disable wasm.lsda() optimization in WasmEHPrepare In every catchpad except `catch (...)`, we add a call to `_Unwind_CallPersonality`, which is a wapper to call the personality function. (In most of other Itanium-based architectures the call is done from libunwind, but in wasm we don't have the control over the VM.) Because the personatlity function is called to figure out whether the current exception is a type we should catch, such as `int` or `SomeClass&`, `catch (...)` does not need the personality function call. For the same reason, all cleanuppads don't need it. When we call `_Unwind_CallPersonality`, we store some necessary info in a data structure called `__wasm_lpad_context` of type `_Unwind_LandingPadContext`, which is defined in the wasm's port of libunwind in Emscripten. Also the personality wrapper function returns some info (selector and the caught pointer) in that data structure, so it is used as a medium for communication. One of the info we need to store is the address for LSDA info for the current function. `wasm.lsda()` intrinsic returns that address. (This intrinsic will be lowered to a symbol that points to the LSDA address.) The simpliest thing is call `wasm.lsda()` every time we need to call `_Unwind_CallPersonality` and store that info in `__wasm_lpad_context` data structure. But we tried to be better than that (D77423 and some more previous CLs), so if catchpad A dominates catchpad B and catchpad A is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B, thinking that the LSDA address is the same for a single function and we already visited catchpad A and `__wasm_lpad_context.lsda` field would already have that value. But this can be incorrect if there is a call to another function, which also can have the personality function and LSDA, between catchpad A and catchpad B, because `__wasm_lpad_context` is a globally defined structure and the callee function will overwrite its `lsda` field. So in this CL we don't try to do any optimizaions on adding `wasm.lsda()` call; we store the result of `wasm.lsda()` every time we call `_Unwind_CallPersonality`. We can do some complicated analysis, like checking if there is a function call between the dominating catchpad and the current catchpad, but at this time it seems overkill. This deletes three tests because they all tested `wasm.ldsa()` call optimization. Fixes https://github.com/emscripten-core/emscripten/issues/13548. Reviewed By: tlively Differential Revision: https://reviews.llvm.org/D97309
2021-02-24 03:00:11 +08:00
// TODO Sometimes storing the LSDA address every time is not necessary, in
// case it is already set in a dominating EH pad and there is no function call
// between from that EH pad to here. Consider optimizing those cases.
// Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
// Pseudocode: _Unwind_CallPersonality(exn);
[WebAssembly] Update WasmEHPrepare for the new spec Clang generates `wasm.get.exception` and `wasm.get.ehselector` intrinsics, which respectively return a caught exception value (a pointer to some C++ exception struct) and a selector (an integer value that tells which C++ `catch` clause the current exception matches, or does not match any). WasmEHPrepare is a pass that does some IR-level preparation before instruction selection. Previously one of things we did in this pass was to convert `wasm.get.exception` intrinsic calls to `wasm.extract.exception` intrinsics. Their semantics were the same except `wasm.extract.exception` did not have a token argument. We maintained these two separate intrinsics with the same semantics because instruction selection couldn't handle token arguments. This `wasm.extract.exception` intrinsic was later converted to `extract_exception` instruction in instruction selection, which was a pseudo instruction to implement `br_on_exn`. Because `br_on_exn` pushed an extracted value onto the value stack after the `end` instruction of a `block`, but LLVM does not have a way of modeling that kind of behavior, so this pseudo instruction was used to pull an extracted value out of thin air, like this: ``` block $l0 ... br_on_exn $cpp_exception $l0 ... end extract_exception ;; pushes values onto the stack ``` In the new spec, we don't need this pseudo instruction anymore because `catch` itself returns a value and we don't have `br_on_exn` anymore. In the spec `catch` returns multiple values (like `br_on_exn`), but here we assume it only returns a single i32, which is sufficient to support C++. So this renames `wasm.get.exception` intrinsic to `wasm.catch`. Because this CL does not yet contain instruction selection for `wasm.catch` intrinsic, all `RUN` lines in exception.ll, eh-lsda.ll, and cfg-stackify-eh.ll, and a single `RUN` line in wasm-eh.cpp (which is an end-to-end test from C++ source to assembly) fail. So this CL temporarily disables those `RUN` lines, and for those test files without any valid remaining `RUN` lines, adds a dummy `RUN` line to make them pass. These tests will be reenabled in later CLs. Reviewed By: dschuff, tlively Differential Revision: https://reviews.llvm.org/D94039
2020-12-26 10:38:59 +08:00
CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
OperandBundleDef("funclet", CPI));
PersCI->setDoesNotThrow();
// Pseudocode: int selector = __wasm_lpad_context.selector;
Instruction *Selector =
IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
// Replace the return value from wasm.get.ehselector() with the selector value
// loaded from __wasm_lpad_context.selector.
assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
GetSelectorCI->replaceAllUsesWith(Selector);
GetSelectorCI->eraseFromParent();
}
void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
[WebAssembly] Exception handling: Switch to the new proposal Summary: This switches the EH implementation to the new proposal: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md (The previous proposal was https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions.md) - Instruction changes - Now we have one single `catch` instruction that returns a except_ref value - `throw` now can take variable number of operations - `rethrow` does not have 'depth' argument anymore - `br_on_exn` queries an except_ref to see if it matches the tag and branches to the given label if true. - `extract_exception` is a pseudo instruction that simulates popping values from wasm stack. This is to make `br_on_exn`, a very special instruction, work: `br_on_exn` puts values onto the stack only if it is taken, and the # of values can vay depending on the tag. - Now there's only one `catch` per `try`, this patch removes all special handling for terminate pad with a call to `__clang_call_terminate`. Before it was the only case there are two catch clauses (a normal `catch` and `catch_all` per `try`). - Make `rethrow` act as a terminator like `throw`. This splits BB after `rethrow` in WasmEHPrepare, and deletes an unnecessary `unreachable` after `rethrow` in LateEHPrepare. - Now we stop at all catchpads (because we add wasm `catch` instruction that catches all exceptions), this creates new `findWasmUnwindDestinations` function in SelectionDAGBuilder. - Now we use `br_on_exn` instrution to figure out if an except_ref matches the current tag or not, LateEHPrepare generates this sequence for catch pads: ``` catch block i32 br_on_exn $__cpp_exception end_block extract_exception ``` - Branch analysis for `br_on_exn` in WebAssemblyInstrInfo - Other various misc. changes to switch to the new proposal. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D57134 llvm-svn: 352598
2019-01-30 11:21:57 +08:00
// If an exception is not caught by a catchpad (i.e., it is a foreign
// exception), it will unwind to its parent catchswitch's unwind destination.
// We don't record an unwind destination for cleanuppads because every
// exception should be caught by it.
for (const auto &BB : *F) {
if (!BB.isEHPad())
continue;
const Instruction *Pad = BB.getFirstNonPHI();
if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
if (!UnwindBB)
continue;
const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
// Currently there should be only one handler per a catchswitch.
EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
else // cleanuppad
EHInfo.setUnwindDest(&BB, UnwindBB);
}
}
}