[lld] [llvm] [WebAssembly] Add support for shared tags (PR #188367)

Jasmine Tang via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 4 22:04:37 PDT 2026


https://github.com/badumbatish updated https://github.com/llvm/llvm-project/pull/188367

>From 22e1c0573be1c785d8d6a44c0815881d2aeee6fb Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Mon, 23 Mar 2026 23:29:52 -0700
Subject: [PATCH 1/4] Precommit test

---
 lld/test/wasm/Inputs/tag-export.s |  3 +++
 lld/test/wasm/shared-tag.s        | 22 ++++++++++++++++++++++
 2 files changed, 25 insertions(+)
 create mode 100644 lld/test/wasm/Inputs/tag-export.s
 create mode 100644 lld/test/wasm/shared-tag.s

diff --git a/lld/test/wasm/Inputs/tag-export.s b/lld/test/wasm/Inputs/tag-export.s
new file mode 100644
index 0000000000000..1eb64b98b2228
--- /dev/null
+++ b/lld/test/wasm/Inputs/tag-export.s
@@ -0,0 +1,3 @@
+  .globl __cpp_exception
+  .tagtype __cpp_exception i32
+__cpp_exception:
diff --git a/lld/test/wasm/shared-tag.s b/lld/test/wasm/shared-tag.s
new file mode 100644
index 0000000000000..b5ddb010fdd88
--- /dev/null
+++ b/lld/test/wasm/shared-tag.s
@@ -0,0 +1,22 @@
+# REQUIRES: wasm
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -mattr=+exception-handling -o %t.o %s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/tag-export.s -o %t.tag.o
+# RUN: wasm-ld -shared --experimental-pic -o %t.tag.so %t.tag.o
+
+## Test that wasm-ld cannot resolve tags exported by shared libraries.
+## See https://github.com/llvm/llvm-project/issues/188120
+
+# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.tag.so 2>&1 | FileCheck %s
+
+
+  .tagtype __cpp_exception i32
+
+  .globl _start
+_start:
+  .functype _start () -> ()
+  i32.const 0
+  throw __cpp_exception
+  end_function
+
+
+# CHECK: error: {{.*}}shared-tag.s.tmp.o: undefined symbol: __cpp_exception

>From 692e60daec6a26df0142ea8dc5777b7c56d3164f Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 24 Mar 2026 15:16:24 -0700
Subject: [PATCH 2/4] Fix shared tag issue

---
 lld/test/wasm/shared-tag.s         |  9 ++++---
 lld/wasm/InputFiles.cpp            |  3 +++
 lld/wasm/SymbolTable.cpp           | 38 ++++++++++++++++++++++++++++++
 lld/wasm/SymbolTable.h             |  2 ++
 lld/wasm/Symbols.cpp               |  2 ++
 lld/wasm/Symbols.h                 | 17 +++++++++++--
 llvm/lib/Object/WasmObjectFile.cpp |  4 ++++
 7 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/lld/test/wasm/shared-tag.s b/lld/test/wasm/shared-tag.s
index b5ddb010fdd88..7dc304264094a 100644
--- a/lld/test/wasm/shared-tag.s
+++ b/lld/test/wasm/shared-tag.s
@@ -3,10 +3,11 @@
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/tag-export.s -o %t.tag.o
 # RUN: wasm-ld -shared --experimental-pic -o %t.tag.so %t.tag.o
 
-## Test that wasm-ld cannot resolve tags exported by shared libraries.
+## Test that wasm-ld can resolve tags exported by shared libraries.
 ## See https://github.com/llvm/llvm-project/issues/188120
 
-# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.tag.so 2>&1 | FileCheck %s
+# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.tag.so
+# RUN: obj2yaml %t.wasm | FileCheck %s
 
 
   .tagtype __cpp_exception i32
@@ -19,4 +20,6 @@ _start:
   end_function
 
 
-# CHECK: error: {{.*}}shared-tag.s.tmp.o: undefined symbol: __cpp_exception
+# CHECK:            Field:           __cpp_exception
+# CHECK-NEXT:       Kind:            TAG
+# CHECK-NEXT:       SigIndex:        0
diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index dcdd138508c1e..ad7f207259a5b 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -453,6 +453,9 @@ void SharedFile::parse() {
       case WASM_SYMBOL_TYPE_DATA:
         s = symtab->addSharedData(name, flags, this);
         break;
+      case WASM_SYMBOL_TYPE_TAG:
+        s = symtab->addSharedTag(name, flags, this, wasmSym.Signature);
+        break;
       default:
         continue;
       }
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 9bd93f317c3c5..11ca4fc23d0b1 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -352,6 +352,44 @@ static void reportFunctionSignatureMismatch(StringRef symName,
                                   isError);
 }
 
