[lld][WebAssembly] Add support for handling table symbols

This commit adds table symbol support in a partial way, while still
including some special cases for the __indirect_function_table symbol.
No change in tests.

Differential Revision: https://reviews.llvm.org/D94075
This commit is contained in:
Andy Wingo 2021-01-05 12:08:58 +01:00
parent 4bb11b3eaf
commit 53e3b81faa
19 changed files with 395 additions and 41 deletions

View File

@ -49,8 +49,11 @@ struct WasmEventType;
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::WasmEventType;
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 {

View File

@ -69,6 +69,7 @@ void InputChunk::verifyRelocTargets() const {
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 @@ void InputChunk::writeTo(uint8_t *buf) const {
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 @@ static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
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 @@ static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
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;

View File

@ -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 @@ void ObjFile::dumpInfo() const {
"\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 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
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 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone)
}
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 @@ void ObjFile::parse(bool ignoreComdats) {
}
setRelocs(functions, codeSection);
// Populate `Tables`.
for (const WasmTable &t : wasmObj->tables())
tables.emplace_back(make<InputTable>(t, this));
// Populate `Globals`.
for (const WasmGlobal &g : wasmObj->globals())
globals.emplace_back(make<InputGlobal>(g, this));
@ -449,6 +458,10 @@ EventSymbol *ObjFile::getEventSymbol(uint32_t index) const {
return cast<EventSymbol>(symbols[index]);
}
TableSymbol *ObjFile::getTableSymbol(uint32_t index) const {
return cast<TableSymbol>(symbols[index]);
}
SectionSymbol *ObjFile::getSectionSymbol(uint32_t index) const {
return cast<SectionSymbol>(symbols[index]);
}
@ -504,6 +517,13 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
return make<DefinedEvent>(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<DefinedTable>(name, flags, this, table);
return symtab->addDefinedTable(name, flags, this, table);
}
}
llvm_unreachable("unknown symbol kind");
}
@ -533,6 +553,14 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) {
return symtab->addUndefinedGlobal(name, sym.Info.ImportName,
sym.Info.ImportModule, flags, this,
sym.GlobalType);
case WASM_SYMBOL_TYPE_TABLE:
if (sym.isBindingLocal())
return make<UndefinedTable>(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");
}

View File

@ -32,6 +32,7 @@ class InputFunction;
class InputSegment;
class InputGlobal;
class InputEvent;
class InputTable;
class InputSection;
// If --reproduce option is given, all input files are written
@ -139,6 +140,7 @@ public:
std::vector<InputFunction *> functions;
std::vector<InputGlobal *> globals;
std::vector<InputEvent *> events;
std::vector<InputTable *> tables;
std::vector<InputSection *> customSections;
llvm::DenseMap<uint32_t, InputSection *> customSectionsByIndex;
@ -148,6 +150,7 @@ public:
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);

60
lld/wasm/InputTable.h Normal file
View File

@ -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<uint32_t> 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

View File

@ -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 @@ void markLive() {
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)

View File

