diff --git a/lld/test/wasm/Inputs/tag-section1.ll b/lld/test/wasm/Inputs/tag-section1.ll --- a/lld/test/wasm/Inputs/tag-section1.ll +++ b/lld/test/wasm/Inputs/tag-section1.ll @@ -1,5 +1,5 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" declare void @llvm.wasm.throw(i32, i8*) diff --git a/lld/test/wasm/Inputs/tag-section2.ll b/lld/test/wasm/Inputs/tag-section2.ll --- a/lld/test/wasm/Inputs/tag-section2.ll +++ b/lld/test/wasm/Inputs/tag-section2.ll @@ -1,5 +1,5 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" declare void @llvm.wasm.throw(i32, i8*) diff --git a/lld/test/wasm/tag-section.ll b/lld/test/wasm/tag-section.ll --- a/lld/test/wasm/tag-section.ll +++ b/lld/test/wasm/tag-section.ll @@ -1,13 +1,21 @@ +; Static code ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %p/Inputs/tag-section1.ll -o %t1.o ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %p/Inputs/tag-section2.ll -o %t2.o ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %s -o %t.o ; RUN: wasm-ld -o %t.wasm %t.o %t1.o %t2.o ; RUN: wasm-ld --export-all -o %t-export-all.wasm %t.o %t1.o %t2.o -; RUN: obj2yaml %t.wasm | FileCheck %s -; RUN: obj2yaml %t-export-all.wasm | FileCheck %s --check-prefix=EXPORT-ALL +; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=NOPIC +; RUN: obj2yaml %t-export-all.wasm | FileCheck %s --check-prefix=NOPIC-EXPORT-ALL + +; PIC code +; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section1.ll -o %t1.o +; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section2.ll -o %t2.o +; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %s -o %t.o +; RUN: wasm-ld --import-undefined --experimental-pic -pie -o %t.wasm %t.o %t1.o %t2.o +; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=PIC target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" declare void @foo(i8*) declare void @bar(i8*) @@ -18,25 +26,44 @@ ret void } -; CHECK: Sections: -; CHECK-NEXT: - Type: TYPE -; CHECK-NEXT: Signatures: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: ParamTypes: [] -; CHECK-NEXT: ReturnTypes: [] -; CHECK-NEXT: - Index: 1 -; CHECK-NEXT: ParamTypes: -; CHECK-NEXT: - I32 -; CHECK-NEXT: ReturnTypes: [] +; NOPIC: Sections: +; NOPIC-NEXT: - Type: TYPE +; NOPIC-NEXT: Signatures: +; NOPIC-NEXT: - Index: 0 +; NOPIC-NEXT: ParamTypes: [] +; NOPIC-NEXT: ReturnTypes: [] +; NOPIC-NEXT: - Index: 1 +; NOPIC-NEXT: ParamTypes: +; NOPIC-NEXT: - I32 +; NOPIC-NEXT: ReturnTypes: [] -; CHECK: - Type: TAG -; CHECK-NEXT: TagTypes: [ 1 ] +; NOPIC: - Type: TAG +; NOPIC-NEXT: TagTypes: [ 1 ] ; Global section has to come after tag section -; CHECK: - Type: GLOBAL +; NOPIC: - Type: GLOBAL + +; NOPIC-EXPORT-ALL: - Type: EXPORT +; NOPIC-EXPORT-ALL-NEXT Exports: +; NOPIC-EXPORT-ALL: - Name: __cpp_exception +; NOPIC-EXPORT-ALL: Kind: TAG +; NOPIC-EXPORT-ALL: Index: 0 + +; In PIC mode, tags are undefined and imported from JS. +; PIC: Sections: +; PIC: - Type: TYPE +; PIC-NEXT: Signatures: +; PIC-NEXT: - Index: 0 +; PIC-NEXT: ParamTypes: +; PIC-NEXT: - I32 +; PIC-NEXT: ReturnTypes: [] +; PIC-NEXT: - Index: 1 +; PIC-NEXT: ParamTypes: [] +; PIC-NEXT: ReturnTypes: [] -; EXPORT-ALL: - Type: EXPORT -; EXPORT-ALL-NEXT Exports: -; EXPORT-ALL: - Name: __cpp_exception -; EXPORT-ALL: Kind: TAG -; EXPORT-ALL: Index: 0 +; PIC: - Type: IMPORT +; PIC-NEXT: Imports: +; PIC: - Module: env +; PIC: Field: __cpp_exception +; PIC-NEXT: Kind: TAG +; PIC-NEXT: SigIndex: 0 diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -663,6 +663,15 @@ return symtab->addUndefinedTable(name, sym.Info.ImportName, sym.Info.ImportModule, flags, this, sym.TableType); + case WASM_SYMBOL_TYPE_TAG: + // We only enter here when there are tag symbols for C++ exceptions + // (__cpp_exception) or C longjmp (__c_longjmp) and we use dynamic linking + // in which we don't define those symbols. These symbols are always binding + // global. + assert(sym.isBindingGlobal()); + return symtab->addUndefinedTag(name, sym.Info.ImportName, + sym.Info.ImportModule, flags, this, + sym.Signature); case WASM_SYMBOL_TYPE_SECTION: llvm_unreachable("section symbols cannot be undefined"); } diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -79,6 +79,10 @@ llvm::Optional importModule, uint32_t flags, InputFile *file, const WasmTableType *type); + Symbol *addUndefinedTag(StringRef name, llvm::Optional importName, + llvm::Optional importModule, + uint32_t flags, InputFile *file, + const WasmSignature *sig); TableSymbol *resolveIndirectFunctionTable(bool required); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -625,6 +625,30 @@ return s; } +Symbol *SymbolTable::addUndefinedTag(StringRef name, + Optional importName, + Optional importModule, + uint32_t flags, InputFile *file, + const WasmSignature *sig) { + LLVM_DEBUG(dbgs() << "addUndefinedTag: " << name << "\n"); + assert(flags & WASM_SYMBOL_UNDEFINED); + + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, file); + if (s->traced) + printTraceSymbolUndefined(name, file); + + if (wasInserted) + replaceSymbol(s, name, importName, importModule, flags, file, + sig); + else if (auto *lazy = dyn_cast(s)) + lazy->fetch(); + else if (s->isDefined()) + checkTagType(s, file, sig); + return s; +} + TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) { WasmLimits limits{0, 0, 0}; // Set by the writer. WasmTableType *type = make(); diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -55,6 +55,7 @@ UndefinedDataKind, UndefinedGlobalKind, UndefinedTableKind, + UndefinedTagKind, LazyKind, }; @@ -66,7 +67,7 @@ return symbolKind == UndefinedFunctionKind || symbolKind == UndefinedDataKind || symbolKind == UndefinedGlobalKind || - symbolKind == UndefinedTableKind; + symbolKind == UndefinedTableKind || symbolKind == UndefinedTagKind; } bool isLazy() const { return symbolKind == LazyKind; } @@ -437,7 +438,9 @@ // and is named '__cpp_exception' for linking. class TagSymbol : public Symbol { public: - static bool classof(const Symbol *s) { return s->kind() == DefinedTagKind; } + static bool classof(const Symbol *s) { + return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind; + } // Get/set the tag index uint32_t getTagIndex() const; @@ -463,6 +466,19 @@ InputTag *tag; }; +class UndefinedTag : public TagSymbol { +public: + UndefinedTag(StringRef name, llvm::Optional importName, + llvm::Optional importModule, uint32_t flags, + InputFile *file = nullptr, const WasmSignature *sig = nullptr) + : TagSymbol(name, UndefinedTagKind, flags, file, sig) { + this->importName = importName; + this->importModule = importModule; + } + + static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; } +}; + // LazySymbol represents a symbol that is not yet in the link, but we know where // to find it if needed. If the resolver finds both Undefined and Lazy for the // same name, it will ask the Lazy to load a file. diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -58,6 +58,8 @@ return "UndefinedGlobal"; case wasm::Symbol::UndefinedTableKind: return "UndefinedTable"; + case wasm::Symbol::UndefinedTagKind: + return "UndefinedTag"; case wasm::Symbol::LazyKind: return "LazyKind"; case wasm::Symbol::SectionKind: @@ -113,6 +115,8 @@ const WasmSignature *Symbol::getSignature() const { if (auto* f = dyn_cast(this)) return f->signature; + if (auto *t = dyn_cast(this)) + return t->signature; if (auto *l = dyn_cast(this)) return l->signature; return nullptr; diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -198,6 +198,7 @@ llvm::DenseMap, uint32_t> importedGlobals; llvm::DenseMap, uint32_t> importedFunctions; llvm::DenseMap, uint32_t> importedTables; + llvm::DenseMap, uint32_t> importedTags; }; class FunctionSection : public SyntheticSection { diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -170,10 +170,14 @@ g->setGlobalIndex(entry.first->second); } } else if (auto *t = dyn_cast(sym)) { - // NB: There's currently only one possible kind of tag, and no - // `UndefinedTag`, so we don't bother de-duplicating tag imports. - importedSymbols.emplace_back(sym); - t->setTagIndex(numImportedTags++); + ImportKey key(*(t->getSignature()), module, name); + auto entry = importedTags.try_emplace(key, numImportedTags); + if (entry.second) { + importedSymbols.emplace_back(sym); + t->setTagIndex(numImportedTags++); + } else { + t->setTagIndex(entry.first->second); + } } else { assert(TableSymbol::classof(sym)); auto *table = cast(sym); diff --git a/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp b/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp --- a/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp @@ -23,12 +23,19 @@ // the symbols has already been created, i.e., we have at least one 'throw' or // 'catch' instruction with the symbol in the module, and emit the symbol only // if so. - for (const char *SymName : {"__cpp_exception", "__c_longjmp"}) { - SmallString<60> NameStr; - Mangler::getNameWithPrefix(NameStr, SymName, Asm->getDataLayout()); - if (Asm->OutContext.lookupSymbol(NameStr)) { - MCSymbol *ExceptionSym = Asm->GetExternalSymbolSymbol(SymName); - Asm->OutStreamer->emitLabel(ExceptionSym); + // + // But in dynamic linking, it is in general not possible to come up with a + // module instantiating order in which tag-defining modules are loaded before + // the importing modules. So we make them undefined symbols here, define tags + // in the JS side, and feed them to each importing module. + if (!Asm->isPositionIndependent()) { + for (const char *SymName : {"__cpp_exception", "__c_longjmp"}) { + SmallString<60> NameStr; + Mangler::getNameWithPrefix(NameStr, SymName, Asm->getDataLayout()); + if (Asm->OutContext.lookupSymbol(NameStr)) { + MCSymbol *ExceptionSym = Asm->GetExternalSymbolSymbol(SymName); + Asm->OutStreamer->emitLabel(ExceptionSym); + } } } } diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -1124,6 +1124,9 @@ } case wasm::WASM_EXTERNAL_TAG: NumImportedTags++; + if (readUint8(Ctx) != 0) // Reserved 'attribute' field + return make_error("invalid attribute", + object_error::parse_failed); Im.SigIndex = readVaruint32(Ctx); if (Im.SigIndex >= NumTypes) return make_error("invalid tag type", @@ -1203,8 +1206,7 @@ Tags.reserve(Count); uint32_t NumTypes = Signatures.size(); while (Count--) { - char Attr = readUint8(Ctx); // Reserved 'attribute' field - if (Attr != 0) + if (readUint8(Ctx) != 0) // Reserved 'attribute' field return make_error("invalid attribute", object_error::parse_failed); uint32_t Type = readVaruint32(Ctx); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -234,14 +234,22 @@ return WasmSym; } + if (Name.startswith("GCC_except_table")) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA); + return WasmSym; + } + SmallVector Returns; SmallVector Params; if (Name == "__cpp_exception" || Name == "__c_longjmp") { WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG); - // We may have multiple C++ compilation units to be linked together, each of - // which defines the exception symbol. To resolve them, we declare them as - // weak. - WasmSym->setWeak(true); + // In static linking we define tag symbols in WasmException::endModule(). + // But we may have multiple objects to be linked together, each of which + // defines the tag symbols. To resolve them, we declare them as weak. In + // dynamic linking we make tag symbols undefined in the backend, define it + // in JS, and feed them to each importing module. + if (!isPositionIndependent()) + WasmSym->setWeak(true); WasmSym->setExternal(true); // Currently both C++ exceptions and C longjmps have a single pointer type diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -1727,14 +1727,22 @@ return SDValue(); // Don't custom lower most intrinsics. case Intrinsic::wasm_lsda: { - 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)); + auto PtrVT = getPointerTy(MF.getDataLayout()); + const char *SymName = MF.createExternalSymbolName( + "GCC_except_table" + std::to_string(MF.getFunctionNumber())); + if (isPositionIndependent()) { + SDValue Node = DAG.getTargetExternalSymbol( + SymName, PtrVT, WebAssemblyII::MO_MEMORY_BASE_REL); + const char *BaseName = MF.createExternalSymbolName("__memory_base"); + SDValue BaseAddr = + DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, + DAG.getTargetExternalSymbol(BaseName, PtrVT)); + SDValue SymAddr = + DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, Node); + return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymAddr); + } + SDValue Node = DAG.getTargetExternalSymbol(SymName, PtrVT); + return DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, Node); } case Intrinsic::wasm_shuffle: { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -417,8 +417,10 @@ def : Pat<(i64 (WebAssemblyWrapper texternalsym:$addr)), (CONST_I64 texternalsym:$addr)>, Requires<[IsNotPIC, HasAddr64]>; -def : Pat<(i32 (WebAssemblyWrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>; -def : Pat<(i64 (WebAssemblyWrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>; +def : Pat<(i32 (WebAssemblyWrapperREL texternalsym:$addr)), + (CONST_I32 texternalsym:$addr)>, Requires<[IsPIC, HasAddr32]>; +def : Pat<(i64 (WebAssemblyWrapperREL texternalsym:$addr)), + (CONST_I64 texternalsym:$addr)>, Requires<[IsPIC, HasAddr64]>; //===----------------------------------------------------------------------===// // Additional sets of instructions. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -278,15 +278,9 @@ MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); break; case MachineOperand::MO_ExternalSymbol: - // The target flag indicates whether this is a symbol for a - // variable or a function. - assert(MO.getTargetFlags() == 0 && - "WebAssembly uses only symbol flags on ExternalSymbols"); MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); 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, MO.getMCSymbol()); diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll --- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll +++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll @@ -1,6 +1,7 @@ -; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck -allow-deprecated-dag-overlap %s +; RUN: llc < %s -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s -check-prefixes=CHECK,NOPIC +; RUN: llc < %s -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic | FileCheck %s -check-prefixes=CHECK,PIC -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" @_ZTIi = external constant i8* @_ZTIf = external constant i8* @@ -60,7 +61,22 @@ ; 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: +; In static linking, we load GCC_except_table as a constant directly. +; NOPIC: i32.const $push[[CONTEXT:.*]]=, __wasm_lpad_context +; NOPIC-NEXT: i32.const $push[[EXCEPT_TABLE:.*]]=, GCC_except_table1 +; NOPIC-NEXT: i32.store 4($pop[[CONTEXT]]), $pop[[EXCEPT_TABLE]] + +; In case of PIC, we make GCC_except_table symbols a relative on based on +; __memory_base. +; PIC: global.get $push[[CONTEXT:.*]]=, __wasm_lpad_context@GOT +; PIC-NEXT: local.tee $push{{.*}}=, $[[CONTEXT_LOCAL:.*]]=, $pop[[CONTEXT]] +; PIC: global.get $push[[MEMORY_BASE:.*]]=, __memory_base +; PIC-NEXT: i32.const $push[[EXCEPT_TABLE_REL:.*]]=, GCC_except_table1@MBREL +; PIC-NEXT: i32.add $push[[EXCEPT_TABLE:.*]]=, $pop[[MEMORY_BASE]], $pop[[EXCEPT_TABLE_REL]] +; PIC-NEXT: i32.store 4($[[CONTEXT_LOCAL]]), $pop[[EXCEPT_TABLE]] + ; CHECK: .section .rodata.gcc_except_table,"",@ ; CHECK-NEXT: .p2align 2 ; CHECK-NEXT: GCC_except_table[[START:[0-9]+]]: