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/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -69,6 +69,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: existingValue = decodeULEB128(loc, &bytesRead); break; case R_WASM_MEMORY_ADDR_LEB64: @@ -152,6 +153,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: @@ -233,6 +235,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: @@ -251,6 +254,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: case R_WASM_TABLE_INDEX_SLEB: case R_WASM_MEMORY_ADDR_SLEB: return 5; 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 @@ -188,7 +190,8 @@ case R_WASM_FUNCTION_INDEX_LEB: case R_WASM_GLOBAL_INDEX_LEB: case R_WASM_GLOBAL_INDEX_I32: - case R_WASM_EVENT_INDEX_LEB: { + case R_WASM_EVENT_INDEX_LEB: + case R_WASM_TABLE_NUMBER_LEB: { const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; return sym.Info.ElementIndex; } @@ -270,6 +273,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"); } @@ -405,6 +410,10 @@ } 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)); @@ -449,6 +458,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]); } @@ -504,6 +517,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"); } @@ -533,6 +553,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" @@ -166,6 +167,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) 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); 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); @@ -410,6 +427,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 +594,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 @@ -35,6 +35,7 @@ class InputGlobal; class InputEvent; class InputSection; +class InputTable; class OutputSection; #define INVALID_INDEX UINT32_MAX @@ -47,11 +48,13 @@ DefinedDataKind, DefinedGlobalKind, DefinedEventKind, + DefinedTableKind, SectionKind, OutputSectionKind, UndefinedFunctionKind, UndefinedDataKind, UndefinedGlobalKind, + UndefinedTableKind, LazyKind, }; @@ -61,7 +64,9 @@ bool isUndefined() const { return symbolKind == UndefinedFunctionKind || - symbolKind == UndefinedDataKind || symbolKind == UndefinedGlobalKind; + symbolKind == UndefinedDataKind || + symbolKind == UndefinedGlobalKind || + symbolKind == UndefinedTableKind; } bool isLazy() const { return symbolKind == LazyKind; } @@ -359,6 +364,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. @@ -524,11 +578,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: @@ -95,6 +101,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"); @@ -130,6 +138,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; @@ -143,6 +153,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; @@ -339,6 +351,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 (const 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 (const 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 @@ -98,7 +98,7 @@ class ImportSection : public SyntheticSection { public: - ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {} + ImportSection(); bool isNeeded() const override { return getNumImports() > 0; } void writeBody() override; void addImport(Symbol *sym); @@ -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 { @@ -146,18 +151,19 @@ 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; + // The linker currently always writes an indirect function table to the + // output, so unless the indirect function table is imported, we need a + // table section. FIXME: Treat __indirect_function_table as a normal + // symbol, and only residualize a table section as needed. + if (!config->importTable) + return true; + return inputTables.size() > 0; } void writeBody() override; + void addTable(InputTable *table); + + std::vector inputTables; }; class MemorySection : public SyntheticSection { 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" @@ -91,6 +92,13 @@ writeSig(bodyOutputStream, *sig); } +ImportSection::ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) { + // FIXME: Remove when we treat __indirect_function_table as any other symbol. + if (config->importTable) { + numImportedTables++; + } +} + uint32_t ImportSection::getNumImports() const { assert(isSealed); uint32_t numImports = importedSymbols.size() + gotSymbols.size(); @@ -117,8 +125,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() { @@ -163,6 +173,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 +187,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 +230,37 @@ } void TableSection::writeBody() { - uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); + bool hasIndirectFunctionTable = !config->importTable; + + uint32_t tableCount = inputTables.size(); + if (hasIndirectFunctionTable) + tableCount++; 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, tableCount, "table count"); + + if (hasIndirectFunctionTable) { + uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); + 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}); + } + + 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() { @@ -458,6 +495,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" @@ -574,6 +575,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; } @@ -636,10 +639,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"); @@ -781,6 +786,12 @@ 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); + } + out.globalSec->assignIndexes(); } @@ -1385,10 +1396,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 @@ -69,6 +69,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 @@ -64,6 +64,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"); @@ -202,7 +217,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 @@ -527,7 +527,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); @@ -609,7 +609,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 { @@ -620,8 +620,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 };