[llvm] [DWARFLinker] Patch DW_AT_LLVM_stmt_sequence in the parallel linker (PR #195388)

via llvm-commits llvm-commits at lists.llvm.org
Mon May 4 09:52:25 PDT 2026


================
@@ -1553,74 +1553,142 @@ Error CompileUnit::cloneAndEmitLineTable(const Triple &TargetTriple) {
       OutLineTable.Rows.clear();
 
     OutLineTable.Sequences = InputLineTable->Sequences;
-  } else {
-    // This vector is the output line table.
-    std::vector<DWARFDebugLine::Row> NewRows;
-    NewRows.reserve(InputLineTable->Rows.size());
-
-    // Current sequence of rows being extracted, before being inserted
-    // in NewRows.
-    std::vector<DWARFDebugLine::Row> Seq;
-
-    const auto &FunctionRanges = getFunctionRanges();
-    std::optional<AddressRangeValuePair> CurrRange;
-
-    // FIXME: This logic is meant to generate exactly the same output as
-    // Darwin's classic dsymutil. There is a nicer way to implement this
-    // by simply putting all the relocated line info in NewRows and simply
-    // sorting NewRows before passing it to emitLineTableForUnit. This
-    // should be correct as sequences for a function should stay
-    // together in the sorted output. There are a few corner cases that
-    // look suspicious though, and that required to implement the logic
-    // this way. Revisit that once initial validation is finished.
-
-    // Iterate over the object file line info and extract the sequences
-    // that correspond to linked functions.
-    for (DWARFDebugLine::Row Row : InputLineTable->Rows) {
-      // Check whether we stepped out of the range. The range is
-      // half-open, but consider accept the end address of the range if
-      // it is marked as end_sequence in the input (because in that
-      // case, the relocation offset is accurate and that entry won't
-      // serve as the start of another function).
-      if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address)) {
-        // We just stepped out of a known range. Insert a end_sequence
-        // corresponding to the end of the range.
-        uint64_t StopAddress =
-            CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL;
-        CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address);
-        if (StopAddress != -1ULL && !Seq.empty()) {
-          // Insert end sequence row with the computed end address, but
-          // the same line as the previous one.
-          auto NextLine = Seq.back();
-          NextLine.Address.Address = StopAddress;
-          NextLine.EndSequence = 1;
-          NextLine.PrologueEnd = 0;
-          NextLine.BasicBlock = 0;
-          NextLine.EpilogueBegin = 0;
-          Seq.push_back(NextLine);
-          insertLineSequence(Seq, NewRows);
-        }
+    return emitDebugLine(TargetTriple, OutLineTable);
+  }
 
-        if (!CurrRange)
-          continue;
+  filterLineTableRows(*InputLineTable, OutLineTable.Rows);
+
+  if (StmtSeqListAttributes.empty())
+    return emitDebugLine(TargetTriple, OutLineTable);
+
+  // When DW_AT_LLVM_stmt_sequence attributes on this CU need their values
+  // rewritten to point at the correct output sequence, have the emitter
+  // record, for every row, the byte offset of the DW_LNE_set_address that
+  // opens the sequence containing that row.
+  //
+  // The patching below MUST run before emitDebugInfo() serializes the
+  // DIE bytes and before OutputSections::applyPatches() runs for this
+  // unit's .debug_info — it writes a local offset into the DIEValue that
+  // the serializer then emits, and a DebugOffsetPatch (registered at DIE
+  // cloning time) later adds the CU's .debug_line start offset to reach
+  // the final absolute value.
+  DenseMap<uint64_t, uint64_t> AddrToSeqStartOffset;
+  if (Error Err =
+          emitDebugLine(TargetTriple, OutLineTable, &AddrToSeqStartOffset))
+    return Err;
+
+  patchStmtSeqAttributes(AddrToSeqStartOffset);
+  return Error::success();
+}
+
+void CompileUnit::filterLineTableRows(
+    const DWARFDebugLine::LineTable &InputLineTable,
+    std::vector<DWARFDebugLine::Row> &NewRows) {
+  NewRows.reserve(InputLineTable.Rows.size());
+
+  // Current sequence of rows being extracted, before being inserted
+  // in NewRows.
+  std::vector<DWARFDebugLine::Row> Seq;
+
+  const auto &FunctionRanges = getFunctionRanges();
+  std::optional<AddressRangeValuePair> CurrRange;
+
+  // FIXME: This logic is meant to generate exactly the same output as
+  // Darwin's classic dsymutil. There is a nicer way to implement this
+  // by simply putting all the relocated line info in NewRows and simply
+  // sorting NewRows before passing it to emitLineTableForUnit. This
+  // should be correct as sequences for a function should stay
+  // together in the sorted output. There are a few corner cases that
+  // look suspicious though, and that required to implement the logic
+  // this way. Revisit that once initial validation is finished.
+
+  // Iterate over the object file line info and extract the sequences
+  // that correspond to linked functions.
+  for (DWARFDebugLine::Row Row : InputLineTable.Rows) {
+    // Check whether we stepped out of the range. The range is
+    // half-open, but consider accept the end address of the range if
+    // it is marked as end_sequence in the input (because in that
+    // case, the relocation offset is accurate and that entry won't
+    // serve as the start of another function).
+    if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address)) {
+      // We just stepped out of a known range. Insert a end_sequence
+      // corresponding to the end of the range.
+      uint64_t StopAddress =
+          CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL;
+      CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address);
+      if (StopAddress != -1ULL && !Seq.empty()) {
+        // Insert end sequence row with the computed end address, but
+        // the same line as the previous one.
+        auto NextLine = Seq.back();
+        NextLine.Address.Address = StopAddress;
+        NextLine.EndSequence = 1;
+        NextLine.PrologueEnd = 0;
+        NextLine.BasicBlock = 0;
+        NextLine.EpilogueBegin = 0;
+        Seq.push_back(NextLine);
+        insertLineSequence(Seq, NewRows);
       }
 
