[WebAssembly] Make EH work with dynamic linking

This makes Wasm EH work with dynamic linking. So far we were only able
to handle destructors, which do not use any tags or LSDA info.

1. This uses `TargetExternalSymbol` for `GCC_except_tableN` symbols,
   which points to the address of per-function LSDA info. It is more
   convenient to use than `MCSymbol` because it can take additional
   target flags.

2. When lowering `wasm_lsda` intrinsic, if PIC is enabled, make the
   symbol relative to `__memory_base` and generate the `add` node. If
   PIC is disabled, continue to use the absolute address.

3. Make tag symbols (`__cpp_exception` and `__c_longjmp`) undefined in
   the backend, because it is hard to make it work with dynamic
   linking's loading order. Instead, we make all tag symbols undefined
   in the LLVM backend and import it from JS.

4. Add support for undefined tags to the linker.

Companion patches:
- https://github.com/WebAssembly/binaryen/pull/4223
- https://github.com/emscripten-core/emscripten/pull/15266

Reviewed By: sbc100

Differential Revision: https://reviews.llvm.org/D111388

GitOrigin-RevId: 9261ee32dc41ae6f2ebf0608c3cec22daf5ec494
diff --git a/test/wasm/Inputs/tag-section1.ll b/test/wasm/Inputs/tag-section1.ll
index 89d9e87..253fc4a 100644
--- a/test/wasm/Inputs/tag-section1.ll
+++ b/test/wasm/Inputs/tag-section1.ll
@@ -1,5 +1,5 @@
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-target triple = "wasm32-unknown-unknown"
+target triple = "wasm32-unknown-emscripten"
 
 declare void @llvm.wasm.throw(i32, i8*)
 
diff --git a/test/wasm/Inputs/tag-section2.ll b/test/wasm/Inputs/tag-section2.ll
index 3499db9..6b82c85 100644
--- a/test/wasm/Inputs/tag-section2.ll
+++ b/test/wasm/Inputs/tag-section2.ll
@@ -1,5 +1,5 @@
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-target triple = "wasm32-unknown-unknown"
+target triple = "wasm32-unknown-emscripten"
 
 declare void @llvm.wasm.throw(i32, i8*)
 
diff --git a/test/wasm/tag-section.ll b/test/wasm/tag-section.ll
index 1f6aa9d..4f55f88 100644
--- a/test/wasm/tag-section.ll
+++ b/test/wasm/tag-section.ll
@@ -1,13 +1,21 @@
+; Static code
 ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %p/Inputs/tag-section1.ll -o %t1.o
 ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %p/Inputs/tag-section2.ll -o %t2.o
 ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling %s -o %t.o
 ; RUN: wasm-ld -o %t.wasm %t.o %t1.o %t2.o
 ; RUN: wasm-ld --export-all -o %t-export-all.wasm %t.o %t1.o %t2.o
-; RUN: obj2yaml %t.wasm | FileCheck %s
-; RUN: obj2yaml %t-export-all.wasm | FileCheck %s --check-prefix=EXPORT-ALL
+; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=NOPIC
+; RUN: obj2yaml %t-export-all.wasm | FileCheck %s --check-prefix=NOPIC-EXPORT-ALL
+
+; PIC code
+; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section1.ll -o %t1.o
+; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section2.ll -o %t2.o
+; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %s -o %t.o
+; RUN: wasm-ld --import-undefined --experimental-pic -pie -o %t.wasm %t.o %t1.o %t2.o
+; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=PIC
 
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-target triple = "wasm32-unknown-unknown"
+target triple = "wasm32-unknown-emscripten"
 
 declare void @foo(i8*)
 declare void @bar(i8*)
@@ -18,25 +26,47 @@
   ret void
 }
 
-; CHECK:      Sections:
-; CHECK-NEXT:   - Type:            TYPE
-; CHECK-NEXT:     Signatures:
-; CHECK-NEXT:       - Index:           0
-; CHECK-NEXT:         ParamTypes:      []
-; CHECK-NEXT:         ReturnTypes:     []
-; CHECK-NEXT:       - Index:           1
-; CHECK-NEXT:         ParamTypes:
-; CHECK-NEXT:           - I32
-; CHECK-NEXT:         ReturnTypes:     []
+; NOPIC:      Sections:
+; NOPIC-NEXT:   - Type:            TYPE
+; NOPIC-NEXT:     Signatures:
+; NOPIC-NEXT:       - Index:           0
+; NOPIC-NEXT:         ParamTypes:      []
+; NOPIC-NEXT:         ReturnTypes:     []
+; NOPIC-NEXT:       - Index:           1
+; NOPIC-NEXT:         ParamTypes:
+; NOPIC-NEXT:           - I32
+; NOPIC-NEXT:         ReturnTypes:     []
 
