[llvm] [WebAssembly] Define `__funcref_call_table` in generated asm and objects (PR #180900)

Demetrius Kanios via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 23 22:11:44 PDT 2026


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/180900

>From 8256b0278333cd4104c761c4753bc1abb0902728 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 11 Jun 2026 21:55:20 -0700
Subject: [PATCH 1/3] Define `__funcref_call_table` in generated asm and
 objects

---
 .../Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 16 ++++++++++++++++
 llvm/test/CodeGen/WebAssembly/funcref-call.ll    |  1 +
 2 files changed, 17 insertions(+)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index c25972343c96a..37eda16077bec 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -398,6 +398,22 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
   // emitDecls() is not called until now.
   emitDecls(M);
 
+  {
+    StringRef Name = "__funcref_call_table";
+    auto *Sym = static_cast<MCSymbolWasm *>(OutContext.lookupSymbol(Name));
+    if (Sym) {
+      if (!Sym->isFunctionTable())
+        OutContext.reportError(SMLoc(), "symbol is not a wasm funcref table");
+
+      if (Sym->isWeak()) {
+        OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
+      }
+      if (!Sym->isDefined()) {
+        OutStreamer->emitLabel(Sym);
+        OutStreamer->addBlankLine();
+      }
+    }
+  }
   // When a function's address is taken, a TABLE_INDEX relocation is emitted
   // against the function symbol at the use site.  However the relocation
   // doesn't explicitly refer to the table.  In the future we may want to
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-call.ll b/llvm/test/CodeGen/WebAssembly/funcref-call.ll
index 9904df2280e81..4f6025de4759c 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-call.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-call.ll
@@ -1,6 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=0 -mattr=+reference-types | FileCheck %s
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=1 -mattr=+reference-types | FileCheck %s
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown --filetype=obj
 
 %funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
 

>From 7b2714ca8a44ff727ff9d2967e10ce7d2d903c2c Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Thu, 11 Jun 2026 22:38:37 -0700
Subject: [PATCH 2/3] Move definition to `emitDecls` and use asserts.

---
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     | 32 +++++++++----------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 37eda16077bec..c78ca5fcda876 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -332,6 +332,22 @@ void WebAssemblyAsmPrinter::emitDecls(const Module &M) {
       emitSymbolType(Sym);
   }
 
+  {
+    StringRef Name = "__funcref_call_table";
+    auto *Sym = static_cast<MCSymbolWasm *>(OutContext.lookupSymbol(Name));
+    if (Sym) {
+      if (!Sym->isFunctionTable())
+        OutContext.reportError(SMLoc(), "symbol is not a wasm funcref table");
+
+      assert(Sym->isWeak());
+      assert(!Sym->isDefined());
+
+      OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
+      OutStreamer->emitLabel(Sym);
+      OutStreamer->addBlankLine();
+    }
+  }
+
   DenseSet<MCSymbol *> InvokeSymbols;
   for (const auto &F : M) {
     if (F.isIntrinsic())
@@ -398,22 +414,6 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
   // emitDecls() is not called until now.
   emitDecls(M);
 
-  {
-    StringRef Name = "__funcref_call_table";
-    auto *Sym = static_cast<MCSymbolWasm *>(OutContext.lookupSymbol(Name));
-    if (Sym) {
-      if (!Sym->isFunctionTable())
-        OutContext.reportError(SMLoc(), "symbol is not a wasm funcref table");
-
-      if (Sym->isWeak()) {
-        OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
-      }
-      if (!Sym->isDefined()) {
-        OutStreamer->emitLabel(Sym);
-        OutStreamer->addBlankLine();
-      }
-    }
-  }
   // When a function's address is taken, a TABLE_INDEX relocation is emitted
   // against the function symbol at the use site.  However the relocation
   // doesn't explicitly refer to the table.  In the future we may want to

>From 891ce8a4da289ebd6540daf817eddbef18f5235a Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 23 Jun 2026 22:11:30 -0700
Subject: [PATCH 3/3] Add comments

---
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index c78ca5fcda876..3124ad224a6f4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -332,6 +332,16 @@ void WebAssemblyAsmPrinter::emitDecls(const Module &M) {
       emitSymbolType(Sym);
   }
 
+  // We handle `__funcref_call_table` specially here.
+  //
+  // Unlike most table symbols, which are attached to a `GlobalVariable`
+  // this one is a directly created, freestanding MCSymbol, much like
+  // `__indirect_function_table`. However, given that the table is always
+  // identical (single element, default initialized), we declare it
+  // weak in each object, and define it here rather than in the linker.
+  //
+  // TODO: consider moving this definition elsewhere, or doing away with
+  // the table entirely (in favor of `call_ref` exclusively).
   {
     StringRef Name = "__funcref_call_table";
     auto *Sym = static_cast<MCSymbolWasm *>(OutContext.lookupSymbol(Name));
@@ -339,11 +349,18 @@ void WebAssemblyAsmPrinter::emitDecls(const Module &M) {
       if (!Sym->isFunctionTable())
         OutContext.reportError(SMLoc(), "symbol is not a wasm funcref table");
 
+      // symbol is declared weak in `getOrCreateFuncrefCallTableSymbol`
       assert(Sym->isWeak());
+      OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
+
+      // Make sure we haven't already emitted it for whatever reason.
       assert(!Sym->isDefined());
 
-      OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
+      // Actually define the symbol.
+      // Confusingly enough, `emitLabel` is what "defines" a MCSymbol.
+      // Provides it a fragment, so that it `!isUndefined`
       OutStreamer->emitLabel(Sym);
+      // No initializer needed. Default ref.null is good
       OutStreamer->addBlankLine();
     }
   }



More information about the llvm-commits mailing list