RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 | FileCheck %s
RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-lto.macho.x86_64 | FileCheck %s --check-prefix=CHECK-LTO
RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK-ARCHIVE
-RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 %p/Inputs/basic-lto.macho.x86_64 %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LTO --check-prefix=CHECK-ARCHIVE
+RUN: llvm-dsymutil -no-output -verbose -oso-prepend-path=%p %p/Inputs/basic.macho.x86_64 %p/Inputs/basic-lto.macho.x86_64 %p/Inputs/basic-archive.macho.x86_64 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LTO --check-prefix=CHECK-ARCHIVE
This test check the basic Dwarf linking process through the debug dumps.
CHECK-NEXT: TAG_compile_unit
CHECK-NOT: TAG
CHECK: AT_name {{.*}}basic1.c
-CHECK-NOT: Found valid debug map entry
-CHECK: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0
-CHECK-NEXT: DW_TAG_subprogram
-CHECK-NEXT: DW_AT_name{{.*}}"main"
CHECK: DEBUG MAP OBJECT: {{.*}}basic2.macho.x86_64.o
CHECK: Input compilation unit:
CHECK-NEXT: TAG_compile_unit
CHECK-NOT: TAG
CHECK: AT_name {{.*}}basic2.c
+
+CHECK: DEBUG MAP OBJECT: {{.*}}basic3.macho.x86_64.o
+CHECK: Input compilation unit:
+CHECK-NEXT: TAG_compile_unit
+CHECK-NOT: TAG
+CHECK: AT_name {{.*}}basic3.c
+
CHECK-NOT: Found valid debug map entry
+CHECK: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0
+CHECK-NEXT: DW_TAG_subprogram
+CHECK-NEXT: DW_AT_name{{.*}}"main"
+
CHECK: Found valid debug map entry: _private_int 0000000000000560 => 0000000100001008
CHECK-NEXT: DW_TAG_variable
CHECK-NEXT: DW_AT_name {{.*}}"private_int"
CHECK-NEXT: DW_TAG_subprogram
CHECK-NEXT: DW_AT_name {{.*}}"inc"
-CHECK: DEBUG MAP OBJECT: {{.*}}basic3.macho.x86_64.o
-CHECK: Input compilation unit:
-CHECK-NEXT: TAG_compile_unit
-CHECK-NOT: TAG
-CHECK: AT_name {{.*}}basic3.c
CHECK-NOT: Found valid debug map entry
CHECK: Found valid debug map entry: _val ffffffffffffffff => 0000000100001004
CHECK-NEXT: DW_TAG_variable
CHECK-ARCHIVE-NEXT: TAG_compile_unit
CHECK-ARCHIVE-NOT: TAG
CHECK-ARCHIVE: AT_name {{.*}}basic1.c
-CHECK-ARCHIVE-NOT: Found valid debug map entry
-CHECK-ARCHIVE: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0
-CHECK-ARCHIVE-NEXT: DW_TAG_subprogram
-CHECK-ARCHIVE-NEXT: DW_AT_name{{.*}}"main"
CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic2.macho.x86_64.o)
CHECK-ARCHIVE: Input compilation unit:
CHECK-ARCHIVE-NEXT: TAG_compile_unit
CHECK-ARCHIVE-NOT: TAG
CHECK-ARCHIVE: AT_name {{.*}}basic2.c
+
+CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic3.macho.x86_64.o)
+CHECK-ARCHIVE: Input compilation unit:
+CHECK-ARCHIVE-NEXT: TAG_compile_unit
+CHECK-ARCHIVE-NOT: TAG
+CHECK-ARCHIVE: AT_name {{.*}}basic3.c
+
+CHECK-ARCHIVE-NOT: Found valid debug map entry
+CHECK-ARCHIVE: Found valid debug map entry: _main 0000000000000000 => 0000000100000ea0
+CHECK-ARCHIVE-NEXT: DW_TAG_subprogram
+CHECK-ARCHIVE-NEXT: DW_AT_name{{.*}}"main"
+
CHECK-ARCHIVE-NOT: Found valid debug map entry
CHECK-ARCHIVE: Found valid debug map entry: _private_int 0000000000000560 => 0000000100001004
CHECK-ARCHIVE-NEXT: DW_TAG_variable
CHECK-ARCHIVE-NEXT: DW_TAG_subprogram
CHECK-ARCHIVE-NEXT: DW_AT_name {{.*}}"inc"
-CHECK-ARCHIVE: DEBUG MAP OBJECT: {{.*}}libbasic.a(basic3.macho.x86_64.o)
-CHECK-ARCHIVE: Input compilation unit:
-CHECK-ARCHIVE-NEXT: TAG_compile_unit
-CHECK-ARCHIVE-NOT: TAG
-CHECK-ARCHIVE: AT_name {{.*}}basic3.c
CHECK-ARCHIVE-NOT: Found valid debug map entry
CHECK-ARCHIVE: Found valid debug map entry: _val ffffffffffffffff => 0000000100001008
CHECK-ARCHIVE-NEXT: DW_TAG_variable
#include "NonRelocatableStringpool.h"
#include "dsymutil.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
unsigned UnitID = 0;
DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath());
+ // First populate the data structure we need for each iteration of the
+ // parallel loop.
+ unsigned NumObjects = Map.getNumberOfObjects();
+ std::vector<LinkContext> ObjectContexts;
+ ObjectContexts.reserve(NumObjects);
+ for (const auto &Obj : Map.objects())
+ ObjectContexts.emplace_back(Map, *this, *Obj.get(), Options.Verbose);
+
// This Dwarf string pool which is only used for uniquing. This one should
// never be used for offsets as its not thread-safe or predictable.
UniquingStringPool UniquingStringPool;
// ODR Contexts for the link.
DeclContextTree ODRContexts;
- for (const auto &Obj : Map.objects()) {
- LinkContext LinkContext(Map, *this, *Obj.get(), Options.Verbose);
-
+ for (LinkContext &LinkContext : ObjectContexts) {
if (Options.Verbose)
- outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
+ outs() << "DEBUG MAP OBJECT: " << LinkContext.DMO.getObjectFilename()
+ << "\n";
// N_AST objects (swiftmodule files) should get dumped directly into the
// appropriate DWARF section.
}
if (!LinkContext.ObjectFile)
+
continue;
// Look for relocations that correspond to debug map entries.
+
if (LLVM_LIKELY(!Options.Update) &&
!LinkContext.RelocMgr.findValidRelocsInDebugInfo(
*LinkContext.ObjectFile, LinkContext.DMO)) {
if (Options.Verbose)
- warn("No valid relocations found: skipping\n");
+ outs() << "No valid relocations found. Skipping.\n";
+
+ // Clear this ObjFile entry as a signal to other loops that we should not
+ // process this iteration.
+ LinkContext.ObjectFile = nullptr;
continue;
}
if (!LinkContext.DwarfContext)
continue;
+ startDebugObject(LinkContext);
+
// In a first phase, just read in the debug info and load all clang modules.
LinkContext.CompileUnits.reserve(
LinkContext.DwarfContext->getNumCompileUnits());
maybeUpdateMaxDwarfVersion(CU->getVersion());
}
}
+ }
- // Now build the DIE parent links that we will use during the next phase.
- for (auto &CurrentUnit : LinkContext.CompileUnits)
- analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0,
- *CurrentUnit, &ODRContexts.getRoot(),
- UniquingStringPool, ODRContexts);
+ ThreadPool pool(2);
- // Then mark all the DIEs that need to be present in the linked
- // output and collect some information about them. Note that this
- // loop can not be merged with the previous one because cross-CU
- // references require the ParentIdx to be setup for every CU in
- // the object file before calling this.
- if (LLVM_UNLIKELY(Options.Update)) {
- for (auto &CurrentUnit : LinkContext.CompileUnits)
- CurrentUnit->markEverythingAsKept();
- Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile, Options);
- } else {
+ // These variables manage the list of processed object files.
+ // The mutex and condition variable are to ensure that this is thread safe.
+ std::mutex ProcessedFilesMutex;
+ std::condition_variable ProcessedFilesConditionVariable;
+ BitVector ProcessedFiles(NumObjects, false);
+
+ // Now do analyzeContextInfo in parallel as it is particularly expensive.
+ pool.async([&]() {
+ for (unsigned i = 0, e = NumObjects; i != e; ++i) {
+ auto &LinkContext = ObjectContexts[i];
+
+ if (!LinkContext.ObjectFile) {
+ std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
+ ProcessedFiles.set(i);
+ ProcessedFilesConditionVariable.notify_one();
+ continue;
+ }
+
+ // Now build the DIE parent links that we will use during the next phase.
for (auto &CurrentUnit : LinkContext.CompileUnits)
- lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges,
- LinkContext.CompileUnits,
- CurrentUnit->getOrigUnit().getUnitDIE(),
- LinkContext.DMO, *CurrentUnit, 0);
+ analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0,
+ *CurrentUnit, &ODRContexts.getRoot(),
+ UniquingStringPool, ODRContexts);
+
+ std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
+ ProcessedFiles.set(i);
+ ProcessedFilesConditionVariable.notify_one();
}
+ });
+
+ // And then the remaining work in serial again.
+ // Note, although this loop runs in serial, it can run in parallel with
+ // the analyzeContextInfo loop so long as we process files with indices >=
+ // than those processed by analyzeContextInfo.
+ pool.async([&]() {
+ for (unsigned i = 0, e = NumObjects; i != e; ++i) {
+ {
+ std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
+ if (!ProcessedFiles[i]) {
+ ProcessedFilesConditionVariable.wait(
+ LockGuard, [&]() { return ProcessedFiles[i]; });
+ }
+ }
- // The calls to applyValidRelocs inside cloneDIE will walk the
- // reloc array again (in the same way findValidRelocsInDebugInfo()
- // did). We need to reset the NextValidReloc index to the beginning.
- LinkContext.RelocMgr.resetValidRelocs();
- if (LinkContext.RelocMgr.hasValidRelocs() || LLVM_UNLIKELY(Options.Update))
- DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, LinkContext.CompileUnits,
- Options)
- .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO,
- LinkContext.Ranges, OffsetsStringPool);
- if (!Options.NoOutput && !LinkContext.CompileUnits.empty() &&
- LLVM_LIKELY(!Options.Update))
- patchFrameInfoForObject(
- LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext,
- LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize());
-
- // Clean-up before starting working on the next object.
- endDebugObject(LinkContext);
- }
+ auto &LinkContext = ObjectContexts[i];
+ if (!LinkContext.ObjectFile)
+ continue;
- // Emit everything that's global.
- if (!Options.NoOutput) {
- Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion);
- Streamer->emitStrings(OffsetsStringPool);
- Streamer->emitAppleNames(AppleNames);
- Streamer->emitAppleNamespaces(AppleNamespaces);
- Streamer->emitAppleTypes(AppleTypes);
- Streamer->emitAppleObjc(AppleObjc);
- }
+ // Then mark all the DIEs that need to be present in the linked output
+ // and collect some information about them.
+ // Note that this loop can not be merged with the previous one because
+ // cross-cu references require the ParentIdx to be setup for every CU in
+ // the object file before calling this.
+ if (LLVM_UNLIKELY(Options.Update)) {
+ for (auto &CurrentUnit : LinkContext.CompileUnits)
+ CurrentUnit->markEverythingAsKept();
+ Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile, Options);
+ } else {
+ for (auto &CurrentUnit : LinkContext.CompileUnits)
+ lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges,
+ LinkContext.CompileUnits,
+ CurrentUnit->getOrigUnit().getUnitDIE(),
+ LinkContext.DMO, *CurrentUnit, 0);
+ }
+
+ // The calls to applyValidRelocs inside cloneDIE will walk the reloc
+ // array again (in the same way findValidRelocsInDebugInfo() did). We
+ // need to reset the NextValidReloc index to the beginning.
+ LinkContext.RelocMgr.resetValidRelocs();
+ if (LinkContext.RelocMgr.hasValidRelocs() ||
+ LLVM_UNLIKELY(Options.Update))
+ DIECloner(*this, LinkContext.RelocMgr, DIEAlloc,
+ LinkContext.CompileUnits, Options)
+ .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO,
+ LinkContext.Ranges, OffsetsStringPool);
+ if (!Options.NoOutput && !LinkContext.CompileUnits.empty() &&
+ LLVM_LIKELY(!Options.Update))
+ patchFrameInfoForObject(
+ LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext,
+ LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize());
+
+ // Clean-up before starting working on the next object.
+ endDebugObject(LinkContext);
+ }
+
+ // Emit everything that's global.
+ if (!Options.NoOutput) {
+ Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion);
+ Streamer->emitStrings(OffsetsStringPool);
+ Streamer->emitAppleNames(AppleNames);
+ Streamer->emitAppleNamespaces(AppleNamespaces);
+ Streamer->emitAppleTypes(AppleTypes);
+ Streamer->emitAppleObjc(AppleObjc);
+ }
+ });
+
+ pool.wait();
return Options.NoOutput ? true : Streamer->finish(Map);
}