+Symbol *SymbolTable::addSharedTag(StringRef name, uint32_t flags,
+                                  InputFile *file, const WasmSignature *sig) {
+  LLVM_DEBUG(dbgs() << "addSharedTag: " << name << " [" << toString(*sig)
+                    << "]\n");
+  Symbol *s;
+  bool wasInserted;
+  std::tie(s, wasInserted) = insert(name, file);
+
+  auto replaceSym = [&](Symbol *sym) {
+    replaceSymbol<SharedTagSymbol>(sym, name, flags, file, sig);
+  };
+
+  // same as addSharedFunction, but this is in its own function
+  if (wasInserted || s->isLazy()) {
+    replaceSym(s);
+    return s;
+  }
+
+  if (!isa<TagSymbol>(s)) {
+    reportTypeError(s, file, WASM_SYMBOL_TYPE_TAG);
+    return s;
+  }
+
+  if (s->isDefined()) {
+    return s;
+  }
+
+  // undefined existing sym
+  auto *existingTag = cast<TagSymbol>(s);
+  const WasmSignature *oldSig = existingTag->signature;
+  if (oldSig && sig && *oldSig != *sig)
+    warn("Tag signature mismatch: " + name + "\n>>> defined as " +
+         toString(*oldSig) + " in " + toString(existingTag->getFile()) +
+         "\n>>> defined as " + toString(*sig) + " in " + toString(file));
+  replaceSym(s);
+  return s;
+}
+
 Symbol *SymbolTable::addSharedFunction(StringRef name, uint32_t flags,
                                        InputFile *file,
                                        const WasmSignature *sig) {
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 649614298c52b..0667cedecdb4f 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -53,6 +53,8 @@ class SymbolTable {
   Symbol *addSharedFunction(StringRef name, uint32_t flags, InputFile *file,
                             const WasmSignature *sig);
   Symbol *addSharedData(StringRef name, uint32_t flags, InputFile *file);
+  Symbol *addSharedTag(StringRef name, uint32_t flags, InputFile *file,
+                       const WasmSignature *sig);
   Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file,
                              InputFunction *function);
   Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file,
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 97a9871a06308..9ce2a9f9f5091 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -72,6 +72,8 @@ std::string toString(wasm::Symbol::Kind kind) {
     return "SharedFunctionKind";
   case wasm::Symbol::SharedDataKind:
     return "SharedDataKind";
+  case wasm::Symbol::SharedTagKind:
+    return "SharedTagSymbol";
   }
   llvm_unreachable("invalid symbol kind");
 }
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 3dda36f88d5e3..47f1b3ad54ea2 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -62,6 +62,7 @@ class Symbol {
     LazyKind,
     SharedFunctionKind,
     SharedDataKind,
+    SharedTagKind,
   };
 
   Kind kind() const { return symbolKind; }
