[llvm] [CodeExtractor] Copy live-in dbg records whole instead of rebuilding (PR #227794)

Oliver Hunt via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 18:11:27 PDT 2026


================
@@ -1270,13 +1270,77 @@ static void eraseDebugIntrinsicsWithNonLocalRefs(Function &F) {
   }
 }
 
+/// Does \p DVR describe its variable for the whole function, rather than from
+/// its position until the next record for the same variable?
+static bool isFunctionWideDbgRecord(const DbgVariableRecord &DVR) {
+  return DVR.isDbgDeclare() || DVR.isDbgDeclareValue();
+}
+
+/// Collect the debug records in the parent function that describe variables
+/// on entry to the extracted region, whose place is now taken by \p TheCall.
+///
+/// A #dbg_declare or #dbg_declare_value holds for the whole function, so one
+/// that uses any of \p Inputs is live on entry wherever it is. A #dbg_value or
+/// #dbg_assign holds only until the next record for the same variable, so only
+/// the last one before the region matters. Those are found by walking back from
+/// the call along the chain of unique predecessors, keeping the first record
+/// found for each variable. At a merge point the incoming paths may disagree,
+/// so the walk stops there: a variable it has not resolved gets no location in
+/// the new function, which is better than a wrong one.
+static void
+collectLiveInDbgRecords(CallInst &TheCall, const SetVector<Value *> &Inputs,
+                        SmallSetVector<DbgVariableRecord *, 8> &LiveIn) {
+  Function *OldFunc = TheCall.getFunction();
+  for (Value *Input : Inputs) {
+    SmallVector<DbgVariableRecord *, 1> Users;
+    findDbgUsers(Input, Users);
+    for (DbgVariableRecord *DVR : Users)
+      if (isFunctionWideDbgRecord(*DVR) && DVR->getFunction() == OldFunc)
+        LiveIn.insert(DVR);
+  }
+
+  // Fragments of each variable already described closer to the call. A record
+  // is superseded if a later one covers an overlapping fragment.
+  DenseMap<std::pair<const DILocalVariable *, const DILocation *>,
+           SmallVector<DIExpression::FragmentInfo, 1>>
+      Described;
+  SmallVector<DbgVariableRecord *, 8> Values;
+  auto Visit = [&](DbgVariableRecord &DVR) {
+    DebugVariable Var(&DVR);
+    DIExpression::FragmentInfo Frag = Var.getFragmentOrDefault();
+    auto &Frags = Described[{Var.getVariable(), Var.getInlinedAt()}];
+    bool Superseded = any_of(Frags, [&](const DIExpression::FragmentInfo &F) {
+      return DIExpression::fragmentsOverlap(F, Frag);
+    });
+    Frags.push_back(Frag);
+    if (!Superseded)
+      Values.push_back(&DVR);
+  };
+
+  BasicBlock *BB = TheCall.getParent();
+  Instruction *From = &TheCall;
+  SmallPtrSet<BasicBlock *, 8> Visited{BB};
+  while (true) {
+    for (Instruction *I = From; I; I = I->getPrevNode())
+      for (DbgRecord &DR : reverse(I->getDbgRecordRange()))
+        if (auto *DVR = dyn_cast<DbgVariableRecord>(&DR))
+          if (!isFunctionWideDbgRecord(*DVR))
+            Visit(*DVR);
+    BB = BB->getUniquePredecessor();
+    if (!BB || !Visited.insert(BB).second)
+      break;
+    From = BB->getTerminator();
+  }
+  // Found nearest first; keep them in program order.
+  LiveIn.insert_range(reverse(Values));
+}
----------------
ojhunt wrote:

This loop is quadratic time - for each instruction you scan all of its predecessors, which includes prior instructions.

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


More information about the llvm-commits mailing list