[WebAssembly] Separate addUndefined into addUndefined{Function,Data,Global}.

Previously, one function adds all types of undefined symbols. That
doesn't fit to the wasm's undefined symbol semantics well because
different types of undefined symbols are very different in wasm.
As a result, separate control flows merge in this addUndefined function
and then separate again for each type. That wasn't easy to read.

This patch separates the function into three functions. Now it is pretty
clear what we are doing for each undefined symbol type.

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

llvm-svn: 326271
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index d8d7be9..b9f4e6e 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -71,63 +71,56 @@
   return {Sym, true};
 }
 
+static void reportTypeError(const Symbol *Existing, const InputFile *File,
+                            StringRef Type) {
+  error("symbol type mismatch: " + toString(*Existing) + "\n>>> defined as " +
+        toString(Existing->getWasmType()) + " in " +
+        toString(Existing->getFile()) + "\n>>> defined as " + Type + " in " +
+        toString(File));
+}
+
+static void checkFunctionType(const Symbol *Existing, const InputFile *File,
+                              const WasmSignature *NewSig) {
+  if (!isa<FunctionSymbol>(Existing)) {
+    reportTypeError(Existing, File, "Function");
+    return;
+  }
+
+  if (!Config->CheckSignatures)
+    return;
+
+  const WasmSignature *OldSig =
+      cast<FunctionSymbol>(Existing)->getFunctionType();
+  if (OldSig && *NewSig != *OldSig) {
+    error("Function type mismatch: " + Existing->getName() +
+          "\n>>> defined as " + toString(*OldSig) + " in " +
+          toString(Existing->getFile()) + "\n>>> defined as " +
+          toString(*NewSig) + " in " + toString(File));
+  }
+}
+
 // 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,
-                             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 (NewType != ExistingType) {
-    error("symbol type mismatch: " + Existing.getName() + "\n>>> defined as " +
-          toString(ExistingType) + " in " + toString(Existing.getFile()) +
-          "\n>>> defined as " + toString(NewType) + " in " + F.getName());
+static void checkGlobalType(const Symbol *Existing, const InputFile *File,
+                            const WasmGlobalType *NewType) {
+  if (!isa<GlobalSymbol>(Existing)) {
+    reportTypeError(Existing, File, "Global");
     return;
   }
 
-  // For function/global symbols, optionally check the type matches too.
-  if (NewType == WASM_SYMBOL_TYPE_DATA || !Config->CheckSignatures)
-    return;
-
-  DEBUG(dbgs() << "checkSymbolTypes: " << Existing.getName() << "\n");
-
-  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());
-  };
-
-  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;
-
-    assert(NewFunctionSig);
-    if (*NewFunctionSig == *OldSig)
-      return;
-
-    ReportError(toString(*OldSig), toString(*NewFunctionSig));
-  } else {
-    auto &Sym = cast<GlobalSymbol>(Existing);
-
-    assert(NewGlobalType != nullptr);
-    const WasmGlobalType *OldType = Sym.getGlobalType();
-    if (*NewGlobalType == *OldType)
-      return;
-
-    ReportError(toString(*OldType), toString(*NewGlobalType));
+  const WasmGlobalType *OldType = cast<GlobalSymbol>(Existing)->getGlobalType();
+  if (*NewType != *OldType) {
+    error("Global type mismatch: " + Existing->getName() + "\n>>> defined as " +
+          toString(*OldType) + " in " + toString(Existing->getFile()) +
+          "\n>>> defined as " + toString(*NewType) + " in " + toString(File));
   }
 }
 
+static void checkDataType(const Symbol *Existing, const InputFile *File) {
+  if (!isa<DataSymbol>(Existing))
+    reportTypeError(Existing, File, "Data");
+}
+
 DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
                                                    const WasmSignature *Type,
                                                    uint32_t Flags) {
@@ -159,29 +152,12 @@
   return replaceSymbol<DefinedGlobal>(S, Name, Flags, nullptr, Global);
 }
 
-static bool shouldReplace(const Symbol &Existing, InputFile *NewFile,
-                          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()) {
-    DEBUG(dbgs() << "replacing existing lazy symbol: " << Existing.getName()
-                 << "\n");
-    return true;
-  }
-
-  // Now we have two wasm symbols, and all wasm symbols that have the same
-  // 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, NewType, NewFuncType, NewGlobalType);
-
+static bool shouldReplace(const Symbol *Existing, InputFile *NewFile,
+                          uint32_t NewFlags) {
   // If existing symbol is undefined, replace it.
-  if (!Existing.isDefined()) {
+  if (!Existing->isDefined()) {
     DEBUG(dbgs() << "resolving existing undefined symbol: "
-                 << Existing.getName() << "\n");
+                 << Existing->getName() << "\n");
     return true;
   }
 
@@ -192,93 +168,131 @@
   }
 
   // If the existing symbol is weak, we should replace it.
-  if (Existing.isWeak()) {
+  if (Existing->isWeak()) {
     DEBUG(dbgs() << "replacing existing weak symbol\n");
     return true;
   }
 
   // Neither symbol is week. They conflict.
-  error("duplicate symbol: " + toString(Existing) + "\n>>> defined in " +
-        toString(Existing.getFile()) + "\n>>> defined in " + toString(NewFile));
+  error("duplicate symbol: " + toString(*Existing) + "\n>>> defined in " +
+        toString(Existing->getFile()) + "\n>>> defined in " +
+        toString(NewFile));
   return true;
 }
 
 Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags,
