[WebAssembly] Add explicit symbol table

This change modified lld to in response the llvm change which
moved to a more explicit symbol table in the object format.

Based on patches by Nicholas Wilson:
 1. https://reviews.llvm.org/D41955
 2. https://reviews.llvm.org/D42585

The primary difference that we see in the test output is that
for relocatable (-r) output we now have symbol table which
replaces exports/imports and globals.

See: https://github.com/WebAssembly/tool-conventions/issues/38
Differential Revision: https://reviews.llvm.org/D43264

llvm-svn: 325861
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 094b566..d8d7be9 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -10,6 +10,7 @@
 #include "SymbolTable.h"
 #include "Config.h"
 #include "InputChunks.h"
+#include "InputGlobal.h"
 #include "WriterUtils.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
@@ -73,50 +74,58 @@
 // Check the type of new symbol matches that of the symbol is replacing.
 // For functions this can also involve verifying that the signatures match.
 static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
-                             bool NewIsFunction, const WasmSignature *NewSig) {
+                             WasmSymbolType NewType,
+                             const WasmSignature *NewFunctionSig,
+                             const WasmGlobalType *NewGlobalType) {
   if (Existing.isLazy())
     return;
 
+  WasmSymbolType ExistingType = Existing.getWasmType();
+
   // First check the symbol types match (i.e. either both are function
   // symbols or both are data symbols).
-  if (isa<FunctionSymbol>(Existing) != NewIsFunction) {
+  if (NewType != ExistingType) {
     error("symbol type mismatch: " + Existing.getName() + "\n>>> defined as " +
-          (isa<FunctionSymbol>(Existing) ? "Function" : "Data") + " in " +
-          toString(Existing.getFile()) + "\n>>> defined as " +
-          (NewIsFunction ? "Function" : "Data") + " in " + F.getName());
+          toString(ExistingType) + " in " + toString(Existing.getFile()) +
+          "\n>>> defined as " + toString(NewType) + " in " + F.getName());
     return;
   }
 
-  // For function symbols, optionally check the function signature matches too.
-  auto *ExistingFunc = dyn_cast<FunctionSymbol>(&Existing);
-  if (!ExistingFunc || !Config->CheckSignatures)
+  // For function/global symbols, optionally check the type matches too.
+  if (NewType == WASM_SYMBOL_TYPE_DATA || !Config->CheckSignatures)
     return;
 
-  const WasmSignature *OldSig = ExistingFunc->getFunctionType();
+  DEBUG(dbgs() << "checkSymbolTypes: " << Existing.getName() << "\n");
 
-  // Skip the signature check if the existing function has no signature (e.g.
-  // if it is an undefined symbol generated by --undefined command line flag).
-  if (OldSig == nullptr)
-    return;
+  auto ReportError = [&](const Twine &Old, const Twine &New) {
+    error(toString(NewType) + " type mismatch: " + Existing.getName() +
+          "\n>>> defined as " + Old + " in " + toString(Existing.getFile()) +
+          "\n>>> defined as " + New + " in " + F.getName());
+  };
 
-  DEBUG(dbgs() << "checkSymbolTypes: " << ExistingFunc->getName() << "\n");
-  assert(NewSig);
+  if (NewType == WASM_SYMBOL_TYPE_FUNCTION) {
+    // Skip the signature check if the existing function has no signature (e.g.
+    // if it is an undefined symbol generated by --undefined command line flag).
+    auto &Sym = cast<FunctionSymbol>(Existing);
+    const WasmSignature *OldSig = Sym.getFunctionType();
+    if (!OldSig)
+      return;
 
-  if (*NewSig == *OldSig)
-    return;
+    assert(NewFunctionSig);
+    if (*NewFunctionSig == *OldSig)
+      return;
 
-  error("function signature mismatch: " + ExistingFunc->getName() +
-        "\n>>> defined as " + toString(*OldSig) + " in " +
-        toString(ExistingFunc->getFile()) + "\n>>> defined as " +
-        toString(*NewSig) + " in " + F.getName());
-}
+    ReportError(toString(*OldSig), toString(*NewFunctionSig));
+  } else {
+    auto &Sym = cast<GlobalSymbol>(Existing);
 
-static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
-                             bool IsFunction, const InputChunk *Chunk) {
-  const WasmSignature *Sig = nullptr;
-  if (auto *F = dyn_cast_or_null<InputFunction>(Chunk))
-    Sig = &F->Signature;
-  return checkSymbolTypes(Existing, F, IsFunction, Sig);
+    assert(NewGlobalType != nullptr);
+    const WasmGlobalType *OldType = Sym.getGlobalType();
+    if (*NewGlobalType == *OldType)
+      return;
+
+    ReportError(toString(*OldType), toString(*NewGlobalType));
+  }
 }
 
 DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