@ -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 @@ static void checkEventType(const Symbol *existing, const InputFile *file,
toString(*newSig) + " in " + toString(file));
}
static void checkTableType(const Symbol *existing, const InputFile *file,
const WasmTableType *newType) {
if (!isa<TableSymbol>(existing)) {
reportTypeError(existing, file, WASM_SYMBOL_TYPE_TABLE);
return;
}
const WasmTableType *oldType = cast<TableSymbol>(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<DataSymbol>(existing))
reportTypeError(existing, file, WASM_SYMBOL_TYPE_DATA);
@ -410,6 +427,30 @@ Symbol *SymbolTable::addDefinedEvent(StringRef name, uint32_t flags,
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<DefinedTable>(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 @@ Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
return s;
}
Symbol *SymbolTable::addUndefinedTable(StringRef name,
Optional<StringRef> importName,
Optional<StringRef> 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<UndefinedTable>(s, name, importName, importModule, flags,
file, type);
else if (auto *lazy = dyn_cast<LazySymbol>(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();

View File

@ -60,6 +60,8 @@ public:
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<StringRef> importName,
@ -73,6 +75,11 @@ public:
llvm::Optional<StringRef> importModule,
uint32_t flags, InputFile *file,
const WasmGlobalType *type);
Symbol *addUndefinedTable(StringRef name,
llvm::Optional<StringRef> importName,
llvm::Optional<StringRef> importModule,
uint32_t flags, InputFile *file,
const WasmTableType *type);
void addLazy(ArchiveFile *f, const llvm::object::Archive::Symbol *sym);

View File

@ -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 @@ std::string toString(wasm::Symbol::Kind kind) {
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 @@ std::string toString(wasm::Symbol::Kind kind) {
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 @@ WasmSymbolType Symbol::getWasmType() const {
return WASM_SYMBOL_TYPE_GLOBAL;
if (isa<EventSymbol>(this))
return WASM_SYMBOL_TYPE_EVENT;
if (isa<TableSymbol>(this))
return WASM_SYMBOL_TYPE_TABLE;
if (isa<SectionSymbol>(this) || isa<OutputSectionSymbol>(this))
return WASM_SYMBOL_TYPE_SECTION;
llvm_unreachable("invalid symbol kind");
@ -130,6 +138,8 @@ bool Symbol::isLive() const {
return g->global->live;
if (auto *e = dyn_cast<DefinedEvent>(this))
return e->event->live;
if (auto *t = dyn_cast<DefinedTable>(this))
return t->table->live;
if (InputChunk *c = getChunk())
return c->live;
return referenced;
@ -143,6 +153,8 @@ void Symbol::markLive() {
g->global->live = true;
if (auto *e = dyn_cast<DefinedEvent>(this))
e->event->live = true;
if (auto *t = dyn_cast<DefinedTable>(this))
t->table->live = true;
if (InputChunk *c = getChunk())
c->live = true;
referenced = true;
@ -339,6 +351,39 @@ DefinedEvent::DefinedEvent(StringRef name, uint32_t flags, InputFile *file,
event ? &event->signature : nullptr),
event(event) {}
void TableSymbol::setLimits(const WasmLimits &limits) {
if (auto *t = dyn_cast<DefinedTable>(this))
t->table->setLimits(limits);
auto *newType = make<WasmTableType>(*tableType);
newType->Limits = limits;
tableType = newType;
}
uint32_t TableSymbol::getTableNumber() const {
if (const auto *t = dyn_cast<DefinedTable>(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<DefinedTable>(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;

View File

@ -35,6 +35,7 @@ class InputFunction;
class InputGlobal;
class InputEvent;
class InputSection;
class InputTable;
class OutputSection;
#define INVALID_INDEX UINT32_MAX
@ -47,11 +48,13 @@ public:
DefinedDataKind,
DefinedGlobalKind,
DefinedEventKind,
DefinedTableKind,
SectionKind,
OutputSectionKind,
UndefinedFunctionKind,
UndefinedDataKind,
UndefinedGlobalKind,
UndefinedTableKind,
LazyKind,
};
@ -61,7 +64,9 @@ public:
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 @@ public:
llvm::Optional<StringRef> 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<StringRef> importName,
llvm::Optional<StringRef> 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<StringRef> importName;
llvm::Optional<StringRef> 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 @@ union SymbolUnion {
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

View File

@ -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 @@ void TypeSection::writeBody() {
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 @@ void ImportSection::addImport(Symbol *sym) {
f->setFunctionIndex(numImportedFunctions++);
else if (auto *g = dyn_cast<GlobalSymbol>(sym))
g->setGlobalIndex(numImportedGlobals++);
else if (auto *e = dyn_cast<EventSymbol>(sym))
e->setEventIndex(numImportedEvents++);
else
cast<EventSymbol>(sym)->setEventIndex(numImportedEvents++);
cast<TableSymbol>(sym)->setTableNumber(numImportedTables++);
}
void ImportSection::writeBody() {
@ -163,6 +173,9 @@ void ImportSection::writeBody() {
} else if (auto *g = dyn_cast<UndefinedGlobal>(sym)) {
import.Field = g->importName ? *g->importName : sym->getName();
import.Module = g->importModule ? *g->importModule : defaultModule;
} else if (auto *t = dyn_cast<UndefinedTable>(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 @@ void ImportSection::writeBody() {
} else if (auto *globalSym = dyn_cast<GlobalSymbol>(sym)) {
import.Kind = WASM_EXTERNAL_GLOBAL;
import.Global = *globalSym->getGlobalType();
} else {
auto *eventSym = cast<EventSymbol>(sym);
} else if (auto *eventSym = dyn_cast<EventSymbol>(sym)) {
import.Kind = WASM_EXTERNAL_EVENT;
import.Event.Attribute = eventSym->getEventType()->Attribute;
import.Event.SigIndex = out.typeSec->lookupType(*eventSym->signature);
} else {
auto *tableSym = cast<TableSymbol>(sym);
import.Kind = WASM_EXTERNAL_TABLE;
import.Table = *tableSym->getTableType();
}
writeImport(os, import);
}
@ -214,16 +230,37 @@ void FunctionSection::addFunction(InputFunction *func) {
}
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");
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 @@ void LinkingSection::writeBody() {
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<TableSymbol>(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<DataSymbol>(sym)) {
writeStr(sub.os, sym->getName(), "sym name");
if (auto *dataSym = dyn_cast<DefinedData>(sym)) {

View File

@ -98,7 +98,7 @@ protected:
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 @@ public:
assert(isSealed);
return numImportedEvents;
}
uint32_t getNumImportedTables() const {
assert(isSealed);
return numImportedTables;
}
std::vector<const Symbol *> importedSymbols;
std::vector<const Symbol *> gotSymbols;
@ -126,6 +130,7 @@ protected:
unsigned numImportedGlobals = 0;
unsigned numImportedFunctions = 0;
unsigned numImportedEvents = 0;
unsigned numImportedTables = 0;
};
class FunctionSection : public SyntheticSection {
@ -146,18 +151,19 @@ 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;
// 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<InputTable *> inputTables;
};
class MemorySection : public SyntheticSection {

View File

@ -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 @@ static bool shouldImport(Symbol *sym) {
return g->importName.hasValue();
if (auto *f = dyn_cast<UndefinedFunction>(sym))
return f->importName.hasValue();
if (auto *t = dyn_cast<UndefinedTable>(sym))
return t->importName.hasValue();
return false;
}
@ -636,10 +639,12 @@ void Writer::calculateExports() {
export_ = {name, WASM_EXTERNAL_GLOBAL, g->getGlobalIndex()};
} else if (auto *e = dyn_cast<DefinedEvent>(sym)) {
export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()};
} else {
auto *d = cast<DefinedData>(sym);
} else if (auto *d = dyn_cast<DefinedData>(sym)) {
out.globalSec->dataAddressGlobals.push_back(d);
export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
} else {
auto *t = cast<DefinedTable>(sym);
export_ = {name, WASM_EXTERNAL_TABLE, t->getTableNumber()};
}
LLVM_DEBUG(dbgs() << "Export: " << name << "\n");
@ -781,6 +786,12 @@ void Writer::assignIndexes() {
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 @@ void Writer::run() {
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();
}

View File

@ -64,6 +64,21 @@ std::string toString(const WasmEventType &type) {
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<ValType>(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 writeEvent(raw_ostream &os, const WasmEvent &event) {
}
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);
}

View File

@ -69,6 +69,7 @@ std::string toString(llvm::wasm::ValType type);
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

View File

@ -192,8 +192,8 @@ struct WasmSymbolInfo {
// For symbols to be exported from the final module
Optional<StringRef> 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;

View File

@ -35,7 +35,8 @@ namespace object {
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 @@ public:
const wasm::WasmSymbolInfo &Info;
const wasm::WasmGlobalType *GlobalType;
const uint8_t TableType;
const wasm::WasmTableType *TableType;
const wasm::WasmEventType *EventType;
const wasm::WasmSignature *Signature;

View File

@ -527,7 +527,7 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
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 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
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 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
} 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;
}

View File

@ -24,7 +24,7 @@ static const EnumEntry<unsigned> WasmSymbolTypes[] = {
#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
};