https://github.com/sbc100 created 
https://github.com/llvm/llvm-project/pull/176617

This change adds initial support to libObject for reading compact imports and 
support for writing compact imports in the linker.

See https://github.com/WebAssembly/compact-import-section

>From 344e8f93a6d199355cdcda4bda82d77a3fd34b01 Mon Sep 17 00:00:00 2001
From: Sam Clegg <[email protected]>
Date: Tue, 13 Jan 2026 10:10:42 -0800
Subject: [PATCH] [WebAssembly] Add initial support for compact imports
 proposal

This change adds initial support to libObject for reading compact
imports and support for writing compact imports in the linker.

See https://github.com/WebAssembly/compact-import-section
---
 clang/include/clang/Options/Options.td        |   2 +
 clang/lib/Basic/Targets/WebAssembly.cpp       |  10 ++
 clang/lib/Basic/Targets/WebAssembly.h         |   1 +
 clang/test/Driver/wasm-features.c             |   6 +
 lld/test/wasm/compact-imports.s               |  27 +++++
 lld/wasm/SyntheticSections.cpp                |  37 +++++-
 lld/wasm/WriterUtils.cpp                      |   4 +
 lld/wasm/WriterUtils.h                        |   2 +
 llvm/include/llvm/Object/Wasm.h               |   2 +
 llvm/lib/Object/WasmObjectFile.cpp            | 114 ++++++++++--------
 .../Target/WebAssembly/WebAssemblySubtarget.h |   2 +
 11 files changed, 156 insertions(+), 51 deletions(-)
 create mode 100644 lld/test/wasm/compact-imports.s

diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 188739e72434a..133bdf32944a7 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5554,6 +5554,8 @@ def mbulk_memory_opt : Flag<["-"], "mbulk-memory-opt">, 
Group<m_wasm_Features_Gr
 def mno_bulk_memory_opt : Flag<["-"], "mno-bulk-memory-opt">, 
Group<m_wasm_Features_Group>;
 def mcall_indirect_overlong : Flag<["-"], "mcall-indirect-overlong">, 
Group<m_wasm_Features_Group>;
 def mno_call_indirect_overlong : Flag<["-"], "mno-call-indirect-overlong">, 
Group<m_wasm_Features_Group>;
+def mcompact_imports : Flag<["-"], "mcompact-imports">, 
Group<m_wasm_Features_Group>;
+def mno_compact_imports : Flag<["-"], "mno-compact-imports">, 
Group<m_wasm_Features_Group>;
 def mexception_handing : Flag<["-"], "mexception-handling">, 
Group<m_wasm_Features_Group>;
 def mno_exception_handing : Flag<["-"], "mno-exception-handling">, 
Group<m_wasm_Features_Group>;
 def mextended_const : Flag<["-"], "mextended-const">, 
