[llvm] [lld] [clang] [clang-tools-extra] [Object][Wasm] Allow parsing of GC types in type and table sections (PR #79235)

Derek Schuff via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 24 17:32:43 PST 2024


https://github.com/dschuff updated https://github.com/llvm/llvm-project/pull/79235

>From 976c98f631e5ed48bb18accbe59c9babd354a924 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Wed, 3 Jan 2024 09:06:37 -0800
Subject: [PATCH 01/12] parse types

---
 llvm/include/llvm/BinaryFormat/Wasm.h |  8 +++
 llvm/lib/Object/WasmObjectFile.cpp    | 83 +++++++++++++++++++++++++++
 2 files changed, 91 insertions(+)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index c7658cc7b7432b3..89499a61b76c8c3 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -265,7 +265,13 @@ enum : unsigned {
   WASM_TYPE_V128 = 0x7B,
   WASM_TYPE_FUNCREF = 0x70,
   WASM_TYPE_EXTERNREF = 0x6F,
+  WASM_TYPE_NULLABLE = 0x63,
   WASM_TYPE_FUNC = 0x60,
+  WASM_TYPE_ARRAY = 0x5E, // Composite types, not used for codegen
+  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
 };
 
@@ -431,11 +437,13 @@ enum class ValType {
   V128 = WASM_TYPE_V128,
   FUNCREF = WASM_TYPE_FUNCREF,
   EXTERNREF = WASM_TYPE_EXTERNREF,
+  OTHERREF,
 };
 
 struct WasmSignature {
   SmallVector<ValType, 1> Returns;
   SmallVector<ValType, 4> Params;
+  enum {Function, Other} Kind = Function;
   // Support empty and tombstone instances, needed by DenseMap.
   enum { Plain, Empty, Tombstone } State = Plain;
 
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 94cd96968ff2010..3d2b06342481aa0 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 <sys/types.h>
 
 #define DEBUG_TYPE "wasm-object"
 
@@ -1104,26 +1106,107 @@ Error WasmObjectFile::parseCustomSection(WasmSection &Sec, ReadContext &Ctx) {
 }
 
 Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
+  auto parseFieldDef = [&]() {
+    int32_t TypeCode = readVarint32((Ctx));
+    uint32_t Mutability = readVaruint32(Ctx);
+  };
+  auto parseRecType = [&]() {
+    uint8_t Form = readUint8(Ctx);
+    if (Form == wasm::WASM_TYPE_REC) {
+      uint32_t Size = readVaruint32(Ctx);
+      assert(Size > 0); // TODO real errors here and below
+      Form = readVaruint32(Ctx);
+    }
+    if (Form == wasm::WASM_TYPE_SUB || Form == wasm::WASM_TYPE_SUB_FINAL) {
+      uint32_t Supers = readVaruint32(Ctx);
+      if (Supers > 0) {
+        assert(Supers == 1);
+        uint32_t SuperIndex = readVaruint32(Ctx);
+      }
+      Form = readVaruint32(Ctx);
+    }
+    if (Form == wasm::WASM_TYPE_STRUCT) {
+      uint32_t NumFields = readVaruint32(Ctx);
+      for (size_t i = 0; i < NumFields; i++) {
+        parseFieldDef();
+      }
+    } else if (Form == wasm::WASM_TYPE_ARRAY) {
+      parseFieldDef();
+    }
+
+  };
+  auto parseParamType = [&](uint32_t code) -> wasm::ValType {
+    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);
+    }
+  };
   uint32_t Count = readVaruint32(Ctx);
   Signatures.reserve(Count);
   while (Count--) {
     wasm::WasmSignature Sig;
     uint8_t Form = readUint8(Ctx);
+    llvm::errs() << llvm::format("Top Count %d form %x", Count, Form) << '\n';
+    if (Form == wasm::WASM_TYPE_REC) {
+      uint32_t Size = readVaruint32(Ctx);
+      assert(Size > 0); // TODO real errors here and below
+      Form = readVaruint32(Ctx);
+      wasm::WasmSignature s; s.Kind = s.Other;
+      Signatures.push_back(s);
+      Count--;
+      llvm::errs() << llvm::format(" Rec size %d form %x", Size, Form) << '\n';
+    }
     if (Form != wasm::WASM_TYPE_FUNC) {
+      wasm::WasmSignature s; s.Kind = s.Other;
+      if (Form == wasm::WASM_TYPE_SUB || Form == wasm::WASM_TYPE_SUB_FINAL) {
+        uint32_t Supers = readVaruint32(Ctx);
+        if (Supers > 0) {
+          assert(Supers == 1);
+          uint32_t SuperIndex = readVaruint32(Ctx);
+        }
+        Form = readVaruint32(Ctx);
+        llvm::errs() << llvm::format(" Sub Supers %d form %x", Supers, Form) << '\n';
+      }
+      if (Form == wasm::WASM_TYPE_STRUCT) {
+        uint32_t NumFields = readVaruint32(Ctx);
+        for (size_t i = 0; i < NumFields; i++) {
+          parseFieldDef();
+        }
+        llvm::errs() << llvm::format(" Struct size %d", NumFields) << '\n';
+      } else if (Form == wasm::WASM_TYPE_ARRAY) {
+        parseFieldDef();
+        llvm::errs() << llvm::format("arr form %x", Form) << '\n';
+      } else {
+        llvm::errs() << llvm::format(" bad form %x", Form) << '\n';
+        return make_error<GenericBinaryError>("bad form", object_error::parse_failed);
+      }
+      Signatures.push_back(s);
+      continue;
       return make_error<GenericBinaryError>("invalid signature type",
                                             object_error::parse_failed);
     }
+
     uint32_t ParamCount = readVaruint32(Ctx);
     Sig.Params.reserve(ParamCount);
+    llvm::errs() << llvm::format("param ct %d ", ParamCount);
     while (ParamCount--) {
       uint32_t ParamType = readUint8(Ctx);
+      if (ParamType == )
       Sig.Params.push_back(wasm::ValType(ParamType));
     }
     uint32_t ReturnCount = readVaruint32(Ctx);
+    llvm::errs() << llvm::format("return ct %d\n", ReturnCount);
     while (ReturnCount--) {
       uint32_t ReturnType = readUint8(Ctx);
       Sig.Returns.push_back(wasm::ValType(ReturnType));
     }
+    
     Signatures.push_back(std::move(Sig));
   }
   if (Ctx.Ptr != Ctx.End)

