diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1321,6 +1321,7 @@ } void LinkerDriver::createFiles(opt::InputArgList &args) { + llvm::TimeTraceScope timeScope("Load input files"); // For --{push,pop}-state. std::vector> stack; @@ -1659,6 +1660,7 @@ // result, the passes after the symbol resolution won't see any // symbols of type CommonSymbol. static void replaceCommonSymbols() { + llvm::TimeTraceScope timeScope("Replace common symbols"); for (Symbol *sym : symtab->symbols()) { auto *s = dyn_cast(sym); if (!s) @@ -1678,6 +1680,7 @@ // created from the DSO. Otherwise, they become dangling references // that point to a non-existent DSO. static void demoteSharedSymbols() { + llvm::TimeTraceScope timeScope("Demote shared symbols"); for (Symbol *sym : symtab->symbols()) { auto *s = dyn_cast(sym); if (!s || s->getFile().isNeeded) @@ -1974,10 +1977,14 @@ // Fail early if the output file or map file is not writable. If a user has a // long link, e.g. due to a large LTO link, they do not wish to run it and // find that it failed because there was a mistake in their command-line. - if (auto e = tryCreateFile(config->outputFile)) - error("cannot open output file " + config->outputFile + ": " + e.message()); - if (auto e = tryCreateFile(config->mapFile)) - error("cannot open map file " + config->mapFile + ": " + e.message()); + { + llvm::TimeTraceScope timeScope("Create output files"); + if (auto e = tryCreateFile(config->outputFile)) + error("cannot open output file " + config->outputFile + ": " + + e.message()); + if (auto e = tryCreateFile(config->mapFile)) + error("cannot open map file " + config->mapFile + ": " + e.message()); + } if (errorCount()) return; @@ -2004,8 +2011,10 @@ // appended to the Files vector. { llvm::TimeTraceScope timeScope("Parse input files"); - for (size_t i = 0; i < files.size(); ++i) + for (size_t i = 0; i < files.size(); ++i) { + llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName()); parseFile(files[i]); + } } // Now that we have every file, we can decide if we will need a @@ -2092,8 +2101,10 @@ // For a relocatable output, version scripts don't make sense, and // parsing a symbol version string (e.g. dropping "@ver1" from a symbol // name "foo@ver1") rather do harm, so we don't call this if -r is given. - if (!config->relocatable) + if (!config->relocatable) { + llvm::TimeTraceScope timeScope("Process symbol versions"); symtab->scanVersionScript(); + } // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. @@ -2123,37 +2134,43 @@ if (!wrapped.empty()) wrapSymbols(wrapped); - // Now that we have a complete list of input files. - // Beyond this point, no new files are added. - // Aggregate all input sections into one place. - for (InputFile *f : objectFiles) - for (InputSectionBase *s : f->getSections()) - if (s && s != &InputSection::discarded) - inputSections.push_back(s); - for (BinaryFile *f : binaryFiles) - for (InputSectionBase *s : f->getSections()) - inputSections.push_back(cast(s)); - - llvm::erase_if(inputSections, [](InputSectionBase *s) { - if (s->type == SHT_LLVM_SYMPART) { - readSymbolPartitionSection(s); - return true; - } + { + llvm::TimeTraceScope timeScope("Aggregate sections"); + // Now that we have a complete list of input files. + // Beyond this point, no new files are added. + // Aggregate all input sections into one place. + for (InputFile *f : objectFiles) + for (InputSectionBase *s : f->getSections()) + if (s && s != &InputSection::discarded) + inputSections.push_back(s); + for (BinaryFile *f : binaryFiles) + for (InputSectionBase *s : f->getSections()) + inputSections.push_back(cast(s)); + } - // We do not want to emit debug sections if --strip-all - // or -strip-debug are given. - if (config->strip == StripPolicy::None) - return false; + { + llvm::TimeTraceScope timeScope("Strip sections"); + llvm::erase_if(inputSections, [](InputSectionBase *s) { + if (s->type == SHT_LLVM_SYMPART) { + readSymbolPartitionSection(s); + return true; + } - if (isDebugSection(*s)) - return true; - if (auto *isec = dyn_cast(s)) - if (InputSectionBase *rel = isec->getRelocatedSection()) - if (isDebugSection(*rel)) - return true; + // We do not want to emit debug sections if --strip-all + // or -strip-debug are given. + if (config->strip == StripPolicy::None) + return false; - return false; - }); + if (isDebugSection(*s)) + return true; + if (auto *isec = dyn_cast(s)) + if (InputSectionBase *rel = isec->getRelocatedSection()) + if (isDebugSection(*rel)) + return true; + + return false; + }); + } // Since we now have a complete set of input files, we can create // a .d file to record build dependencies. @@ -2225,23 +2242,33 @@ if (!config->relocatable) combineEhSections(); - // Create output sections described by SECTIONS commands. - script->processSectionCommands(); - - // Linker scripts control how input sections are assigned to output sections. - // Input sections that were not handled by scripts are called "orphans", and - // they are assigned to output sections by the default rule. Process that. - script->addOrphanSections(); - - // Migrate InputSectionDescription::sectionBases to sections. This includes - // merging MergeInputSections into a single MergeSyntheticSection. From this - // point onwards InputSectionDescription::sections should be used instead of - // sectionBases. - for (BaseCommand *base : script->sectionCommands) - if (auto *sec = dyn_cast(base)) - sec->finalizeInputSections(); - llvm::erase_if(inputSections, - [](InputSectionBase *s) { return isa(s); }); + { + llvm::TimeTraceScope timeScope("Assign sections"); + + // Create output sections described by SECTIONS commands. + script->processSectionCommands(); + + // Linker scripts control how input sections are assigned to output + // sections. Input sections that were not handled by scripts are called + // "orphans", and they are assigned to output sections by the default rule. + // Process that. + script->addOrphanSections(); + } + + { + llvm::TimeTraceScope timeScope("Merge/finalize input sections"); + + // Migrate InputSectionDescription::sectionBases to sections. This includes + // merging MergeInputSections into a single MergeSyntheticSection. From this + // point onwards InputSectionDescription::sections should be used instead of + // sectionBases. + for (BaseCommand *base : script->sectionCommands) + if (auto *sec = dyn_cast(base)) + sec->finalizeInputSections(); + llvm::erase_if(inputSections, [](InputSectionBase *s) { + return isa(s); + }); + } // Two input sections with different output sections should not be folded. // ICF runs after processSectionCommands() so that we know the output sections. diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/TimeProfiler.h" using namespace llvm; using namespace llvm::sys; @@ -238,6 +239,7 @@ // This is for -l. Optional elf::searchLibrary(StringRef name) { + llvm::TimeTraceScope timeScope("Locate library", name); if (name.startswith(":")) return findFromSearchPaths(name.substr(1)); return searchLibraryBaseName(name); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -105,6 +105,8 @@ } Optional elf::readFile(StringRef path) { + llvm::TimeTraceScope timeScope("Load input files", path); + // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. if (!config->chroot.empty() && path.startswith("/")) diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" +#include "llvm/Support/TimeProfiler.h" #include #include #include @@ -765,6 +766,7 @@ } void LinkerScript::diagnoseOrphanHandling() const { + llvm::TimeTraceScope timeScope("Diagnose orphan sections"); for (const InputSectionBase *sec : orphanSections) { // Input SHT_REL[A] retained by --emit-relocs are ignored by // computeInputSections(). Don't warn/error. diff --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp --- a/lld/ELF/MapFile.cpp +++ b/lld/ELF/MapFile.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -142,6 +143,8 @@ if (config->mapFile.empty()) return; + llvm::TimeTraceScope timeScope("Write map file"); + // Open a map file for writing. std::error_code ec; raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -20,6 +20,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/SHA1.h" +#include "llvm/Support/TimeProfiler.h" #include #include @@ -285,6 +286,8 @@ !name.startswith(".debug_")) return; + llvm::TimeTraceScope timeScope("Compress debug sections"); + // Create a section header. zDebugHeader.resize(sizeof(Elf_Chdr)); auto *hdr = reinterpret_cast(zDebugHeader.data()); diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -32,6 +32,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TimeProfiler.h" #include #include #include @@ -1620,17 +1621,23 @@ } void elf::readLinkerScript(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read linker script", + mb.getBufferIdentifier()); ScriptParser(mb).readLinkerScript(); } void elf::readVersionScript(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read version script", + mb.getBufferIdentifier()); ScriptParser(mb).readVersionScript(); } void elf::readDynamicList(MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read dynamic list", mb.getBufferIdentifier()); ScriptParser(mb).readDynamicList(); } void elf::readDefsym(StringRef name, MemoryBufferRef mb) { + llvm::TimeTraceScope timeScope("Read defsym input", name); ScriptParser(mb).readDefsym(name); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -158,7 +158,6 @@ } template void elf::writeResult() { - llvm::TimeTraceScope timeScope("Write output file"); Writer().run(); } @@ -205,6 +204,7 @@ } void elf::combineEhSections() { + llvm::TimeTraceScope timeScope("Combine EH sections"); for (InputSectionBase *&s : inputSections) { // Ignore dead sections and the partition end marker (.part.end), // whose partition number is out of bounds. @@ -634,30 +634,34 @@ checkSections(); // It does not make sense try to open the file if we have error already. - if (errorCount()) - return; - // Write the result down to a file. - openFile(); if (errorCount()) return; - if (!config->oFormatBinary) { - if (config->zSeparate != SeparateSegmentKind::None) - writeTrapInstr(); - writeHeader(); - writeSections(); - } else { - writeSectionsBinary(); - } + { + llvm::TimeTraceScope timeScope("Write output file"); + // Write the result down to a file. + openFile(); + if (errorCount()) + return; - // Backfill .note.gnu.build-id section content. This is done at last - // because the content is usually a hash value of the entire output file. - writeBuildId(); - if (errorCount()) - return; + if (!config->oFormatBinary) { + if (config->zSeparate != SeparateSegmentKind::None) + writeTrapInstr(); + writeHeader(); + writeSections(); + } else { + writeSectionsBinary(); + } + + // Backfill .note.gnu.build-id section content. This is done at last + // because the content is usually a hash value of the entire output file. + writeBuildId(); + if (errorCount()) + return; - if (auto e = buffer->commit()) - error("failed to write to the output file: " + toString(std::move(e))); + if (auto e = buffer->commit()) + error("failed to write to the output file: " + toString(std::move(e))); + } } template @@ -763,6 +767,7 @@ template void Writer::copyLocalSymbols() { if (!in.symTab) return; + llvm::TimeTraceScope timeScope("Add local symbols"); if (config->copyRelocs && config->discard != DiscardPolicy::None) markUsedLocalSymbols(); for (InputFile *file : objectFiles) { @@ -1504,6 +1509,7 @@ } template void Writer::sortSections() { + llvm::TimeTraceScope timeScope("Sort sections"); script->adjustSectionsBeforeSorting(); // Don't sort if using -r. It is not necessary and we want to preserve the @@ -1637,6 +1643,7 @@ } template void Writer::resolveShfLinkOrder() { + llvm::TimeTraceScope timeScope("Resolve SHF_LINK_ORDER"); for (OutputSection *sec : outputSections) { if (!(sec->flags & SHF_LINK_ORDER)) continue; @@ -1679,8 +1686,10 @@ } static void finalizeSynthetic(SyntheticSection *sec) { - if (sec && sec->isNeeded() && sec->getParent()) + if (sec && sec->isNeeded() && sec->getParent()) { + llvm::TimeTraceScope timeScope("Finalize synthetic sections", sec->name); sec->finalizeContents(); + } } // We need to generate and finalize the content that depends on the address of @@ -1688,6 +1697,7 @@ // addresses we must converge to a fixed point. We do that here. See the comment // in Writer::finalizeSections(). template void Writer::finalizeAddressDependentContent() { + llvm::TimeTraceScope timeScope("Finalize address dependent content"); ThunkCreator tc; AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; @@ -1944,11 +1954,14 @@ } } - // This responsible for splitting up .eh_frame section into - // pieces. The relocation scan uses those pieces, so this has to be - // earlier. - for (Partition &part : partitions) - finalizeSynthetic(part.ehFrame); + { + llvm::TimeTraceScope timeScope("Finalize .eh_frame"); + // This responsible for splitting up .eh_frame section into + // pieces. The relocation scan uses those pieces, so this has to be + // earlier. + for (Partition &part : partitions) + finalizeSynthetic(part.ehFrame); + } for (Symbol *sym : symtab->symbols()) sym->isPreemptible = computeIsPreemptible(*sym); @@ -1957,14 +1970,17 @@ // by declareSymbols) to actual definitions. script->processSymbolAssignments(); - // Scan relocations. This must be done after every symbol is declared so that - // we can correctly decide if a dynamic relocation is needed. This is called - // after processSymbolAssignments() because it needs to know whether a - // linker-script-defined symbol is absolute. - ppc64noTocRelax.clear(); - if (!config->relocatable) { - forEachRelSec(scanRelocations); - reportUndefinedSymbols(); + { + llvm::TimeTraceScope timeScope("Scan relocations"); + // Scan relocations. This must be done after every symbol is declared so + // that we can correctly decide if a dynamic relocation is needed. This is + // called after processSymbolAssignments() because it needs to know whether + // a linker-script-defined symbol is absolute. + ppc64noTocRelax.clear(); + if (!config->relocatable) { + forEachRelSec(scanRelocations); + reportUndefinedSymbols(); + } } if (in.plt && in.plt->isNeeded()) @@ -1994,31 +2010,34 @@ toString(*sym) + " [--no-allow-shlib-undefined]"); } - // Now that we have defined all possible global symbols including linker- - // synthesized ones. Visit all symbols to give the finishing touches. - for (Symbol *sym : symtab->symbols()) { - if (!includeInSymtab(*sym)) - continue; - if (in.symTab) - in.symTab->addSymbol(sym); - - if (sym->includeInDynsym()) { - partitions[sym->partition - 1].dynSymTab->addSymbol(sym); - if (auto *file = dyn_cast_or_null(sym->file)) - if (file->isNeeded && !sym->isUndefined()) - addVerneed(sym); + { + llvm::TimeTraceScope timeScope("Add symbols to symtabs"); + // Now that we have defined all possible global symbols including linker- + // synthesized ones. Visit all symbols to give the finishing touches. + for (Symbol *sym : symtab->symbols()) { + if (!includeInSymtab(*sym)) + continue; + if (in.symTab) + in.symTab->addSymbol(sym); + + if (sym->includeInDynsym()) { + partitions[sym->partition - 1].dynSymTab->addSymbol(sym); + if (auto *file = dyn_cast_or_null(sym->file)) + if (file->isNeeded && !sym->isUndefined()) + addVerneed(sym); + } } - } - // We also need to scan the dynamic relocation tables of the other partitions - // and add any referenced symbols to the partition's dynsym. - for (Partition &part : MutableArrayRef(partitions).slice(1)) { - DenseSet syms; - for (const SymbolTableEntry &e : part.dynSymTab->getSymbols()) - syms.insert(e.sym); - for (DynamicReloc &reloc : part.relaDyn->relocs) - if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second) - part.dynSymTab->addSymbol(reloc.sym); + // We also need to scan the dynamic relocation tables of the other + // partitions and add any referenced symbols to the partition's dynsym. + for (Partition &part : MutableArrayRef(partitions).slice(1)) { + DenseSet syms; + for (const SymbolTableEntry &e : part.dynSymTab->getSymbols()) + syms.insert(e.sym); + for (DynamicReloc &reloc : part.relaDyn->relocs) + if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second) + part.dynSymTab->addSymbol(reloc.sym); + } } // Do not proceed if there was an undefined symbol. @@ -2099,35 +2118,39 @@ // have the headers, we can find out which sections they point to. setReservedSymbolSections(); - finalizeSynthetic(in.bss); - finalizeSynthetic(in.bssRelRo); - finalizeSynthetic(in.symTabShndx); - finalizeSynthetic(in.shStrTab); - finalizeSynthetic(in.strTab); - finalizeSynthetic(in.got); - finalizeSynthetic(in.mipsGot); - finalizeSynthetic(in.igotPlt); - finalizeSynthetic(in.gotPlt); - finalizeSynthetic(in.relaIplt); - finalizeSynthetic(in.relaPlt); - finalizeSynthetic(in.plt); - finalizeSynthetic(in.iplt); - finalizeSynthetic(in.ppc32Got2); - finalizeSynthetic(in.partIndex); - - // Dynamic section must be the last one in this list and dynamic - // symbol table section (dynSymTab) must be the first one. - for (Partition &part : partitions) { - finalizeSynthetic(part.dynSymTab); - finalizeSynthetic(part.gnuHashTab); - finalizeSynthetic(part.hashTab); - finalizeSynthetic(part.verDef); - finalizeSynthetic(part.relaDyn); - finalizeSynthetic(part.relrDyn); - finalizeSynthetic(part.ehFrameHdr); - finalizeSynthetic(part.verSym); - finalizeSynthetic(part.verNeed); - finalizeSynthetic(part.dynamic); + { + llvm::TimeTraceScope timeScope("Finalize synthetic sections"); + + finalizeSynthetic(in.bss); + finalizeSynthetic(in.bssRelRo); + finalizeSynthetic(in.symTabShndx); + finalizeSynthetic(in.shStrTab); + finalizeSynthetic(in.strTab); + finalizeSynthetic(in.got); + finalizeSynthetic(in.mipsGot); + finalizeSynthetic(in.igotPlt); + finalizeSynthetic(in.gotPlt); + finalizeSynthetic(in.relaIplt); + finalizeSynthetic(in.relaPlt); + finalizeSynthetic(in.plt); + finalizeSynthetic(in.iplt); + finalizeSynthetic(in.ppc32Got2); + finalizeSynthetic(in.partIndex); + + // Dynamic section must be the last one in this list and dynamic + // symbol table section (dynSymTab) must be the first one. + for (Partition &part : partitions) { + finalizeSynthetic(part.dynSymTab); + finalizeSynthetic(part.gnuHashTab); + finalizeSynthetic(part.hashTab); + finalizeSynthetic(part.verDef); + finalizeSynthetic(part.relaDyn); + finalizeSynthetic(part.relrDyn); + finalizeSynthetic(part.ehFrameHdr); + finalizeSynthetic(part.verSym); + finalizeSynthetic(part.verNeed); + finalizeSynthetic(part.dynamic); + } } if (!script->hasSectionsCommand && !config->relocatable) @@ -2158,9 +2181,13 @@ if (errorCount()) return; - // finalizeAddressDependentContent may have added local symbols to the static symbol table. - finalizeSynthetic(in.symTab); - finalizeSynthetic(in.ppc64LongBranchTarget); + { + llvm::TimeTraceScope timeScope("Finalize synthetic sections"); + // finalizeAddressDependentContent may have added local symbols to the + // static symbol table. + finalizeSynthetic(in.symTab); + finalizeSynthetic(in.ppc64LongBranchTarget); + } // Relaxation to delete inter-basic block jumps created by basic block // sections. Run after in.symTab is finalized as optimizeBasicBlockJumps