[llvm] 7f409cd - [Object][Wasm] Allow parsing of GC types in type and table sections (#79235)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 25 09:48:42 PST 2024


Author: Derek Schuff
Date: 2024-01-25T09:48:38-08:00
New Revision: 7f409cd82b322038f08a984a07377758e76b0e4c

URL: https://github.com/llvm/llvm-project/commit/7f409cd82b322038f08a984a07377758e76b0e4c
DIFF: https://github.com/llvm/llvm-project/commit/7f409cd82b322038f08a984a07377758e76b0e4c.diff

LOG: [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.

Added: 
    llvm/test/Object/Inputs/WASM/multi-table.wasm
    llvm/test/Object/wasm-obj2yaml-tables.test

Modified: 
    lld/wasm/InputChunks.h
    lld/wasm/InputElement.h
    lld/wasm/InputFiles.cpp
    lld/wasm/WriterUtils.cpp
    llvm/include/llvm/BinaryFormat/Wasm.h
    llvm/include/llvm/Object/Wasm.h
    llvm/lib/MC/WasmObjectWriter.cpp
    llvm/lib/Object/WasmObjectFile.cpp
    llvm/lib/ObjectYAML/WasmYAML.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp

Removed: 
    


################################################################################
diff  --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index 1e430832fb84c7..ad1d45e335eac9 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 46e21d7c7dfd80..10dc2a3e4a826a 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 f5e946aca8b2a8..f43c39b218787c 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -81,6 +81,9 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
     std::unique_ptr<Binary> bin =
         CHECK(createBinary(mb), mb.getBufferIdentifier());
     auto *obj = cast<WasmObjectFile>(bin.get());
+    if (obj->hasUnmodeledTypes())
+      fatal(toString(mb.getBufferIdentifier()) +
+            "file has unmodeled reference or GC types");
     if (obj->isSharedObject())
       return make<SharedFile>(mb);
     return make<ObjFile>(mb, archiveName, lazy);

diff  --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index 418192f3023757..cdd2c42f939efe 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 c6f837531363e1..cd13b7014bfe26 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<ValType, 1> Returns;
   SmallVector<ValType, 4> 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 6b8edb90e144b5..927dce882f6ae5 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<SubtargetFeatures> 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 ead4f9eff4c851..985f9351f4a308 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -972,6 +972,8 @@ void WasmObjectWriter::writeTableSection(ArrayRef<wasm::WasmTable> 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 b9a8e970216ba6..953e7c70b5f6e3 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 <cassert>
 #include <cstdint>
 #include <cstring>
+#include <limits>
 
 #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<wasm::ValType>(readULEB128(Ctx));
-    if (Ty != wasm::ValType::EXTERNREF) {
-      return make_error<GenericBinaryError>("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<uint8_t>(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<GenericBinaryError>("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<GenericBinaryError>("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<GenericBinaryError>(
+                "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<GenericBinaryError>("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<GenericBinaryError>("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<GenericBinaryError>("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<wasm::ValType>::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<GenericBinaryError>(
           "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<GenericBinaryError>("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<GenericBinaryError>("invalid reference type",
+            Segment.ElemKind != wasm::ValType::EXTERNREF &&
+            Segment.ElemKind != wasm::ValType::OTHERREF) {
+          return make_error<GenericBinaryError>("invalid elem type",
                                                 object_error::parse_failed);
         }
       } else {
         if (ElemKind != 0)
-          return make_error<GenericBinaryError>("invalid elemtype",
+          return make_error<GenericBinaryError>("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<GenericBinaryError>(
-          "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 9502fe5e407788..3b53788eddbabc 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<WasmYAML::ElemSegment>::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<WasmYAML::SymbolKind>::enumeration(
 
 void ScalarEnumerationTraits<WasmYAML::ValueType>::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<WasmYAML::ValueType>::enumeration(
   ECase(V128);
   ECase(FUNCREF);
   ECase(EXTERNREF);
-  ECase(FUNC);
+  ECase(OTHERREF);
 #undef ECase
 }
 
@@ -631,9 +635,11 @@ void ScalarEnumerationTraits<WasmYAML::Opcode>::enumeration(
 
 void ScalarEnumerationTraits<WasmYAML::TableType>::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 908efbb8d32155..fb949d4b19a3e1 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 00000000000000..47f5d8311cb74f
Binary files /dev/null and b/llvm/test/Object/Inputs/WASM/multi-table.wasm 
diff er

diff  --git a/llvm/test/Object/wasm-obj2yaml-tables.test b/llvm/test/Object/wasm-obj2yaml-tables.test
new file mode 100644
index 00000000000000..870ffd179a1c25
--- /dev/null
+++ b/llvm/test/Object/wasm-obj2yaml-tables.test
@@ -0,0 +1,112 @@
+RUN: obj2yaml %p/Inputs/WASM/multi-table.wasm | FileCheck %s
+
+
+# CHECK:  - Type:            TABLE
+# CHECK:    Tables:
+# CHECK:      - Index:           1
+# CHECK:        ElemType:        FUNCREF
+# CHECK:        Limits:
+# CHECK:          Flags:           [ HAS_MAX ]
+# CHECK:          Minimum:         0x3
+# CHECK:          Maximum:         0x3
+# CHECK:      - Index:           2
+# CHECK:        ElemType:        FUNCREF
+# CHECK:        Limits:
+# CHECK:          Flags:           [ HAS_MAX ]
+# CHECK:          Minimum:         0x4
+# CHECK:          Maximum:         0x4
+# CHECK:      - Index:           3
+# CHECK:        ElemType:        EXTERNREF
+# CHECK:        Limits:
+# CHECK:          Minimum:         0x0
+# CHECK:      - Index:           4
+# CHECK:        ElemType:        OTHERREF
+# CHECK:        Limits:
+# CHECK:          Flags:           [ HAS_MAX ]
+# CHECK:          Minimum:         0x5
+# CHECK:          Maximum:         0x5
+# CHECK:  - Type:            GLOBAL
+# CHECK:    Globals:
+# CHECK:      - Index:           0
+# CHECK:        Type:            OTHERREF
+# CHECK:        Mutable:         false
+# CHECK:        InitExpr:
+# CHECK:          Extended:        true
+# CHECK:          Body:            D2000B
+# CHECK:      - Index:           1
+# CHECK:        Type:            I32
+# CHECK:        Mutable:         false
+# CHECK:        InitExpr:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# CHECK:  - Type:            ELEM
+# CHECK:    Segments:
+# CHECK:      - Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# CHECK:        Functions:       [ 0 ]
+# CHECK:      - Flags:           2
+# CHECK:        TableNumber:     1
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# CHECK:        Functions:       [ 0 ]
+# CHECK:      - Flags:           2
+# CHECK:        TableNumber:     1
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           1
+# CHECK:        Functions:       [ 0, 1 ]
+# CHECK:      - Flags:           6
+# CHECK:        TableNumber:     2
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          GLOBAL_GET
+# CHECK:          Index:           1
+# There are 2 funcions encoded with initexprs in this segment
+# but initexprs in tables are unmodeled.
+# CHECK:        Functions:       [  ]
+# CHECK:      - Flags:           6
+# CHECK:        TableNumber:     2
+# This elemkind is OTHERREF because it's encoded as a typed funcref
+# CHECK:        ElemKind:        OTHERREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           2
+# CHECK:        Functions:       [  ]
+# CHECK:      - Flags:           1
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# CHECK:        Functions:       [ 0, 1 ]
+# CHECK:      - Flags:           5
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# Empty function list, encoded with initexprs
+# CHECK:        Functions:       [  ]
+# CHECK:      - Flags:           5
+# CHECK:        ElemKind:        OTHERREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# Function list encoded with initexprs
+# CHECK:        Functions:       [  ]
+# CHECK:      - Flags:           1
+# CHECK:        ElemKind:        FUNCREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# CHECK:        Functions:       [  ]
+# CHECK:      - Flags:           6
+# CHECK:        TableNumber:     4
+# CHECK:        ElemKind:        OTHERREF
+# CHECK:        Offset:
+# CHECK:          Opcode:          I32_CONST
+# CHECK:          Value:           0
+# Function list encoded with initexprs
+# CHECK:        Functions:       [  ]


        


More information about the llvm-commits mailing list