diff --git a/lld/include/lld/Common/LLVM.h b/lld/include/lld/Common/LLVM.h --- a/lld/include/lld/Common/LLVM.h +++ b/lld/include/lld/Common/LLVM.h @@ -49,8 +49,11 @@ struct WasmFunction; struct WasmGlobal; struct WasmGlobalType; +struct WasmLimits; struct WasmRelocation; struct WasmSignature; +struct WasmTable; +struct WasmTableType; } // namespace wasm } // namespace llvm @@ -88,8 +91,11 @@ using llvm::wasm::WasmFunction; using llvm::wasm::WasmGlobal; using llvm::wasm::WasmGlobalType; +using llvm::wasm::WasmLimits; using llvm::wasm::WasmRelocation; using llvm::wasm::WasmSignature; +using llvm::wasm::WasmTable; +using llvm::wasm::WasmTableType; } // end namespace lld. namespace std { diff --git a/lld/test/wasm/alias.s b/lld/test/wasm/alias.s --- a/lld/test/wasm/alias.s +++ b/lld/test/wasm/alias.s @@ -22,14 +22,6 @@ # CHECK-NEXT: ReturnTypes: [] # CHECK-NEXT: - Type: FUNCTION # CHECK-NEXT: FunctionTypes: [ 0 ] -# CHECK-NEXT: - Type: TABLE -# CHECK-NEXT: Tables: -# CHECK-NEXT: - Index: 0 -# CHECK-NEXT: ElemType: FUNCREF -# CHECK-NEXT: Limits: -# CHECK-NEXT: Flags: [ HAS_MAX ] -# CHECK-NEXT: Initial: 0x1 -# CHECK-NEXT: Maximum: 0x1 # CHECK-NEXT: - Type: MEMORY # CHECK-NEXT: Memories: # CHECK-NEXT: - Initial: 0x2 diff --git a/lld/test/wasm/init-fini.ll b/lld/test/wasm/init-fini.ll --- a/lld/test/wasm/init-fini.ll +++ b/lld/test/wasm/init-fini.ll @@ -139,15 +139,15 @@ ; RELOC-NEXT: InitFunctions [ ; RELOC-NEXT: 0 (priority=101) ; RELOC-NEXT: 1 (priority=101) -; RELOC-NEXT: 14 (priority=101) -; RELOC-NEXT: 10 (priority=101) -; RELOC-NEXT: 20 (priority=101) -; RELOC-NEXT: 10 (priority=202) -; RELOC-NEXT: 22 (priority=202) +; RELOC-NEXT: 15 (priority=101) +; RELOC-NEXT: 11 (priority=101) +; RELOC-NEXT: 21 (priority=101) +; RELOC-NEXT: 11 (priority=202) +; RELOC-NEXT: 23 (priority=202) ; RELOC-NEXT: 0 (priority=1001) -; RELOC-NEXT: 16 (priority=1001) -; RELOC-NEXT: 10 (priority=2002) -; RELOC-NEXT: 24 (priority=2002) +; RELOC-NEXT: 17 (priority=1001) +; RELOC-NEXT: 11 (priority=2002) +; RELOC-NEXT: 25 (priority=2002) ; RELOC-NEXT: 9 (priority=4000) -; RELOC-NEXT: 18 (priority=4000) +; RELOC-NEXT: 19 (priority=4000) ; RELOC-NEXT: ] diff --git a/lld/test/wasm/local-symbols.ll b/lld/test/wasm/local-symbols.ll --- a/lld/test/wasm/local-symbols.ll +++ b/lld/test/wasm/local-symbols.ll @@ -35,14 +35,6 @@ ; CHECK-NEXT: ReturnTypes: [] ; CHECK-NEXT: - Type: FUNCTION ; CHECK-NEXT: FunctionTypes: [ 0, 1 ] -; CHECK-NEXT: - Type: TABLE -; CHECK-NEXT: Tables: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Flags: [ HAS_MAX ] -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: Maximum: 0x1 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x2 diff --git a/lld/test/wasm/locals-duplicate.test b/lld/test/wasm/locals-duplicate.test --- a/lld/test/wasm/locals-duplicate.test +++ b/lld/test/wasm/locals-duplicate.test @@ -251,40 +251,40 @@ ; RELOC-NEXT: - Type: CODE ; RELOC-NEXT: Relocations: ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 18 +; RELOC-NEXT: Index: 19 ; RELOC-NEXT: Offset: 0x13 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB ; RELOC-NEXT: Index: 3 ; RELOC-NEXT: Offset: 0x1C ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 19 +; RELOC-NEXT: Index: 20 ; RELOC-NEXT: Offset: 0x25 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 16 +; RELOC-NEXT: Index: 17 ; RELOC-NEXT: Offset: 0x2E ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB ; RELOC-NEXT: Index: 0 ; RELOC-NEXT: Offset: 0x37 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 17 +; RELOC-NEXT: Index: 18 ; RELOC-NEXT: Offset: 0x40 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 10 +; RELOC-NEXT: Index: 11 ; RELOC-NEXT: Offset: 0x58 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 22 +; RELOC-NEXT: Index: 23 ; RELOC-NEXT: Offset: 0x61 ; RELOC-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB -; RELOC-NEXT: Index: 23 +; RELOC-NEXT: Index: 24 ; RELOC-NEXT: Offset: 0x6A ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 8 +; RELOC-NEXT: Index: 9 ; RELOC-NEXT: Offset: 0x73 ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 20 +; RELOC-NEXT: Index: 21 ; RELOC-NEXT: Offset: 0x7C ; RELOC-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB -; RELOC-NEXT: Index: 21 +; RELOC-NEXT: Index: 22 ; RELOC-NEXT: Offset: 0x85 ; RELOC-NEXT: Functions: ; RELOC-NEXT: - Index: 0 @@ -407,87 +407,92 @@ ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 8 ; RELOC-NEXT: - Index: 8 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 +; RELOC-NEXT: - Index: 9 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func1 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 9 -; RELOC-NEXT: - Index: 9 +; RELOC-NEXT: - Index: 10 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global1B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 12 -; RELOC-NEXT: - Index: 10 +; RELOC-NEXT: - Index: 11 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global1 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Offset: 4 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 11 +; RELOC-NEXT: - Index: 12 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global2B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 13 -; RELOC-NEXT: - Index: 12 +; RELOC-NEXT: - Index: 13 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_global3B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 14 -; RELOC-NEXT: - Index: 13 +; RELOC-NEXT: - Index: 14 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func1B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 15 -; RELOC-NEXT: - Index: 14 +; RELOC-NEXT: - Index: 15 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func2B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 16 -; RELOC-NEXT: - Index: 15 +; RELOC-NEXT: - Index: 16 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: get_func3B ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 17 -; RELOC-NEXT: - Index: 16 +; RELOC-NEXT: - Index: 17 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func1 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 0 -; RELOC-NEXT: - Index: 17 +; RELOC-NEXT: - Index: 18 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 2 -; RELOC-NEXT: - Index: 18 +; RELOC-NEXT: - Index: 19 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global1 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 19 +; RELOC-NEXT: - Index: 20 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 2 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 20 +; RELOC-NEXT: - Index: 21 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func2 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 10 -; RELOC-NEXT: - Index: 21 +; RELOC-NEXT: - Index: 22 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: colliding_func3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Function: 11 -; RELOC-NEXT: - Index: 22 +; RELOC-NEXT: - Index: 23 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global2 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] ; RELOC-NEXT: Segment: 1 ; RELOC-NEXT: Offset: 4 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 23 +; RELOC-NEXT: - Index: 24 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: colliding_global3 ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] diff --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll --- a/lld/test/wasm/pie.ll +++ b/lld/test/wasm/pie.ll @@ -42,14 +42,6 @@ ; CHECK: - Type: IMPORT ; CHECK-NEXT: Imports: ; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __indirect_function_table -; CHECK-NEXT: Kind: TABLE -; CHECK-NEXT: Table: -; CHECK-NEXT: Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: - Module: env ; CHECK-NEXT: Field: __stack_pointer ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 @@ -64,5 +56,13 @@ ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 ; CHECK-NEXT: GlobalMutable: false +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __indirect_function_table +; CHECK-NEXT: Kind: TABLE +; CHECK-NEXT: Table: +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: ElemType: FUNCREF +; CHECK-NEXT: Limits: +; CHECK-NEXT: Initial: 0x1 diff --git a/lld/test/wasm/section-symbol-relocs.yaml b/lld/test/wasm/section-symbol-relocs.yaml --- a/lld/test/wasm/section-symbol-relocs.yaml +++ b/lld/test/wasm/section-symbol-relocs.yaml @@ -54,8 +54,8 @@ # RELOC-NEXT: - Index: 0 # RELOC-NEXT: Kind: SECTION # RELOC-NEXT: Flags: [ BINDING_LOCAL ] -# RELOC-NEXT: Section: 2 +# RELOC-NEXT: Section: 1 # RELOC-NEXT: - Index: 1 # RELOC-NEXT: Kind: SECTION # RELOC-NEXT: Flags: [ BINDING_LOCAL ] -# RELOC-NEXT: Section: 3 +# RELOC-NEXT: Section: 2 diff --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll --- a/lld/test/wasm/shared.ll +++ b/lld/test/wasm/shared.ll @@ -69,14 +69,6 @@ ; CHECK-NEXT: Memory: ; CHECK-NEXT: Initial: 0x0 ; CHECK-NEXT: - Module: env -; CHECK-NEXT: Field: __indirect_function_table -; CHECK-NEXT: Kind: TABLE -; CHECK-NEXT: Table: -; CHECK-NEXT: Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Initial: 0x2 -; CHECK-NEXT: - Module: env ; CHECK-NEXT: Field: __stack_pointer ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: GlobalType: I32 @@ -95,6 +87,14 @@ ; CHECK-NEXT: Field: func_external ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: SigIndex: 1 +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: __indirect_function_table +; CHECK-NEXT: Kind: TABLE +; CHECK-NEXT: Table: +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: ElemType: FUNCREF +; CHECK-NEXT: Limits: +; CHECK-NEXT: Initial: 0x2 ; CHECK-NEXT: - Module: GOT.mem ; CHECK-NEXT: Field: indirect_func ; CHECK-NEXT: Kind: GLOBAL diff --git a/lld/test/wasm/signature-mismatch.ll b/lld/test/wasm/signature-mismatch.ll --- a/lld/test/wasm/signature-mismatch.ll +++ b/lld/test/wasm/signature-mismatch.ll @@ -77,17 +77,22 @@ ; RELOC-NEXT: Segment: 0 ; RELOC-NEXT: Size: 4 ; RELOC-NEXT: - Index: 3 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 +; RELOC-NEXT: - Index: 4 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: call_ret32 ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 3 -; RELOC-NEXT: - Index: 4 +; RELOC-NEXT: - Index: 5 ; RELOC-NEXT: Kind: DATA ; RELOC-NEXT: Name: ret32_address ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Segment: 1 ; RELOC-NEXT: Size: 4 -; RELOC-NEXT: - Index: 5 +; RELOC-NEXT: - Index: 6 ; RELOC-NEXT: Kind: FUNCTION ; RELOC-NEXT: Name: 'signature_mismatch:ret32' ; RELOC-NEXT: Flags: [ BINDING_LOCAL ] diff --git a/lld/test/wasm/stack-pointer.ll b/lld/test/wasm/stack-pointer.ll --- a/lld/test/wasm/stack-pointer.ll +++ b/lld/test/wasm/stack-pointer.ll @@ -30,14 +30,6 @@ ; CHECK-NEXT: GlobalMutable: true ; CHECK-NEXT: - Type: FUNCTION ; CHECK-NEXT: FunctionTypes: [ 0 ] -; CHECK-NEXT: - Type: TABLE -; CHECK-NEXT: Tables: -; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: ElemType: FUNCREF -; CHECK-NEXT: Limits: -; CHECK-NEXT: Flags: [ HAS_MAX ] -; CHECK-NEXT: Initial: 0x1 -; CHECK-NEXT: Maximum: 0x1 ; CHECK-NEXT: - Type: MEMORY ; CHECK-NEXT: Memories: ; CHECK-NEXT: - Initial: 0x0 diff --git a/lld/test/wasm/weak-alias.ll b/lld/test/wasm/weak-alias.ll --- a/lld/test/wasm/weak-alias.ll +++ b/lld/test/wasm/weak-alias.ll @@ -276,6 +276,11 @@ ; RELOC-NEXT: Name: call_direct_ptr ; RELOC-NEXT: Flags: [ ] ; RELOC-NEXT: Function: 5 +; RELOC-NEXT: - Index: 8 +; RELOC-NEXT: Kind: TABLE +; RELOC-NEXT: Name: __indirect_function_table +; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +; RELOC-NEXT: Table: 0 ; RELOC-NEXT: - Type: CUSTOM ; RELOC-NEXT: Name: name ; RELOC-NEXT: FunctionNames: diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include "InputChunks.h" #include "InputGlobal.h" +#include "InputTable.h" #include "MarkLive.h" #include "SymbolTable.h" #include "Writer.h" @@ -799,6 +800,58 @@ symtab->wrap(w.sym, w.real, w.wrap); } +static TableSymbol *provideIndirectFunctionTable(const StringRef &Name) { + const uint32_t InvalidIndex = -1; + WasmLimits limits{0, 0, 0}; // Set by the writer. + WasmTableType type{uint8_t(ValType::FUNCREF), limits}; + WasmTable desc{InvalidIndex, type, Name}; + InputTable *table = make(desc, nullptr); + uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN; + TableSymbol *sym = symtab->addSyntheticTable(Name, flags, table); + sym->markLive(); + sym->forceExport = config->exportTable; + return sym; +} + +static TableSymbol * +createUndefinedIndirectFunctionTable(const StringRef &Name) { + WasmLimits limits{0, 0, 0}; // Set by the writer. + WasmTableType *type = make(); + type->ElemType = uint8_t(ValType::FUNCREF); + type->Limits = limits; + StringRef module(defaultModule); + uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN; + flags |= WASM_SYMBOL_UNDEFINED; + Symbol *sym = + symtab->addUndefinedTable(Name, Name, module, flags, nullptr, type); + sym->markLive(); + sym->forceExport = config->exportTable; + return cast(sym); +} + +static TableSymbol *resolveIndirectFunctionTable() { + // Even though we may not need a table, if the user explicitly specified + // --import-table or --export-table, ensure a table is residualized. + const StringRef Name(functionTableName); + if (config->importTable) + return createUndefinedIndirectFunctionTable(Name); + if (config->exportTable) + return provideIndirectFunctionTable(Name); + + // Otherwise, check to the symtab to find the indirect function table. + if (Symbol *sym = symtab->find(Name)) { + if (sym->isLive()) { + if (auto *t = dyn_cast(sym)) { + return t->isDefined() ? t : provideIndirectFunctionTable(Name); + } + } + } + + // An indirect function table will only be present in the symbol table if + // needed by a reloc; if we get here, we don't need one. + return nullptr; +} + void LinkerDriver::link(ArrayRef argsArr) { WasmOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -988,6 +1041,12 @@ // Do size optimizations: garbage collection markLive(); + // Provide the indirect funciton table if needed. + symtab->indirectFunctionTableSymbol = resolveIndirectFunctionTable(); + + if (errorCount()) + return; + // Write the result to the file. writeResult(); } diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -72,6 +72,7 @@ existingValue = decodeULEB128(loc, &bytesRead); break; case R_WASM_MEMORY_ADDR_LEB64: + case R_WASM_TABLE_NUMBER_LEB: existingValue = decodeULEB128(loc, &bytesRead); paddedLEBWidth = 10; break; @@ -151,6 +152,7 @@ case R_WASM_GLOBAL_INDEX_LEB: case R_WASM_EVENT_INDEX_LEB: case R_WASM_MEMORY_ADDR_LEB: + case R_WASM_TABLE_NUMBER_LEB: encodeULEB128(value, loc, 5); break; case R_WASM_MEMORY_ADDR_LEB64: @@ -232,6 +234,7 @@ case R_WASM_EVENT_INDEX_LEB: case R_WASM_MEMORY_ADDR_LEB: case R_WASM_MEMORY_ADDR_LEB64: + case R_WASM_TABLE_NUMBER_LEB: return encodeULEB128(value, buf); case R_WASM_TABLE_INDEX_SLEB: case R_WASM_TABLE_INDEX_SLEB64: diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -32,6 +32,7 @@ class InputSegment; class InputGlobal; class InputEvent; +class InputTable; class InputSection; // If --reproduce option is given, all input files are written @@ -139,6 +140,7 @@ std::vector functions; std::vector globals; std::vector events; + std::vector tables; std::vector customSections; llvm::DenseMap customSectionsByIndex; @@ -148,6 +150,7 @@ GlobalSymbol *getGlobalSymbol(uint32_t index) const; SectionSymbol *getSectionSymbol(uint32_t index) const; EventSymbol *getEventSymbol(uint32_t index) const; + TableSymbol *getTableSymbol(uint32_t index) const; private: Symbol *createDefined(const WasmSymbol &sym); diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -11,6 +11,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "InputTable.h" #include "OutputSegment.h" #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" @@ -94,7 +95,8 @@ "\n Symbols : " + Twine(symbols.size()) + "\n Function Imports : " + Twine(wasmObj->getNumImportedFunctions()) + "\n Global Imports : " + Twine(wasmObj->getNumImportedGlobals()) + - "\n Event Imports : " + Twine(wasmObj->getNumImportedEvents())); + "\n Event Imports : " + Twine(wasmObj->getNumImportedEvents()) + + "\n Table Imports : " + Twine(wasmObj->getNumImportedTables())); } // Relocations contain either symbol or type indices. This function takes a @@ -268,6 +270,8 @@ } case R_WASM_SECTION_OFFSET_I32: return getSectionSymbol(reloc.Index)->section->outputOffset + reloc.Addend; + case R_WASM_TABLE_NUMBER_LEB: + return getTableSymbol(reloc.Index)->getTableNumber(); default: llvm_unreachable("unknown relocation type"); } @@ -400,6 +404,11 @@ } setRelocs(functions, codeSection); + // Populate `Tables`. + for (const WasmTable &t : wasmObj->tables()) { + tables.emplace_back(make(t, this)); + } + // Populate `Globals`. for (const WasmGlobal &g : wasmObj->globals()) globals.emplace_back(make(g, this)); @@ -410,8 +419,11 @@ // Populate `Symbols` based on the symbols in the object. symbols.reserve(wasmObj->getNumberOfSymbols()); + bool haveTableSymbol = false; for (const SymbolRef &sym : wasmObj->symbols()) { const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl()); + if (wasmSym.isTypeTable()) + haveTableSymbol = true; if (wasmSym.isDefined()) { // createDefined may fail if the symbol is comdat excluded in which case // we fall back to creating an undefined symbol @@ -423,6 +435,58 @@ size_t idx = symbols.size(); symbols.push_back(createUndefined(wasmSym, isCalledDirectly[idx])); } + + // As a stopgap measure while implementing table support, if the object file + // has table definitions or imports but no table symbols, synthesize symbols + // for those tables. Mark as NO_STRIP to ensure they reach the output file, + // even if there are no TABLE_NUMBER relocs against them. + if (!haveTableSymbol) { + uint32_t tableNumber = 0; + const WasmGlobalType *GlobalType = nullptr; + const WasmEventType *EventType = nullptr; + const WasmSignature *Signature = nullptr; + if (wasmObj->getNumImportedTables()) { + for (auto &Import : wasmObj->imports()) { + if (Import.Kind == WASM_EXTERNAL_TABLE) { + auto *Info = make(); + // FIXME: Synthesize as Module.Field. Need somewhere to put the + // string backing buffer though. + Info->Name = Import.Field; + Info->Kind = WASM_SYMBOL_TYPE_TABLE; + Info->ImportModule = Import.Module; + Info->ImportName = Import.Field; + Info->Flags = WASM_SYMBOL_UNDEFINED; + Info->Flags |= WASM_SYMBOL_NO_STRIP; + Info->ElementIndex = tableNumber++; + LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: " + << Info->Name << "\n"); + auto *WasmSym = make(*Info, GlobalType, &Import.Table, + EventType, Signature); + symbols.push_back(createUndefined(*WasmSym, false)); + // Because there are no TABLE_NUMBER relocs in this case, we can't + // compute accurate liveness info; instead, just mark the symbol as + // always live. + symbols.back()->markLive(); + } + } + } + for (auto &Table : tables) { + auto *Info = make(); + // Empty name. + Info->Kind = WASM_SYMBOL_TYPE_TABLE; + Info->Flags = WASM_SYMBOL_BINDING_LOCAL; + Info->Flags |= WASM_SYMBOL_VISIBILITY_HIDDEN; + Info->Flags |= WASM_SYMBOL_NO_STRIP; + Info->ElementIndex = tableNumber++; + LLVM_DEBUG(dbgs() << "Synthesizing symbol for table definition: " + << Info->Name << "\n"); + auto *WasmSym = make(*Info, GlobalType, &Table->getType(), + EventType, Signature); + symbols.push_back(createDefined(*WasmSym)); + // Mark live, for the same reasons as for imported tables. + symbols.back()->markLive(); + } + } } bool ObjFile::isExcludedByComdat(InputChunk *chunk) const { @@ -444,6 +508,10 @@ return cast(symbols[index]); } +TableSymbol *ObjFile::getTableSymbol(uint32_t index) const { + return cast(symbols[index]); +} + SectionSymbol *ObjFile::getSectionSymbol(uint32_t index) const { return cast(symbols[index]); } @@ -495,6 +563,13 @@ return make(name, flags, this, event); return symtab->addDefinedEvent(name, flags, this, event); } + case WASM_SYMBOL_TYPE_TABLE: { + InputTable *table = + tables[sym.Info.ElementIndex - wasmObj->getNumImportedTables()]; + if (sym.isBindingLocal()) + return make(name, flags, this, table); + return symtab->addDefinedTable(name, flags, this, table); + } } llvm_unreachable("unknown symbol kind"); } @@ -524,6 +599,14 @@ return symtab->addUndefinedGlobal(name, sym.Info.ImportName, sym.Info.ImportModule, flags, this, sym.GlobalType); + case WASM_SYMBOL_TYPE_TABLE: + if (sym.isBindingLocal()) + return make(name, sym.Info.ImportName, + sym.Info.ImportModule, flags, this, + sym.TableType); + return symtab->addUndefinedTable(name, sym.Info.ImportName, + sym.Info.ImportModule, flags, this, + sym.TableType); case WASM_SYMBOL_TYPE_SECTION: llvm_unreachable("section symbols cannot be undefined"); } diff --git a/lld/wasm/InputTable.h b/lld/wasm/InputTable.h new file mode 100644 --- /dev/null +++ b/lld/wasm/InputTable.h @@ -0,0 +1,60 @@ +//===- InputTable.h --------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_WASM_INPUT_TABLE_H +#define LLD_WASM_INPUT_TABLE_H + +#include "Config.h" +#include "InputFiles.h" +#include "WriterUtils.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/Wasm.h" + +namespace lld { +namespace wasm { + +// Represents a single Wasm Table within an input file. These are combined to +// form the final TABLES section. +class InputTable { +public: + InputTable(const WasmTable &t, ObjFile *f) + : file(f), table(t), live(!config->gcSections) {} + + StringRef getName() const { return table.SymbolName; } + const WasmTableType &getType() const { return table.Type; } + + // Somewhat confusingly, we generally use the term "table index" to refer to + // the offset of a function in the well-known indirect function table. We + // refer to different tables instead by "table numbers". + uint32_t getTableNumber() const { return tableNumber.getValue(); } + bool hasTableNumber() const { return tableNumber.hasValue(); } + void setTableNumber(uint32_t n) { + assert(!hasTableNumber()); + tableNumber = n; + } + + void setLimits(const WasmLimits &Limits) { table.Type.Limits = Limits; } + + ObjFile *file; + WasmTable table; + + bool live = false; + +protected: + llvm::Optional tableNumber; +}; + +} // namespace wasm + +inline std::string toString(const wasm::InputTable *t) { + return (toString(t->file) + ":(" + t->getName() + ")").str(); +} + +} // namespace lld + +#endif // LLD_WASM_INPUT_TABLE_H diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -23,6 +23,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "InputTable.h" #include "SymbolTable.h" #include "Symbols.h" @@ -175,6 +176,9 @@ for (InputEvent *e : obj->events) if (!e->live) message("removing unused section " + toString(e)); + for (InputTable *t : obj->tables) + if (!t->live) + message("removing unused section " + toString(t)); } for (InputChunk *c : symtab->syntheticFunctions) if (!c->live) @@ -182,6 +186,9 @@ for (InputGlobal *g : symtab->syntheticGlobals) if (!g->live) message("removing unused section " + toString(g)); + for (InputTable *t : symtab->syntheticTables) + if (!t->live) + message("removing unused section " + toString(t)); } } diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -60,6 +60,8 @@ InputGlobal *g); Symbol *addDefinedEvent(StringRef name, uint32_t flags, InputFile *file, InputEvent *e); + Symbol *addDefinedTable(StringRef name, uint32_t flags, InputFile *file, + InputTable *t); Symbol *addUndefinedFunction(StringRef name, llvm::Optional importName, @@ -73,6 +75,11 @@ llvm::Optional importModule, uint32_t flags, InputFile *file, const WasmGlobalType *type); + Symbol *addUndefinedTable(StringRef name, + llvm::Optional importName, + llvm::Optional importModule, + uint32_t flags, InputFile *file, + const WasmTableType *type); void addLazy(ArchiveFile *f, const llvm::object::Archive::Symbol *sym); @@ -86,6 +93,8 @@ DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0); DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags, InputGlobal *global); + DefinedTable *addSyntheticTable(StringRef name, uint32_t flags, + InputTable *global); void handleSymbolVariants(); void handleWeakUndefines(); @@ -96,6 +105,9 @@ std::vector bitcodeFiles; std::vector syntheticFunctions; std::vector syntheticGlobals; + std::vector syntheticTables; + + TableSymbol *indirectFunctionTableSymbol = nullptr; private: std::pair insert(StringRef name, const InputFile *file); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -11,6 +11,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "InputTable.h" #include "WriterUtils.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" @@ -191,6 +192,22 @@ toString(*newSig) + " in " + toString(file)); } +static void checkTableType(const Symbol *existing, const InputFile *file, + const WasmTableType *newType) { + if (!isa(existing)) { + reportTypeError(existing, file, WASM_SYMBOL_TYPE_TABLE); + return; + } + + const WasmTableType *oldType = cast(existing)->getTableType(); + if (newType->ElemType != oldType->ElemType) { + error("Table type mismatch: " + existing->getName() + "\n>>> defined as " + + toString(*oldType) + " in " + toString(existing->getFile()) + + "\n>>> defined as " + toString(*newType) + " in " + toString(file)); + } + // FIXME: No assertions currently on the limits. +} + static void checkDataType(const Symbol *existing, const InputFile *file) { if (!isa(existing)) reportTypeError(existing, file, WASM_SYMBOL_TYPE_DATA); @@ -253,6 +270,18 @@ return replaceSymbol(s, name, flags, nullptr, global); } +DefinedTable *SymbolTable::addSyntheticTable(StringRef name, uint32_t flags, + InputTable *table) { + LLVM_DEBUG(dbgs() << "addSyntheticTable: " << name << " -> " << table + << "\n"); + Symbol *s = find(name); + assert(!s || s->isUndefined()); + if (!s) + s = insertName(name).first; + syntheticTables.emplace_back(table); + return replaceSymbol(s, name, flags, nullptr, table); +} + static bool shouldReplace(const Symbol *existing, InputFile *newFile, uint32_t newFlags) { // If existing symbol is undefined, replace it. @@ -410,6 +439,30 @@ return s; } +Symbol *SymbolTable::addDefinedTable(StringRef name, uint32_t flags, + InputFile *file, InputTable *table) { + LLVM_DEBUG(dbgs() << "addDefinedTable:" << name << "\n"); + + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, file); + + auto replaceSym = [&]() { + replaceSymbol(s, name, flags, file, table); + }; + + if (wasInserted || s->isLazy()) { + replaceSym(); + return s; + } + + checkTableType(s, file, &table->getType()); + + if (shouldReplace(s, file, flags)) + replaceSym(); + return s; +} + // This function get called when an undefined symbol is added, and there is // already an existing one in the symbols table. In this case we check that // custom 'import-module' and 'import-field' symbol attributes agree. @@ -553,6 +606,30 @@ return s; } +Symbol *SymbolTable::addUndefinedTable(StringRef name, + Optional importName, + Optional importModule, + uint32_t flags, InputFile *file, + const WasmTableType *type) { + LLVM_DEBUG(dbgs() << "addUndefinedTable: " << 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, type); + else if (auto *lazy = dyn_cast(s)) + lazy->fetch(); + else if (s->isDefined()) + checkTableType(s, file, type); + return s; +} + void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) { LLVM_DEBUG(dbgs() << "addLazy: " << sym->getName() << "\n"); StringRef name = sym->getName(); diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -27,6 +27,7 @@ extern const char *functionTableName; using llvm::wasm::WasmSymbolType; +using llvm::wasm::WasmTable; class InputFile; class InputChunk; @@ -35,6 +36,7 @@ class InputGlobal; class InputEvent; class InputSection; +class InputTable; class OutputSection; #define INVALID_INDEX UINT32_MAX @@ -47,11 +49,13 @@ DefinedDataKind, DefinedGlobalKind, DefinedEventKind, + DefinedTableKind, SectionKind, OutputSectionKind, UndefinedFunctionKind, UndefinedDataKind, UndefinedGlobalKind, + UndefinedTableKind, LazyKind, }; @@ -61,7 +65,9 @@ bool isUndefined() const { return symbolKind == UndefinedFunctionKind || - symbolKind == UndefinedDataKind || symbolKind == UndefinedGlobalKind; + symbolKind == UndefinedDataKind || + symbolKind == UndefinedGlobalKind || + symbolKind == UndefinedTableKind; } bool isLazy() const { return symbolKind == LazyKind; } @@ -356,6 +362,55 @@ llvm::Optional importModule; }; +class TableSymbol : public Symbol { +public: + static bool classof(const Symbol *s) { + return s->kind() == DefinedTableKind || s->kind() == UndefinedTableKind; + } + + const WasmTableType *getTableType() const { return tableType; } + void setLimits(const WasmLimits &Limits); + + // Get/set the table number + uint32_t getTableNumber() const; + void setTableNumber(uint32_t number); + bool hasTableNumber() const; + +protected: + TableSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f, + const WasmTableType *type) + : Symbol(name, k, flags, f), tableType(type) {} + + const WasmTableType *tableType; + uint32_t tableNumber = INVALID_INDEX; +}; + +class DefinedTable : public TableSymbol { +public: + DefinedTable(StringRef name, uint32_t flags, InputFile *file, + InputTable *table); + + static bool classof(const Symbol *s) { return s->kind() == DefinedTableKind; } + + InputTable *table; +}; + +class UndefinedTable : public TableSymbol { +public: + UndefinedTable(StringRef name, llvm::Optional importName, + llvm::Optional importModule, uint32_t flags, + InputFile *file, const WasmTableType *type) + : TableSymbol(name, UndefinedTableKind, flags, file, type), + importName(importName), importModule(importModule) {} + + static bool classof(const Symbol *s) { + return s->kind() == UndefinedTableKind; + } + + llvm::Optional importName; + llvm::Optional importModule; +}; + // Wasm events are features that suspend the current execution and transfer the // control flow to a corresponding handler. Currently the only supported event // kind is exceptions. @@ -512,11 +567,13 @@ alignas(DefinedData) char b[sizeof(DefinedData)]; alignas(DefinedGlobal) char c[sizeof(DefinedGlobal)]; alignas(DefinedEvent) char d[sizeof(DefinedEvent)]; - alignas(LazySymbol) char e[sizeof(LazySymbol)]; - alignas(UndefinedFunction) char f[sizeof(UndefinedFunction)]; - alignas(UndefinedData) char g[sizeof(UndefinedData)]; - alignas(UndefinedGlobal) char h[sizeof(UndefinedGlobal)]; - alignas(SectionSymbol) char i[sizeof(SectionSymbol)]; + alignas(DefinedTable) char e[sizeof(DefinedTable)]; + alignas(LazySymbol) char f[sizeof(LazySymbol)]; + alignas(UndefinedFunction) char g[sizeof(UndefinedFunction)]; + alignas(UndefinedData) char h[sizeof(UndefinedData)]; + alignas(UndefinedGlobal) char i[sizeof(UndefinedGlobal)]; + alignas(UndefinedTable) char j[sizeof(UndefinedTable)]; + alignas(SectionSymbol) char k[sizeof(SectionSymbol)]; }; // It is important to keep the size of SymbolUnion small for performance and diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -12,9 +12,11 @@ #include "InputEvent.h" #include "InputFiles.h" #include "InputGlobal.h" +#include "InputTable.h" #include "OutputSections.h" #include "OutputSegment.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #define DEBUG_TYPE "lld" @@ -46,6 +48,8 @@ return "DefinedData"; case wasm::Symbol::DefinedGlobalKind: return "DefinedGlobal"; + case wasm::Symbol::DefinedTableKind: + return "DefinedTable"; case wasm::Symbol::DefinedEventKind: return "DefinedEvent"; case wasm::Symbol::UndefinedFunctionKind: @@ -54,6 +58,8 @@ return "UndefinedData"; case wasm::Symbol::UndefinedGlobalKind: return "UndefinedGlobal"; + case wasm::Symbol::UndefinedTableKind: + return "UndefinedTable"; case wasm::Symbol::LazyKind: return "LazyKind"; case wasm::Symbol::SectionKind: @@ -93,6 +99,8 @@ return WASM_SYMBOL_TYPE_GLOBAL; if (isa(this)) return WASM_SYMBOL_TYPE_EVENT; + if (isa(this)) + return WASM_SYMBOL_TYPE_TABLE; if (isa(this) || isa(this)) return WASM_SYMBOL_TYPE_SECTION; llvm_unreachable("invalid symbol kind"); @@ -128,6 +136,8 @@ return g->global->live; if (auto *e = dyn_cast(this)) return e->event->live; + if (auto *t = dyn_cast(this)) + return t->table->live; if (InputChunk *c = getChunk()) return c->live; return referenced; @@ -141,6 +151,8 @@ g->global->live = true; if (auto *e = dyn_cast(this)) e->event->live = true; + if (auto *t = dyn_cast(this)) + t->table->live = true; if (InputChunk *c = getChunk()) c->live = true; referenced = true; @@ -337,6 +349,39 @@ event ? &event->signature : nullptr), event(event) {} +void TableSymbol::setLimits(const WasmLimits &Limits) { + if (auto *t = dyn_cast(this)) + t->table->setLimits(Limits); + auto *newType = make(*tableType); + newType->Limits = Limits; + tableType = newType; +} + +uint32_t TableSymbol::getTableNumber() const { + if (auto *t = dyn_cast(this)) + return t->table->getTableNumber(); + assert(tableNumber != INVALID_INDEX); + return tableNumber; +} + +void TableSymbol::setTableNumber(uint32_t number) { + LLVM_DEBUG(dbgs() << "setTableNumber " << name << " -> " << number << "\n"); + assert(tableNumber == INVALID_INDEX); + tableNumber = number; +} + +bool TableSymbol::hasTableNumber() const { + if (auto *t = dyn_cast(this)) + return t->table->hasTableNumber(); + return tableNumber != INVALID_INDEX; +} + +DefinedTable::DefinedTable(StringRef name, uint32_t flags, InputFile *file, + InputTable *table) + : TableSymbol(name, DefinedTableKind, flags, file, + table ? &table->getType() : nullptr), + table(table) {} + const OutputSectionSymbol *SectionSymbol::getOutputSectionSymbol() const { assert(section->outputSec && section->outputSec->sectionSym); return section->outputSec->sectionSym; diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -117,6 +117,10 @@ assert(isSealed); return numImportedEvents; } + uint32_t getNumImportedTables() const { + assert(isSealed); + return numImportedTables; + } std::vector importedSymbols; std::vector gotSymbols; @@ -126,6 +130,7 @@ unsigned numImportedGlobals = 0; unsigned numImportedFunctions = 0; unsigned numImportedEvents = 0; + unsigned numImportedTables = 0; }; class FunctionSection : public SyntheticSection { @@ -145,19 +150,11 @@ public: TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {} - bool isNeeded() const override { - // Always output a table section (or table import), even if there are no - // indirect calls. There are two reasons for this: - // 1. For executables it is useful to have an empty table slot at 0 - // which can be filled with a null function call handler. - // 2. If we don't do this, any program that contains a call_indirect but - // no address-taken function will fail at validation time since it is - // a validation error to include a call_indirect instruction if there - // is not table. - return !config->importTable; - } - + bool isNeeded() const override { return inputTables.size() > 0; }; void writeBody() override; + void addTable(InputTable *table); + + std::vector inputTables; }; class MemorySection : public SyntheticSection { @@ -368,6 +365,8 @@ NameSection *nameSec; ProducersSection *producersSec; TargetFeaturesSection *targetFeaturesSec; + + TableSymbol *indirectFunctionTableSymbol; }; extern OutStruct out; diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -15,6 +15,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "InputTable.h" #include "OutputSegment.h" #include "SymbolTable.h" #include "llvm/Support/Path.h" @@ -96,8 +97,6 @@ uint32_t numImports = importedSymbols.size() + gotSymbols.size(); if (config->importMemory) ++numImports; - if (config->importTable) - ++numImports; return numImports; } @@ -117,8 +116,10 @@ f->setFunctionIndex(numImportedFunctions++); else if (auto *g = dyn_cast(sym)) g->setGlobalIndex(numImportedGlobals++); + else if (auto *e = dyn_cast(sym)) + e->setEventIndex(numImportedEvents++); else - cast(sym)->setEventIndex(numImportedEvents++); + cast(sym)->setTableNumber(numImportedTables++); } void ImportSection::writeBody() { @@ -144,17 +145,6 @@ writeImport(os, import); } - if (config->importTable) { - uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); - WasmImport import; - import.Module = defaultModule; - import.Field = functionTableName; - import.Kind = WASM_EXTERNAL_TABLE; - import.Table.ElemType = WASM_TYPE_FUNCREF; - import.Table.Limits = {0, tableSize, 0}; - writeImport(os, import); - } - for (const Symbol *sym : importedSymbols) { WasmImport import; if (auto *f = dyn_cast(sym)) { @@ -163,6 +153,9 @@ } else if (auto *g = dyn_cast(sym)) { import.Field = g->importName ? *g->importName : sym->getName(); import.Module = g->importModule ? *g->importModule : defaultModule; + } else if (auto *t = dyn_cast(sym)) { + import.Field = t->importName ? *t->importName : sym->getName(); + import.Module = t->importModule ? *t->importModule : defaultModule; } else { import.Field = sym->getName(); import.Module = defaultModule; @@ -174,11 +167,14 @@ } else if (auto *globalSym = dyn_cast(sym)) { import.Kind = WASM_EXTERNAL_GLOBAL; import.Global = *globalSym->getGlobalType(); - } else { - auto *eventSym = cast(sym); + } else if (auto *eventSym = dyn_cast(sym)) { import.Kind = WASM_EXTERNAL_EVENT; import.Event.Attribute = eventSym->getEventType()->Attribute; import.Event.SigIndex = out.typeSec->lookupType(*eventSym->signature); + } else { + auto *tableSym = cast(sym); + import.Kind = WASM_EXTERNAL_TABLE; + import.Table = *tableSym->getTableType(); } writeImport(os, import); } @@ -214,16 +210,20 @@ } void TableSection::writeBody() { - uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); - raw_ostream &os = bodyOutputStream; - writeUleb128(os, 1, "table count"); - WasmLimits limits; - if (config->growableTable) - limits = {0, tableSize, 0}; - else - limits = {WASM_LIMITS_FLAG_HAS_MAX, tableSize, tableSize}; - writeTableType(os, WasmTableType{WASM_TYPE_FUNCREF, limits}); + + writeUleb128(os, inputTables.size(), "table count"); + for (const InputTable *table : inputTables) + writeTableType(os, table->getType()); +} + +void TableSection::addTable(InputTable *table) { + if (!table->live) + return; + uint32_t tableNumber = + out.importSec->getNumImportedTables() + inputTables.size(); + inputTables.push_back(table); + table->setTableNumber(tableNumber); } void MemorySection::writeBody() { @@ -457,6 +457,10 @@ writeUleb128(sub.os, e->getEventIndex(), "index"); if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) writeStr(sub.os, sym->getName(), "sym name"); + } else if (auto *t = dyn_cast(sym)) { + writeUleb128(sub.os, t->getTableNumber(), "table number"); + if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) + writeStr(sub.os, sym->getName(), "sym name"); } else if (isa(sym)) { writeStr(sub.os, sym->getName(), "sym name"); if (auto *dataSym = dyn_cast(sym)) { diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -11,6 +11,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "InputTable.h" #include "MapFile.h" #include "OutputSections.h" #include "OutputSegment.h" @@ -554,6 +555,8 @@ return g->importName.hasValue(); if (auto *f = dyn_cast(sym)) return f->importName.hasValue(); + if (auto *t = dyn_cast(sym)) + return t->importName.hasValue(); return false; } @@ -616,10 +619,12 @@ export_ = {name, WASM_EXTERNAL_GLOBAL, g->getGlobalIndex()}; } else if (auto *e = dyn_cast(sym)) { export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()}; - } else { - auto *d = cast(sym); + } else if (auto *d = dyn_cast(sym)) { out.globalSec->dataAddressGlobals.push_back(d); export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++}; + } else { + auto *t = cast(sym); + export_ = {name, WASM_EXTERNAL_TABLE, t->getTableNumber()}; } LLVM_DEBUG(dbgs() << "Export: " << name << "\n"); @@ -761,6 +766,26 @@ out.eventSec->addEvent(event); } + for (ObjFile *file : symtab->objectFiles) { + LLVM_DEBUG(dbgs() << "Tables: " << file->getName() << "\n"); + for (InputTable *table : file->tables) + out.tableSec->addTable(table); + } + + for (InputTable *table : symtab->syntheticTables) + out.tableSec->addTable(table); + + if (symtab->indirectFunctionTableSymbol) { + uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); + WasmLimits limits = {0, tableSize, 0}; + if (symtab->indirectFunctionTableSymbol->isDefined() && + !config->growableTable) { + limits.Flags |= WASM_LIMITS_FLAG_HAS_MAX; + limits.Maximum = limits.Initial; + } + symtab->indirectFunctionTableSymbol->setLimits(limits); + } + out.globalSec->assignIndexes(); } @@ -1257,10 +1282,12 @@ log("Defined Functions: " + Twine(out.functionSec->inputFunctions.size())); log("Defined Globals : " + Twine(out.globalSec->numGlobals())); log("Defined Events : " + Twine(out.eventSec->inputEvents.size())); + log("Defined Tables : " + Twine(out.tableSec->inputTables.size())); log("Function Imports : " + Twine(out.importSec->getNumImportedFunctions())); log("Global Imports : " + Twine(out.importSec->getNumImportedGlobals())); log("Event Imports : " + Twine(out.importSec->getNumImportedEvents())); + log("Table Imports : " + Twine(out.importSec->getNumImportedTables())); for (ObjFile *file : symtab->objectFiles) file->dumpInfo(); } diff --git a/lld/wasm/WriterUtils.h b/lld/wasm/WriterUtils.h --- a/lld/wasm/WriterUtils.h +++ b/lld/wasm/WriterUtils.h @@ -66,6 +66,7 @@ std::string toString(const llvm::wasm::WasmSignature &sig); std::string toString(const llvm::wasm::WasmGlobalType &type); std::string toString(const llvm::wasm::WasmEventType &type); +std::string toString(const llvm::wasm::WasmTableType &type); } // namespace lld diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp --- a/lld/wasm/WriterUtils.cpp +++ b/lld/wasm/WriterUtils.cpp @@ -66,6 +66,21 @@ return "unknown"; } +static std::string toString(const llvm::wasm::WasmLimits &limits) { + std::string ret; + ret += "flags=0x" + std::to_string(limits.Flags); + ret += "; initial=" + std::to_string(limits.Initial); + if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX) + ret += "; max=" + std::to_string(limits.Maximum); + return ret; +} + +std::string toString(const WasmTableType &type) { + SmallString<128> ret(""); + return "type=" + toString(static_cast(type.ElemType)) + + "; limits=[" + toString(type.Limits) + "]"; +} + namespace wasm { void debugWrite(uint64_t offset, const Twine &msg) { LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n"); @@ -196,7 +211,7 @@ } void writeTableType(raw_ostream &os, const WasmTableType &type) { - writeU8(os, WASM_TYPE_FUNCREF, "table type"); + writeValueType(os, ValType(type.ElemType), "table type"); writeLimits(os, type.Limits); } diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -192,8 +192,8 @@ // For symbols to be exported from the final module Optional ExportName; union { - // For function or global symbols, the index in function or global index - // space. + // For function, table, or global symbols, the index in function, table, or + // global index space. uint32_t ElementIndex; // For a data symbols, the address of the data relative to segment. WasmDataReference DataRef; diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h --- a/llvm/include/llvm/Object/Wasm.h +++ b/llvm/include/llvm/Object/Wasm.h @@ -35,7 +35,8 @@ class WasmSymbol { public: WasmSymbol(const wasm::WasmSymbolInfo &Info, - const wasm::WasmGlobalType *GlobalType, const uint8_t TableType, + const wasm::WasmGlobalType *GlobalType, + const wasm::WasmTableType *TableType, const wasm::WasmEventType *EventType, const wasm::WasmSignature *Signature) : Info(Info), GlobalType(GlobalType), TableType(TableType), @@ -43,7 +44,7 @@ const wasm::WasmSymbolInfo &Info; const wasm::WasmGlobalType *GlobalType; - const uint8_t TableType; + const wasm::WasmTableType *TableType; const wasm::WasmEventType *EventType; const wasm::WasmSignature *Signature; 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 @@ -518,7 +518,7 @@ wasm::WasmSymbolInfo Info; const wasm::WasmSignature *Signature = nullptr; const wasm::WasmGlobalType *GlobalType = nullptr; - uint8_t TableType = 0; + const wasm::WasmTableType *TableType = nullptr; const wasm::WasmEventType *EventType = nullptr; Info.Kind = readUint8(Ctx); @@ -600,7 +600,7 @@ Info.Name = readString(Ctx); unsigned TableIndex = Info.ElementIndex - NumImportedTables; wasm::WasmTable &Table = Tables[TableIndex]; - TableType = Table.Type.ElemType; + TableType = &Table.Type; if (Table.SymbolName.empty()) Table.SymbolName = Info.Name; } else { @@ -611,8 +611,7 @@ } else { Info.Name = Import.Field; } - TableType = Import.Table.ElemType; - // FIXME: Parse limits here too. + TableType = &Import.Table; if (!Import.Module.empty()) { Info.ImportModule = Import.Module; } diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -24,7 +24,7 @@ #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_TYPE_##X } ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), - ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), + ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), ENUM_ENTRY(TABLE), #undef ENUM_ENTRY };