-                                        InputFile *F, InputFunction *Function) {
+                                        InputFile *File,
+                                        InputFunction *Function) {
   DEBUG(dbgs() << "addDefinedFunction: " << Name << "\n");
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
-  if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_FUNCTION, Flags,
-                                   &Function->Signature))
-    replaceSymbol<DefinedFunction>(S, Name, Flags, F, Function);
+
+  if (WasInserted || S->isLazy()) {
+    replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
+    return S;
+  }
+
+  checkFunctionType(S, File, &Function->Signature);
+
+  if (shouldReplace(S, File, Flags))
+    replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
   return S;
 }
 
 Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
-                                    InputFile *F, InputSegment *Segment,
+                                    InputFile *File, 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, WASM_SYMBOL_TYPE_DATA, Flags))
-    replaceSymbol<DefinedData>(S, Name, Flags, F, Segment, Address, Size);
+
+  if (WasInserted || S->isLazy()) {
+    replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
+    return S;
+  }
+
+  checkDataType(S, File);
+
+  if (shouldReplace(S, File, Flags))
+    replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
   return S;
 }
 
 Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
-                                      InputFile *F, InputGlobal *Global) {
+                                      InputFile *File, 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);
+
+  if (WasInserted || S->isLazy()) {
+    replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
+    return S;
+  }
+
+  checkGlobalType(S, File, &Global->getType());
+
+  if (shouldReplace(S, File, Flags))
+    replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
   return S;
 }
 
-Symbol *SymbolTable::addUndefined(StringRef Name, WasmSymbolType Type,
-                                  uint32_t Flags, InputFile *F,
-                                  const WasmSignature *FunctionType,
-                                  const WasmGlobalType *GlobalType) {
-  DEBUG(dbgs() << "addUndefined type=" << Type << ": " << Name << "\n");
+Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags,
+                                          InputFile *File,
+                                          const WasmSignature *Sig) {
+  DEBUG(dbgs() << "addUndefinedFunction: " << Name << "\n");
 
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
 
-  if (WasInserted) {
-    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;
-  }
-
-  if (auto *Lazy = dyn_cast<LazySymbol>(S)) {
-    DEBUG(dbgs() << "resolved by existing lazy\n");
+  if (WasInserted)
+    replaceSymbol<UndefinedFunction>(S, Name, Flags, File, Sig);
+  else if (auto *Lazy = dyn_cast<LazySymbol>(S))
     cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
-    return S;
-  }
-
-  if (S->isDefined()) {
-    DEBUG(dbgs() << "resolved by existing\n");
-    checkSymbolTypes(*S, *F, Type, FunctionType, GlobalType);
-  }
-
+  else if (S->isDefined())
+    checkFunctionType(S, File, Sig);
   return S;
 }
 
-void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol *Sym) {
+Symbol *SymbolTable::addUndefinedData(StringRef Name, uint32_t Flags,
+                                      InputFile *File) {
+  DEBUG(dbgs() << "addUndefinedData: " << Name << "\n");
+
+  Symbol *S;
+  bool WasInserted;
+  std::tie(S, WasInserted) = insert(Name);
+
+  if (WasInserted)
+    replaceSymbol<UndefinedData>(S, Name, Flags, File);
+  else if (auto *Lazy = dyn_cast<LazySymbol>(S))
+    cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
+  else if (S->isDefined())
+    checkDataType(S, File);
+  return S;
+}
+
+Symbol *SymbolTable::addUndefinedGlobal(StringRef Name, uint32_t Flags,
+                                        InputFile *File,
+                                        const WasmGlobalType *Type) {
+  DEBUG(dbgs() << "addUndefinedGlobal: " << Name << "\n");
+
+  Symbol *S;
+  bool WasInserted;
+  std::tie(S, WasInserted) = insert(Name);
+
+  if (WasInserted)
+    replaceSymbol<UndefinedGlobal>(S, Name, Flags, File, Type);
+  else if (auto *Lazy = dyn_cast<LazySymbol>(S))
+    cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
+  else if (S->isDefined())
+    checkGlobalType(S, File, Type);
+  return S;
+}
+
+void SymbolTable::addLazy(ArchiveFile *File, const Archive::Symbol *Sym) {
   DEBUG(dbgs() << "addLazy: " << Sym->getName() << "\n");
   StringRef Name = Sym->getName();
 
@@ -287,14 +301,14 @@
   std::tie(S, WasInserted) = insert(Name);
 
   if (WasInserted) {
-    replaceSymbol<LazySymbol>(S, Name, F, *Sym);
+    replaceSymbol<LazySymbol>(S, Name, File, *Sym);
     return;
   }
 
   // If there is an existing undefined symbol, load a new one from the archive.
   if (S->isUndefined()) {
     DEBUG(dbgs() << "replacing existing undefined\n");
-    F->addMember(Sym);
+    File->addMember(Sym);
   }
 }