Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1379,12 +1379,16 @@ auto Pers = classifyEHPersonality(FuncInfo.Fn->getPersonalityFn()); bool IsMSVCCXX = Pers == EHPersonality::MSVC_CXX; bool IsCoreCLR = Pers == EHPersonality::CoreCLR; + bool IsWasmCXX = Pers == EHPersonality::Wasm_CXX; MachineBasicBlock *CatchPadMBB = FuncInfo.MBB; // In MSVC C++ and CoreCLR, catchblocks are funclets and need prologues. - if (IsMSVCCXX || IsCoreCLR) + if (IsMSVCCXX || IsCoreCLR || IsWasmCXX) CatchPadMBB->setIsEHFuncletEntry(); - DAG.setRoot(DAG.getNode(ISD::CATCHPAD, getCurSDLoc(), MVT::Other, getControlRoot())); + // Wasm does not need catchpads anymore + if (!IsWasmCXX) + DAG.setRoot(DAG.getNode(ISD::CATCHPAD, getCurSDLoc(), MVT::Other, + getControlRoot())); } void SelectionDAGBuilder::visitCatchRet(const CatchReturnInst &I) { @@ -1449,6 +1453,7 @@ classifyEHPersonality(FuncInfo.Fn->getPersonalityFn()); bool IsMSVCCXX = Personality == EHPersonality::MSVC_CXX; bool IsCoreCLR = Personality == EHPersonality::CoreCLR; + bool IsWasmCXX = Personality == EHPersonality::Wasm_CXX; while (EHPadBB) { const Instruction *Pad = EHPadBB->getFirstNonPHI(); @@ -1468,7 +1473,7 @@ for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) { UnwindDests.emplace_back(FuncInfo.MBBMap[CatchPadBB], Prob); // For MSVC++ and the CLR, catchblocks are funclets and need prologues. - if (IsMSVCCXX || IsCoreCLR) + if (IsMSVCCXX || IsCoreCLR || IsWasmCXX) UnwindDests.back().first->setIsEHFuncletEntry(); } NewEHPadBB = CatchSwitch->getUnwindDest(); @@ -6164,6 +6169,13 @@ HasTailCall = true; return nullptr; } + + case Intrinsic::wasm_landingpad_index: { + // TODO store landing pad index in a map, which will be used when generating + // LSDA information + return nullptr; + } + } } @@ -6313,7 +6325,10 @@ DAG.setRoot(DAG.getEHLabel(getCurSDLoc(), getRoot(), EndLabel)); // Inform MachineModuleInfo of range. - if (MF.hasEHFunclets()) { + auto Pers = classifyEHPersonality(FuncInfo.Fn->getPersonalityFn()); + // There is a platform (e.g. wasm) that uses funclet style IR but does not + // actually use outlined funclets and their LSDA info style. + if (MF.hasEHFunclets() && isFuncletEHPersonality(Pers)) { assert(CLI.CS); WinEHFuncInfo *EHInfo = DAG.getMachineFunction().getWinEHFuncInfo(); EHInfo->addIPToStateRange(cast(CLI.CS.getInstruction()), Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -39,6 +39,7 @@ WebAssemblyRegColoring.cpp WebAssemblyRegNumbering.cpp WebAssemblyRegStackify.cpp + WebAssemblyReplaceFuncletReturns.cpp WebAssemblyReplacePhysRegs.cpp WebAssemblyRuntimeLibcallSignatures.cpp WebAssemblySelectionDAGInfo.cpp Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -47,6 +47,7 @@ FunctionPass *createWebAssemblyExplicitLocals(); FunctionPass *createWebAssemblyFixIrreducibleControlFlow(); FunctionPass *createWebAssemblyCFGSort(); +FunctionPass *createWebAssemblyReplaceFuncletReturns(); FunctionPass *createWebAssemblyCFGStackify(); FunctionPass *createWebAssemblyLowerBrUnless(); FunctionPass *createWebAssemblyRegNumbering(); @@ -69,6 +70,7 @@ void initializeWebAssemblyExplicitLocalsPass(PassRegistry &); void initializeWebAssemblyFixIrreducibleControlFlowPass(PassRegistry &); void initializeWebAssemblyCFGSortPass(PassRegistry &); +void initializeWebAssemblyReplaceFuncletReturnsPass(PassRegistry &); void initializeWebAssemblyCFGStackifyPass(PassRegistry &); void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &); void initializeWebAssemblyRegNumberingPass(PassRegistry &); Index: lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -145,9 +145,6 @@ std::prev(InsertPos)->getOpcode() != WebAssembly::END_LOOP) --InsertPos; } - // The header block in which a 'block' mark will be inserted should have a - // terminator because it is branching to a non-layout successor. - assert(InsertPos != Header->end()); // Add the BLOCK. MachineInstr *Begin = Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -90,6 +90,7 @@ SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -151,6 +151,9 @@ // Trap lowers to wasm unreachable setOperationAction(ISD::TRAP, MVT::Other, Legal); + // Exception handling intrinsics + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + setMaxAtomicSizeInBitsSupported(64); } @@ -737,6 +740,8 @@ return LowerFRAMEADDR(Op, DAG); case ISD::CopyToReg: return LowerCopyToReg(Op, DAG); + case ISD::INTRINSIC_WO_CHAIN: + return LowerINTRINSIC_WO_CHAIN(Op, DAG); } } @@ -869,6 +874,21 @@ MachinePointerInfo(SV), 0); } +SDValue +WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = cast(Op.getOperand(0))->getZExtValue(); + SDLoc DL(Op); + switch (IntNo) { + default: + return {}; // Don't custom lower most intrinsics. + + case Intrinsic::wasm_lsda: + // TODO For now, just return 0 not to crash + return DAG.getConstant(0, DL, Op.getValueType()); + } +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -140,9 +140,25 @@ def END_TRY : I<(outs), (ins), [], "end_try", 0x0b>; } // Uses = [VALUE_STACK], Defs = [VALUE_STACK] -} // Defs = [ARGUMENTS] +// Catching an exception: catch / catch_all +let hasCtrlDep = 1 in { +def CATCH_I32 : I<(outs I32:$dst), (ins i32imm:$tag), + [(set I32:$dst, (int_wasm_catch imm:$tag))], + "i32.catch \t$dst, $tag", 0x07>; +def CATCH_I64 : I<(outs I64:$dst), (ins i32imm:$tag), + [(set I64:$dst, (int_wasm_catch imm:$tag))], + "i64.catch \t$dst, $tag", 0x07>; +def CATCH_ALL : I<(outs), (ins), [], "catch_all", 0x05>; +} -// rethrow takes a relative depth as an argument, for which currently only 0 is -// possible for C++. Once other languages need depths other than 0, depths will -// be computed in CFGStackify. -def : Pat<(int_wasm_rethrow), (RETHROW 0)>; +// Pseudo instructions: cleanupret / catchret +// They are not return instructions in wasm, but setting 'isReturn' to true as +// in X86 is necessary for computing funclet membership. +let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, + isCodeGenOnly = 1, isReturn = 1 in { + def CLEANUPRET : I<(outs), (ins), [(cleanupret)], "", 0>; + def CATCHRET : I<(outs), (ins bb_op:$dst, bb_op:$from), + [(catchret bb:$dst, bb:$from)], "", 0>; +} + +} // Defs = [ARGUMENTS] Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -30,7 +30,8 @@ WebAssemblyInstrInfo::WebAssemblyInstrInfo(const WebAssemblySubtarget &STI) : WebAssemblyGenInstrInfo(WebAssembly::ADJCALLSTACKDOWN, - WebAssembly::ADJCALLSTACKUP), + WebAssembly::ADJCALLSTACKUP, + WebAssembly::CATCHRET), RI(STI.getTargetTriple()) {} bool WebAssemblyInstrInfo::isReallyTriviallyReMaterializable( Index: lib/Target/WebAssembly/WebAssemblyRegStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -160,10 +160,9 @@ // and/or uses the stack pointer value. static void Query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read, bool &Write, bool &Effects, bool &StackPointer) { - assert(!MI.isPosition()); assert(!MI.isTerminator()); - if (MI.isDebugInstr()) + if (MI.isDebugInstr() || MI.isPosition()) return; // Check for loads. Index: lib/Target/WebAssembly/WebAssemblyRegisterInfo.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegisterInfo.h +++ lib/Target/WebAssembly/WebAssemblyRegisterInfo.h @@ -45,6 +45,7 @@ const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override; + const uint32_t *getNoPreservedMask() const override { return nullptr; } }; } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyReplaceFuncletReturns.cpp =================================================================== --- /dev/null +++ lib/Target/WebAssembly/WebAssemblyReplaceFuncletReturns.cpp @@ -0,0 +1,80 @@ +//===--- WebAssemblyReplaceFuncletReturns.cpp - Replace Funclet Returns ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Replaces catchret/cleanupret instructions with branches and rethrows. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssembly.h" +#include "WebAssemblySubtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +using namespace llvm; + +#define DEBUG_TYPE "wasm-replace-funclet-returns" + +namespace { +class WebAssemblyReplaceFuncletReturns final : public MachineFunctionPass { + StringRef getPassName() const override { + return "WebAssembly Replace Funclet Returns"; + } + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyReplaceFuncletReturns() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char WebAssemblyReplaceFuncletReturns::ID = 0; +INITIALIZE_PASS(WebAssemblyReplaceFuncletReturns, DEBUG_TYPE, + "WebAssembly Replace Funclet Returns", false, false) + +FunctionPass *llvm::createWebAssemblyReplaceFuncletReturns() { + return new WebAssemblyReplaceFuncletReturns(); +} + +bool WebAssemblyReplaceFuncletReturns::runOnMachineFunction( + MachineFunction &MF) { + if (!MF.getFunction().hasPersonalityFn()) + return false; + + bool Changed = false; + const auto &TII = *MF.getSubtarget().getInstrInfo(); + + for (auto &MBB : MF) { + auto Pos = MBB.getFirstTerminator(); + if (Pos == MBB.end()) + continue; + MachineInstr *TI = &*Pos; + + switch (TI->getOpcode()) { + case WebAssembly::CATCHRET: { + // Replace a catchret with a branch + MachineBasicBlock *TBB = TI->getOperand(0).getMBB(); + if (!MBB.isLayoutSuccessor(TBB)) + BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR)) + .addMBB(TBB); + TI->eraseFromParent(); + Changed = true; + break; + } + case WebAssembly::CLEANUPRET: { + // Replace a cleanupret with a rethrow + BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) + .addImm(0); + TI->eraseFromParent(); + Changed = true; + break; + } + } + } + return Changed; +} Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -66,6 +66,7 @@ initializeWebAssemblyExplicitLocalsPass(PR); initializeWebAssemblyFixIrreducibleControlFlowPass(PR); initializeWebAssemblyCFGSortPass(PR); + initializeWebAssemblyReplaceFuncletReturnsPass(PR); initializeWebAssemblyCFGStackifyPass(PR); initializeWebAssemblyLowerBrUnlessPass(PR); initializeWebAssemblyRegNumberingPass(PR); @@ -324,6 +325,9 @@ // BLOCK and LOOP markers. addPass(createWebAssemblyCFGSort()); + // Replace funclet return instructions with branches and rethrows + addPass(createWebAssemblyReplaceFuncletReturns()); + // Insert BLOCK and LOOP markers. addPass(createWebAssemblyCFGStackify()); Index: test/CodeGen/WebAssembly/exception.ll =================================================================== --- test/CodeGen/WebAssembly/exception.ll +++ test/CodeGen/WebAssembly/exception.ll @@ -1,22 +1,89 @@ -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -exception-model=wasm | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +%struct.Cleanup = type { i8 } + +@_ZTIi = external constant i8* + declare void @llvm.wasm.throw(i32, i8*) -declare void @llvm.wasm.rethrow() -; CHECK-LABEL: throw: +; CHECK-LABEL: test_throw: ; CHECK-NEXT: i32.const $push0=, 0 ; CHECK-NEXT: throw 0, $pop0 -define void @throw() { +define void @test_throw() { call void @llvm.wasm.throw(i32 0, i8* null) ret void } -; CHECK-LABEL: rethrow: -; CHECK-NEXT: rethrow 0 -define void @rethrow() { - call void @llvm.wasm.rethrow() +; CHECK-LABEL: test_catch: +; CHECK: call foo@FUNCTION +; CHECK: i32.catch $push{{.+}}=, 0 +; CHECK-DAG: i32.store __wasm_lpad_context +; CHECK-DAG: i32.store __wasm_lpad_context+4 +; CHECK: i32.call $push{{.+}}=, _Unwind_CallPersonality@FUNCTION +; CHECK: i32.call $push{{.+}}=, __cxa_begin_catch@FUNCTION +; CHECK: call __cxa_end_catch@FUNCTION +; CHECK: call __cxa_rethrow@FUNCTION +define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +entry: + invoke void @foo() + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchswitch within none [label %catch.start] unwind to caller + +catch.start: ; preds = %catch.dispatch + %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] + %2 = call i8* @llvm.wasm.get.exception() + %3 = call i32 @llvm.wasm.get.ehselector() + %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches = icmp eq i32 %3, %4 + br i1 %matches, label %catch, label %rethrow + +catch: ; preds = %catch.start + %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] + call void @__cxa_end_catch() [ "funclet"(token %1) ] + catchret from %1 to label %try.cont + +rethrow: ; preds = %catch.start + call void @__cxa_rethrow() [ "funclet"(token %1) ] + unreachable + +try.cont: ; preds = %entry, %catch ret void } + +; CHECK-LABEL: test_cleanup: +; CHECK: call foo@FUNCTION +; CHECK: return +; CHECK: i32.call $push20=, _ZN7CleanupD1Ev@FUNCTION +; CHECK: rethrow 0 +define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +entry: + %c = alloca %struct.Cleanup, align 1 + invoke void @foo() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + %call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) + ret void + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + %call1 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ] + cleanupret from %0 unwind to caller +} + +declare void @foo() +declare void @func(i32) +declare i32 @__gxx_wasm_personality_v0(...) +declare i8* @llvm.wasm.get.exception() +declare i32 @llvm.wasm.get.ehselector() +declare i32 @llvm.eh.typeid.for(i8*) +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() +declare void @__cxa_rethrow() +declare void @__clang_call_terminate(i8*) +declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)