-      // Ignore empty sequences.
-      if (Row.EndSequence && Seq.empty())
+      if (!CurrRange)
         continue;
+    }
+
+    // Ignore empty sequences.
+    if (Row.EndSequence && Seq.empty())
+      continue;
 
-      // Relocate row address and add it to the current sequence.
-      Row.Address.Address += CurrRange->Value;
-      Seq.emplace_back(Row);
+    // Relocate row address and add it to the current sequence.
+    Row.Address.Address += CurrRange->Value;
+    Seq.emplace_back(Row);
 
-      if (Row.EndSequence)
-        insertLineSequence(Seq, NewRows);
-    }
+    if (Row.EndSequence)
+      insertLineSequence(Seq, NewRows);
+  }
+}
 
-    OutLineTable.Rows = std::move(NewRows);
+void CompileUnit::patchStmtSeqAttributes(
+    const DenseMap<uint64_t, uint64_t> &AddrToSeqStartOffset) {
+  const uint64_t InvalidOffset = getFormParams().getDwarfMaxOffset();
+  const auto &FunctionRanges = getFunctionRanges();
+
+  for (const CompileUnit::StmtSeqPatch &Patch : StmtSeqListAttributes) {
+    uint64_t NewStmtSeq = InvalidOffset;
+    if (Patch.InputFirstAddr) {
+      if (auto Range =
+              FunctionRanges.getRangeThatContains(*Patch.InputFirstAddr)) {
+        uint64_t OutAddr = *Patch.InputFirstAddr + Range->Value;
+        auto It = AddrToSeqStartOffset.find(OutAddr);
+        if (It != AddrToSeqStartOffset.end())
+          NewStmtSeq = It->second;
+      }
+    }
+    // When resolution fails, the InvalidOffset sentinel must survive the
+    // combination-time section-offset fixup. The patch applier preserves
+    // InvalidOffset as-is so consumers see a clean invalid marker rather
+    // than StartOffset - 1.
+    *Patch.Value = DIEValue(Patch.Value->getAttribute(), Patch.Value->getForm(),
+                            DIEInteger(NewStmtSeq));
   }
+}
 
-  return emitDebugLine(TargetTriple, OutLineTable);
+std::optional<uint64_t>
+CompileUnit::getStmtSeqFirstAddress(uint64_t StmtSeqOffset) {
----------------
alx32 wrote:

Should we also mirror the classic linker's fallback here for sequences the parser does not add to `LT->Sequences`? Otherwise a `DW_AT_LLVM_stmt_sequence` that [classic can recover from row boundaries](https://github.com/llvm/llvm-project/blob/8186fd07edcf369bd8613c548b38637ec1544410/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp#L481-L487) would become the invalid sentinel in the parallel path. We've had issues with this in the past. 

https://github.com/llvm/llvm-project/pull/195388


More information about the llvm-commits mailing list