Refactor symbol version assignmnt code.
authorRui Ueyama <ruiu@google.com>
Tue, 15 Nov 2016 18:41:52 +0000 (18:41 +0000)
committerRui Ueyama <ruiu@google.com>
Tue, 15 Nov 2016 18:41:52 +0000 (18:41 +0000)
The code to handle symbol versions is getting tricky and hard to
understand, so it is probably time to simplify it. This patch does
the following.

 - Add `DemangledSyms` variable to SymbolTable so that we don't
   need to pass it around to findDemangled.
 - Define `initDemangledSyms` to initialize the variable lazily.
 - hasExternCpp is removed because we no longer have to initialize
   the map eagerly.
 - scanScriptVersion is split.
 - Comments are updated.

llvm-svn: 287002

lld/ELF/SymbolTable.cpp
lld/ELF/SymbolTable.h

index e25e866..efbcba9 100644 (file)
@@ -484,7 +484,7 @@ template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
   return SymVector[V.Idx]->body();
 }
 
-// Returns a list of defined symbols that match with a given regex.
+// Returns a list of defined symbols that match with a given pattern.
 template <class ELFT>
 std::vector<SymbolBody *> SymbolTable<ELFT>::findAll(const StringMatcher &M) {
   std::vector<SymbolBody *> Res;
@@ -579,6 +579,8 @@ template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
       B->symbol()->ExportDynamic = true;
 }
 