>From 28273084c7b3f10dbff88ffe418e12d4e7c7c159 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 4 Jan 2024 11:07:38 -0800
Subject: [PATCH 02/12] type section working

---
 llvm/include/llvm/BinaryFormat/Wasm.h | 16 +++++++-
 llvm/lib/Object/WasmObjectFile.cpp    | 59 +++++++++++----------------
 2 files changed, 38 insertions(+), 37 deletions(-)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 89499a61b76c8c3..2cfb69fcf8f8276 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -263,8 +263,18 @@ 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, // Composite types, not used for codegen
@@ -437,13 +447,15 @@ enum class ValType {
   V128 = WASM_TYPE_V128,
   FUNCREF = WASM_TYPE_FUNCREF,
   EXTERNREF = WASM_TYPE_EXTERNREF,
-  OTHERREF,
+  OTHERREF, // Unmodeled value types include ref types with heap types other than funcref or externref
 };
 
+// Represents anything that can be encoded in the type section, but only
+// signatures are actually modeled. TODO: maybe refactor to make this explicit.
 struct WasmSignature {
   SmallVector<ValType, 1> Returns;
   SmallVector<ValType, 4> Params;
-  enum {Function, Other} Kind = Function;
+  enum {Function, Other} Kind = Function; // Recursive, Composite(Array,Struct),  
   // Support empty and tombstone instances, needed by DenseMap.
   enum { Plain, Empty, Tombstone } State = Plain;
 
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 3d2b06342481aa0..639d49b242694c9 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1106,37 +1106,11 @@ Error WasmObjectFile::parseCustomSection(WasmSection &Sec, ReadContext &Ctx) {
 }
 
 Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
-  auto parseFieldDef = [&]() {
-    int32_t TypeCode = readVarint32((Ctx));
-    uint32_t Mutability = readVaruint32(Ctx);
-  };
-  auto parseRecType = [&]() {
-    uint8_t Form = readUint8(Ctx);
-    if (Form == wasm::WASM_TYPE_REC) {
-      uint32_t Size = readVaruint32(Ctx);
-      assert(Size > 0); // TODO real errors here and below
-      Form = readVaruint32(Ctx);
-    }
-    if (Form == wasm::WASM_TYPE_SUB || Form == wasm::WASM_TYPE_SUB_FINAL) {
-      uint32_t Supers = readVaruint32(Ctx);
-      if (Supers > 0) {
-        assert(Supers == 1);
-        uint32_t SuperIndex = readVaruint32(Ctx);
-      }
-      Form = readVaruint32(Ctx);
-    }
-    if (Form == wasm::WASM_TYPE_STRUCT) {
-      uint32_t NumFields = readVaruint32(Ctx);
-      for (size_t i = 0; i < NumFields; i++) {
-        parseFieldDef();
-      }
-    } else if (Form == wasm::WASM_TYPE_ARRAY) {
-      parseFieldDef();
-    }
 
-  };
-  auto parseParamType = [&](uint32_t code) -> wasm::ValType {
-    switch(code) {
+  auto parseValType = [&](uint32_t Code) -> wasm::ValType {
+    // only directly encoded FUNCREF/EXTERNREF are supported (not ref null func/ref null extern)
+    llvm::errs() << llvm::format(" val type %x ", Code);
+    switch(Code) {
       case wasm::WASM_TYPE_I32:
       case wasm::WASM_TYPE_I64:
       case wasm::WASM_TYPE_F32:
@@ -1144,9 +1118,20 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       case wasm::WASM_TYPE_V128:
       case wasm::WASM_TYPE_FUNCREF:
       case wasm::WASM_TYPE_EXTERNREF:
-        return wasm::ValType(code);
+        return wasm::ValType(Code);
+    }
+    if (Code == wasm::WASM_TYPE_NULLABLE || Code == wasm::WASM_TYPE_NONNULLABLE) {
+      readVarint64(Ctx);
     }
+    return wasm::ValType(wasm::ValType::OTHERREF);
   };
+  auto parseFieldDef = [&]() {
+    uint32_t TypeCode = readVaruint32((Ctx));
+    parseValType(TypeCode);
+    uint32_t Mutability = readVaruint32(Ctx);
+    llvm::errs() << llvm:: format(" mut %d ", Mutability);
+  };
+
   uint32_t Count = readVaruint32(Ctx);
   Signatures.reserve(Count);
   while (Count--) {
@@ -1156,6 +1141,8 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
     if (Form == wasm::WASM_TYPE_REC) {
       uint32_t Size = readVaruint32(Ctx);
       assert(Size > 0); // TODO real errors here and below
+      Signatures.reserve(Signatures.size() + Size);
+      Count += Size;
       Form = readVaruint32(Ctx);
       wasm::WasmSignature s; s.Kind = s.Other;
       Signatures.push_back(s);
@@ -1197,17 +1184,19 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
     llvm::errs() << llvm::format("param ct %d ", ParamCount);
     while (ParamCount--) {
       uint32_t ParamType = readUint8(Ctx);
-      if (ParamType == )
-      Sig.Params.push_back(wasm::ValType(ParamType));
+      Sig.Returns.push_back(parseValType(ParamType));
+      continue;
+      
     }
     uint32_t ReturnCount = readVaruint32(Ctx);
-    llvm::errs() << llvm::format("return ct %d\n", ReturnCount);
+    llvm::errs() << llvm::format("\nreturn ct %d ", ReturnCount);
     while (ReturnCount--) {
       uint32_t ReturnType = readUint8(Ctx);
-      Sig.Returns.push_back(wasm::ValType(ReturnType));
+      Sig.Returns.push_back(parseValType(ReturnType));
     }
     
     Signatures.push_back(std::move(Sig));
+    llvm::errs() << '\n';
   }
   if (Ctx.Ptr != Ctx.End)
     return make_error<GenericBinaryError>("type section ended prematurely",

>From e716a1470836d18af1a1032880ed1afec47f4aa5 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 4 Jan 2024 11:57:02 -0800
Subject: [PATCH 03/12] some cleanup

---
 llvm/lib/Object/WasmObjectFile.cpp | 43 +++++++++++++++---------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 639d49b242694c9..c7916fdebac85a0 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -30,7 +30,6 @@
 #include <cassert>
 #include <cstdint>
 #include <cstring>
-#include <sys/types.h>
 
 #define DEBUG_TYPE "wasm-object"
 
@@ -1106,7 +1105,6 @@ Error WasmObjectFile::parseCustomSection(WasmSection &Sec, ReadContext &Ctx) {
 }
 
 Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
-
   auto parseValType = [&](uint32_t Code) -> wasm::ValType {
     // only directly encoded FUNCREF/EXTERNREF are supported (not ref null func/ref null extern)
     llvm::errs() << llvm::format(" val type %x ", Code);
@@ -1121,13 +1119,13 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
         return wasm::ValType(Code);
     }
     if (Code == wasm::WASM_TYPE_NULLABLE || Code == wasm::WASM_TYPE_NONNULLABLE) {
-      readVarint64(Ctx);
+      /* Discard HeapType */ readVarint64(Ctx);
     }
     return wasm::ValType(wasm::ValType::OTHERREF);
   };
   auto parseFieldDef = [&]() {
     uint32_t TypeCode = readVaruint32((Ctx));
-    parseValType(TypeCode);
+    /* Discard StorageType */ parseValType(TypeCode);
     uint32_t Mutability = readVaruint32(Ctx);
     llvm::errs() << llvm:: format(" mut %d ", Mutability);
   };
@@ -1139,33 +1137,36 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
     uint8_t Form = readUint8(Ctx);
     llvm::errs() << llvm::format("Top Count %d form %x", Count, Form) << '\n';
     if (Form == wasm::WASM_TYPE_REC) {
-      uint32_t Size = readVaruint32(Ctx);
-      assert(Size > 0); // TODO real errors here and below
-      Signatures.reserve(Signatures.size() + Size);
-      Count += Size;
-      Form = readVaruint32(Ctx);
-      wasm::WasmSignature s; s.Kind = s.Other;
-      Signatures.push_back(s);
-      Count--;
-      llvm::errs() << llvm::format(" Rec size %d form %x", Size, Form) << '\n';
+      // 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);
+      assert(RecSize > 0); // TODO real errors here and below
+      Signatures.reserve(Signatures.size() + RecSize);
+      Count += RecSize;
+      llvm::errs() << llvm::format(" Rec size %d\n", RecSize);
+      Sig.Kind = wasm::WasmSignature::Other;
+      Signatures.push_back(std::move(Sig));
+      continue;
     }
     if (Form != wasm::WASM_TYPE_FUNC) {
-      wasm::WasmSignature s; s.Kind = s.Other;
+      // 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) {
           assert(Supers == 1);
-          uint32_t SuperIndex = readVaruint32(Ctx);
+          /* Discard SuperIndex */ readVaruint32(Ctx);
         }
         Form = readVaruint32(Ctx);
         llvm::errs() << llvm::format(" Sub Supers %d form %x", Supers, Form) << '\n';
       }
       if (Form == wasm::WASM_TYPE_STRUCT) {
-        uint32_t NumFields = readVaruint32(Ctx);
-        for (size_t i = 0; i < NumFields; i++) {
+        uint32_t FieldCount = readVaruint32(Ctx);
+        while (FieldCount--) {
           parseFieldDef();
         }
-        llvm::errs() << llvm::format(" Struct size %d", NumFields) << '\n';
+        llvm::errs() << llvm::format(" Struct size %d", FieldCount) << '\n';
       } else if (Form == wasm::WASM_TYPE_ARRAY) {
         parseFieldDef();
         llvm::errs() << llvm::format("arr form %x", Form) << '\n';
@@ -1173,10 +1174,9 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
         llvm::errs() << llvm::format(" bad form %x", Form) << '\n';
         return make_error<GenericBinaryError>("bad form", object_error::parse_failed);
       }
-      Signatures.push_back(s);
+      Sig.Kind = wasm::WasmSignature::Other;
+      Signatures.push_back(std::move(Sig));
       continue;
-      return make_error<GenericBinaryError>("invalid signature type",
-                                            object_error::parse_failed);
     }
 
     uint32_t ParamCount = readVaruint32(Ctx);
@@ -1186,7 +1186,6 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       uint32_t ParamType = readUint8(Ctx);
       Sig.Returns.push_back(parseValType(ParamType));
       continue;
-      
     }
     uint32_t ReturnCount = readVaruint32(Ctx);
     llvm::errs() << llvm::format("\nreturn ct %d ", ReturnCount);

>From 2b0b6195883979b19d3d029ee56cc69d1957abaf Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 4 Jan 2024 14:35:20 -0800
Subject: [PATCH 04/12] parses dart global section

---
 llvm/include/llvm/BinaryFormat/Wasm.h |  9 +++
 llvm/lib/Object/WasmObjectFile.cpp    | 80 ++++++++++++++++++---------
 2 files changed, 62 insertions(+), 27 deletions(-)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 2cfb69fcf8f8276..a378b20ded75fd7 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -316,6 +316,15 @@ 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_FIXED = 0x08,
 };
 
 // Opcodes used in synthetic functions.
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index c7916fdebac85a0..4d33bf82ec43078 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -30,6 +30,7 @@
 #include <cassert>
 #include <cstdint>
 #include <cstring>
+#include <limits>
 
 #define DEBUG_TYPE "wasm-object"
 
@@ -174,6 +175,25 @@ 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/ref null extern)
+    llvm::errs() << llvm::format(" val type %x ", Code);
+    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;
@@ -197,10 +217,10 @@ 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);
+    wasm::ValType Ty = parseValType(Ctx, readVaruint32(Ctx));
+    if (Ty != wasm::ValType::EXTERNREF) { // maybe something special if the type isn't one we understand?
+      //return make_error<GenericBinaryError>("invalid type for ref.null",
+      //                                      object_error::parse_failed);
     }
     break;
   }
