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

Mostly following the structure of other Shared* constructs

Fixes: #188120
diff --git a/lld/test/wasm/Inputs/libsearch-dyn.s b/lld/test/wasm/Inputs/libsearch-dyn.s
index bb59580..96c351d 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_tag
 
 .section .data,"",@
 _bar:
@@ -6,3 +6,6 @@
 
 _dynamic:
 .size _dynamic,4
+
+.tagtype _foo_tag i32
+_foo_tag:
diff --git a/lld/test/wasm/dylink.s b/lld/test/wasm/dylink.s
index ab604fc..d40778c 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
@@ -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_tag i32
 .globl _start
 _start:
   .functype _start () -> ()
@@ -28,6 +29,8 @@
   drop
   i32.const _bar@GOT
   drop
+  i32.const 0
+  throw _foo_tag
   end_function
 
 # CHECK:      Sections:
@@ -39,3 +42,7 @@
 # CHECK-NEXT:     TableAlignment:  0
 # CHECK-NEXT:     Needed:
 # CHECK-NEXT:       - {{.*}}.lib.so
+#
+# CHECK:         Field:          _foo_tag
+# CHECK-NEXT:    Kind:            TAG
+# CHECK-NEXT:    SigIndex:        1
diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index dcdd138..ad7f207 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -453,6 +453,9 @@
       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 9bd93f3..05e653e 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -352,6 +352,44 @@
                                   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;
+  }
+
+  auto *existingTag = dyn_cast<TagSymbol>(s);
+  if (!existingTag) {
+    reportTypeError(s, file, WASM_SYMBOL_TYPE_TAG);
+    return s;
+  }
+
+  if (s->isDefined()) {
+    return s;
+  }
+
+  // undefined existing sym
+  const WasmSignature *oldSig = existingTag->signature;
+  if (oldSig && sig && *oldSig != *sig)
+    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;
+}
+
 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 6496142..0667ced 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -53,6 +53,8 @@
   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 97a9871..9ce2a9f 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -72,6 +72,8 @@
     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 3dda36f..47f1b3a 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -62,6 +62,7 @@
     LazyKind,
     SharedFunctionKind,
     SharedDataKind,
+    SharedTagKind,
   };
 
   Kind kind() const { return symbolKind; }
@@ -77,7 +78,8 @@
 
   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 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 @@
   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 @@
   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 852dc23..5f125ff 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1547,6 +1547,10 @@
                                               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;