+// A helper function to set a version to a symbol.
+// Essentially, assigning two different versions to the same symbol is an error.
 static void setVersionId(SymbolBody *Body, StringRef VersionName,
                          StringRef Name, uint16_t Version) {
   if (!Body || Body->isUndefined()) {
@@ -594,47 +596,50 @@ static void setVersionId(SymbolBody *Body, StringRef VersionName,
   Sym->VersionId = Version;
 }
 
-// Returns a map from demangled symbols to symbol objects.
-// The relationship is 1:N instead of 1:1 because with the symbol
-// versioning, more than one symbol may have the same name.
+// Initialize DemangledSyms with a map from demangled symbols to symbol
+// objects. Used to handle "extern C++" directive in version scripts.
+//
+// The map will contain all demangled symbols. That can be very large,
+// and in LLD we generally want to avoid do anything for each symbol.
+// Then, why are we doing this? Here's why.
+//
+// Users can use "extern C++ {}" directive to match against demangled
+// C++ symbols. For example, you can write a pattern such as
+// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
+// other than trying to match a pattern against all demangled symbols.
+// So, if "extern C++" feature is used, we need to demangle all known
+// symbols.
 template <class ELFT>
-std::map<std::string, std::vector<SymbolBody *>>
-SymbolTable<ELFT>::getDemangledSyms() {
-  std::map<std::string, std::vector<SymbolBody *>> Result;
+void SymbolTable<ELFT>::initDemangledSyms() {
+  if (DemangledSyms)
+    return;
+  DemangledSyms.emplace();
+
   for (Symbol *Sym : SymVector) {
     SymbolBody *B = Sym->body();
-    Result[demangle(B->getName())].push_back(B);
+    (*DemangledSyms)[demangle(B->getName())].push_back(B);
   }
-  return Result;
-}
-
-static bool hasExternCpp() {
-  for (VersionDefinition &V : Config->VersionDefinitions)
-    for (SymbolVersion Ver : V.Globals)
-      if (Ver.IsExternCpp)
-        return true;
-  return false;
 }
 
-static ArrayRef<SymbolBody *>
-findDemangled(std::map<std::string, std::vector<SymbolBody *>> &D,
-              StringRef Name) {
-  auto I = D.find(Name);
-  if (I != D.end())
+template <class ELFT>
+ArrayRef<SymbolBody *> SymbolTable<ELFT>::findDemangled(StringRef Name) {
+  initDemangledSyms();
+  auto I = DemangledSyms->find(Name);
+  if (I != DemangledSyms->end())
     return I->second;
   return {};
 }
 
-static std::vector<SymbolBody *>
-findAllDemangled(const std::map<std::string, std::vector<SymbolBody *>> &D,
-                 StringMatcher &M) {
+template <class ELFT>
+std::vector<SymbolBody *>
+SymbolTable<ELFT>::findAllDemangled(const StringMatcher &M) {
+  initDemangledSyms();
   std::vector<SymbolBody *> Res;
-  for (auto &P : D) {
-    if (M.match(P.first))
+  for (auto &P : *DemangledSyms)
+    if (M.match(P.first()))
       for (SymbolBody *Body : P.second)
         if (!Body->isUndefined())
           Res.push_back(Body);
-  }
   return Res;
 }
 
@@ -661,6 +666,23 @@ template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
     B->symbol()->VersionId = VER_NDX_GLOBAL;
 }
 
+template <class ELFT>
+void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
+                                              size_t VersionId) {
+  if (!Ver.HasWildcards)
+    return;
+  StringMatcher M({Ver.Name});
+  std::vector<SymbolBody *> Syms =
+      Ver.IsExternCpp ? findAllDemangled(M) : findAll(M);
+
+  // Exact matching takes precendence over fuzzy matching,
+  // so we set a version to a symbol only if no version has been assigned
+  // to the symbol. This behavior is compatible with GNU.
+  for (SymbolBody *B : Syms)
+    if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
+      B->symbol()->VersionId = VersionId;
+}
+
 // This function processes version scripts by updating VersionId
 // member of symbols.
 template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
@@ -677,15 +699,6 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
   // Each version definition has a glob pattern, and all symbols that match
   // with the pattern get that version.
 
-  // Users can use "extern C++ {}" directive to match against demangled
-  // C++ symbols. For example, you can write a pattern such as
-  // "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
-  // other than trying to match a regexp against all demangled symbols.
-  // So, if "extern C++" feature is used, we demangle all known symbols.
-  std::map<std::string, std::vector<SymbolBody *>> Demangled;
-  if (hasExternCpp())
-    Demangled = getDemangledSyms();
-
   // First, we assign versions to exact matching symbols,
   // i.e. version definitions not containing any glob meta-characters.
   for (VersionDefinition &V : Config->VersionDefinitions) {
@@ -695,7 +708,7 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
 
       StringRef N = Ver.Name;
       if (Ver.IsExternCpp) {
-        for (SymbolBody *B : findDemangled(Demangled, N))
+        for (SymbolBody *B : findDemangled(N))
           setVersionId(B, V.Name, N, V.Id);
         continue;
       }
@@ -712,26 +725,12 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
   // i.e. version definitions containing glob meta-characters.
   // Note that because the last match takes precedence over previous matches,
   // we iterate over the definitions in the reverse order.
-  auto assignFuzzyVersion = [&](SymbolVersion &Ver, size_t Version) {
-    if (!Ver.HasWildcards)
-      return;
-    StringMatcher M({Ver.Name});
-    std::vector<SymbolBody *> Syms =
-        Ver.IsExternCpp ? findAllDemangled(Demangled, M) : findAll(M);
-    // Exact matching takes precendence over fuzzy matching,
-    // so we set a version to a symbol only if no version has been assigned
-    // to the symbol. This behavior is compatible with GNU.
-    for (SymbolBody *B : Syms)
-      if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
-        B->symbol()->VersionId = Version;
-  };
-
   for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) {
     VersionDefinition &V = Config->VersionDefinitions[I];
     for (SymbolVersion &Ver : V.Locals)
-      assignFuzzyVersion(Ver, VER_NDX_LOCAL);
+      assignWildcardVersion(Ver, VER_NDX_LOCAL);
     for (SymbolVersion &Ver : V.Globals)
-      assignFuzzyVersion(Ver, V.Id);
+      assignWildcardVersion(Ver, V.Id);
   }
 }
 
index 1223750..d78287c 100644 (file)
@@ -98,8 +98,12 @@ private:
                                    uint8_t Visibility, bool CanOmitFromDynSym,
                                    InputFile *File);
 
-  std::map<std::string, std::vector<SymbolBody *>> getDemangledSyms();
+  ArrayRef<SymbolBody *> findDemangled(StringRef Name);
+  std::vector<SymbolBody *> findAllDemangled(const StringMatcher &M);
+
+  void initDemangledSyms();
   void handleAnonymousVersion();
+  void assignWildcardVersion(SymbolVersion Ver, size_t VersionId);
 
   struct SymIndex {
     SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
@@ -130,6 +134,13 @@ private:
   // Set of .so files to not link the same shared object file more than once.
   llvm::DenseSet<StringRef> SoNames;
 
+  // A map from demangled symbol names to their symbol objects.
+  // This mapping is 1:N because two symbols with different versions
+  // can have the same name. We use this map to handle "extern C++ {}"
+  // directive in version scripts.
+  llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
+
+  // For LTO.
   std::unique_ptr<BitcodeCompiler> Lto;
 };