@@ -218,14 +238,20 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
     Ctx.Ptr = Start;
     while (true) {
       uint8_t Opcode = readOpcode(Ctx);
+      llvm::errs() << llvm::format(" opcode %x", Opcode);
       switch (Opcode) {
       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:
@@ -234,8 +260,22 @@ 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:
+        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_END:
         Expr.Body = ArrayRef<uint8_t>(Start, Ctx.Ptr - Start);
+        llvm::errs() << "\n";
         return Error::success();
       default:
         return make_error<GenericBinaryError>(
@@ -1105,27 +1145,9 @@ Error WasmObjectFile::parseCustomSection(WasmSection &Sec, ReadContext &Ctx) {
 }
 
 Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
-  auto parseValType = [&](uint32_t Code) -> wasm::ValType {
-    // only directly encoded FUNCREF/EXTERNREF are supported (not ref null func/ref null extern)
-    llvm::errs() << llvm::format(" val type %x ", Code);
-    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);
-  };
   auto parseFieldDef = [&]() {
     uint32_t TypeCode = readVaruint32((Ctx));
-    /* Discard StorageType */ parseValType(TypeCode);
+    /* Discard StorageType */ parseValType(Ctx, TypeCode);
     uint32_t Mutability = readVaruint32(Ctx);
     llvm::errs() << llvm:: format(" mut %d ", Mutability);
   };
@@ -1184,14 +1206,14 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
     llvm::errs() << llvm::format("param ct %d ", ParamCount);
     while (ParamCount--) {
       uint32_t ParamType = readUint8(Ctx);
-      Sig.Returns.push_back(parseValType(ParamType));
+      Sig.Returns.push_back(parseValType(Ctx, ParamType));
       continue;
     }
     uint32_t ReturnCount = readVaruint32(Ctx);
     llvm::errs() << llvm::format("\nreturn ct %d ", ReturnCount);
     while (ReturnCount--) {
       uint32_t ReturnType = readUint8(Ctx);
-      Sig.Returns.push_back(parseValType(ReturnType));
+      Sig.Returns.push_back(parseValType(Ctx, ReturnType));
     }
     
     Signatures.push_back(std::move(Sig));
@@ -1350,8 +1372,12 @@ 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);
+    llvm::errs() << llvm::format("Read global %d index %d, type %x mut %d\n", Globals.capacity() -Count-1, Global.Index, GlobalOpcode, Global.Type.Mutable);
     if (Error Err = readInitExpr(Global.InitExpr, Ctx))
       return Err;
     Globals.push_back(Global);

>From 9a524b5431a49e3103230108573d43e8b2df4bd4 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 4 Jan 2024 14:47:13 -0800
Subject: [PATCH 05/12] fix params

---
 llvm/lib/Object/WasmObjectFile.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 4d33bf82ec43078..13234e0ad2f28f9 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1206,7 +1206,7 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
     llvm::errs() << llvm::format("param ct %d ", ParamCount);
     while (ParamCount--) {
       uint32_t ParamType = readUint8(Ctx);
-      Sig.Returns.push_back(parseValType(Ctx, ParamType));
+      Sig.Params.push_back(parseValType(Ctx, ParamType));
       continue;
     }
     uint32_t ReturnCount = readVaruint32(Ctx);

>From 2d3c70fd4c45087943d7040164c2c6a4bb181c7b Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 4 Jan 2024 15:11:20 -0800
Subject: [PATCH 06/12] add GC opcodes not used by dart sample

---
 llvm/include/llvm/BinaryFormat/Wasm.h | 4 ++++
 llvm/lib/Object/WasmObjectFile.cpp    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index a378b20ded75fd7..395f0ab6d2465f0 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -324,7 +324,11 @@ enum : unsigned {
 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.
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 13234e0ad2f28f9..1bd2b8e75b6ba31 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -267,12 +267,16 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
       // 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);
         llvm::errs() << "\n";

>From db8bd762f77a7cd3c4ebe45817a89ad9b222c1ad Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Mon, 8 Jan 2024 16:59:47 -0800
Subject: [PATCH 07/12] passes tests, doesn't choke on multi-table.wast

---
 llvm/include/llvm/BinaryFormat/Wasm.h |  7 ++--
 llvm/lib/Object/WasmObjectFile.cpp    | 59 ++++++++++++++++++++-------
 2 files changed, 48 insertions(+), 18 deletions(-)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 395f0ab6d2465f0..b48048a38e77f7a 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -81,7 +81,7 @@ struct WasmLimits {
 };
 
 struct WasmTableType {
-  uint8_t ElemType;
+  uint8_t ElemType; // TODO: make this a ValType?
   WasmLimits Limits;
 };
 
@@ -110,7 +110,7 @@ struct WasmInitExpr {
 };
 
 struct WasmGlobalType {
-  uint8_t Type;
+  uint8_t Type; // TODO: make this a ValType?
   bool Mutable;
 };
 
@@ -363,7 +363,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;
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 1bd2b8e75b6ba31..e3ffea5f8ce20be 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -303,7 +303,8 @@ static wasm::WasmLimits readLimits(WasmObjectFile::ReadContext &Ctx) {
 
 static wasm::WasmTableType readTableType(WasmObjectFile::ReadContext &Ctx) {
   wasm::WasmTableType TableType;
-  TableType.ElemType = readUint8(Ctx);
+  auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
+  TableType.ElemType = (uint8_t)ElemType;
   TableType.Limits = readLimits(Ctx);
   return TableType;
 }
@@ -1617,7 +1618,15 @@ 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;
+
+    llvm::errs() << llvm::format("segment %d flags %x, InitExprs %d HasKind %d\n",
+      ElemSegments.capacity() - Count-1, Segment.Flags, HasInitExprs,HasElemKind);
+    if (HasTableNumber)
       Segment.TableNumber = readVaruint32(Ctx);
     else
       Segment.TableNumber = 0;
@@ -1625,41 +1634,61 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
       return make_error<GenericBinaryError>("invalid TableNumber",
                                             object_error::parse_failed);
 
-    if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) {
+    if (IsDeclarative && false) {
+      // Declarative segments are not used or understood by LLVM
+      /* Discard type/kind */ readVaruint32(Ctx);
+      auto DeclCount = readVaruint32(Ctx);
+      while(DeclCount--) {
+        readVaruint32(Ctx);
+      }
+      // Dummy element?
+
+    }
+    if (IsPassive || IsDeclarative) {
       Segment.Offset.Extended = false;
       Segment.Offset.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
       Segment.Offset.Inst.Value.Int32 = 0;
     } else {
       if (Error Err = readInitExpr(Segment.Offset, Ctx))
         return Err;
+      llvm::errs() << llvm::format( " active seg, read initexpr opcode %x\n", Segment.Offset.Inst.Opcode);
     }
 
-    if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
+    if (HasElemKind) {
       Segment.ElemKind = readUint8(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)) {
-          return make_error<GenericBinaryError>("invalid reference type",
+            Segment.ElemKind != uint8_t(wasm::ValType::EXTERNREF) &&
+            Segment.ElemKind != uint8_t(wasm::ValType::OTHERREF)) {
+          return make_error<GenericBinaryError>("invalid elem type",
                                                 object_error::parse_failed);
         }
       } else {
         if (Segment.ElemKind != 0)
-          return make_error<GenericBinaryError>("invalid elemtype",
+          return make_error<GenericBinaryError>("invalid elem type",
                                                 object_error::parse_failed);
         Segment.ElemKind = uint8_t(wasm::ValType::FUNCREF);
       }
+    } else if (HasInitExprs) {
+      auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
+      Segment.ElemKind = uint8_t(ElemType);
     } else {
-      Segment.ElemKind = uint8_t(wasm::ValType::FUNCREF);
+      Segment.ElemKind = (uint8_t)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));
+    llvm::errs() << llvm::format(" num elems %d\n", NumElems);
+
+    if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS) {
+      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);
   }

