Index: include/llvm/CodeGen/MachineFunction.h =================================================================== --- include/llvm/CodeGen/MachineFunction.h +++ include/llvm/CodeGen/MachineFunction.h @@ -316,6 +316,9 @@ /// Map a landing pad's EH symbol to the call site indexes. DenseMap> LPadToCallSiteMap; + /// Map a landing pad to its index. + DenseMap WasmLPadToIndexMap; + /// Map of invoke call site index values to associated begin EH_LABEL. DenseMap CallSiteMap; @@ -810,7 +813,8 @@ LandingPadInfo &getOrCreateLandingPadInfo(MachineBasicBlock *LandingPad); /// Remap landing pad labels and remove any deleted landing pads. - void tidyLandingPads(DenseMap *LPMap = nullptr); + void tidyLandingPads(DenseMap *LPMap = nullptr, + bool TidyIfNoBeginLabels = true); /// Return a reference to the landing pad info for the current function. const std::vector &getLandingPads() const { @@ -853,6 +857,22 @@ /// Map the landing pad's EH symbol to the call site indexes. void setCallSiteLandingPad(MCSymbol *Sym, ArrayRef Sites); + /// Map the landing pad to its index. Used for Wasm exception handling. + void setWasmLandingPadIndex(const MachineBasicBlock *LPad, unsigned Index) { + WasmLPadToIndexMap[LPad] = Index; + } + + /// Returns true if the landing pad has an associate index in wasm EH. + bool hasWasmLandingPadIndex(const MachineBasicBlock *LPad) const { + return WasmLPadToIndexMap.count(LPad); + } + + /// Get the index in wasm EH for a given landing pad. + unsigned getWasmLandingPadIndex(const MachineBasicBlock *LPad) const { + assert(hasWasmLandingPadIndex(LPad)); + return WasmLPadToIndexMap.lookup(LPad); + } + /// Get the call site indexes for a landing pad EH symbol. SmallVectorImpl &getCallSiteLandingPad(MCSymbol *Sym) { assert(hasCallSiteLandingPad(Sym) && Index: include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- include/llvm/IR/IntrinsicsWebAssembly.td +++ include/llvm/IR/IntrinsicsWebAssembly.td @@ -60,7 +60,8 @@ // WebAssembly EH must maintain the landingpads in the order assigned to them // by WasmEHPrepare pass to generate landingpad table in EHStreamer. This is // used in order to give them the indices in WasmEHPrepare. -def int_wasm_landingpad_index: Intrinsic<[], [llvm_i32_ty], [IntrNoMem]>; +def int_wasm_landingpad_index: Intrinsic<[], [llvm_token_ty, llvm_i32_ty], + [IntrNoMem]>; // Returns LSDA address of the current function. def int_wasm_lsda : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>; Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -16,6 +16,7 @@ #include "CodeViewDebug.h" #include "DwarfDebug.h" #include "DwarfException.h" +#include "WasmException.h" #include "WinCFGuard.h" #include "WinException.h" #include "llvm/ADT/APFloat.h" @@ -356,7 +357,7 @@ } break; case ExceptionHandling::Wasm: - // TODO to prevent warning + ES = new WasmException(this); break; } if (ES) Index: lib/CodeGen/AsmPrinter/CMakeLists.txt =================================================================== --- lib/CodeGen/AsmPrinter/CMakeLists.txt +++ lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -23,6 +23,7 @@ WinCFGuard.cpp WinException.cpp CodeViewDebug.cpp + WasmException.cpp DEPENDS intrinsics_gen Index: lib/CodeGen/AsmPrinter/EHStreamer.h =================================================================== --- lib/CodeGen/AsmPrinter/EHStreamer.h +++ lib/CodeGen/AsmPrinter/EHStreamer.h @@ -85,9 +85,10 @@ /// zero for the landing pad and the action. Calls marked 'nounwind' have /// no entry and must not be contained in the try-range of any entry - they /// form gaps in the table. Entries must be ordered by try-range address. - void computeCallSiteTable(SmallVectorImpl &CallSites, - const SmallVectorImpl &LandingPads, - const SmallVectorImpl &FirstActions); + virtual void computeCallSiteTable( + SmallVectorImpl &CallSites, + const SmallVectorImpl &LandingPads, + const SmallVectorImpl &FirstActions); /// Emit landing pads and actions. /// @@ -108,7 +109,9 @@ /// found the frame is unwound and handling continues. /// 3. Type id table contains references to all the C++ typeinfo for all /// catches in the function. This tables is reversed indexed base 1. - void emitExceptionTable(); + /// + /// Returns the starting symbol of an exception table. + MCSymbol *emitExceptionTable(); virtual void emitTypeInfos(unsigned TTypeEncoding, MCSymbol *TTBaseLabel); Index: lib/CodeGen/AsmPrinter/EHStreamer.cpp =================================================================== --- lib/CodeGen/AsmPrinter/EHStreamer.cpp +++ lib/CodeGen/AsmPrinter/EHStreamer.cpp @@ -344,7 +344,9 @@ /// unwound and handling continues. /// 3. Type ID table contains references to all the C++ typeinfo for all /// catches in the function. This tables is reverse indexed base 1. -void EHStreamer::emitExceptionTable() { +/// +/// Returns the starting symbol of an exception table. +MCSymbol *EHStreamer::emitExceptionTable() { const MachineFunction *MF = Asm->MF; const std::vector &TypeInfos = MF->getTypeInfos(); const std::vector &FilterIds = MF->getFilterIds(); @@ -374,6 +376,7 @@ computeCallSiteTable(CallSites, LandingPads, FirstActions); bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; + bool IsWasm = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::Wasm; unsigned CallSiteEncoding = IsSJLJ ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_uleb128; bool HaveTTData = !TypeInfos.empty() || !FilterIds.empty(); @@ -456,8 +459,8 @@ Asm->EmitLabelDifferenceAsULEB128(CstEndLabel, CstBeginLabel); Asm->OutStreamer->EmitLabel(CstBeginLabel); - // SjLj Exception handling - if (IsSJLJ) { + // SjLj / Wasm Exception handling + if (IsSJLJ || IsWasm) { unsigned idx = 0; for (SmallVectorImpl::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I, ++idx) { @@ -602,7 +605,12 @@ emitTypeInfos(TTypeEncoding, TTBaseLabel); } + // In wasm, we need to emit .size before the alignment, so we bail out here. + if (IsWasm) + return GCCETSym; + Asm->EmitAlignment(2); + return GCCETSym; } void EHStreamer::emitTypeInfos(unsigned TTypeEncoding, MCSymbol *TTBaseLabel) { Index: lib/CodeGen/AsmPrinter/WasmException.h =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WasmException.h @@ -0,0 +1,42 @@ +//===-- WasmException.h - Wasm Exception Framework -------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing WebAssembly exception info into asm +// files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_CODEGEN_ASMPRINTER_WASMEXCEPTION_H +#define LLVM_LIB_CODEGEN_ASMPRINTER_WASMEXCEPTION_H + +#include "EHStreamer.h" +#include "llvm/CodeGen/AsmPrinter.h" + +namespace llvm { + +class LLVM_LIBRARY_VISIBILITY WasmException : public EHStreamer { +public: + WasmException(AsmPrinter *A) : EHStreamer(A) {} + + void endModule() override {} + void beginFunction(const MachineFunction *MF) override {} + virtual void markFunctionEnd() override; + void endFunction(const MachineFunction *MF) override; + +protected: + // Compute the call site table for wasm EH. + void computeCallSiteTable( + SmallVectorImpl &CallSites, + const SmallVectorImpl &LandingPads, + const SmallVectorImpl &FirstActions) override; +}; + +} // End of namespace llvm + +#endif Index: lib/CodeGen/AsmPrinter/WasmException.cpp =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WasmException.cpp @@ -0,0 +1,82 @@ +//===-- CodeGen/AsmPrinter/WasmException.cpp - Wasm Exception Impl --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing WebAssembly exception info into asm +// files. +// +//===----------------------------------------------------------------------===// + +#include "WasmException.h" +#include "llvm/MC/MCStreamer.h" +using namespace llvm; + +void WasmException::markFunctionEnd() { + // Get rid of any dead landing pads. + if (!Asm->MF->getLandingPads().empty()) { + auto *NonConstMF = const_cast(Asm->MF); + // Wasm does not set BeginLabel and EndLabel information for landing pads, + // so we should set the second argument false. + NonConstMF->tidyLandingPads(nullptr, /* TidyIfNoBeginLabels */ false); + } +} + +void WasmException::endFunction(const MachineFunction *MF) { + bool ShouldEmitExceptionTable = false; + for (const auto &Info : MF->getLandingPads()) { + if (MF->hasWasmLandingPadIndex(Info.LandingPadBlock)) { + ShouldEmitExceptionTable = true; + break; + } + } + if (!ShouldEmitExceptionTable) + return; + MCSymbol *LSDALabel = emitExceptionTable(); + assert(LSDALabel && ".GCC_exception_table has not been emitted!"); + + // Wasm requires every data section symbol to have a .size set. So we emit an + // end marker and set the size as the difference between the start end the end + // marker. + MCSymbol *LSDAEndLabel = Asm->createTempSymbol("GCC_except_table_end"); + Asm->OutStreamer->EmitLabel(LSDAEndLabel); + MCContext &OutContext = Asm->OutStreamer->getContext(); + const MCExpr *SizeExp = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(LSDAEndLabel, OutContext), + MCSymbolRefExpr::create(LSDALabel, OutContext), OutContext); + Asm->OutStreamer->emitELFSize(LSDALabel, SizeExp); + Asm->EmitAlignment(2); +} + +// Compute the call-site table for wasm EH. Even though we use the same function +// name to share the common routines, a call site entry in the table corresponds +// to not a call site for possibly-throwing functions but a landing pad. In wasm +// EH the VM is responsible for stack unwinding. After an exception occurs and +// the stack is unwound, the control flow is transferred to wasm 'catch' +// instruction by the VM, after which the personality function is called from +// the compiler-generated code. Refer to WasmEHPrepare pass for more +// information. +void WasmException::computeCallSiteTable( + SmallVectorImpl &CallSites, + const SmallVectorImpl &LandingPads, + const SmallVectorImpl &FirstActions) { + MachineFunction &MF = *Asm->MF; + for (unsigned I = 0, N = LandingPads.size(); I < N; ++I) { + const LandingPadInfo *Info = LandingPads[I]; + MachineBasicBlock *LPad = Info->LandingPadBlock; + // We don't emit LSDA for single catch (...). + if (!MF.hasWasmLandingPadIndex(LPad)) + continue; + // Wasm EH must maintain the EH pads in the order assigned to them by the + // WasmEHPrepare pass. + unsigned LPadIndex = MF.getWasmLandingPadIndex(LPad); + CallSiteEntry Site = {nullptr, nullptr, Info, FirstActions[I]}; + if (CallSites.size() < LPadIndex + 1) + CallSites.resize(LPadIndex + 1); + CallSites[LPadIndex] = Site; + } +} Index: lib/CodeGen/MachineFunction.cpp =================================================================== --- lib/CodeGen/MachineFunction.cpp +++ lib/CodeGen/MachineFunction.cpp @@ -661,8 +661,11 @@ } } - } else if (isa(FirstI)) { - // TODO + } else if (const auto *CPI = dyn_cast(FirstI)) { + for (unsigned I = CPI->getNumArgOperands(); I != 0; --I) { + Value *TypeInfo = CPI->getArgOperand(I - 1)->stripPointerCasts(); + addCatchTypeInfo(LandingPad, dyn_cast(TypeInfo)); + } } else { assert(isa(FirstI) && "Invalid landingpad!"); @@ -687,7 +690,8 @@ LP.TypeIds.push_back(getFilterIDFor(IdsInFilter)); } -void MachineFunction::tidyLandingPads(DenseMap *LPMap) { +void MachineFunction::tidyLandingPads(DenseMap *LPMap, + bool TidyIfNoBeginLabels) { for (unsigned i = 0; i != LandingPads.size(); ) { LandingPadInfo &LandingPad = LandingPads[i]; if (LandingPad.LandingPadLabel && @@ -702,24 +706,25 @@ continue; } - for (unsigned j = 0, e = LandingPads[i].BeginLabels.size(); j != e; ++j) { - MCSymbol *BeginLabel = LandingPad.BeginLabels[j]; - MCSymbol *EndLabel = LandingPad.EndLabels[j]; - if ((BeginLabel->isDefined() || - (LPMap && (*LPMap)[BeginLabel] != 0)) && - (EndLabel->isDefined() || - (LPMap && (*LPMap)[EndLabel] != 0))) continue; - - LandingPad.BeginLabels.erase(LandingPad.BeginLabels.begin() + j); - LandingPad.EndLabels.erase(LandingPad.EndLabels.begin() + j); - --j; - --e; - } + if (TidyIfNoBeginLabels) { + for (unsigned j = 0, e = LandingPads[i].BeginLabels.size(); j != e; ++j) { + MCSymbol *BeginLabel = LandingPad.BeginLabels[j]; + MCSymbol *EndLabel = LandingPad.EndLabels[j]; + if ((BeginLabel->isDefined() || (LPMap && (*LPMap)[BeginLabel] != 0)) && + (EndLabel->isDefined() || (LPMap && (*LPMap)[EndLabel] != 0))) + continue; + + LandingPad.BeginLabels.erase(LandingPad.BeginLabels.begin() + j); + LandingPad.EndLabels.erase(LandingPad.EndLabels.begin() + j); + --j; + --e; + } - // Remove landing pads with no try-ranges. - if (LandingPads[i].BeginLabels.empty()) { - LandingPads.erase(LandingPads.begin() + i); - continue; + // Remove landing pads with no try-ranges. + if (LandingPads[i].BeginLabels.empty()) { + LandingPads.erase(LandingPads.begin() + i); + continue; + } } // If there is no landing pad, ensure that the list of typeids is empty. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6270,12 +6270,12 @@ return nullptr; } - case Intrinsic::wasm_landingpad_index: { - // TODO store landing pad index in a map, which will be used when generating - // LSDA information + case Intrinsic::wasm_landingpad_index: + // Information this intrinsic contained has been transferred to + // MachineFunction in SelectionDAGISel::PrepareEHLandingPad. We can safely + // delete it now. return nullptr; } - } } void SelectionDAGBuilder::visitConstrainedFPIntrinsic( @@ -6432,7 +6432,7 @@ WinEHFuncInfo *EHInfo = DAG.getMachineFunction().getWinEHFuncInfo(); EHInfo->addIPToStateRange(cast(CLI.CS.getInstruction()), BeginLabel, EndLabel); - } else { + } else if (!isScopedEHPersonality(Pers)) { MF.addInvoke(FuncInfo.MBBMap[EHPadBB], BeginLabel, EndLabel); } } Index: lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -27,6 +27,7 @@ #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -1137,44 +1138,71 @@ const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy(CurDAG->getDataLayout())); + auto Pers = classifyEHPersonality(PersonalityFn); + // Catchpads have one live-in register, which typically holds the exception // pointer or code. - if (const auto *CPI = dyn_cast(LLVMBB->getFirstNonPHI())) { - if (hasExceptionPointerOrCodeUser(CPI)) { - // Get or create the virtual register to hold the pointer or code. Mark - // the live in physreg and copy into the vreg. - MCPhysReg EHPhysReg = TLI->getExceptionPointerRegister(PersonalityFn); - assert(EHPhysReg && "target lacks exception pointer register"); - MBB->addLiveIn(EHPhysReg); - unsigned VReg = FuncInfo->getCatchPadExceptionPointerVReg(CPI, PtrRC); - BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), - TII->get(TargetOpcode::COPY), VReg) - .addReg(EHPhysReg, RegState::Kill); + if (isFuncletEHPersonality(Pers)) { + if (const auto *CPI = dyn_cast(LLVMBB->getFirstNonPHI())) { + if (hasExceptionPointerOrCodeUser(CPI)) { + // Get or create the virtual register to hold the pointer or code. Mark + // the live in physreg and copy into the vreg. + MCPhysReg EHPhysReg = TLI->getExceptionPointerRegister(PersonalityFn); + assert(EHPhysReg && "target lacks exception pointer register"); + MBB->addLiveIn(EHPhysReg); + unsigned VReg = FuncInfo->getCatchPadExceptionPointerVReg(CPI, PtrRC); + BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), + TII->get(TargetOpcode::COPY), VReg) + .addReg(EHPhysReg, RegState::Kill); + } } return true; } - if (!LLVMBB->isLandingPad()) - return true; - // Add a label to mark the beginning of the landing pad. Deletion of the // landing pad can thus be detected via the MachineModuleInfo. MCSymbol *Label = MF->addLandingPad(MBB); - // Assign the call site to the landing pad's begin label. - MF->setCallSiteLandingPad(Label, SDB->LPadToCallSiteMap[MBB]); - const MCInstrDesc &II = TII->get(TargetOpcode::EH_LABEL); BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), II) .addSym(Label); - // Mark exception register as live in. - if (unsigned Reg = TLI->getExceptionPointerRegister(PersonalityFn)) - FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC); + if (Pers == EHPersonality::Wasm_CXX) { + if (const auto *CPI = dyn_cast(LLVMBB->getFirstNonPHI())) { + // In case of single catch (...), we don't emit LSDA, so we don't need + // this information. + bool IsSingleCatchAllClause = + CPI->getNumArgOperands() == 1 && + cast(CPI->getArgOperand(0))->isNullValue(); + if (!IsSingleCatchAllClause) { + // Create a mapping from landing pad label to landing pad index. + bool IntrFound = false; + for (const User *U : CPI->users()) { + if (const auto *Call = dyn_cast(U)) { + Intrinsic::ID IID = Call->getIntrinsicID(); + if (IID == Intrinsic::wasm_landingpad_index) { + Value *IndexArg = Call->getArgOperand(1); + int Index = cast(IndexArg)->getZExtValue(); + MF->setWasmLandingPadIndex(MBB, Index); + IntrFound = true; + break; + } + } + } + assert(IntrFound && "wasm.landingpad.index intrinsic not found!"); + } + } - // Mark exception selector register as live in. - if (unsigned Reg = TLI->getExceptionSelectorRegister(PersonalityFn)) - FuncInfo->ExceptionSelectorVirtReg = MBB->addLiveIn(Reg, PtrRC); + } else { + // Assign the call site to the landing pad's begin label. + MF->setCallSiteLandingPad(Label, SDB->LPadToCallSiteMap[MBB]); + // Mark exception register as live in. + if (unsigned Reg = TLI->getExceptionPointerRegister(PersonalityFn)) + FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC); + // Mark exception selector register as live in. + if (unsigned Reg = TLI->getExceptionSelectorRegister(PersonalityFn)) + FuncInfo->ExceptionSelectorVirtReg = MBB->addLiveIn(Reg, PtrRC); + } return true; } Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1748,6 +1748,10 @@ void TargetLoweringObjectFileWasm::InitializeWasm() { StaticCtorSection = getContext().getWasmSection(".init_array", SectionKind::getData()); + + // We don't use PersonalityEncoding and LSDAEncoding because we don't emit + // .cfi directives. We use TTypeEncoding to encode typeinfo global variables. + TTypeEncoding = dwarf::DW_EH_PE_absptr; } MCSection *TargetLoweringObjectFileWasm::getStaticCtorSection( Index: lib/CodeGen/WasmEHPrepare.cpp =================================================================== --- lib/CodeGen/WasmEHPrepare.cpp +++ lib/CodeGen/WasmEHPrepare.cpp @@ -300,7 +300,7 @@ // This is to create a map of in // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables. // Pseudocode: wasm.landingpad.index(Index); - IRB.CreateCall(LPadIndexF, IRB.getInt32(Index)); + IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)}); // Pseudocode: __wasm_lpad_context.lpad_index = index; IRB.CreateStore(IRB.getInt32(Index), LPadIndexField); Index: lib/MC/MCObjectFileInfo.cpp =================================================================== --- lib/MC/MCObjectFileInfo.cpp +++ lib/MC/MCObjectFileInfo.cpp @@ -743,6 +743,12 @@ DwarfPubNamesSection = Ctx->getWasmSection(".debug_pubnames", SectionKind::getMetadata()); DwarfPubTypesSection = Ctx->getWasmSection(".debug_pubtypes", SectionKind::getMetadata()); + // Wasm use data section for LSDA. + // TODO Consider putting each function's exception table in a separate + // section, as in -function-sections, to facilitate lld's --gc-section. + LSDASection = Ctx->getWasmSection(".rodata.gcc_except_table", + SectionKind::getReadOnlyWithRel()); + // TODO: Define more sections. } Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -634,10 +634,12 @@ llvm_unreachable("The fill should be an assembler constant"); DataBytes.insert(DataBytes.end(), Fill->getValueSize() * NumValues, Fill->getValue()); + } else if (auto *LEB = dyn_cast(&Frag)) { + const SmallVectorImpl &Contents = LEB->getContents(); + DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); } else { const auto &DataFrag = cast(Frag); const SmallVectorImpl &Contents = DataFrag.getContents(); - DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); } } Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -21,6 +21,7 @@ #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/IR/DiagnosticInfo.h" @@ -961,8 +962,15 @@ 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()); + MachineFunction &MF = DAG.getMachineFunction(); + EVT VT = Op.getValueType(); + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout()); + auto &Context = MF.getMMI().getContext(); + MCSymbol *S = Context.getOrCreateSymbol(Twine("GCC_except_table") + + Twine(MF.getFunctionNumber())); + return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, + DAG.getMCSymbol(S, PtrVT)); } } Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -285,6 +285,8 @@ (CONST_I32 tglobaladdr:$addr)>; def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), (CONST_I32 texternalsym:$addr)>; +def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>; +def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>; //===----------------------------------------------------------------------===// // Additional sets of instructions. Index: lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -244,6 +244,13 @@ (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0, (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0); break; + case MachineOperand::MO_MCSymbol: + // This is currently used only for LSDA symbols (GCC_except_table), + // because global addresses or other external symbols are handled above. + assert(MO.getTargetFlags() == 0 && + "WebAssembly does not use target flags on MCSymbol"); + MCOp = LowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false); + break; } OutMI.addOperand(MCOp); Index: test/CodeGen/WebAssembly/eh-lsda.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/eh-lsda.ll @@ -0,0 +1,213 @@ +; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling | FileCheck -allow-deprecated-dag-overlap %s +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +@_ZTIi = external constant i8* +@_ZTIf = external constant i8* +@_ZTId = external constant i8* + +; Single catch (...) does not need an exception table. +; CHECK-LABEL: test0: +; CHECK-NOT: GCC_except_table +define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +entry: + invoke void @may_throw() + 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* null] + %2 = call i8* @llvm.wasm.get.exception(token %1) + %3 = call i32 @llvm.wasm.get.ehselector(token %1) + %4 = 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 + +try.cont: ; preds = %entry, %catch.start + ret void +} + +; Exception table generation + shared action test. +; There are three landing pads. The second landing pad should share action table +; entries with the first landing pad because they end with the same sequence +; (double -> ...). But the third landing table cannot share action table entries +; with others, so it should create its own entries. +; CHECK-LABEL: test1: +; CHECK: .section .rodata.gcc_except_table,"",@ +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: GCC_except_table[[START:[0-9]+]]: +; CHECK-NEXT: .Lexception0: +; CHECK-NEXT: .int8 255 # @LPStart Encoding = omit +; CHECK-NEXT: .int8 0 # @TType Encoding = absptr +; CHECK-NEXT: .uleb128 .Lttbase0-.Lttbaseref0 +; CHECK-NEXT: .Lttbaseref0: +; CHECK-NEXT: .int8 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 .Lcst_end0-.Lcst_begin0 +; CHECK-NEXT: .Lcst_begin0: +; CHECK-NEXT: .int8 0 # >> Call Site 0 << +; CHECK-NEXT: # On exception at call site 0 +; CHECK-NEXT: .int8 7 # Action: 4 +; CHECK-NEXT: .int8 1 # >> Call Site 1 << +; CHECK-NEXT: # On exception at call site 1 +; CHECK-NEXT: .int8 3 # Action: 2 +; CHECK-NEXT: .int8 2 # >> Call Site 2 << +; CHECK-NEXT: # On exception at call site 2 +; CHECK-NEXT: .int8 11 # Action: 6 +; CHECK-NEXT: .Lcst_end0: +; CHECK-NEXT: .int8 1 # >> Action Record 1 << +; CHECK-NEXT: # Catch TypeInfo 1 +; CHECK-NEXT: .int8 0 # No further actions +; CHECK-NEXT: .int8 2 # >> Action Record 2 << +; CHECK-NEXT: # Catch TypeInfo 2 +; CHECK-NEXT: .int8 125 # Continue to action 1 +; CHECK-NEXT: .int8 3 # >> Action Record 3 << +; CHECK-NEXT: # Catch TypeInfo 3 +; CHECK-NEXT: .int8 125 # Continue to action 2 +; CHECK-NEXT: .int8 4 # >> Action Record 4 << +; CHECK-NEXT: # Catch TypeInfo 4 +; CHECK-NEXT: .int8 125 # Continue to action 3 +; CHECK-NEXT: .int8 3 # >> Action Record 5 << +; CHECK-NEXT: # Catch TypeInfo 3 +; CHECK-NEXT: .int8 0 # No further actions +; CHECK-NEXT: .int8 4 # >> Action Record 6 << +; CHECK-NEXT: # Catch TypeInfo 4 +; CHECK-NEXT: .int8 125 # Continue to action 5 +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: # >> Catch TypeInfos << +; CHECK-NEXT: .int32 _ZTIi # TypeInfo 4 +; CHECK-NEXT: .int32 _ZTIf # TypeInfo 3 +; CHECK-NEXT: .int32 _ZTId # TypeInfo 2 +; CHECK-NEXT: .int32 0 # TypeInfo 1 +; CHECK-NEXT: .Lttbase0: +; CHECK-NEXT: .LGCC_except_table_end[[END:[0-9]+]]: +; CHECK-NEXT: .size GCC_except_table[[START]], .LGCC_except_table_end[[END]]-GCC_except_table[[START]] +; CHECK-NEXT: .p2align 2 +define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { +entry: + invoke void @may_throw() + 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*), i8* bitcast (i8** @_ZTIf to i8*), i8* bitcast (i8** @_ZTId to i8*), i8* null] + %2 = call i8* @llvm.wasm.get.exception(token %1) + %3 = call i32 @llvm.wasm.get.ehselector(token %1) + %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches = icmp eq i32 %3, %4 + br i1 %matches, label %catch10, label %catch.fallthrough + +catch10: ; preds = %catch.start + %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] + %6 = bitcast i8* %5 to i32* + %7 = load i32, i32* %6, align 4 + call void @__cxa_end_catch() [ "funclet"(token %1) ] + catchret from %1 to label %try.cont + +try.cont: ; preds = %entry, %catch, %catch4, %catch7, %catch10 + invoke void @may_throw() + to label %try.cont23 unwind label %catch.dispatch14 + +catch.dispatch14: ; preds = %try.cont + %8 = catchswitch within none [label %catch.start15] unwind to caller + +catch.start15: ; preds = %catch.dispatch14 + %9 = catchpad within %8 [i8* bitcast (i8** @_ZTId to i8*), i8* null] + %10 = call i8* @llvm.wasm.get.exception(token %9) + %11 = call i32 @llvm.wasm.get.ehselector(token %9) + %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) + %matches16 = icmp eq i32 %11, %12 + %13 = call i8* @__cxa_begin_catch(i8* %10) [ "funclet"(token %9) ] + br i1 %matches16, label %catch20, label %catch17 + +catch20: ; preds = %catch.start15 + %14 = bitcast i8* %13 to double* + %15 = load double, double* %14, align 8 + call void @__cxa_end_catch() [ "funclet"(token %9) ] + catchret from %9 to label %try.cont23 + +try.cont23: ; preds = %try.cont, %catch17, %catch20 + invoke void @may_throw() + to label %try.cont36 unwind label %catch.dispatch25 + +catch.dispatch25: ; preds = %try.cont23 + %16 = catchswitch within none [label %catch.start26] unwind to caller + +catch.start26: ; preds = %catch.dispatch25 + %17 = catchpad within %16 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTIf to i8*)] + %18 = call i8* @llvm.wasm.get.exception(token %17) + %19 = call i32 @llvm.wasm.get.ehselector(token %17) + %20 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) + %matches27 = icmp eq i32 %19, %20 + br i1 %matches27, label %catch33, label %catch.fallthrough28 + +catch33: ; preds = %catch.start26 + %21 = call i8* @__cxa_begin_catch(i8* %18) [ "funclet"(token %17) ] + %22 = bitcast i8* %21 to i32* + %23 = load i32, i32* %22, align 4 + call void @__cxa_end_catch() [ "funclet"(token %17) ] + catchret from %17 to label %try.cont36 + +catch.fallthrough28: ; preds = %catch.start26 + %24 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) + %matches29 = icmp eq i32 %19, %24 + br i1 %matches29, label %catch30, label %rethrow + +catch30: ; preds = %catch.fallthrough28 + %25 = call i8* @__cxa_begin_catch(i8* %18) [ "funclet"(token %17) ] + %26 = bitcast i8* %25 to float* + %27 = load float, float* %26, align 4 + call void @__cxa_end_catch() [ "funclet"(token %17) ] + catchret from %17 to label %try.cont36 + +rethrow: ; preds = %catch.fallthrough28 + call void @__cxa_rethrow() [ "funclet"(token %17) ] + unreachable + +try.cont36: ; preds = %try.cont23, %catch30, %catch33 + ret void + +catch17: ; preds = %catch.start15 + call void @__cxa_end_catch() [ "funclet"(token %9) ] + catchret from %9 to label %try.cont23 + +catch.fallthrough: ; preds = %catch.start + %28 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIf to i8*)) + %matches1 = icmp eq i32 %3, %28 + br i1 %matches1, label %catch7, label %catch.fallthrough2 + +catch7: ; preds = %catch.fallthrough + %29 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] + %30 = bitcast i8* %29 to float* + %31 = load float, float* %30, align 4 + call void @__cxa_end_catch() [ "funclet"(token %1) ] + catchret from %1 to label %try.cont + +catch.fallthrough2: ; preds = %catch.fallthrough + %32 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) + %matches3 = icmp eq i32 %3, %32 + %33 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] + br i1 %matches3, label %catch4, label %catch + +catch4: ; preds = %catch.fallthrough2 + %34 = bitcast i8* %33 to double* + %35 = load double, double* %34, align 8 + call void @__cxa_end_catch() [ "funclet"(token %1) ] + catchret from %1 to label %try.cont + +catch: ; preds = %catch.fallthrough2 + call void @__cxa_end_catch() [ "funclet"(token %1) ] + catchret from %1 to label %try.cont +} + +declare void @may_throw() +declare i32 @llvm.eh.typeid.for(i8*) +declare i8* @llvm.wasm.get.exception(token) +declare i32 @llvm.wasm.get.ehselector(token) +declare void @__cxa_rethrow() +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() +declare i32 @__gxx_wasm_personality_v0(...) Index: test/CodeGen/WebAssembly/wasmehprepare.ll =================================================================== --- test/CodeGen/WebAssembly/wasmehprepare.ll +++ test/CodeGen/WebAssembly/wasmehprepare.ll @@ -30,7 +30,7 @@ ; CHECK: catch.start: ; CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad ; CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.catch(i32 0) -; CHECK-NEXT: call void @llvm.wasm.landingpad.index(i32 0) +; CHECK-NEXT: call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0) ; CHECK-NEXT: store i32 0, i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0) ; CHECK-NEXT: %[[LSDA:.*]] = call i8* @llvm.wasm.lsda() ; CHECK-NEXT: store i8* %[[LSDA]], i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1) @@ -98,7 +98,7 @@ %matches = icmp eq i32 %8, %9 br i1 %matches, label %catch4, label %rethrow ; CHECK: catch.start3: -; CHECK: call void @llvm.wasm.landingpad.index(i32 0) +; CHECK: call void @llvm.wasm.landingpad.index(token %{{.+}}, i32 0) catch4: ; preds = %catch.start3 %10 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] @@ -311,7 +311,7 @@ declare void @__clang_call_terminate(i8*) ; CHECK-DAG: declare i8* @llvm.wasm.catch(i32) -; CHECK-DAG: declare void @llvm.wasm.landingpad.index(i32) +; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32) ; CHECK-DAG: declare i8* @llvm.wasm.lsda() ; CHECK-DAG: declare i32 @_Unwind_CallPersonality(i8*)