@@ -77,7 +78,8 @@ class Symbol {
 
   bool isLazy() const { return symbolKind == LazyKind; }
   bool isShared() const {
-    return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind;
+    return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind ||
+           symbolKind == SharedTagKind;
   }
 
   bool isLocal() const;
@@ -461,7 +463,8 @@ class UndefinedTable : public TableSymbol {
 class TagSymbol : public Symbol {
 public:
   static bool classof(const Symbol *s) {
-    return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind;
+    return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind ||
+           s->kind() == SharedTagKind;
   }
 
   // Get/set the tag index
@@ -501,6 +504,15 @@ class UndefinedTag : public TagSymbol {
   static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; }
 };
 
+class SharedTagSymbol : public TagSymbol {
+public:
+  SharedTagSymbol(StringRef name, uint32_t flags, InputFile *f,
+                  const WasmSignature *sig)
+      : TagSymbol(name, SharedTagKind, flags, f, sig) {}
+
+  static bool classof(const Symbol *s) { return s->kind() == SharedTagKind; }
+};
+
 class SharedFunctionSymbol : public FunctionSymbol {
 public:
   SharedFunctionSymbol(StringRef name, uint32_t flags, InputFile *file,
@@ -553,6 +565,7 @@ union SymbolUnion {
   alignas(UndefinedTable) char j[sizeof(UndefinedTable)];
   alignas(SectionSymbol) char k[sizeof(SectionSymbol)];
   alignas(SharedFunctionSymbol) char l[sizeof(SharedFunctionSymbol)];
+  alignas(SharedTagSymbol) char m[sizeof(SharedTagSymbol)];
 };
 
 // It is important to keep the size of SymbolUnion small for performance and
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 852dc23f99e54..5f125ffb10198 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1547,6 +1547,10 @@ Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
                                               object_error::parse_failed);
       Info.Kind = wasm::WASM_SYMBOL_TYPE_TAG;
       Info.ElementIndex = Ex.Index;
+      if (isDefinedTagIndex(Ex.Index)) {
+        unsigned TagIndex = Ex.Index - NumImportedTags;
+        Signature = &Signatures[Tags[TagIndex].SigIndex];
+      }
       break;
     case wasm::WASM_EXTERNAL_MEMORY:
       break;

>From 75f3296674f3b972a26370993ecb658b2dec5a37 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Tue, 31 Mar 2026 21:30:25 -0700
Subject: [PATCH 3/4] Addresses PR review

---
 lld/test/wasm/Inputs/libsearch-dyn.s |  6 +++++-
 lld/test/wasm/Inputs/tag-export.s    |  3 ---
 lld/test/wasm/dylink.s               | 11 ++++++++---
 lld/test/wasm/shared-tag.s           | 25 -------------------------
 lld/wasm/SymbolTable.cpp             | 10 +++++-----
 5 files changed, 18 insertions(+), 37 deletions(-)
 delete mode 100644 lld/test/wasm/Inputs/tag-export.s
 delete mode 100644 lld/test/wasm/shared-tag.s

diff --git a/lld/test/wasm/Inputs/libsearch-dyn.s b/lld/test/wasm/Inputs/libsearch-dyn.s
index bb59580b830f5..11c5a1099e4b5 100644
--- a/lld/test/wasm/Inputs/libsearch-dyn.s
+++ b/lld/test/wasm/Inputs/libsearch-dyn.s
@@ -1,4 +1,4 @@
-.globl _bar,_dynamic
+.globl _bar,_dynamic,__foo_exception
 
 .section .data,"",@
 _bar:
@@ -6,3 +6,7 @@ _bar:
 
 _dynamic:
 .size _dynamic,4
+
+
+.tagtype __foo_exception i32
+__foo_exception:
diff --git a/lld/test/wasm/Inputs/tag-export.s b/lld/test/wasm/Inputs/tag-export.s
deleted file mode 100644
index 1eb64b98b2228..0000000000000
--- a/lld/test/wasm/Inputs/tag-export.s
+++ /dev/null
@@ -1,3 +0,0 @@
-  .globl __cpp_exception
-  .tagtype __cpp_exception i32
-__cpp_exception:
diff --git a/lld/test/wasm/dylink.s b/lld/test/wasm/dylink.s
index ab604fc1adc18..3736b029ec7da 100644
--- a/lld/test/wasm/dylink.s
+++ b/lld/test/wasm/dylink.s
@@ -1,4 +1,4 @@
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -mattr=+exception-handling -o %t.o %s
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/libsearch-dyn.s -o %t.dyn.o
 # RUN: wasm-ld --experimental-pic -shared %t.ret32.o %t.dyn.o -o %t.lib.so
@@ -8,7 +8,7 @@
 
 # Same again for wasm64
 
-# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-emscripten -o %t.o %s
+# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-emscripten -mattr=+exception-handling -o %t.o %s
 # RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o
 # RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-emscripten %p/Inputs/libsearch-dyn.s -o %t.dyn.o
 # RUN: wasm-ld --experimental-pic -mwasm64 -shared %t.ret32.o %t.dyn.o -o %t.lib.so
@@ -19,7 +19,7 @@
 # ERROR: error: {{.*}}: undefined symbol: ret32
 # ERROR: error: {{.*}}: undefined symbol: _bar
 .functype ret32 (f32) -> (i32)
-
+.tagtype __foo_exception i32
 .globl _start
 _start:
   .functype _start () -> ()
@@ -28,6 +28,8 @@ _start:
   drop
   i32.const _bar at GOT
   drop
+  i32.const 0
+  throw __foo_exception
   end_function
 
 # CHECK:      Sections:
@@ -39,3 +41,6 @@ _start:
 # CHECK-NEXT:     TableAlignment:  0
 # CHECK-NEXT:     Needed:
 # CHECK-NEXT:       - {{.*}}.lib.so
+# CHECK:         Field:          __foo_exception
+# CHECK-NEXT:    Kind:            TAG
+# CHECK-NEXT:    SigIndex:        1
diff --git a/lld/test/wasm/shared-tag.s b/lld/test/wasm/shared-tag.s
deleted file mode 100644
index 7dc304264094a..0000000000000
--- a/lld/test/wasm/shared-tag.s
+++ /dev/null
@@ -1,25 +0,0 @@
-# REQUIRES: wasm
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -mattr=+exception-handling -o %t.o %s
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/tag-export.s -o %t.tag.o
-# RUN: wasm-ld -shared --experimental-pic -o %t.tag.so %t.tag.o
-
-## Test that wasm-ld can resolve tags exported by shared libraries.
-## See https://github.com/llvm/llvm-project/issues/188120
-
-# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.tag.so
-# RUN: obj2yaml %t.wasm | FileCheck %s
-
-
-  .tagtype __cpp_exception i32
-
-  .globl _start
-_start:
-  .functype _start () -> ()
-  i32.const 0
-  throw __cpp_exception
-  end_function
-
-
-# CHECK:            Field:           __cpp_exception
-# CHECK-NEXT:       Kind:            TAG
-# CHECK-NEXT:       SigIndex:        0
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 11ca4fc23d0b1..05e653ea23eda 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -370,7 +370,8 @@ Symbol *SymbolTable::addSharedTag(StringRef name, uint32_t flags,
     return s;
   }
 
-  if (!isa<TagSymbol>(s)) {
+  auto *existingTag = dyn_cast<TagSymbol>(s);
+  if (!existingTag) {
     reportTypeError(s, file, WASM_SYMBOL_TYPE_TAG);
     return s;
   }
@@ -380,12 +381,11 @@ Symbol *SymbolTable::addSharedTag(StringRef name, uint32_t flags,
   }
 
   // undefined existing sym
-  auto *existingTag = cast<TagSymbol>(s);
   const WasmSignature *oldSig = existingTag->signature;
   if (oldSig && sig && *oldSig != *sig)
-    warn("Tag signature mismatch: " + name + "\n>>> defined as " +
-         toString(*oldSig) + " in " + toString(existingTag->getFile()) +
-         "\n>>> defined as " + toString(*sig) + " in " + toString(file));
+    error("Tag signature mismatch: " + name + "\n>>> defined as " +
+          toString(*oldSig) + " in " + toString(existingTag->getFile()) +
+          "\n>>> defined as " + toString(*sig) + " in " + toString(file));
   replaceSym(s);
   return s;
 }

>From 25adedf0b4ad742345e4e5370c16c1ae4650205f Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Sat, 4 Apr 2026 22:04:23 -0700
Subject: [PATCH 4/4] Addresses PR reviews

---
 lld/test/wasm/Inputs/libsearch-dyn.s | 7 +++----
 lld/test/wasm/dylink.s               | 8 +++++---
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/lld/test/wasm/Inputs/libsearch-dyn.s b/lld/test/wasm/Inputs/libsearch-dyn.s
index 11c5a1099e4b5..96c351d6262f8 100644
--- a/lld/test/wasm/Inputs/libsearch-dyn.s
+++ b/lld/test/wasm/Inputs/libsearch-dyn.s
@@ -1,4 +1,4 @@
-.globl _bar,_dynamic,__foo_exception
+.globl _bar,_dynamic,_foo_tag
 
 .section .data,"",@
 _bar:
@@ -7,6 +7,5 @@ _bar:
 _dynamic:
 .size _dynamic,4
 
-
-.tagtype __foo_exception i32
-__foo_exception:
+.tagtype _foo_tag i32
+_foo_tag:
diff --git a/lld/test/wasm/dylink.s b/lld/test/wasm/dylink.s
index 3736b029ec7da..d40778c3b2d6f 100644
--- a/lld/test/wasm/dylink.s
+++ b/lld/test/wasm/dylink.s
@@ -18,8 +18,9 @@
 
 # ERROR: error: {{.*}}: undefined symbol: ret32
 # ERROR: error: {{.*}}: undefined symbol: _bar
+# ERROR: error: {{.*}}: undefined symbol: _foo_tag
 .functype ret32 (f32) -> (i32)
-.tagtype __foo_exception i32
+.tagtype _foo_tag i32
 .globl _start
 _start:
   .functype _start () -> ()
@@ -29,7 +30,7 @@ _start:
   i32.const _bar at GOT
   drop
   i32.const 0
-  throw __foo_exception
+  throw _foo_tag
   end_function
 
 # CHECK:      Sections:
@@ -41,6 +42,7 @@ _start:
 # CHECK-NEXT:     TableAlignment:  0
 # CHECK-NEXT:     Needed:
 # CHECK-NEXT:       - {{.*}}.lib.so
-# CHECK:         Field:          __foo_exception
+#
+# CHECK:         Field:          _foo_tag
 # CHECK-NEXT:    Kind:            TAG
 # CHECK-NEXT:    SigIndex:        1



More information about the llvm-commits mailing list