>From 04e8a530b40b93ebd2ed3f0f6ed6699435aae2ae Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Fri, 12 Jan 2024 17:17:40 -0800
Subject: [PATCH 08/12] Make TableType a ValType

---
 lld/wasm/InputFiles.cpp                       |   2 +-
 lld/wasm/SymbolTable.cpp                      |   4 +-
 llvm/include/llvm/BinaryFormat/Wasm.h         | 417 +++++++++---------
 llvm/include/llvm/BinaryFormat/WasmTraits.h   |   4 +-
 llvm/include/llvm/MC/MCSymbolWasm.h           |   4 +-
 llvm/lib/MC/WasmObjectWriter.cpp              |   4 +-
 llvm/lib/Object/WasmObjectFile.cpp            |  29 +-
 llvm/lib/ObjectYAML/WasmYAML.cpp              |   7 +-
 .../AsmParser/WebAssemblyAsmParser.cpp        |   2 +-
 .../WebAssembly/WebAssemblyUtilities.cpp      |   2 +-
 llvm/tools/obj2yaml/wasm2yaml.cpp             |   4 +-
 11 files changed, 244 insertions(+), 235 deletions(-)

diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index 5709a5ced584c72..97c88587231ba93 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -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;
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 76370525c371995..b5c138cd76392d2 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -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;
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index b48048a38e77f7a..1e7ece3363caa26 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -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; // TODO: make this a ValType?
-  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;
-  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
@@ -452,6 +245,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,
@@ -461,7 +259,210 @@ enum class ValType {
   V128 = WASM_TYPE_V128,
   FUNCREF = WASM_TYPE_FUNCREF,
   EXTERNREF = WASM_TYPE_EXTERNREF,
-  OTHERREF, // Unmodeled value types include ref types with heap types other than funcref or externref
+  OTHERREF = 0xff, // Unmodeled value types include ref types with heap types other than funcref or 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;
 };
 
 // Represents anything that can be encoded in the type section, but only
