From 7f409cd82b322038f08a984a07377758e76b0e4c Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 25 Jan 2024 09:48:38 -0800 Subject: [PATCH] [Object][Wasm] Allow parsing of GC types in type and table sections (#79235) This change allows a WasmObjectFile to be created from a wasm file even if it uses typed funcrefs and GC types. It does not significantly change how lib/Object models its various internal types (e.g. WasmSignature, WasmElemSegment), so LLVM does not really "support" or understand such files, but it is sufficient to parse the type, global and element sections, discarding types that are not understood. This is useful for low-level binary tools such as nm and objcopy, which use only limited aspects of the binary (such as function definitions) or deal with sections as opaque blobs. This is done by allowing `WasmValType` to have a value of `OTHERREF` (representing any unmodeled reference type), and adding a field to `WasmSignature` indicating it's a placeholder for an unmodeled reference type (since there is a 1:1 correspondence between WasmSignature objects and types in the type section). Then the object file parsers for the type and element sections are expanded to parse encoded reference types and discard any unmodeled fields. --- lld/wasm/InputChunks.h | 5 +- lld/wasm/InputElement.h | 4 +- lld/wasm/InputFiles.cpp | 3 + lld/wasm/WriterUtils.cpp | 2 + llvm/include/llvm/BinaryFormat/Wasm.h | 47 ++++- llvm/include/llvm/Object/Wasm.h | 6 +- llvm/lib/MC/WasmObjectWriter.cpp | 2 + llvm/lib/Object/WasmObjectFile.cpp | 173 +++++++++++++++--- llvm/lib/ObjectYAML/WasmYAML.cpp | 12 +- .../WebAssembly/WebAssemblyAsmPrinter.cpp | 3 +- llvm/test/Object/Inputs/WASM/multi-table.wasm | Bin 0 -> 185 bytes llvm/test/Object/wasm-obj2yaml-tables.test | 112 ++++++++++++ 12 files changed, 333 insertions(+), 36 deletions(-) create mode 100644 llvm/test/Object/Inputs/WASM/multi-table.wasm create mode 100644 llvm/test/Object/wasm-obj2yaml-tables.test diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h index 1e430832fb84c..ad1d45e335eac 100644 --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -259,10 +259,13 @@ class InputFunction : public InputChunk { file->codeSection->Content.slice(inputSectionOffset, function->Size); debugName = function->DebugName; comdat = function->Comdat; + assert(s.Kind != WasmSignature::Placeholder); } InputFunction(StringRef name, const WasmSignature &s) - : InputChunk(nullptr, InputChunk::Function, name), signature(s) {} + : InputChunk(nullptr, InputChunk::Function, name), signature(s) { + assert(s.Kind == WasmSignature::Function); + } static bool classof(const InputChunk *c) { return c->kind() == InputChunk::Function || diff --git a/lld/wasm/InputElement.h b/lld/wasm/InputElement.h index 46e21d7c7dfd8..10dc2a3e4a826 100644 --- a/lld/wasm/InputElement.h +++ b/lld/wasm/InputElement.h @@ -76,7 +76,9 @@ class InputGlobal : public InputElement { class InputTag : public InputElement { public: InputTag(const WasmSignature &s, const WasmTag &t, ObjFile *f) - : InputElement(t.SymbolName, f), signature(s) {} + : InputElement(t.SymbolName, f), signature(s) { + assert(s.Kind == WasmSignature::Tag); + } const WasmSignature &signature; }; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index f5e946aca8b2a..f43c39b218787 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -81,6 +81,9 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName, std::unique_ptr bin = CHECK(createBinary(mb), mb.getBufferIdentifier()); auto *obj = cast(bin.get()); + if (obj->hasUnmodeledTypes()) + fatal(toString(mb.getBufferIdentifier()) + + "file has unmodeled reference or GC types"); if (obj->isSharedObject()) return make(mb); return make(mb, archiveName, lazy); diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp index 418192f302375..cdd2c42f939ef 100644 --- a/lld/wasm/WriterUtils.cpp +++ b/lld/wasm/WriterUtils.cpp @@ -35,6 +35,8 @@ std::string toString(ValType type) { return "funcref"; case ValType::EXTERNREF: return "externref"; + case ValType::OTHERREF: + return "otherref"; } llvm_unreachable("Invalid wasm::ValType"); } diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h index c6f837531363e..cd13b7014bfe2 100644 --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -56,9 +56,25 @@ enum : unsigned { WASM_TYPE_F32 = 0x7D, WASM_TYPE_F64 = 0x7C, WASM_TYPE_V128 = 0x7B, + WASM_TYPE_NULLFUNCREF = 0x73, + WASM_TYPE_NULLEXTERNREF = 0x72, + WASM_TYPE_NULLREF = 0x71, WASM_TYPE_FUNCREF = 0x70, WASM_TYPE_EXTERNREF = 0x6F, + WASM_TYPE_ANYREF = 0x6E, + WASM_TYPE_EQREF = 0x6D, + WASM_TYPE_I31REF = 0x6C, + WASM_TYPE_STRUCTREF = 0x6B, + WASM_TYPE_ARRAYREF = 0x6A, + WASM_TYPE_EXNREF = 0x69, + WASM_TYPE_NONNULLABLE = 0x64, + WASM_TYPE_NULLABLE = 0x63, WASM_TYPE_FUNC = 0x60, + WASM_TYPE_ARRAY = 0x5E, + WASM_TYPE_STRUCT = 0x5F, + WASM_TYPE_SUB = 0x50, + WASM_TYPE_SUB_FINAL = 0x4F, + WASM_TYPE_REC = 0x4E, WASM_TYPE_NORESULT = 0x40, // for blocks with no result values }; @@ -93,6 +109,20 @@ enum : unsigned { WASM_OPCODE_I64_SUB = 0x7d, WASM_OPCODE_I64_MUL = 0x7e, WASM_OPCODE_REF_NULL = 0xd0, + WASM_OPCODE_REF_FUNC = 0xd2, + WASM_OPCODE_GC_PREFIX = 0xfb, +}; + +// Opcodes in the GC-prefixed space (0xfb) +enum : unsigned { + WASM_OPCODE_STRUCT_NEW = 0x00, + WASM_OPCODE_STRUCT_NEW_DEFAULT = 0x01, + WASM_OPCODE_ARRAY_NEW = 0x06, + WASM_OPCODE_ARRAY_NEW_DEFAULT = 0x07, + WASM_OPCODE_ARRAY_NEW_FIXED = 0x08, + WASM_OPCODE_REF_I31 = 0x1c, + // any.convert_extern and extern.convert_any don't seem to be supported by + // Binaryen. }; // Opcodes used in synthetic functions. @@ -127,7 +157,8 @@ enum : unsigned { enum : unsigned { WASM_ELEM_SEGMENT_IS_PASSIVE = 0x01, - WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER = 0x02, + WASM_ELEM_SEGMENT_IS_DECLARATIVE = 0x02, // if passive == 1 + WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER = 0x02, // if passive == 0 WASM_ELEM_SEGMENT_HAS_INIT_EXPRS = 0x04, }; const unsigned WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND = 0x3; @@ -229,6 +260,9 @@ enum class ValType { V128 = WASM_TYPE_V128, FUNCREF = WASM_TYPE_FUNCREF, EXTERNREF = WASM_TYPE_EXTERNREF, + // Unmodeled value types include ref types with heap types other than + // func or extern, and type-specialized funcrefs + OTHERREF = 0xff, }; struct WasmDylinkImportInfo { @@ -297,6 +331,8 @@ struct WasmInitExprMVP { } Value; }; +// Extended-const init exprs and exprs with GC types are not explicitly +// modeled, but the raw body of the expr is attached. struct WasmInitExpr { uint8_t Extended; // Set to non-zero if extended const is used (i.e. more than // one instruction) @@ -367,6 +403,11 @@ struct WasmDataSegment { uint32_t Comdat; // from the "comdat info" section }; +// Represents a Wasm element segment, with some limitations compared the spec: +// 1) Does not model passive or declarative segments (Segment will end up with +// an Offset field of i32.const 0) +// 2) Does not model init exprs (Segment will get an empty Functions list) +// 2) Does not model types other than basic funcref/externref (see ValType) struct WasmElemSegment { uint32_t Flags; uint32_t TableNumber; @@ -436,6 +477,10 @@ struct WasmLinkingData { struct WasmSignature { SmallVector Returns; SmallVector Params; + // LLVM can parse types other than functions encoded in the type section, + // but does not actually model them. Instead a placeholder signature is + // created in the Object's signature list. + enum { Function, Tag, Placeholder } Kind = Function; // Support empty and tombstone instances, needed by DenseMap. enum { Plain, Empty, Tombstone } State = Plain; diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h index 6b8edb90e144b..927dce882f6ae 100644 --- a/llvm/include/llvm/Object/Wasm.h +++ b/llvm/include/llvm/Object/Wasm.h @@ -39,7 +39,9 @@ class WasmSymbol { const wasm::WasmTableType *TableType, const wasm::WasmSignature *Signature) : Info(Info), GlobalType(GlobalType), TableType(TableType), - Signature(Signature) {} + Signature(Signature) { + assert(!Signature || Signature->Kind != wasm::WasmSignature::Placeholder); + } const wasm::WasmSymbolInfo &Info; const wasm::WasmGlobalType *GlobalType; @@ -209,6 +211,7 @@ class WasmObjectFile : public ObjectFile { Expected getFeatures() const override; bool isRelocatableObject() const override; bool isSharedObject() const; + bool hasUnmodeledTypes() const { return HasUnmodeledTypes; } struct ReadContext { const uint8_t *Start; @@ -291,6 +294,7 @@ class WasmObjectFile : public ObjectFile { bool HasLinkingSection = false; bool HasDylinkSection = false; bool HasMemory64 = false; + bool HasUnmodeledTypes = false; wasm::WasmLinkingData LinkingData; uint32_t NumImportedGlobals = 0; uint32_t NumImportedTables = 0; diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index ead4f9eff4c85..985f9351f4a30 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -972,6 +972,8 @@ void WasmObjectWriter::writeTableSection(ArrayRef Tables) { encodeULEB128(Tables.size(), W->OS); for (const wasm::WasmTable &Table : Tables) { + assert(Table.Type.ElemType != wasm::ValType::OTHERREF && + "Cannot encode general ref-typed tables"); encodeULEB128((uint32_t)Table.Type.ElemType, W->OS); encodeULEB128(Table.Type.Limits.Flags, W->OS); encodeULEB128(Table.Type.Limits.Minimum, W->OS); diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp index b9a8e970216ba..953e7c70b5f6e 100644 --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/TargetParser/SubtargetFeature.h" @@ -29,6 +30,7 @@ #include #include #include +#include #define DEBUG_TYPE "wasm-object" @@ -173,6 +175,26 @@ static uint8_t readOpcode(WasmObjectFile::ReadContext &Ctx) { return readUint8(Ctx); } +static wasm::ValType parseValType(WasmObjectFile::ReadContext &Ctx, + uint32_t Code) { + // only directly encoded FUNCREF/EXTERNREF are supported + // (not ref null func or ref null extern) + switch (Code) { + case wasm::WASM_TYPE_I32: + case wasm::WASM_TYPE_I64: + case wasm::WASM_TYPE_F32: + case wasm::WASM_TYPE_F64: + case wasm::WASM_TYPE_V128: + case wasm::WASM_TYPE_FUNCREF: + case wasm::WASM_TYPE_EXTERNREF: + return wasm::ValType(Code); + } + if (Code == wasm::WASM_TYPE_NULLABLE || Code == wasm::WASM_TYPE_NONNULLABLE) { + /* Discard HeapType */ readVarint64(Ctx); + } + return wasm::ValType(wasm::ValType::OTHERREF); +} + static Error readInitExpr(wasm::WasmInitExpr &Expr, WasmObjectFile::ReadContext &Ctx) { auto Start = Ctx.Ptr; @@ -196,11 +218,7 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr, Expr.Inst.Value.Global = readULEB128(Ctx); break; case wasm::WASM_OPCODE_REF_NULL: { - wasm::ValType Ty = static_cast(readULEB128(Ctx)); - if (Ty != wasm::ValType::EXTERNREF) { - return make_error("invalid type for ref.null", - object_error::parse_failed); - } + /* Discard type */ parseValType(Ctx, readVaruint32(Ctx)); break; } default: @@ -221,10 +239,15 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr, case wasm::WASM_OPCODE_I32_CONST: case wasm::WASM_OPCODE_GLOBAL_GET: case wasm::WASM_OPCODE_REF_NULL: + case wasm::WASM_OPCODE_REF_FUNC: case wasm::WASM_OPCODE_I64_CONST: + readULEB128(Ctx); + break; case wasm::WASM_OPCODE_F32_CONST: + readFloat32(Ctx); + break; case wasm::WASM_OPCODE_F64_CONST: - readULEB128(Ctx); + readFloat64(Ctx); break; case wasm::WASM_OPCODE_I32_ADD: case wasm::WASM_OPCODE_I32_SUB: @@ -233,6 +256,23 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr, case wasm::WASM_OPCODE_I64_SUB: case wasm::WASM_OPCODE_I64_MUL: break; + case wasm::WASM_OPCODE_GC_PREFIX: + break; + // The GC opcodes are in a separate (prefixed space). This flat switch + // structure works as long as there is no overlap between the GC and + // general opcodes used in init exprs. + case wasm::WASM_OPCODE_STRUCT_NEW: + case wasm::WASM_OPCODE_STRUCT_NEW_DEFAULT: + case wasm::WASM_OPCODE_ARRAY_NEW: + case wasm::WASM_OPCODE_ARRAY_NEW_DEFAULT: + readULEB128(Ctx); // heap type index + break; + case wasm::WASM_OPCODE_ARRAY_NEW_FIXED: + readULEB128(Ctx); // heap type index + readULEB128(Ctx); // array size + break; + case wasm::WASM_OPCODE_REF_I31: + break; case wasm::WASM_OPCODE_END: Expr.Body = ArrayRef(Start, Ctx.Ptr - Start); return Error::success(); @@ -258,7 +298,8 @@ static wasm::WasmLimits readLimits(WasmObjectFile::ReadContext &Ctx) { static wasm::WasmTableType readTableType(WasmObjectFile::ReadContext &Ctx) { wasm::WasmTableType TableType; - TableType.ElemType = wasm::ValType(readVaruint32(Ctx)); + auto ElemType = parseValType(Ctx, readVaruint32(Ctx)); + TableType.ElemType = ElemType; TableType.Limits = readLimits(Ctx); return TableType; } @@ -1104,26 +1145,75 @@ Error WasmObjectFile::parseCustomSection(WasmSection &Sec, ReadContext &Ctx) { } Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) { + auto parseFieldDef = [&]() { + uint32_t TypeCode = readVaruint32((Ctx)); + /* Discard StorageType */ parseValType(Ctx, TypeCode); + /* Discard Mutability */ readVaruint32(Ctx); + }; + uint32_t Count = readVaruint32(Ctx); Signatures.reserve(Count); while (Count--) { wasm::WasmSignature Sig; uint8_t Form = readUint8(Ctx); + if (Form == wasm::WASM_TYPE_REC) { + // Rec groups expand the type index space (beyond what was declared at + // the top of the section, and also consume one element in that space. + uint32_t RecSize = readVaruint32(Ctx); + if (RecSize == 0) + return make_error("Rec group size cannot be 0", + object_error::parse_failed); + Signatures.reserve(Signatures.size() + RecSize); + Count += RecSize; + Sig.Kind = wasm::WasmSignature::Placeholder; + Signatures.push_back(std::move(Sig)); + HasUnmodeledTypes = true; + continue; + } if (Form != wasm::WASM_TYPE_FUNC) { - return make_error("invalid signature type", - object_error::parse_failed); + // Currently LLVM only models function types, and not other composite + // types. Here we parse the type declarations just enough to skip past + // them in the binary. + if (Form == wasm::WASM_TYPE_SUB || Form == wasm::WASM_TYPE_SUB_FINAL) { + uint32_t Supers = readVaruint32(Ctx); + if (Supers > 0) { + if (Supers != 1) + return make_error( + "Invalid number of supertypes", object_error::parse_failed); + /* Discard SuperIndex */ readVaruint32(Ctx); + } + Form = readVaruint32(Ctx); + } + if (Form == wasm::WASM_TYPE_STRUCT) { + uint32_t FieldCount = readVaruint32(Ctx); + while (FieldCount--) { + parseFieldDef(); + } + } else if (Form == wasm::WASM_TYPE_ARRAY) { + parseFieldDef(); + } else { + return make_error("bad form", + object_error::parse_failed); + } + Sig.Kind = wasm::WasmSignature::Placeholder; + Signatures.push_back(std::move(Sig)); + HasUnmodeledTypes = true; + continue; } + uint32_t ParamCount = readVaruint32(Ctx); Sig.Params.reserve(ParamCount); while (ParamCount--) { uint32_t ParamType = readUint8(Ctx); - Sig.Params.push_back(wasm::ValType(ParamType)); + Sig.Params.push_back(parseValType(Ctx, ParamType)); + continue; } uint32_t ReturnCount = readVaruint32(Ctx); while (ReturnCount--) { uint32_t ReturnType = readUint8(Ctx); - Sig.Returns.push_back(wasm::ValType(ReturnType)); + Sig.Returns.push_back(parseValType(Ctx, ReturnType)); } + Signatures.push_back(std::move(Sig)); } if (Ctx.Ptr != Ctx.End) @@ -1164,7 +1254,8 @@ Error WasmObjectFile::parseImportSection(ReadContext &Ctx) { NumImportedTables++; auto ElemType = Im.Table.ElemType; if (ElemType != wasm::ValType::FUNCREF && - ElemType != wasm::ValType::EXTERNREF) + ElemType != wasm::ValType::EXTERNREF && + ElemType != wasm::ValType::OTHERREF) return make_error("invalid table element type", object_error::parse_failed); break; @@ -1221,7 +1312,8 @@ Error WasmObjectFile::parseTableSection(ReadContext &Ctx) { Tables.push_back(T); auto ElemType = Tables.back().Type.ElemType; if (ElemType != wasm::ValType::FUNCREF && - ElemType != wasm::ValType::EXTERNREF) { + ElemType != wasm::ValType::EXTERNREF && + ElemType != wasm::ValType::OTHERREF) { return make_error("invalid table element type", object_error::parse_failed); } @@ -1263,6 +1355,7 @@ Error WasmObjectFile::parseTagSection(ReadContext &Ctx) { wasm::WasmTag Tag; Tag.Index = NumImportedTags + Tags.size(); Tag.SigIndex = Type; + Signatures[Type].Kind = wasm::WasmSignature::Tag; Tags.push_back(Tag); } @@ -1279,7 +1372,10 @@ Error WasmObjectFile::parseGlobalSection(ReadContext &Ctx) { while (Count--) { wasm::WasmGlobal Global; Global.Index = NumImportedGlobals + Globals.size(); - Global.Type.Type = readUint8(Ctx); + auto GlobalOpcode = readVaruint32(Ctx); + auto GlobalType = parseValType(Ctx, GlobalOpcode); + // assert(GlobalType <= std::numeric_limits::max()); + Global.Type.Type = (uint8_t)GlobalType; Global.Type.Mutable = readVaruint1(Ctx); if (Error Err = readInitExpr(Global.InitExpr, Ctx)) return Err; @@ -1516,15 +1612,28 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) { return make_error( "Unsupported flags for element segment", object_error::parse_failed); - if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER) + bool IsPassive = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) != 0; + bool IsDeclarative = + IsPassive && (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_DECLARATIVE); + bool HasTableNumber = + !IsPassive && + (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER); + bool HasInitExprs = + (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS); + bool HasElemKind = + (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) && + !HasInitExprs; + + if (HasTableNumber) Segment.TableNumber = readVaruint32(Ctx); else Segment.TableNumber = 0; + if (!isValidTableNumber(Segment.TableNumber)) return make_error("invalid TableNumber", object_error::parse_failed); - if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) { + if (IsPassive || IsDeclarative) { Segment.Offset.Extended = false; Segment.Offset.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST; Segment.Offset.Inst.Value.Int32 = 0; @@ -1533,33 +1642,41 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) { return Err; } - if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) { + if (HasElemKind) { auto ElemKind = readVaruint32(Ctx); if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS) { - Segment.ElemKind = wasm::ValType(ElemKind); + Segment.ElemKind = parseValType(Ctx, ElemKind); if (Segment.ElemKind != wasm::ValType::FUNCREF && - Segment.ElemKind != wasm::ValType::EXTERNREF) { - return make_error("invalid reference type", + Segment.ElemKind != wasm::ValType::EXTERNREF && + Segment.ElemKind != wasm::ValType::OTHERREF) { + return make_error("invalid elem type", object_error::parse_failed); } } else { if (ElemKind != 0) - return make_error("invalid elemtype", + return make_error("invalid elem type", object_error::parse_failed); Segment.ElemKind = wasm::ValType::FUNCREF; } + } else if (HasInitExprs) { + auto ElemType = parseValType(Ctx, readVaruint32(Ctx)); + Segment.ElemKind = ElemType; } else { Segment.ElemKind = wasm::ValType::FUNCREF; } - if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS) - return make_error( - "elem segment init expressions not yet implemented", - object_error::parse_failed); - uint32_t NumElems = readVaruint32(Ctx); - while (NumElems--) { - Segment.Functions.push_back(readVaruint32(Ctx)); + + if (HasInitExprs) { + while (NumElems--) { + wasm::WasmInitExpr Expr; + if (Error Err = readInitExpr(Expr, Ctx)) + return Err; + } + } else { + while (NumElems--) { + Segment.Functions.push_back(readVaruint32(Ctx)); + } } ElemSegments.push_back(Segment); } diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp index 9502fe5e40778..3b53788eddbab 100644 --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -12,6 +12,7 @@ #include "llvm/ObjectYAML/WasmYAML.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/YAMLTraits.h" @@ -383,6 +384,8 @@ void MappingTraits::mapping( if (!IO.outputting() || Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) IO.mapOptional("ElemKind", Segment.ElemKind); + // TODO: Omit "offset" for passive segments? It's neither meaningful nor + // encoded. IO.mapRequired("Offset", Segment.Offset); IO.mapRequired("Functions", Segment.Functions); } @@ -593,7 +596,8 @@ void ScalarEnumerationTraits::enumeration( void ScalarEnumerationTraits::enumeration( IO &IO, WasmYAML::ValueType &Type) { -#define ECase(X) IO.enumCase(Type, #X, wasm::WASM_TYPE_##X); +#define CONCAT(X) (uint32_t) wasm::ValType::X +#define ECase(X) IO.enumCase(Type, #X, CONCAT(X)); ECase(I32); ECase(I64); ECase(F32); @@ -601,7 +605,7 @@ void ScalarEnumerationTraits::enumeration( ECase(V128); ECase(FUNCREF); ECase(EXTERNREF); - ECase(FUNC); + ECase(OTHERREF); #undef ECase } @@ -631,9 +635,11 @@ void ScalarEnumerationTraits::enumeration( void ScalarEnumerationTraits::enumeration( IO &IO, WasmYAML::TableType &Type) { -#define ECase(X) IO.enumCase(Type, #X, wasm::WASM_TYPE_##X); +#define CONCAT(X) (uint32_t) wasm::ValType::X +#define ECase(X) IO.enumCase(Type, #X, CONCAT(X)); ECase(FUNCREF); ECase(EXTERNREF); + ECase(OTHERREF); #undef ECase } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 908efbb8d3215..fb949d4b19a3e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -125,8 +125,9 @@ static char getInvokeSig(wasm::ValType VT) { return 'F'; case wasm::ValType::EXTERNREF: return 'X'; + default: + llvm_unreachable("Unhandled wasm::ValType enum"); } - llvm_unreachable("Unhandled wasm::ValType enum"); } // Given the wasm signature, generate the invoke name in the format JS glue code diff --git a/llvm/test/Object/Inputs/WASM/multi-table.wasm b/llvm/test/Object/Inputs/WASM/multi-table.wasm new file mode 100644 index 0000000000000000000000000000000000000000..47f5d8311cb74f76485577df85578b62f896361d GIT binary patch literal 185 zcmX|(K@P$&3xuz=}#z8j}?*n0KSOwBAkLaSfjDmi)G&kfW|u@g