Group<m_wasm_Features_Group>;
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp 
b/clang/lib/Basic/Targets/WebAssembly.cpp
index 5bbb7af4c2ca1..5c0a3ec583273 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -56,6 +56,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) 
const {
       .Case("bulk-memory", HasBulkMemory)
       .Case("bulk-memory-opt", HasBulkMemoryOpt)
       .Case("call-indirect-overlong", HasCallIndirectOverlong)
+      .Case("compact-imports", HasCompactImports)
       .Case("exception-handling", HasExceptionHandling)
       .Case("extended-const", HasExtendedConst)
       .Case("fp16", HasFP16)
@@ -191,6 +192,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
   auto addBleedingEdgeFeatures = [&]() {
     addGenericFeatures();
     Features["atomics"] = true;
+    Features["comptact-imports"] = true;
     Features["exception-handling"] = true;
     Features["extended-const"] = true;
     Features["fp16"] = true;
@@ -247,6 +249,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
       HasCallIndirectOverlong = false;
       continue;
     }
+    if (Feature == "+compact-imports") {
+      HasCallIndirectOverlong = true;
+      continue;
+    }
+    if (Feature == "-compact-imports") {
+      HasCallIndirectOverlong = false;
+      continue;
+    }
     if (Feature == "+exception-handling") {
       HasExceptionHandling = true;
       continue;
diff --git a/clang/lib/Basic/Targets/WebAssembly.h 
b/clang/lib/Basic/Targets/WebAssembly.h
index 3634330ec6698..b0f81531b880d 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -62,6 +62,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public 
TargetInfo {
   bool HasBulkMemory = false;
   bool HasBulkMemoryOpt = false;
   bool HasCallIndirectOverlong = false;
+  bool HasCompactImports = false;
   bool HasExceptionHandling = false;
   bool HasExtendedConst = false;
   bool HasFP16 = false;
diff --git a/clang/test/Driver/wasm-features.c 
b/clang/test/Driver/wasm-features.c
index f0215ecc185c6..89ced36eeffab 100644
--- a/clang/test/Driver/wasm-features.c
+++ b/clang/test/Driver/wasm-features.c
@@ -106,3 +106,9 @@
 
 // WIDE-ARITH: "-target-feature" "+wide-arithmetic"
 // NO-WIDE-ARITH: "-target-feature" "-wide-arithmetic"
+
+// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcompact-imports 2>&1 
| FileCheck %s -check-prefix=COMPACT-IMPORTS
+// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-compact-imports 
2>&1 | FileCheck %s -check-prefix=NO-COMPACT-IMPORTS
+
+// COMPACT-IMPORTS: "-target-feature" "+compact-imports"
+// NO-COMPACT-IMPORTS: "-target-feature" "-compact-imports"
diff --git a/lld/test/wasm/compact-imports.s b/lld/test/wasm/compact-imports.s
new file mode 100644
index 0000000000000..7c7ed198f57d4
--- /dev/null
+++ b/lld/test/wasm/compact-imports.s
@@ -0,0 +1,27 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --unresolved-symbols=import-dynamic %t.o -o %t.wasm
+
+.functype foo () -> ()
+.functype bar () -> ()
+
+.globl _start
+_start:
+  .functype _start () -> ()
+  call foo
+  call bar
+  end_function
+
+.section .custom_section.target_features,"",@
+.int8 1
+.int8 43
+.int8 15
+.ascii "compact-imports"
+
+# Neither llvm-readobj nor obj2yaml currently report compact imports 
differently
+# so the check here is just for the size of the import section.  The Size here
+# is larger than 20 bytes without compact imports enabled.
+
+# RUN: llvm-readobj --sections %t.wasm | FileCheck %s
+
+# CHECK:         Type: IMPORT (0x2)
+# CHECK-NEXT:    Size: 20
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 5e7b9c229f3ed..81d8ef298d5a5 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -241,9 +241,12 @@ void ImportSection::addImport(Symbol *sym) {
 void ImportSection::writeBody() {
   raw_ostream &os = bodyOutputStream;
 
-  writeUleb128(os, getNumImports(), "import count");
+  uint32_t numImports = getNumImports();
+  writeUleb128(os, numImports, "import count");
 
   bool is64 = ctx.arg.is64.value_or(false);
+  std::vector<WasmImport> imports;
+  imports.reserve(numImports);
 
   if (ctx.arg.memoryImport) {
     WasmImport import;
@@ -264,7 +267,7 @@ void ImportSection::writeBody() {
       import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_PAGE_SIZE;
       import.Memory.PageSize = ctx.arg.pageSize;
     }
-    writeImport(os, import);
+    imports.push_back(import);
   }
 
   for (const Symbol *sym : importedSymbols) {
@@ -286,7 +289,7 @@ void ImportSection::writeBody() {
       import.Kind = WASM_EXTERNAL_TABLE;
       import.Table = *tableSym->getTableType();
     }
-    writeImport(os, import);
+    imports.push_back(import);
   }
 
   for (const Symbol *sym : gotSymbols) {
@@ -299,7 +302,33 @@ void ImportSection::writeBody() {
     else
       import.Module = "GOT.func";
     import.Field = sym->getName();
-    writeImport(os, import);
+    imports.push_back(import);
+  }
+
+  bool hasCompactImports = 
out.targetFeaturesSec->features.contains("compact-imports");
+  uint32_t i = 0;
+  while (i < numImports) {
+    const WasmImport& import = imports[i];
+    if (hasCompactImports) {
+      uint32_t groupSize = 1;
+      for (uint32_t j = i + 1; j < numImports; j++) {
+        if (imports[j].Module == import.Module)
+          groupSize++;
+        else
+          break;
+      }
+      if (groupSize > 1) {
+        writeStr(os, import.Module, "module name");
+        writeStr(os, "", "empty field name");
+        writeU8(os, 0x7F, "compact imports constant");
+        writeUleb128(os, groupSize, "num compact imports");
+        while (groupSize--) {
+          writeCompactImport(os, imports[i++]);
+        }
+        continue;
+      }
+    }
+    writeImport(os, imports[i++]);
   }
 }
 
diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index b78c354ffb97b..a23d0febb364f 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -218,6 +218,10 @@ void writeTableType(raw_ostream &os, const WasmTableType 
&type) {
 
 void writeImport(raw_ostream &os, const WasmImport &import) {
   writeStr(os, import.Module, "import module name");
+  writeCompactImport(os, import);
+}
+
+void writeCompactImport(raw_ostream &os, const WasmImport &import) {
   writeStr(os, import.Field, "import field name");
   writeU8(os, import.Kind, "import kind");
   switch (import.Kind) {
diff --git a/lld/wasm/WriterUtils.h b/lld/wasm/WriterUtils.h
index 2be79d1d86e97..061199b168080 100644
--- a/lld/wasm/WriterUtils.h
+++ b/lld/wasm/WriterUtils.h
@@ -62,6 +62,8 @@ void writeTableType(raw_ostream &os, const 
llvm::wasm::WasmTableType &type);
 
 void writeImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
 
+void writeCompactImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
+
 void writeExport(raw_ostream &os, const llvm::wasm::WasmExport &export_);
 
 } // namespace wasm
diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h
index b4ba50778c152..55e2148e42670 100644
--- a/llvm/include/llvm/Object/Wasm.h
+++ b/llvm/include/llvm/Object/Wasm.h
@@ -250,6 +250,8 @@ class LLVM_ABI WasmObjectFile : public ObjectFile {
   Error parseSection(WasmSection &Sec);
   Error parseCustomSection(WasmSection &Sec, ReadContext &Ctx);
 
+  Error parseImport(ReadContext &Ctx, wasm::WasmImport& Im);
+
   // Standard section types
   Error parseTypeSection(ReadContext &Ctx);
   Error parseImportSection(ReadContext &Ctx);
diff --git a/llvm/lib/Object/WasmObjectFile.cpp 
b/llvm/lib/Object/WasmObjectFile.cpp
index ee7a3068af91d..4111afb8005f3 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -276,7 +276,7 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr,
         return Error::success();
       default:
         return make_error<GenericBinaryError>(
-            Twine("invalid opcode in init_expr: ") + Twine(unsigned(Opcode)),
+            "invalid opcode in init_expr: " + Twine(unsigned(Opcode)),
             object_error::parse_failed);
       }
     }
@@ -1267,60 +1267,80 @@ Error WasmObjectFile::parseTypeSection(ReadContext 
&Ctx) {
   return Error::success();
 }
 
+Error WasmObjectFile::parseImport(ReadContext &Ctx, wasm::WasmImport& Im) {
+  switch (Im.Kind) {
+  case wasm::WASM_EXTERNAL_FUNCTION:
+    NumImportedFunctions++;
+    Im.SigIndex = readVaruint32(Ctx);
+    if (Im.SigIndex >= Signatures.size())
+      return make_error<GenericBinaryError>("invalid function type",
+                                            object_error::parse_failed);
+    break;
+  case wasm::WASM_EXTERNAL_GLOBAL:
+    NumImportedGlobals++;
+    Im.Global.Type = readUint8(Ctx);
+    Im.Global.Mutable = readVaruint1(Ctx);
+    break;
+  case wasm::WASM_EXTERNAL_MEMORY:
+    Im.Memory = readLimits(Ctx);
+    if (Im.Memory.Flags & wasm::WASM_LIMITS_FLAG_IS_64)
+      HasMemory64 = true;
+    break;
+  case wasm::WASM_EXTERNAL_TABLE: {
+    Im.Table = readTableType(Ctx);
+    NumImportedTables++;
+    auto ElemType = Im.Table.ElemType;
+    if (ElemType != wasm::ValType::FUNCREF &&
+        ElemType != wasm::ValType::EXTERNREF &&
+        ElemType != wasm::ValType::EXNREF &&
+        ElemType != wasm::ValType::OTHERREF)
+      return make_error<GenericBinaryError>("invalid table element type",
+                                            object_error::parse_failed);
+    break;
+  }
+  case wasm::WASM_EXTERNAL_TAG:
+    NumImportedTags++;
+    if (readUint8(Ctx) != 0) // Reserved 'attribute' field
+      return make_error<GenericBinaryError>("invalid attribute",
+                                            object_error::parse_failed);
+    Im.SigIndex = readVaruint32(Ctx);
+    if (Im.SigIndex >= Signatures.size())
+      return make_error<GenericBinaryError>("invalid tag type",
+                                            object_error::parse_failed);
+    break;
+  default:
+    return make_error<GenericBinaryError>("unexpected import kind: " + 
Twine(unsigned(Im.Kind)),
+                                          object_error::parse_failed);
+  }
+  Imports.push_back(Im);
+  return Error::success();
+}
+
 Error WasmObjectFile::parseImportSection(ReadContext &Ctx) {
   uint32_t Count = readVaruint32(Ctx);
-  uint32_t NumTypes = Signatures.size();
   Imports.reserve(Count);
-  for (uint32_t I = 0; I < Count; I++) {
+  uint32_t I = 0;
+  while (I < Count) {
     wasm::WasmImport Im;
     Im.Module = readString(Ctx);
     Im.Field = readString(Ctx);
     Im.Kind = readUint8(Ctx);
-    switch (Im.Kind) {
-    case wasm::WASM_EXTERNAL_FUNCTION:
-      NumImportedFunctions++;
-      Im.SigIndex = readVaruint32(Ctx);
-      if (Im.SigIndex >= NumTypes)
-        return make_error<GenericBinaryError>("invalid function type",
-                                              object_error::parse_failed);
-      break;
-    case wasm::WASM_EXTERNAL_GLOBAL:
-      NumImportedGlobals++;
-      Im.Global.Type = readUint8(Ctx);
-      Im.Global.Mutable = readVaruint1(Ctx);
-      break;
-    case wasm::WASM_EXTERNAL_MEMORY:
-      Im.Memory = readLimits(Ctx);
-      if (Im.Memory.Flags & wasm::WASM_LIMITS_FLAG_IS_64)
-        HasMemory64 = true;
-      break;
-    case wasm::WASM_EXTERNAL_TABLE: {
-      Im.Table = readTableType(Ctx);
-      NumImportedTables++;
-      auto ElemType = Im.Table.ElemType;
-      if (ElemType != wasm::ValType::FUNCREF &&
-          ElemType != wasm::ValType::EXTERNREF &&
-          ElemType != wasm::ValType::EXNREF &&
-          ElemType != wasm::ValType::OTHERREF)
-        return make_error<GenericBinaryError>("invalid table element type",
-                                              object_error::parse_failed);
-      break;
-    }
-    case wasm::WASM_EXTERNAL_TAG:
-      NumImportedTags++;
-      if (readUint8(Ctx) != 0) // Reserved 'attribute' field
-        return make_error<GenericBinaryError>("invalid attribute",
-                                              object_error::parse_failed);
-      Im.SigIndex = readVaruint32(Ctx);
-      if (Im.SigIndex >= NumTypes)
-        return make_error<GenericBinaryError>("invalid tag type",
-                                              object_error::parse_failed);
-      break;
-    default:
-      return make_error<GenericBinaryError>("unexpected import kind",
-                                            object_error::parse_failed);
+    // 0x7E along with and empty Field string signals a block of compact
+    // imports
+    if (Im.Kind == 0x7F && Im.Field == "") {
+      uint32_t NumCompactImports = readVaruint32(Ctx);
+      while (NumCompactImports--) {
+        Im.Field = readString(Ctx);
+        Im.Kind = readUint8(Ctx);
+        Error rtn = parseImport(Ctx, Im);
+        if (rtn) return rtn;
+        I++;
+      }
+    } else {
+      Error rtn = parseImport(Ctx, Im);
+      if (rtn) return rtn;
+      I++;
     }
-    Imports.push_back(Im);
   }
   if (Ctx.Ptr != Ctx.End)
     return make_error<GenericBinaryError>("import section ended prematurely",
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h 
b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 2f88bbba05d00..922cffe0315d3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -43,6 +43,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool HasBulkMemory = false;
   bool HasBulkMemoryOpt = false;
   bool HasCallIndirectOverlong = false;
+  bool HasCompactImports = false;
   bool HasExceptionHandling = false;
   bool HasExtendedConst = false;
   bool HasFP16 = false;
@@ -100,6 +101,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool hasBulkMemory() const { return HasBulkMemory; }
   bool hasBulkMemoryOpt() const { return HasBulkMemoryOpt; }
   bool hasCallIndirectOverlong() const { return HasCallIndirectOverlong; }
+  bool hasCompactImports() const { return HasCompactImports; }
   bool hasExceptionHandling() const { return HasExceptionHandling; }
   bool hasExtendedConst() const { return HasExtendedConst; }
   bool hasFP16() const { return HasFP16; }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to