diff --git a/llvm/include/llvm/BinaryFormat/WasmTraits.h b/llvm/include/llvm/BinaryFormat/WasmTraits.h
index bef9dd3291ca76e..a279ce37a791ed9 100644
--- a/llvm/include/llvm/BinaryFormat/WasmTraits.h
+++ b/llvm/include/llvm/BinaryFormat/WasmTraits.h
@@ -87,11 +87,11 @@ 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(
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h
index c67bd64e7cbdfda..0ce95c72a73f702 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -112,7 +112,7 @@ class MCSymbolWasm : public MCSymbol {
 
   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 @@ class MCSymbolWasm : public MCSymbol {
     // 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});
   }
 };
 
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index e43f111113b408f..9d8770271163eae 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -972,7 +972,9 @@ void WasmObjectWriter::writeTableSection(ArrayRef<wasm::WasmTable> Tables) {
 
   encodeULEB128(Tables.size(), W->OS);
   for (const wasm::WasmTable &Table : Tables) {
-    encodeULEB128(Table.Type.ElemType, W->OS);
+    llvm::errs() << "Table " << Table.Index << std::string(Table.SymbolName) << " type " << (uint32_t)Table.Type.ElemType << "\n";
+    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);
     if (Table.Type.Limits.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX)
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index e3ffea5f8ce20be..b67b815ed2b72d7 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -304,7 +304,7 @@ static wasm::WasmLimits readLimits(WasmObjectFile::ReadContext &Ctx) {
 static wasm::WasmTableType readTableType(WasmObjectFile::ReadContext &Ctx) {
   wasm::WasmTableType TableType;
   auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
-  TableType.ElemType = (uint8_t)ElemType;
+  TableType.ElemType = ElemType;
   TableType.Limits = readLimits(Ctx);
   return TableType;
 }
@@ -1261,8 +1261,9 @@ 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 &&
+          ElemType != wasm::ValType::OTHERREF)
         return make_error<GenericBinaryError>("invalid table element type",
                                               object_error::parse_failed);
       break;
@@ -1318,8 +1319,9 @@ 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 &&
+        ElemType != wasm::ValType::OTHERREF) {
       return make_error<GenericBinaryError>("invalid table element type",
                                             object_error::parse_failed);
     }
@@ -1655,25 +1657,26 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
     }
 
     if (HasElemKind) {
-      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 != uint8_t(wasm::ValType::OTHERREF)) {
+        Segment.ElemKind = parseValType(Ctx, ElemKind);
+        if (Segment.ElemKind != wasm::ValType::FUNCREF &&
+            Segment.ElemKind != wasm::ValType::EXTERNREF &&
+            Segment.ElemKind != wasm::ValType::OTHERREF) {
           return make_error<GenericBinaryError>("invalid elem type",
                                                 object_error::parse_failed);
         }
       } else {
-        if (Segment.ElemKind != 0)
+        if (ElemKind != 0)
           return make_error<GenericBinaryError>("invalid elem type",
                                                 object_error::parse_failed);
-        Segment.ElemKind = uint8_t(wasm::ValType::FUNCREF);
+        Segment.ElemKind = wasm::ValType::FUNCREF;
       }
     } else if (HasInitExprs) {
       auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
-      Segment.ElemKind = uint8_t(ElemType);
+      Segment.ElemKind = ElemType;
     } else {
-      Segment.ElemKind = (uint8_t)wasm::ValType::FUNCREF;
+      Segment.ElemKind = wasm::ValType::FUNCREF;
     }
 
     uint32_t NumElems = readVaruint32(Ctx);
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index 9502fe5e4077881..797902e31ac32ff 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"
@@ -593,7 +594,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 +603,8 @@ void ScalarEnumerationTraits<WasmYAML::ValueType>::enumeration(
   ECase(V128);
   ECase(FUNCREF);
   ECase(EXTERNREF);
-  ECase(FUNC);
+  ECase(OTHERREF);
+  //ECase(FUNC);
 #undef ECase
 }
 
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 1b92997f03f11c9..3cc4d50271eb114 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -851,7 +851,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
       // 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");
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 189111cef7d0a04..ac7cf5b37fcaa49 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -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);
   }
diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp
index 8557a38405193bb..c15d64cef8d65e4 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -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);

>From 64e0ae785149714ac848833dc4af34ab28e95ef9 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Fri, 19 Jan 2024 16:11:01 -0800
Subject: [PATCH 09/12] obj2yaml dumps elemkind

---
 lld/wasm/WriterUtils.cpp                      |   2 +
 llvm/include/llvm/BinaryFormat/Wasm.h         |   4 +-
 llvm/lib/Object/WasmObjectFile.cpp            |   7 +-
 llvm/lib/ObjectYAML/WasmYAML.cpp              |   5 +-
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     |   3 +-
 llvm/test/Object/Inputs/WASM/multi-table.wasm | Bin 0 -> 185 bytes
 llvm/test/Object/wasm-obj2yaml-tables.test    | 112 ++++++++++++++++++
 7 files changed, 127 insertions(+), 6 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/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index cc8ed0b1de23712..02c8dd677f635aa 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 9dbed31c21ffffd..2bcd87039ceb00f 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -259,7 +259,9 @@ enum class ValType {
   V128 = WASM_TYPE_V128,
   FUNCREF = WASM_TYPE_FUNCREF,
   EXTERNREF = WASM_TYPE_EXTERNREF,
-  OTHERREF = 0xff, // Unmodeled value types include ref types with heap types other than funcref or externref
+  // Unmodeled value types include ref types with heap types other than
+  // funcr or extern, and type-specialized funcrefs
+  OTHERREF = 0xff,
 };
 
 struct WasmDylinkImportInfo {
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index b67b815ed2b72d7..6edab3e0502d132 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1626,10 +1626,11 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
     bool HasInitExprs = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
     bool HasElemKind = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) && !HasInitExprs;
 
-    llvm::errs() << llvm::format("segment %d flags %x, InitExprs %d HasKind %d\n",
+    llvm::errs() << llvm::format("\nsegment %d flags %x, InitExprs %d HasKind %d\n",
       ElemSegments.capacity() - Count-1, Segment.Flags, HasInitExprs,HasElemKind);
-    if (HasTableNumber)
+    if (HasTableNumber) {
       Segment.TableNumber = readVaruint32(Ctx);
+      llvm::errs() << " table " << Segment.TableNumber << "\n"; }
     else
       Segment.TableNumber = 0;
     if (!isValidTableNumber(Segment.TableNumber))
@@ -1682,7 +1683,7 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
     uint32_t NumElems = readVaruint32(Ctx);
     llvm::errs() << llvm::format(" num elems %d\n", NumElems);
 
-    if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS) {
+    if (HasInitExprs) {
       while(NumElems--) {
         wasm::WasmInitExpr Expr;
         if(Error Err = readInitExpr(Expr, Ctx))
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index 797902e31ac32ff..908778daa0304eb 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -384,6 +384,7 @@ 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);
 }
@@ -634,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 908efbb8d32155d..fb949d4b19a3e12 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 at P$&3<P&=ClWCd;>xuz=}#z8j}?*n0KSOwBAkLaSfjDmi)G&kfW|u at g<SYx
zBa52G-mccD+GzF!Dwt9}O9eXog&BTY%K#w^;2<3WW|`<sBKY1t%Pd|otaGHJsp;3^
ZoGR=u)2KtX==*($my0HJxw(SJd;l027?A(~

literal 0
HcmV?d00001

diff --git a/llvm/test/Object/wasm-obj2yaml-tables.test b/llvm/test/Object/wasm-obj2yaml-tables.test
new file mode 100644
index 000000000000000..870ffd179a1c25b
--- /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:       [  ]

>From 8d0e4528e2d3fdbed1e951e41a728d62b93513b9 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Tue, 23 Jan 2024 16:36:30 -0800
Subject: [PATCH 10/12] update signature kinds, clang-format

---
 lld/wasm/InputChunks.h                |  5 ++++-
 lld/wasm/InputElement.h               |  4 +++-
 llvm/include/llvm/BinaryFormat/Wasm.h | 12 +++++++++---
 llvm/include/llvm/Object/Wasm.h       |  4 +++-
 llvm/lib/Object/WasmObjectFile.cpp    | 13 +++++++++----
 5 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index 1e430832fb84c72..ad1d45e335eac90 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 46e21d7c7dfd800..10dc2a3e4a826a9 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/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 2bcd87039ceb00f..9f13cf4e36a855e 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -400,6 +400,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;
@@ -466,12 +471,13 @@ struct WasmLinkingData {
   std::vector<WasmSymbolInfo> SymbolTable;
 };
 
-// Represents anything that can be encoded in the type section, but only
-// signatures are actually modeled. TODO: maybe refactor to make this explicit.
 struct WasmSignature {
   SmallVector<ValType, 1> Returns;
   SmallVector<ValType, 4> Params;
-  enum {Function, Other} Kind = Function; // Recursive, Composite(Array,Struct),  
+  // 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 6b8edb90e144b58..737a6eb9355c64b 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;
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 6edab3e0502d132..48e351f34057204 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1167,11 +1167,13 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       // 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);
-      assert(RecSize > 0); // TODO real errors here and below
+      if (RecSize == 0)
+        return make_error<GenericBinaryError>("Rec group size cannot be 0",
+                                              object_error::parse_failed);
       Signatures.reserve(Signatures.size() + RecSize);
       Count += RecSize;
       llvm::errs() << llvm::format(" Rec size %d\n", RecSize);
-      Sig.Kind = wasm::WasmSignature::Other;
+      Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
       continue;
     }
@@ -1182,7 +1184,9 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       if (Form == wasm::WASM_TYPE_SUB || Form == wasm::WASM_TYPE_SUB_FINAL) {
         uint32_t Supers = readVaruint32(Ctx);
         if (Supers > 0) {
-          assert(Supers == 1);
+          if (Supers != 1)
+            return make_error<GenericBinaryError>(
+                "Invalid number of supertypes", object_error::parse_failed);
           /* Discard SuperIndex */ readVaruint32(Ctx);
         }
         Form = readVaruint32(Ctx);
@@ -1201,7 +1205,7 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
         llvm::errs() << llvm::format(" bad form %x", Form) << '\n';
         return make_error<GenericBinaryError>("bad form", object_error::parse_failed);
       }
-      Sig.Kind = wasm::WasmSignature::Other;
+      Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
       continue;
     }
@@ -1363,6 +1367,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);
   }
 

>From 1cb58ca7602fc604f0d146fcc06fe5abeca6c240 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Tue, 23 Jan 2024 17:02:57 -0800
Subject: [PATCH 11/12] clean up, remove printfs, clang-format

---
 llvm/include/llvm/BinaryFormat/Wasm.h |  11 ++-
 llvm/lib/MC/WasmObjectWriter.cpp      |   4 +-
 llvm/lib/Object/WasmObjectFile.cpp    | 101 ++++++++++----------------
 llvm/lib/ObjectYAML/WasmYAML.cpp      |   8 +-
 4 files changed, 52 insertions(+), 72 deletions(-)

diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 9f13cf4e36a855e..cd13b7014bfe265 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -70,7 +70,7 @@ enum : unsigned {
   WASM_TYPE_NONNULLABLE = 0x64,
   WASM_TYPE_NULLABLE = 0x63,
   WASM_TYPE_FUNC = 0x60,
-  WASM_TYPE_ARRAY = 0x5E, // Composite types, not used for codegen
+  WASM_TYPE_ARRAY = 0x5E,
   WASM_TYPE_STRUCT = 0x5F,
   WASM_TYPE_SUB = 0x50,
   WASM_TYPE_SUB_FINAL = 0x4F,
@@ -121,7 +121,8 @@ enum : unsigned {
   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.
+  // any.convert_extern and extern.convert_any don't seem to be supported by
+  // Binaryen.
 };
 
 // Opcodes used in synthetic functions.
@@ -156,7 +157,7 @@ enum : unsigned {
 
 enum : unsigned {
   WASM_ELEM_SEGMENT_IS_PASSIVE = 0x01,
-  WASM_ELEM_SEGMENT_IS_DECLARATIVE = 0x02, // if passive == 1
+  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,
 };
@@ -260,7 +261,7 @@ enum class ValType {
   FUNCREF = WASM_TYPE_FUNCREF,
   EXTERNREF = WASM_TYPE_EXTERNREF,
   // Unmodeled value types include ref types with heap types other than
-  // funcr or extern, and type-specialized funcrefs
+  // func or extern, and type-specialized funcrefs
   OTHERREF = 0xff,
 };
 
@@ -330,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)
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 9d8770271163eae..985f9351f4a3085 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -972,8 +972,8 @@ void WasmObjectWriter::writeTableSection(ArrayRef<wasm::WasmTable> Tables) {
 
   encodeULEB128(Tables.size(), W->OS);
   for (const wasm::WasmTable &Table : Tables) {
-    llvm::errs() << "Table " << Table.Index << std::string(Table.SymbolName) << " type " << (uint32_t)Table.Type.ElemType << "\n";
-    assert(Table.Type.ElemType != wasm::ValType::OTHERREF && "Cannot encode general ref-typed 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 48e351f34057204..e7006473449bc7a 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -175,23 +175,24 @@ 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/ref null extern)
-    llvm::errs() << llvm::format(" val type %x ", Code);
-    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 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,
@@ -217,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 = parseValType(Ctx, readVaruint32(Ctx));
-    if (Ty != wasm::ValType::EXTERNREF) { // maybe something special if the type isn't one we understand?
-      //return make_error<GenericBinaryError>("invalid type for ref.null",
-      //                                      object_error::parse_failed);
-    }
+    /* Discard type */ parseValType(Ctx, readVaruint32(Ctx));
     break;
   }
   default:
@@ -238,7 +235,6 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
     Ctx.Ptr = Start;
     while (true) {
       uint8_t Opcode = readOpcode(Ctx);
-      llvm::errs() << llvm::format(" opcode %x", Opcode);
       switch (Opcode) {
       case wasm::WASM_OPCODE_I32_CONST:
       case wasm::WASM_OPCODE_GLOBAL_GET:
@@ -279,7 +275,6 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
         break;
       case wasm::WASM_OPCODE_END:
         Expr.Body = ArrayRef<uint8_t>(Start, Ctx.Ptr - Start);
-        llvm::errs() << "\n";
         return Error::success();
       default:
         return make_error<GenericBinaryError>(
@@ -1153,8 +1148,7 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
   auto parseFieldDef = [&]() {
     uint32_t TypeCode = readVaruint32((Ctx));
     /* Discard StorageType */ parseValType(Ctx, TypeCode);
-    uint32_t Mutability = readVaruint32(Ctx);
-    llvm::errs() << llvm:: format(" mut %d ", Mutability);
+    /* Discard Mutability */ readVaruint32(Ctx);
   };
 
   uint32_t Count = readVaruint32(Ctx);
@@ -1162,7 +1156,6 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
   while (Count--) {
     wasm::WasmSignature Sig;
     uint8_t Form = readUint8(Ctx);
-    llvm::errs() << llvm::format("Top Count %d form %x", Count, Form) << '\n';
     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.
@@ -1172,7 +1165,6 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
                                               object_error::parse_failed);
       Signatures.reserve(Signatures.size() + RecSize);
       Count += RecSize;
-      llvm::errs() << llvm::format(" Rec size %d\n", RecSize);
       Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
       continue;
@@ -1190,20 +1182,17 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
           /* Discard SuperIndex */ readVaruint32(Ctx);
         }
         Form = readVaruint32(Ctx);
-        llvm::errs() << llvm::format(" Sub Supers %d form %x", Supers, Form) << '\n';
       }
       if (Form == wasm::WASM_TYPE_STRUCT) {
         uint32_t FieldCount = readVaruint32(Ctx);
         while (FieldCount--) {
           parseFieldDef();
         }
-        llvm::errs() << llvm::format(" Struct size %d", FieldCount) << '\n';
       } else if (Form == wasm::WASM_TYPE_ARRAY) {
         parseFieldDef();
-        llvm::errs() << llvm::format("arr form %x", Form) << '\n';
       } else {
-        llvm::errs() << llvm::format(" bad form %x", Form) << '\n';
-        return make_error<GenericBinaryError>("bad form", object_error::parse_failed);
+        return make_error<GenericBinaryError>("bad form",
+                                              object_error::parse_failed);
       }
       Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
@@ -1212,21 +1201,18 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
 
     uint32_t ParamCount = readVaruint32(Ctx);
     Sig.Params.reserve(ParamCount);
-    llvm::errs() << llvm::format("param ct %d ", ParamCount);
     while (ParamCount--) {
       uint32_t ParamType = readUint8(Ctx);
       Sig.Params.push_back(parseValType(Ctx, ParamType));
       continue;
     }
     uint32_t ReturnCount = readVaruint32(Ctx);
-    llvm::errs() << llvm::format("\nreturn ct %d ", ReturnCount);
     while (ReturnCount--) {
       uint32_t ReturnType = readUint8(Ctx);
       Sig.Returns.push_back(parseValType(Ctx, ReturnType));
     }
-    
+
     Signatures.push_back(std::move(Sig));
-    llvm::errs() << '\n';
   }
   if (Ctx.Ptr != Ctx.End)
     return make_error<GenericBinaryError>("type section ended prematurely",
@@ -1386,10 +1372,9 @@ Error WasmObjectFile::parseGlobalSection(ReadContext &Ctx) {
     Global.Index = NumImportedGlobals + Globals.size();
     auto GlobalOpcode = readVaruint32(Ctx);
     auto GlobalType = parseValType(Ctx, GlobalOpcode);
-    //assert(GlobalType <= std::numeric_limits<wasm::ValType>::max());
+    // assert(GlobalType <= std::numeric_limits<wasm::ValType>::max());
     Global.Type.Type = (uint8_t)GlobalType;
     Global.Type.Mutable = readVaruint1(Ctx);
-    llvm::errs() << llvm::format("Read global %d index %d, type %x mut %d\n", Globals.capacity() -Count-1, Global.Index, GlobalOpcode, Global.Type.Mutable);
     if (Error Err = readInitExpr(Global.InitExpr, Ctx))
       return Err;
     Globals.push_back(Global);
@@ -1626,32 +1611,26 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
           "Unsupported flags for element segment", object_error::parse_failed);
 
     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;
-
-    llvm::errs() << llvm::format("\nsegment %d flags %x, InitExprs %d HasKind %d\n",
-      ElemSegments.capacity() - Count-1, Segment.Flags, HasInitExprs,HasElemKind);
-    if (HasTableNumber) {
+    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);
-      llvm::errs() << " table " << Segment.TableNumber << "\n"; }
     else
       Segment.TableNumber = 0;
+
     if (!isValidTableNumber(Segment.TableNumber))
       return make_error<GenericBinaryError>("invalid TableNumber",
                                             object_error::parse_failed);
 
-    if (IsDeclarative && false) {
-      // Declarative segments are not used or understood by LLVM
-      /* Discard type/kind */ readVaruint32(Ctx);
-      auto DeclCount = readVaruint32(Ctx);
-      while(DeclCount--) {
-        readVaruint32(Ctx);
-      }
-      // Dummy element?
-
-    }
     if (IsPassive || IsDeclarative) {
       Segment.Offset.Extended = false;
       Segment.Offset.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
@@ -1659,7 +1638,6 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
     } else {
       if (Error Err = readInitExpr(Segment.Offset, Ctx))
         return Err;
-      llvm::errs() << llvm::format( " active seg, read initexpr opcode %x\n", Segment.Offset.Inst.Opcode);
     }
 
     if (HasElemKind) {
@@ -1686,12 +1664,11 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
     }
 
     uint32_t NumElems = readVaruint32(Ctx);
-    llvm::errs() << llvm::format(" num elems %d\n", NumElems);
 
     if (HasInitExprs) {
-      while(NumElems--) {
+      while (NumElems--) {
         wasm::WasmInitExpr Expr;
-        if(Error Err = readInitExpr(Expr, Ctx))
+        if (Error Err = readInitExpr(Expr, Ctx))
           return Err;
       }
     } else {
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index 908778daa0304eb..3b53788eddbabcc 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -384,7 +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.
+  // TODO: Omit "offset" for passive segments? It's neither meaningful nor
+  // encoded.
   IO.mapRequired("Offset", Segment.Offset);
   IO.mapRequired("Functions", Segment.Functions);
 }
@@ -595,7 +596,7 @@ void ScalarEnumerationTraits<WasmYAML::SymbolKind>::enumeration(
 
 void ScalarEnumerationTraits<WasmYAML::ValueType>::enumeration(
     IO &IO, WasmYAML::ValueType &Type) {
-#define CONCAT(X) (uint32_t)wasm::ValType::X
+#define CONCAT(X) (uint32_t) wasm::ValType::X
 #define ECase(X) IO.enumCase(Type, #X, CONCAT(X));
   ECase(I32);
   ECase(I64);
@@ -605,7 +606,6 @@ void ScalarEnumerationTraits<WasmYAML::ValueType>::enumeration(
   ECase(FUNCREF);
   ECase(EXTERNREF);
   ECase(OTHERREF);
-  //ECase(FUNC);
 #undef ECase
 }
 
@@ -635,7 +635,7 @@ void ScalarEnumerationTraits<WasmYAML::Opcode>::enumeration(
 
 void ScalarEnumerationTraits<WasmYAML::TableType>::enumeration(
     IO &IO, WasmYAML::TableType &Type) {
-#define CONCAT(X) (uint32_t)wasm::ValType::X
+#define CONCAT(X) (uint32_t) wasm::ValType::X
 #define ECase(X) IO.enumCase(Type, #X, CONCAT(X));
   ECase(FUNCREF);
   ECase(EXTERNREF);

>From 1326cf3dc711acd40179902310660b265cfe25d2 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Wed, 24 Jan 2024 17:32:20 -0800
Subject: [PATCH 12/12] Check linker inputs for unmodeled types

---
 lld/wasm/InputFiles.cpp            | 3 +++
 llvm/include/llvm/Object/Wasm.h    | 2 ++
 llvm/lib/Object/WasmObjectFile.cpp | 2 ++
 3 files changed, 7 insertions(+)

diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index f5e946aca8b2a8f..f43c39b218787c3 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/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h
index 737a6eb9355c64b..927dce882f6ae55 100644
--- a/llvm/include/llvm/Object/Wasm.h
+++ b/llvm/include/llvm/Object/Wasm.h
@@ -211,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;
@@ -293,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/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index e7006473449bc7a..953e7c70b5f6e31 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1167,6 +1167,7 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       Count += RecSize;
       Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
+      HasUnmodeledTypes = true;
       continue;
     }
     if (Form != wasm::WASM_TYPE_FUNC) {
@@ -1196,6 +1197,7 @@ Error WasmObjectFile::parseTypeSection(ReadContext &Ctx) {
       }
       Sig.Kind = wasm::WasmSignature::Placeholder;
       Signatures.push_back(std::move(Sig));
+      HasUnmodeledTypes = true;
       continue;
     }
 



More information about the cfe-commits mailing list