-; CHECK:        - Type:            TAG
-; CHECK-NEXT:     TagTypes:        [ 1 ]
+; NOPIC:        - Type:            TAG
+; NOPIC-NEXT:     TagTypes:        [ 1 ]
 
 ; Global section has to come after tag section
-; CHECK:        - Type:            GLOBAL
+; NOPIC:        - Type:            GLOBAL
 
-; EXPORT-ALL:   - Type:            EXPORT
-; EXPORT-ALL-NEXT Exports:
-; EXPORT-ALL:       - Name:            __cpp_exception
-; EXPORT-ALL:         Kind:            TAG
-; EXPORT-ALL:         Index:           0
+; NOPIC-EXPORT-ALL:   - Type:            EXPORT
+; NOPIC-EXPORT-ALL-NEXT Exports:
+; NOPIC-EXPORT-ALL:       - Name:            __cpp_exception
+; NOPIC-EXPORT-ALL:         Kind:            TAG
+; NOPIC-EXPORT-ALL:         Index:           0
+
+; In PIC mode, tags are undefined and imported from JS.
+; PIC:        Sections:
+; PIC:         - Type:            TYPE
+; PIC-NEXT:      Signatures:
+; PIC-NEXT:        - Index:           0
+; PIC-NEXT:          ParamTypes:
+; PIC-NEXT:            - I32
+; PIC-NEXT:          ReturnTypes:     []
+; PIC-NEXT:        - Index:           1
+; PIC-NEXT:          ParamTypes:      []
+; PIC-NEXT:          ReturnTypes:     []
+
+; PIC:         - Type:            IMPORT
+; PIC-NEXT:      Imports:
+; PIC:             - Module:          env
+; PIC:               Field:           __cpp_exception
+; PIC-NEXT:          Kind:            TAG
+; PIC-NEXT:          SigIndex:        0
+
+; In PIC mode, tags should NOT be defined in the module; they are imported.
+; PIC-NOT:     - Type:            TAG
diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp
index 9e7f1ca..29bc0b3 100644
--- a/wasm/InputFiles.cpp
+++ b/wasm/InputFiles.cpp
@@ -663,6 +663,14 @@
     return symtab->addUndefinedTable(name, sym.Info.ImportName,
                                      sym.Info.ImportModule, flags, this,
                                      sym.TableType);
+  case WASM_SYMBOL_TYPE_TAG:
+    if (sym.isBindingLocal())
+      return make<UndefinedTag>(name, sym.Info.ImportName,
+                                sym.Info.ImportModule, flags, this,
+                                sym.Signature);
+    return symtab->addUndefinedTag(name, sym.Info.ImportName,
+                                   sym.Info.ImportModule, flags, this,
+                                   sym.Signature);
   case WASM_SYMBOL_TYPE_SECTION:
     llvm_unreachable("section symbols cannot be undefined");
   }
diff --git a/wasm/SymbolTable.cpp b/wasm/SymbolTable.cpp
index 877909c..b204fae 100644
--- a/wasm/SymbolTable.cpp
+++ b/wasm/SymbolTable.cpp
@@ -625,6 +625,30 @@
   return s;
 }
 
+Symbol *SymbolTable::addUndefinedTag(StringRef name,
+                                     Optional<StringRef> importName,
+                                     Optional<StringRef> importModule,
+                                     uint32_t flags, InputFile *file,
+                                     const WasmSignature *sig) {
+  LLVM_DEBUG(dbgs() << "addUndefinedTag: " << name << "\n");
+  assert(flags & WASM_SYMBOL_UNDEFINED);
+
+  Symbol *s;
+  bool wasInserted;
+  std::tie(s, wasInserted) = insert(name, file);
+  if (s->traced)
+    printTraceSymbolUndefined(name, file);
+
+  if (wasInserted)
+    replaceSymbol<UndefinedTag>(s, name, importName, importModule, flags, file,
+                                sig);
+  else if (auto *lazy = dyn_cast<LazySymbol>(s))
+    lazy->fetch();
+  else if (s->isDefined())
+    checkTagType(s, file, sig);
+  return s;
+}
+
 TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
   WasmLimits limits{0, 0, 0}; // Set by the writer.
   WasmTableType *type = make<WasmTableType>();
diff --git a/wasm/SymbolTable.h b/wasm/SymbolTable.h
index aea2f16..c2a3ee3 100644
--- a/wasm/SymbolTable.h
+++ b/wasm/SymbolTable.h
@@ -79,6 +79,10 @@
                             llvm::Optional<StringRef> importModule,
                             uint32_t flags, InputFile *file,
                             const WasmTableType *type);
+  Symbol *addUndefinedTag(StringRef name, llvm::Optional<StringRef> importName,
+                          llvm::Optional<StringRef> importModule,
+                          uint32_t flags, InputFile *file,
+                          const WasmSignature *sig);
 
   TableSymbol *resolveIndirectFunctionTable(bool required);
 
diff --git a/wasm/Symbols.cpp b/wasm/Symbols.cpp
index 4658b82..79bec10 100644
--- a/wasm/Symbols.cpp
+++ b/wasm/Symbols.cpp
@@ -58,6 +58,8 @@
     return "UndefinedGlobal";
   case wasm::Symbol::UndefinedTableKind:
     return "UndefinedTable";
+  case wasm::Symbol::UndefinedTagKind:
+    return "UndefinedTag";
   case wasm::Symbol::LazyKind:
     return "LazyKind";
   case wasm::Symbol::SectionKind:
@@ -113,6 +115,8 @@
 const WasmSignature *Symbol::getSignature() const {
   if (auto* f = dyn_cast<FunctionSymbol>(this))
     return f->signature;
+  if (auto *t = dyn_cast<TagSymbol>(this))
+    return t->signature;
   if (auto *l = dyn_cast<LazySymbol>(this))
     return l->signature;
   return nullptr;
diff --git a/wasm/Symbols.h b/wasm/Symbols.h
index 54f7b61..342ebcf 100644
--- a/wasm/Symbols.h
+++ b/wasm/Symbols.h
@@ -55,6 +55,7 @@
     UndefinedDataKind,
     UndefinedGlobalKind,
     UndefinedTableKind,
+    UndefinedTagKind,
     LazyKind,
   };
 
@@ -66,7 +67,7 @@
     return symbolKind == UndefinedFunctionKind ||
            symbolKind == UndefinedDataKind ||
            symbolKind == UndefinedGlobalKind ||
-           symbolKind == UndefinedTableKind;
+           symbolKind == UndefinedTableKind || symbolKind == UndefinedTagKind;
   }
 
   bool isLazy() const { return symbolKind == LazyKind; }
@@ -437,7 +438,9 @@
 // and is named '__cpp_exception' for linking.
 class TagSymbol : public Symbol {
 public:
-  static bool classof(const Symbol *s) { return s->kind() == DefinedTagKind; }
+  static bool classof(const Symbol *s) {
+    return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind;
+  }
 
   // Get/set the tag index
   uint32_t getTagIndex() const;
@@ -463,6 +466,19 @@
   InputTag *tag;
 };
 
+class UndefinedTag : public TagSymbol {
+public:
+  UndefinedTag(StringRef name, llvm::Optional<StringRef> importName,
+               llvm::Optional<StringRef> importModule, uint32_t flags,
+               InputFile *file = nullptr, const WasmSignature *sig = nullptr)
+      : TagSymbol(name, UndefinedTagKind, flags, file, sig) {
+    this->importName = importName;
+    this->importModule = importModule;
+  }
+
+  static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; }
+};
+
 // LazySymbol represents a symbol that is not yet in the link, but we know where
 // to find it if needed. If the resolver finds both Undefined and Lazy for the
 // same name, it will ask the Lazy to load a file.
diff --git a/wasm/SyntheticSections.cpp b/wasm/SyntheticSections.cpp
index d70ade4..be1a82b 100644
--- a/wasm/SyntheticSections.cpp
+++ b/wasm/SyntheticSections.cpp
@@ -170,10 +170,14 @@
       g->setGlobalIndex(entry.first->second);
     }
   } else if (auto *t = dyn_cast<TagSymbol>(sym)) {
-    // NB: There's currently only one possible kind of tag, and no
-    // `UndefinedTag`, so we don't bother de-duplicating tag imports.
-    importedSymbols.emplace_back(sym);
-    t->setTagIndex(numImportedTags++);
+    ImportKey<WasmSignature> key(*(t->getSignature()), module, name);
+    auto entry = importedTags.try_emplace(key, numImportedTags);
+    if (entry.second) {
+      importedSymbols.emplace_back(sym);
+      t->setTagIndex(numImportedTags++);
+    } else {
+      t->setTagIndex(entry.first->second);
+    }
   } else {
     assert(TableSymbol::classof(sym));
     auto *table = cast<TableSymbol>(sym);
diff --git a/wasm/SyntheticSections.h b/wasm/SyntheticSections.h
index 421bbab..4caab9d 100644
--- a/wasm/SyntheticSections.h
+++ b/wasm/SyntheticSections.h
@@ -198,6 +198,7 @@
   llvm::DenseMap<ImportKey<WasmGlobalType>, uint32_t> importedGlobals;
   llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedFunctions;
   llvm::DenseMap<ImportKey<WasmTableType>, uint32_t> importedTables;
+  llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedTags;
 };
 
 class FunctionSection : public SyntheticSection {