@@ -140,9 +149,21 @@
   return replaceSymbol<DefinedData>(S, Name, Flags);
 }
 
+DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef Name, uint32_t Flags,
+                                               InputGlobal *Global) {
+  DEBUG(dbgs() << "addSyntheticGlobal: " << Name << " -> " << Global << "\n");
+  Symbol *S;
+  bool WasInserted;
+  std::tie(S, WasInserted) = insert(Name);
+  assert(WasInserted);
+  return replaceSymbol<DefinedGlobal>(S, Name, Flags, nullptr, Global);
+}
+
 static bool shouldReplace(const Symbol &Existing, InputFile *NewFile,
-                          uint32_t NewFlags, InputChunk *NewChunk,
-                          bool NewIsFunction) {
+                          WasmSymbolType NewType, uint32_t NewFlags,
+                          const WasmSignature *NewFuncType = nullptr,
+                          const WasmGlobalType *NewGlobalType = nullptr) {
+
   // If existing symbol is lazy, replace it without checking types since
   // lazy symbols don't have any type information.
   if (Existing.isLazy()) {
@@ -155,7 +176,7 @@
   // symbol name must have the same type, even if they are undefined. This
   // is different from ELF because symbol types are not that significant
   // in ELF, and undefined symbols in ELF don't have type in the first place.
-  checkSymbolTypes(Existing, *NewFile, NewIsFunction, NewChunk);
+  checkSymbolTypes(Existing, *NewFile, NewType, NewFuncType, NewGlobalType);
 
   // If existing symbol is undefined, replace it.
   if (!Existing.isDefined()) {
@@ -188,38 +209,58 @@
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
-  if (WasInserted || shouldReplace(*S, F, Flags, Function, true))
+  if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_FUNCTION, Flags,
+                                   &Function->Signature))
     replaceSymbol<DefinedFunction>(S, Name, Flags, F, Function);
   return S;
 }
 
 Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
-                                      InputFile *F, InputSegment *Segment,
-                                      uint32_t Address) {
+                                    InputFile *F, InputSegment *Segment,
+                                    uint32_t Address, uint32_t Size) {
   DEBUG(dbgs() << "addDefinedData:" << Name << " addr:" << Address << "\n");
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
-  if (WasInserted || shouldReplace(*S, F, Flags, Segment, false))
-    replaceSymbol<DefinedData>(S, Name, Flags, F, Segment, Address);
+  if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_DATA, Flags))
+    replaceSymbol<DefinedData>(S, Name, Flags, F, Segment, Address, Size);
   return S;
 }
 
-Symbol *SymbolTable::addUndefined(StringRef Name, Symbol::Kind Kind,
+Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
+                                      InputFile *F, InputGlobal *Global) {
+  DEBUG(dbgs() << "addDefinedGlobal:" << Name << "\n");
+  Symbol *S;
+  bool WasInserted;
+  std::tie(S, WasInserted) = insert(Name);
+  if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_GLOBAL, Flags,
+                                   nullptr, &Global->getType()))
+    replaceSymbol<DefinedGlobal>(S, Name, Flags, F, Global);
+  return S;
+}
+
+Symbol *SymbolTable::addUndefined(StringRef Name, WasmSymbolType Type,
                                   uint32_t Flags, InputFile *F,
-                                  const WasmSignature *Type) {
-  DEBUG(dbgs() << "addUndefined: " << Name << "\n");
+                                  const WasmSignature *FunctionType,
+                                  const WasmGlobalType *GlobalType) {
+  DEBUG(dbgs() << "addUndefined type=" << Type << ": " << Name << "\n");
 
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
 
-  bool IsFunction = Kind == Symbol::UndefinedFunctionKind;
   if (WasInserted) {
-    if (IsFunction)
-      replaceSymbol<UndefinedFunction>(S, Name, Flags, F, Type);
-    else
+    switch (Type) {
+    case WASM_SYMBOL_TYPE_FUNCTION:
+      replaceSymbol<UndefinedFunction>(S, Name, Flags, F, FunctionType);
+      break;
+    case WASM_SYMBOL_TYPE_GLOBAL:
+      replaceSymbol<UndefinedGlobal>(S, Name, Flags, F, GlobalType);
+      break;
+    case WASM_SYMBOL_TYPE_DATA:
       replaceSymbol<UndefinedData>(S, Name, Flags, F);
+      break;
+    }
     return S;
   }
 
@@ -231,8 +272,9 @@
 
   if (S->isDefined()) {
     DEBUG(dbgs() << "resolved by existing\n");
-    checkSymbolTypes(*S, *F, IsFunction, Type);
+    checkSymbolTypes(*S, *F, Type, FunctionType, GlobalType);
   }
+
   return S;
 }