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*)
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*)
+; 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*)
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: []
+
+; PIC: - Type: IMPORT
+; PIC-NEXT: Imports:
+; PIC: - Module: env
+; PIC: Field: __cpp_exception
+; PIC-NEXT: Kind: TAG
+; PIC-NEXT: SigIndex: 0
-; EXPORT-ALL: - Type: EXPORT
-; EXPORT-ALL-NEXT Exports:
-; EXPORT-ALL: - Name: __cpp_exception
-; EXPORT-ALL: Kind: TAG
-; EXPORT-ALL: Index: 0
+; In PIC mode, tags should NOT be defined in the module; they are imported.
+; PIC-NOT: - Type: TAG
return symtab->addUndefinedTable(name, sym.Info.ImportName,
sym.Info.ImportModule, flags, this,
sym.TableType);
+ case WASM_SYMBOL_TYPE_TAG:
+ if (sym.isBindingLocal())
+ return make<UndefinedTag>(name, sym.Info.ImportName,
+ sym.Info.ImportModule, flags, this,
+ sym.Signature);
+ 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");
}
return s;
}
+Symbol *SymbolTable::addUndefinedTag(StringRef name,
+ Optional<StringRef> importName,
+ Optional<StringRef> 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<UndefinedTag>(s, name, importName, importModule, flags, file,
+ sig);
+ else if (auto *lazy = dyn_cast<LazySymbol>(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<WasmTableType>();
llvm::Optional<StringRef> importModule,
uint32_t flags, InputFile *file,
const WasmTableType *type);
+ Symbol *addUndefinedTag(StringRef name, llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> importModule,
+ uint32_t flags, InputFile *file,
+ const WasmSignature *sig);
TableSymbol *resolveIndirectFunctionTable(bool required);
return "UndefinedGlobal";
case wasm::Symbol::UndefinedTableKind:
return "UndefinedTable";
+ case wasm::Symbol::UndefinedTagKind:
+ return "UndefinedTag";
case wasm::Symbol::LazyKind:
return "LazyKind";
case wasm::Symbol::SectionKind:
const WasmSignature *Symbol::getSignature() const {
if (auto* f = dyn_cast<FunctionSymbol>(this))
return f->signature;
+ if (auto *t = dyn_cast<TagSymbol>(this))
+ return t->signature;
if (auto *l = dyn_cast<LazySymbol>(this))
return l->signature;
return nullptr;
UndefinedDataKind,
UndefinedGlobalKind,
UndefinedTableKind,
+ UndefinedTagKind,
LazyKind,
};
return symbolKind == UndefinedFunctionKind ||
symbolKind == UndefinedDataKind ||
symbolKind == UndefinedGlobalKind ||
- symbolKind == UndefinedTableKind;
+ symbolKind == UndefinedTableKind || symbolKind == UndefinedTagKind;
}
bool isLazy() const { return symbolKind == LazyKind; }
// 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;
InputTag *tag;
};
+class UndefinedTag : public TagSymbol {
+public:
+ UndefinedTag(StringRef name, llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> 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.
g->setGlobalIndex(entry.first->second);
}
} else if (auto *t = dyn_cast<TagSymbol>(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<WasmSignature> 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<TableSymbol>(sym);
llvm::DenseMap<ImportKey<WasmGlobalType>, uint32_t> importedGlobals;
llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedFunctions;
llvm::DenseMap<ImportKey<WasmTableType>, uint32_t> importedTables;
+ llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedTags;
};
class FunctionSection : public SyntheticSection {
// 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);
+ }
}
}
}
}
case wasm::WASM_EXTERNAL_TAG:
NumImportedTags++;
+ if (readUint8(Ctx) != 0) // Reserved 'attribute' field
+ return make_error<GenericBinaryError>("invalid attribute",
+ object_error::parse_failed);
Im.SigIndex = readVaruint32(Ctx);
if (Im.SigIndex >= NumTypes)
return make_error<GenericBinaryError>("invalid tag type",
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<GenericBinaryError>("invalid attribute",
object_error::parse_failed);
uint32_t Type = readVaruint32(Ctx);
return WasmSym;
}
+ if (Name.startswith("GCC_except_table")) {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA);
+ return WasmSym;
+ }
+
SmallVector<wasm::ValType, 4> Returns;
SmallVector<wasm::ValType, 4> 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
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: {
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.
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());
-; 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
-
-target triple = "wasm32-unknown-unknown"
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s -check-prefixes=CHECK,NOPIC -DPTR=32
+; RUN: llc < %s --mtriple=wasm64-unknown-unknown -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s -check-prefixes=CHECK,NOPIC -DPTR=64
+; RUN: llc < %s --mtriple=wasm32-unknown-emscripten -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s -check-prefixes=CHECK,NOPIC -DPTR=32
+; RUN: llc < %s --mtriple=wasm64-unknown-emscripten -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s -check-prefixes=CHECK,NOPIC -DPTR=64
+; RUN: llc < %s --mtriple=wasm32-unknown-emscripten -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 -DPTR=32
+; RUN: llc < %s --mtriple=wasm64-unknown-emscripten -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 -DPTR=64
@_ZTIi = external constant i8*
@_ZTIf = external constant i8*
; 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: i[[PTR]].const $push[[CONTEXT:.*]]=, __wasm_lpad_context
+; NOPIC-NEXT: i[[PTR]].const $push[[EXCEPT_TABLE:.*]]=, GCC_except_table1
+; NOPIC-NEXT: i[[PTR]].store {{[48]}}($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: i[[PTR]].const $push[[EXCEPT_TABLE_REL:.*]]=, GCC_except_table1@MBREL
+; PIC-NEXT: i[[PTR]].add $push[[EXCEPT_TABLE:.*]]=, $pop[[MEMORY_BASE]], $pop[[EXCEPT_TABLE_REL]]
+; PIC-NEXT: i[[PTR]].store {{[48]}}($[[CONTEXT_LOCAL]]), $pop[[EXCEPT_TABLE]]
+
; CHECK: .section .rodata.gcc_except_table,"",@
; CHECK-NEXT: .p2align 2
; CHECK-NEXT: GCC_except_table[[START:[0-9]+]]:
; 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: .int[[PTR]] _ZTIi # TypeInfo 4
+; CHECK-NEXT: .int[[PTR]] _ZTIf # TypeInfo 3
+; CHECK-NEXT: .int[[PTR]] _ZTId # TypeInfo 2
+; CHECK-NEXT: .int[[PTR]] 0 # TypeInfo 1
; CHECK-NEXT: .Lttbase0:
; CHECK-NEXT: .p2align 2
; CHECK-NEXT: .LGCC_except_table_end[[END:[0-9]+]]: