[WebAssembly] Use ValType instead of integer types to model wasm tables (#78012)

LLVM models some features found in the binary format with raw integers
and others with nested or enumerated types. This PR switches modeling of
tables and segments to use wasm::ValType rather than uint32_t. This NFC
change is in preparation for modeling more reference types, but IMO is
also cleaner and closer to the spec.
This commit is contained in:
Derek Schuff 2024-01-17 11:29:19 -08:00 committed by GitHub
parent bc90b91885
commit 103fa3250c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 232 additions and 230 deletions

View File

@ -320,7 +320,7 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded(
// it has an unexpected name or type, assume that it's not actually the
// indirect function table.
if (tableImport->Field != functionTableName ||
tableImport->Table.ElemType != uint8_t(ValType::FUNCREF)) {
tableImport->Table.ElemType != ValType::FUNCREF) {
error(toString(this) + ": table import " + Twine(tableImport->Field) +
" is missing a symbol table entry.");
return;

View File

@ -676,7 +676,7 @@ Symbol *SymbolTable::addUndefinedTag(StringRef name,
TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
WasmLimits limits{0, 0, 0}; // Set by the writer.
WasmTableType *type = make<WasmTableType>();
type->ElemType = uint8_t(ValType::FUNCREF);
type->ElemType = ValType::FUNCREF;
type->Limits = limits;
StringRef module(defaultModule);
uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
@ -690,7 +690,7 @@ TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
TableSymbol *SymbolTable::createDefinedIndirectFunctionTable(StringRef name) {
const uint32_t invalidIndex = -1;
WasmLimits limits{0, 0, 0}; // Set by the writer.
WasmTableType type{uint8_t(ValType::FUNCREF), limits};
WasmTableType type{ValType::FUNCREF, limits};
WasmTable desc{invalidIndex, type, name};
InputTable *table = make<InputTable>(desc, nullptr);
uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;

View File

@ -31,213 +31,6 @@ const uint32_t WasmMetadataVersion = 0x2;
// Wasm uses a 64k page size
const uint32_t WasmPageSize = 65536;
struct WasmObjectHeader {
StringRef Magic;
uint32_t Version;
};
struct WasmDylinkImportInfo {
StringRef Module;
StringRef Field;
uint32_t Flags;
};
struct WasmDylinkExportInfo {
StringRef Name;
uint32_t Flags;
};
struct WasmDylinkInfo {
uint32_t MemorySize; // Memory size in bytes
uint32_t MemoryAlignment; // P2 alignment of memory
uint32_t TableSize; // Table size in elements
uint32_t TableAlignment; // P2 alignment of table
std::vector<StringRef> Needed; // Shared library dependencies
std::vector<WasmDylinkImportInfo> ImportInfo;
std::vector<WasmDylinkExportInfo> ExportInfo;
};
struct WasmProducerInfo {
std::vector<std::pair<std::string, std::string>> Languages;
std::vector<std::pair<std::string, std::string>> Tools;
std::vector<std::pair<std::string, std::string>> SDKs;
};
struct WasmFeatureEntry {
uint8_t Prefix;
std::string Name;
};
struct WasmExport {
StringRef Name;
uint8_t Kind;
uint32_t Index;
};
struct WasmLimits {
uint8_t Flags;
uint64_t Minimum;
uint64_t Maximum;
};
struct WasmTableType {
uint8_t ElemType;
WasmLimits Limits;
};
struct WasmTable {
uint32_t Index;
WasmTableType Type;
StringRef SymbolName; // from the "linking" section
};
struct WasmInitExprMVP {
uint8_t Opcode;
union {
int32_t Int32;
int64_t Int64;
uint32_t Float32;
uint64_t Float64;
uint32_t Global;
} Value;
};
struct WasmInitExpr {
uint8_t Extended; // Set to non-zero if extended const is used (i.e. more than
// one instruction)
WasmInitExprMVP Inst;
ArrayRef<uint8_t> Body;
};
struct WasmGlobalType {
uint8_t Type;
bool Mutable;
};
struct WasmGlobal {
uint32_t Index;
WasmGlobalType Type;
WasmInitExpr InitExpr;
StringRef SymbolName; // from the "linking" section
};
struct WasmTag {
uint32_t Index;
uint32_t SigIndex;
StringRef SymbolName; // from the "linking" section
};
struct WasmImport {
StringRef Module;
StringRef Field;
uint8_t Kind;
union {
uint32_t SigIndex;
WasmGlobalType Global;
WasmTableType Table;
WasmLimits Memory;
};
};
struct WasmLocalDecl {
uint8_t Type;
uint32_t Count;
};
struct WasmFunction {
uint32_t Index;
uint32_t SigIndex;
std::vector<WasmLocalDecl> Locals;
ArrayRef<uint8_t> Body;
uint32_t CodeSectionOffset;
uint32_t Size;
uint32_t CodeOffset; // start of Locals and Body
std::optional<StringRef> ExportName; // from the "export" section
StringRef SymbolName; // from the "linking" section
StringRef DebugName; // from the "name" section
uint32_t Comdat; // from the "comdat info" section
};
struct WasmDataSegment {
uint32_t InitFlags;
// Present if InitFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX.
uint32_t MemoryIndex;
// Present if InitFlags & WASM_DATA_SEGMENT_IS_PASSIVE == 0.
WasmInitExpr Offset;
ArrayRef<uint8_t> Content;
StringRef Name; // from the "segment info" section
uint32_t Alignment;
uint32_t LinkingFlags;
uint32_t Comdat; // from the "comdat info" section
};
struct WasmElemSegment {
uint32_t Flags;
uint32_t TableNumber;
uint8_t ElemKind;
WasmInitExpr Offset;
std::vector<uint32_t> Functions;
};
// Represents the location of a Wasm data symbol within a WasmDataSegment, as
// the index of the segment, and the offset and size within the segment.
struct WasmDataReference {
uint32_t Segment;
uint64_t Offset;
uint64_t Size;
};
struct WasmRelocation {
uint8_t Type; // The type of the relocation.
uint32_t Index; // Index into either symbol or type index space.
uint64_t Offset; // Offset from the start of the section.
int64_t Addend; // A value to add to the symbol.
};
struct WasmInitFunc {
uint32_t Priority;
uint32_t Symbol;
};
struct WasmSymbolInfo {
StringRef Name;
uint8_t Kind;
uint32_t Flags;
// For undefined symbols the module of the import
std::optional<StringRef> ImportModule;
// For undefined symbols the name of the import
std::optional<StringRef> ImportName;
// For symbols to be exported from the final module
std::optional<StringRef> ExportName;
union {
// 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;
};
};
enum class NameType {
FUNCTION,
GLOBAL,
DATA_SEGMENT,
};
struct WasmDebugName {
NameType Type;
uint32_t Index;
StringRef Name;
};
struct WasmLinkingData {
uint32_t Version;
std::vector<WasmInitFunc> InitFunctions;
std::vector<StringRef> Comdats;
std::vector<WasmSymbolInfo> SymbolTable;
};
enum : unsigned {
WASM_SEC_CUSTOM = 0, // Custom / User-defined section
WASM_SEC_TYPE = 1, // Function signature declarations
@ -422,6 +215,11 @@ enum : unsigned {
#undef WASM_RELOC
struct WasmObjectHeader {
StringRef Magic;
uint32_t Version;
};
// Subset of types that a value can have
enum class ValType {
I32 = WASM_TYPE_I32,
@ -433,6 +231,208 @@ enum class ValType {
EXTERNREF = WASM_TYPE_EXTERNREF,
};
struct WasmDylinkImportInfo {
StringRef Module;
StringRef Field;
uint32_t Flags;
};
struct WasmDylinkExportInfo {
StringRef Name;
uint32_t Flags;
};
struct WasmDylinkInfo {
uint32_t MemorySize; // Memory size in bytes
uint32_t MemoryAlignment; // P2 alignment of memory
uint32_t TableSize; // Table size in elements
uint32_t TableAlignment; // P2 alignment of table
std::vector<StringRef> Needed; // Shared library dependencies
std::vector<WasmDylinkImportInfo> ImportInfo;
std::vector<WasmDylinkExportInfo> ExportInfo;
};
struct WasmProducerInfo {
std::vector<std::pair<std::string, std::string>> Languages;
std::vector<std::pair<std::string, std::string>> Tools;
std::vector<std::pair<std::string, std::string>> SDKs;
};
struct WasmFeatureEntry {
uint8_t Prefix;
std::string Name;
};
struct WasmExport {
StringRef Name;
uint8_t Kind;
uint32_t Index;
};
struct WasmLimits {
uint8_t Flags;
uint64_t Minimum;
uint64_t Maximum;
};
struct WasmTableType {
ValType ElemType;
WasmLimits Limits;
};
struct WasmTable {
uint32_t Index;
WasmTableType Type;
StringRef SymbolName; // from the "linking" section
};
struct WasmInitExprMVP {
uint8_t Opcode;
union {
int32_t Int32;
int64_t Int64;
uint32_t Float32;
uint64_t Float64;
uint32_t Global;
} Value;
};
struct WasmInitExpr {
uint8_t Extended; // Set to non-zero if extended const is used (i.e. more than
// one instruction)
WasmInitExprMVP Inst;
ArrayRef<uint8_t> Body;
};
struct WasmGlobalType {
uint8_t Type; // TODO: make this a ValType?
bool Mutable;
};
struct WasmGlobal {
uint32_t Index;
WasmGlobalType Type;
WasmInitExpr InitExpr;
StringRef SymbolName; // from the "linking" section
};
struct WasmTag {
uint32_t Index;
uint32_t SigIndex;
StringRef SymbolName; // from the "linking" section
};
struct WasmImport {
StringRef Module;
StringRef Field;
uint8_t Kind;
union {
uint32_t SigIndex;
WasmGlobalType Global;
WasmTableType Table;
WasmLimits Memory;
};
};
struct WasmLocalDecl {
uint8_t Type;
uint32_t Count;
};
struct WasmFunction {
uint32_t Index;
uint32_t SigIndex;
std::vector<WasmLocalDecl> Locals;
ArrayRef<uint8_t> Body;
uint32_t CodeSectionOffset;
uint32_t Size;
uint32_t CodeOffset; // start of Locals and Body
std::optional<StringRef> ExportName; // from the "export" section
StringRef SymbolName; // from the "linking" section
StringRef DebugName; // from the "name" section
uint32_t Comdat; // from the "comdat info" section
};
struct WasmDataSegment {
uint32_t InitFlags;
// Present if InitFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX.
uint32_t MemoryIndex;
// Present if InitFlags & WASM_DATA_SEGMENT_IS_PASSIVE == 0.
WasmInitExpr Offset;
ArrayRef<uint8_t> Content;
StringRef Name; // from the "segment info" section
uint32_t Alignment;
uint32_t LinkingFlags;
uint32_t Comdat; // from the "comdat info" section
};
struct WasmElemSegment {
uint32_t Flags;
uint32_t TableNumber;
ValType ElemKind;
WasmInitExpr Offset;
std::vector<uint32_t> Functions;
};
// Represents the location of a Wasm data symbol within a WasmDataSegment, as
// the index of the segment, and the offset and size within the segment.
struct WasmDataReference {
uint32_t Segment;
uint64_t Offset;
uint64_t Size;
};
struct WasmRelocation {
uint8_t Type; // The type of the relocation.
uint32_t Index; // Index into either symbol or type index space.
uint64_t Offset; // Offset from the start of the section.
int64_t Addend; // A value to add to the symbol.
};
struct WasmInitFunc {
uint32_t Priority;
uint32_t Symbol;
};
struct WasmSymbolInfo {
StringRef Name;
uint8_t Kind;
uint32_t Flags;
// For undefined symbols the module of the import
std::optional<StringRef> ImportModule;
// For undefined symbols the name of the import
std::optional<StringRef> ImportName;
// For symbols to be exported from the final module
std::optional<StringRef> ExportName;
union {
// 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;
};
};
enum class NameType {
FUNCTION,
GLOBAL,
DATA_SEGMENT,
};
struct WasmDebugName {
NameType Type;
uint32_t Index;
StringRef Name;
};
struct WasmLinkingData {
uint32_t Version;
std::vector<WasmInitFunc> InitFunctions;
std::vector<StringRef> Comdats;
std::vector<WasmSymbolInfo> SymbolTable;
};
struct WasmSignature {
SmallVector<ValType, 1> Returns;
SmallVector<ValType, 4> Params;

View File

@ -87,11 +87,12 @@ template <> struct DenseMapInfo<wasm::WasmLimits, void> {
template <> struct DenseMapInfo<wasm::WasmTableType, void> {
static wasm::WasmTableType getEmptyKey() {
return wasm::WasmTableType{
0, DenseMapInfo<wasm::WasmLimits, void>::getEmptyKey()};
wasm::ValType(0), DenseMapInfo<wasm::WasmLimits, void>::getEmptyKey()};
}
static wasm::WasmTableType getTombstoneKey() {
return wasm::WasmTableType{
1, DenseMapInfo<wasm::WasmLimits, void>::getTombstoneKey()};
wasm::ValType(1),
DenseMapInfo<wasm::WasmLimits, void>::getTombstoneKey()};
}
static unsigned getHashValue(const wasm::WasmTableType &TableType) {
return hash_combine(

View File

@ -112,7 +112,7 @@ public:
bool isFunctionTable() const {
return isTable() && hasTableType() &&
getTableType().ElemType == wasm::WASM_TYPE_FUNCREF;
getTableType().ElemType == wasm::ValType::FUNCREF;
}
void setFunctionTable() {
setType(wasm::WASM_SYMBOL_TYPE_TABLE);
@ -144,7 +144,7 @@ public:
// Declare a table with element type VT and no limits (min size 0, no max
// size).
wasm::WasmLimits Limits = {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};
setTableType({uint8_t(VT), Limits});
setTableType({VT, Limits});
}
};

View File

@ -972,7 +972,7 @@ void WasmObjectWriter::writeTableSection(ArrayRef<wasm::WasmTable> Tables) {
encodeULEB128(Tables.size(), W->OS);
for (const wasm::WasmTable &Table : Tables) {
encodeULEB128(Table.Type.ElemType, W->OS);
encodeULEB128((uint32_t)Table.Type.ElemType, W->OS);
encodeULEB128(Table.Type.Limits.Flags, W->OS);
encodeULEB128(Table.Type.Limits.Minimum, W->OS);
if (Table.Type.Limits.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX)

View File

@ -258,7 +258,7 @@ static wasm::WasmLimits readLimits(WasmObjectFile::ReadContext &Ctx) {
static wasm::WasmTableType readTableType(WasmObjectFile::ReadContext &Ctx) {
wasm::WasmTableType TableType;
TableType.ElemType = readUint8(Ctx);
TableType.ElemType = wasm::ValType(readVaruint32(Ctx));
TableType.Limits = readLimits(Ctx);
return TableType;
}
@ -1163,8 +1163,8 @@ Error WasmObjectFile::parseImportSection(ReadContext &Ctx) {
Im.Table = readTableType(Ctx);
NumImportedTables++;
auto ElemType = Im.Table.ElemType;
if (ElemType != wasm::WASM_TYPE_FUNCREF &&
ElemType != wasm::WASM_TYPE_EXTERNREF)
if (ElemType != wasm::ValType::FUNCREF &&
ElemType != wasm::ValType::EXTERNREF)
return make_error<GenericBinaryError>("invalid table element type",
object_error::parse_failed);
break;
@ -1220,8 +1220,8 @@ Error WasmObjectFile::parseTableSection(ReadContext &Ctx) {
T.Index = NumImportedTables + Tables.size();
Tables.push_back(T);
auto ElemType = Tables.back().Type.ElemType;
if (ElemType != wasm::WASM_TYPE_FUNCREF &&
ElemType != wasm::WASM_TYPE_EXTERNREF) {
if (ElemType != wasm::ValType::FUNCREF &&
ElemType != wasm::ValType::EXTERNREF) {
return make_error<GenericBinaryError>("invalid table element type",
object_error::parse_failed);
}
@ -1534,21 +1534,22 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
}
if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
Segment.ElemKind = readUint8(Ctx);
auto ElemKind = readVaruint32(Ctx);
if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS) {
if (Segment.ElemKind != uint8_t(wasm::ValType::FUNCREF) &&
Segment.ElemKind != uint8_t(wasm::ValType::EXTERNREF)) {
Segment.ElemKind = wasm::ValType(ElemKind);
if (Segment.ElemKind != wasm::ValType::FUNCREF &&
Segment.ElemKind != wasm::ValType::EXTERNREF) {
return make_error<GenericBinaryError>("invalid reference type",
object_error::parse_failed);
}
} else {
if (Segment.ElemKind != 0)
if (ElemKind != 0)
return make_error<GenericBinaryError>("invalid elemtype",
object_error::parse_failed);
Segment.ElemKind = uint8_t(wasm::ValType::FUNCREF);
Segment.ElemKind = wasm::ValType::FUNCREF;
}
} else {
Segment.ElemKind = uint8_t(wasm::ValType::FUNCREF);
Segment.ElemKind = wasm::ValType::FUNCREF;
}
if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS)

View File

@ -851,7 +851,7 @@ public:
// symbol
auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
wasm::WasmTableType Type = {uint8_t(*ElemType), Limits};
wasm::WasmTableType Type = {*ElemType, Limits};
WasmSym->setTableType(Type);
TOut.emitTableType(WasmSym);
return expect(AsmToken::EndOfStatement, "EOL");

View File

@ -134,7 +134,7 @@ MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
Sym->setWeak(true);
wasm::WasmLimits Limits = {0, 1, 1};
wasm::WasmTableType TableType = {wasm::WASM_TYPE_FUNCREF, Limits};
wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits};
Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
Sym->setTableType(TableType);
}

View File

@ -44,7 +44,7 @@ static WasmYAML::Table makeTable(uint32_t Index,
const wasm::WasmTableType &Type) {
WasmYAML::Table T;
T.Index = Index;
T.ElemType = Type.ElemType;
T.ElemType = (uint32_t)Type.ElemType;
T.TableLimits = makeLimits(Type.Limits);
return T;
}
@ -334,7 +334,7 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
WasmYAML::ElemSegment Seg;
Seg.Flags = Segment.Flags;
Seg.TableNumber = Segment.TableNumber;
Seg.ElemKind = Segment.ElemKind;
Seg.ElemKind = (uint32_t)Segment.ElemKind;
Seg.Offset.Extended = Segment.Offset.Extended;
if (Seg.Offset.Extended) {
Seg.Offset.Body = yaml::BinaryRef(Segment.Offset.Body);