[lld] r347687 - [PDB] Add symbol records in bulk

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 27 11:00:23 PST 2018


Author: rnk
Date: Tue Nov 27 11:00:23 2018
New Revision: 347687

URL: http://llvm.org/viewvc/llvm-project?rev=347687&view=rev
Log:
[PDB] Add symbol records in bulk

Summary:
This speeds up linking clang.exe/pdb with /DEBUG:GHASH by 31%, from
12.9s to 9.8s.

Symbol records are typically small (16.7 bytes on average), but we
processed them one at a time. CVSymbol is a relatively "large" type. It
wraps an ArrayRef<uint8_t> with a kind an optional 32-bit hash, which we
don't need. Before this change, each DbiModuleDescriptorBuilder would
maintain an array of CVSymbols, and would write them individually with a
BinaryItemStream.

With this change, we now add symbols that happen to appear contiguously
in bulk. For each .debug$S section (roughly one per function), we
allocate two copies, one for relocation, and one for realignment
purposes. For runs of symbols that go in the module stream, which is
most symbols, we now add them as a single ArrayRef<uint8_t>, so the
vector DbiModuleDescriptorBuilder is roughly linear in the number of
.debug$S sections (O(# funcs)) instead of the number of symbol records
(very large).

Some stats on symbol sizes for the curious:
  PDB size: 507M
  sym bytes: 316,508,016
  sym count:  18,954,971
  sym byte avg: 16.7

As future work, we may be able to skip copying symbol records in the
linker for realignment purposes if we make LLVM write them aligned into
the object file. We need to double check that such symbol records are
still compatible with link.exe, but if so, it's definitely worth doing,
since my profile shows we spend 500ms in memcpy in the symbol merging
code. We could potentially cut that in half by saving a copy.
Alternatively, we could apply the relocations *after* we iterate the
symbols. This would require some careful re-engineering of the
relocation processing code, though.

Reviewers: zturner, aganea, ruiu

Subscribers: hiraditya, llvm-commits

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

Modified:
    lld/trunk/COFF/PDB.cpp

Modified: lld/trunk/COFF/PDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/PDB.cpp?rev=347687&r1=347686&r2=347687&view=diff
==============================================================================
--- lld/trunk/COFF/PDB.cpp (original)
+++ lld/trunk/COFF/PDB.cpp Tue Nov 27 11:00:23 2018
@@ -760,9 +760,11 @@ static bool remapTypeIndex(TypeIndex &TI
 }
 
 static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
-                                     MutableArrayRef<uint8_t> Contents,
+                                     MutableArrayRef<uint8_t> RecordBytes,
                                      const CVIndexMap &IndexMap,
                                      ArrayRef<TiReference> TypeRefs) {
+  MutableArrayRef<uint8_t> Contents =
+      RecordBytes.drop_front(sizeof(RecordPrefix));
   for (const TiReference &Ref : TypeRefs) {
     unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
     if (Contents.size() < Ref.Offset + ByteSize)
@@ -808,7 +810,7 @@ recordStringTableReferences(SymbolKind K
   switch (Kind) {
   case SymbolKind::S_FILESTATIC:
     // FileStaticSym::ModFileOffset
-    recordStringTableReferenceAtOffset(Contents, 4, StrTableRefs);
+    recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs);
     break;
   case SymbolKind::S_DEFRANGE:
   case SymbolKind::S_DEFRANGE_SUBFIELD:
@@ -873,21 +875,22 @@ static void translateIdSymbols(MutableAr
 
 /// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
 /// The object file may not be aligned.
-static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
-                                                 BumpPtrAllocator &Alloc) {
+static MutableArrayRef<uint8_t>
+copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &AlignedMem) {
   size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
   assert(Size >= 4 && "record too short");
   assert(Size <= MaxRecordLength && "record too long");
-  void *Mem = Alloc.Allocate(Size, 4);
+  assert(AlignedMem.size() >= Size && "didn't preallocate enough");
 
   // Copy the symbol record and zero out any padding bytes.
-  MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size);
+  MutableArrayRef<uint8_t> NewData = AlignedMem.take_front(Size);
+  AlignedMem = AlignedMem.drop_front(Size);
   memcpy(NewData.data(), Sym.data().data(), Sym.length());
   memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
 
   // Update the record prefix length. It should point to the beginning of the
   // next record.
-  auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
+  auto *Prefix = reinterpret_cast<RecordPrefix *>(NewData.data());
   Prefix->RecordLen = Size - 2;
   return NewData;
 }
@@ -1001,8 +1004,8 @@ static bool symbolGoesInGlobalsStream(co
   }
 }
 
-static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
-                            const CVSymbol &Sym) {
+static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
+                            unsigned SymOffset, const CVSymbol &Sym) {
   switch (Sym.kind()) {
   case SymbolKind::S_CONSTANT:
   case SymbolKind::S_UDT:
@@ -1018,12 +1021,12 @@ static void addGlobalSymbol(pdb::GSIStre
     if (Sym.kind() == SymbolKind::S_LPROC32)
       K = SymbolRecordKind::LocalProcRef;
     ProcRefSym PS(K);
-    PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex());
+    PS.Module = ModIndex;
     // For some reason, MSVC seems to add one to this value.
     ++PS.Module;
     PS.Name = getSymbolName(Sym);
     PS.SumName = 0;
-    PS.SymOffset = File.ModuleDBI->getNextSymbolOffset();
+    PS.SymOffset = SymOffset;
     Builder.addGlobalSymbol(PS);
     break;
   }
@@ -1039,8 +1042,53 @@ void PDBLinker::mergeSymbolRecords(ObjFi
   cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer));
   SmallVector<SymbolScope, 4> Scopes;
 
+  // Iterate every symbol to check if any need to be realigned, and if so, how
+  // much space we need to allocate for them.
+  bool NeedsRealignment = false;
+  unsigned RealignedSize = 0;
   auto EC = forEachCodeViewRecord<CVSymbol>(
       SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+        RealignedSize += alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
+        NeedsRealignment |= RealignedSize != Sym.length();
+        return Error::success();
+      });
+
+  // If any of the symbol record lengths was corrupt, ignore them all, warn
+  // about it, and move on.
+  if (EC) {
+    warn("corrupt symbol records in " + File->getName());
+    consumeError(std::move(EC));
+    return;
+  }
+
+  // If any symbol needed realignment, allocate enough contiguous memory for
+  // them all. Typically symbol subsections are small enough that this will not
+  // cause fragmentation.
+  MutableArrayRef<uint8_t> AlignedSymbolMem;
+  if (NeedsRealignment) {
+    void *AlignedData =
+        Alloc.Allocate(RealignedSize, alignOf(CodeViewContainer::Pdb));
+    AlignedSymbolMem = makeMutableArrayRef(
+        reinterpret_cast<uint8_t *>(AlignedData), RealignedSize);
+  }
+
+  // Iterate again, this time doing the real work.
+  unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset();
+  ArrayRef<uint8_t> BulkSymbols;
+  cantFail(forEachCodeViewRecord<CVSymbol>(
+      SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+        // Align the record if required.
+        MutableArrayRef<uint8_t> RecordBytes;
+        if (NeedsRealignment) {
+          RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem);
+          Sym = CVSymbol(Sym.kind(), RecordBytes);
+        } else {
+          // Otherwise, we can actually mutate the symbol directly, since we
+          // copied it to apply relocations.
+          RecordBytes = makeMutableArrayRef(
+              const_cast<uint8_t *>(Sym.data().data()), Sym.length());
+        }
+
         // Discover type index references in the record. Skip it if we don't
         // know where they are.
         SmallVector<TiReference, 32> TypeRefs;
@@ -1050,45 +1098,51 @@ void PDBLinker::mergeSymbolRecords(ObjFi
           return Error::success();
         }
 
-        // Copy the symbol and fix the symbol record alignment. The symbol
-        // record in the object file may not be aligned.
-        MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
-        Sym = CVSymbol(Sym.kind(), NewData);
-
         // Re-map all the type index references.
-        MutableArrayRef<uint8_t> Contents =
-            NewData.drop_front(sizeof(RecordPrefix));
-        remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap,
+        remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap,
                                  TypeRefs);
 
         // An object file may have S_xxx_ID symbols, but these get converted to
         // "real" symbols in a PDB.
-        translateIdSymbols(NewData, getIDTable());
-        Sym = CVSymbol(symbolKind(NewData), NewData);
+        translateIdSymbols(RecordBytes, getIDTable());
+        Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes);
 
         // If this record refers to an offset in the object file's string table,
         // add that item to the global PDB string table and re-write the index.
-        recordStringTableReferences(Sym.kind(), Contents, StringTableRefs);
+        recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs);
 
         // Fill in "Parent" and "End" fields by maintaining a stack of scopes.
         if (symbolOpensScope(Sym.kind()))
-          scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), Sym);
+          scopeStackOpen(Scopes, CurSymOffset, Sym);
         else if (symbolEndsScope(Sym.kind()))
-          scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
+          scopeStackClose(Scopes, CurSymOffset, File);
 
         // Add the symbol to the globals stream if necessary.  Do this before
         // adding the symbol to the module since we may need to get the next
         // symbol offset, and writing to the module's symbol stream will update
         // that offset.
         if (symbolGoesInGlobalsStream(Sym))
-          addGlobalSymbol(Builder.getGsiBuilder(), *File, Sym);
+          addGlobalSymbol(Builder.getGsiBuilder(),
+                          File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym);
 
-        // Add the symbol to the module.
-        if (symbolGoesInModuleStream(Sym))
-          File->ModuleDBI->addSymbol(Sym);
+        if (symbolGoesInModuleStream(Sym)) {
+          // Add symbols to the module in bulk. If this symbol is contiguous
+          // with the previous run of symbols to add, combine the ranges. If
+          // not, close the previous range of symbols and start a new one.
+          if (Sym.data().data() == BulkSymbols.end()) {
+            BulkSymbols = makeArrayRef(BulkSymbols.data(),
+                                       BulkSymbols.size() + Sym.length());
+          } else {
+            File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
+            BulkSymbols = RecordBytes;
+          }
+          CurSymOffset += Sym.length();
+        }
         return Error::success();
-      });
-  cantFail(std::move(EC));
+      }));
+
+  // Add any remaining symbols we've accumulated.
+  File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
 }
 
 // Allocate memory for a .debug$S / .debug$F section and relocate it.




More information about the llvm-commits mailing list