forked from OSchip/llvm-project
[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:
parent
4bb11b3eaf
commit
53e3b81faa
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
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 @@ 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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue