[lld] [lld-macho] Parallelize ObjFile::sourceFile() during STABS emission. (PR #222087)

Liza Burakova via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 10:56:08 PDT 2026


https://github.com/liza371 created https://github.com/llvm/llvm-project/pull/222087

This is part of the ld64.lld performance improvements tracked by #222068

Currently, SymtabSection::emitStabs() calls emitBeginSourceStab() it passes in file->sourceFile(), which will call
ObjFile::sourceFile(). This method reads the component's root DIE, which is a bottleneck when done serially as at this stage the DWARF sections have not been parsed yet.

This commit parallelizes the calls to ObjFile::sourceFile(), as we already have the file ids stable sorted.

AI tool use: this is one of the commits that was prototyped by AI. I rewrote this commit and ran the benchmarking myself.

**Benchmarks for mac_debug_component_sym_2**
Benchmark 1: Apple ld
Time (mean ± σ):      2.244 s ±  0.025 s    [User: 9.294 s, System: 8.530 s]
Range (min … max):    2.204 s …  2.300 s    20 runs

Benchmark 2: LLD Baseline
Time (mean ± σ):      8.411 s ±  0.721 s    [User: 7.080 s, System: 2.718 s]
Range (min … max):    7.821 s … 11.171 s    20 runs

Benchmark 3: LLD Optimized
Time (mean ± σ):      8.082 s ±  0.283 s    [User: 7.597 s, System: 2.677 s]
Range (min … max):    7.617 s …  8.664 s    20 runs

**Benchmarks for mac_rel_component_sym_2**
Benchmark 1: Apple ld
Time (mean ± σ):      1.195 s ±  0.006 s    [User: 2.621 s, System: 12.442 s]
Range (min … max):    1.186 s …  1.209 s    20 runs

Benchmark 2: LLD Baseline
Time (mean ± σ):      2.528 s ±  0.023 s    [User: 1.971 s, System: 1.533 s]
Range (min … max):    2.489 s …  2.575 s    20 runs

Benchmark 3: LLD Optimized
Time (mean ± σ):      2.239 s ±  0.033 s    [User: 2.356 s, System: 1.650 s]
Range (min … max):    2.190 s …  2.345 s    20 runs

We did not see notable improvements for Chromium release builds with symbol level 0 or 1, which is not super surprising as this change is targeting debug symbols.

>From 19fbd8af5c683288ce50c58f52de0e28b5c9a8bf Mon Sep 17 00:00:00 2001
From: Liza Burakova <liza at chromium.org>
Date: Fri, 4 Sep 2026 12:55:21 -0400
Subject: [PATCH] [lld-macho] Parallelize ObjFile::sourceFile() during STABS
 emission.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This is part of the lld performance improvements tracked by

Currently, SymtabSection::emitStabs() calls emitBeginSourceStab()
it passes in file->sourceFile(), which will call
ObjFile::sourceFile(). This method reads the component's root DIE,
which is a bottleneck when done serially as at this stage the
DWARF sections have not been parsed yet.

This commit parallelizes the calls to ObjFile::sourceFile(), as
we already have the file ids stabel sorted.

AI tool use: this is one of the commits that was prototyped by AI.
I rewrote this commit and ran the benchmarking myself.

Benchmark for mac_debug_component_sym_2
Benchmark 1: Apple ld
Time (mean ± σ):      2.244 s ±  0.025 s    [User: 9.294 s, System: 8.530 s]
Range (min … max):    2.204 s …  2.300 s    20 runs

Benchmark 2: LLD Baseline
Time (mean ± σ):      8.411 s ±  0.721 s    [User: 7.080 s, System: 2.718 s]
Range (min … max):    7.821 s … 11.171 s    20 runs

Benchmark 3: LLD Optimized
Time (mean ± σ):      8.082 s ±  0.283 s    [User: 7.597 s, System: 2.677 s]
Range (min … max):    7.617 s …  8.664 s    20 runs

Benchmark for mac_rel_component_sym_2
Benchmark 1: Apple ld
Time (mean ± σ):      1.195 s ±  0.006 s    [User: 2.621 s, System: 12.442 s]
Range (min … max):    1.186 s …  1.209 s    20 runs

Benchmark 2: LLD Baseline
Time (mean ± σ):      2.528 s ±  0.023 s    [User: 1.971 s, System: 1.533 s]
Range (min … max):    2.489 s …  2.575 s    20 runs

Benchmark 3: LLD Optimized
Time (mean ± σ):      2.239 s ±  0.033 s    [User: 2.356 s, System: 1.650 s]
Range (min … max):    2.190 s …  2.345 s    20 runs

We did not see notable improvements for Chromium release builds with
symbol level 0 or 1, which is not super surprising as this change is targeting
debug symbols.
---
 lld/MachO/SyntheticSections.cpp | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index c4277d54edd38..a179f7edd10c0 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -1239,7 +1239,6 @@ void SymtabSection::emitStabs() {
     if (auto *defined = dyn_cast<Defined>(sym)) {
       // Excluded symbols should have been filtered out in finalizeContents().
       assert(defined->includeInSymtab);
-
       if (defined->isAbsolute())
         continue;
 
@@ -1263,10 +1262,22 @@ void SymtabSection::emitStabs() {
 
   llvm::stable_sort(symbolsNeedingStabs, llvm::less_second());
 
+  std::vector<ObjFile *> stabFiles;
+  for (const SortingPair &pair : symbolsNeedingStabs) {
+    ObjFile* file = cast<ObjFile>(pair.first->originalIsec->getFile());
+    if (stabFiles.empty() || stabFiles.back() != file)
+      stabFiles.push_back(file);
+  }
+  std::vector<std::string> stabSourceFiles(stabFiles.size());
+  parallelFor(0, stabFiles.size(), [&](size_t i) {
+    stabSourceFiles[i] = stabFiles[i]->sourceFile();
+  });
+
   // Emit STABS symbols so that dsymutil and/or the debugger can map address
   // regions in the final binary to the source and object files from which they
   // originated.
   InputFile *lastFile = nullptr;
+  size_t stabFileIdx = 0;
   for (SortingPair &pair : symbolsNeedingStabs) {
     Defined *defined = pair.first;
     // When emitting STABS entries for a symbol, always use the original
@@ -1282,7 +1293,8 @@ void SymtabSection::emitStabs() {
         emitEndSourceStab();
       lastFile = file;
 
-      emitBeginSourceStab(file->sourceFile());
+      assert(stabFiles[stabFileIdx] == file);
+      emitBeginSourceStab(stabSourceFiles[stabFileIdx++]);
       emitObjectFileStab(file);
     }
 



More information about the llvm-commits mailing list