Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DebugInfo][RemoveDIs] Instrument loop-deletion for DPValues #73042

Merged
merged 3 commits into from
Nov 23, 2023

Conversation

jmorse
Copy link
Member

@jmorse jmorse commented Nov 21, 2023

Loop deletion identifies dbg.value intrinsics in the loop, sets them to undef/poison, and sinks them to the exit of the loop, to ensure that any variable assignments that happen in a deleted loop are "optimised out". This needs to be replicated for DPValues, the non-instruction replacement for dbg.value intrinsics.

The movement API for DPValues is (deliberately) more limited than dbg.values, which is tricky because inserting the collection of dbg.values at an arbitary iterator can insert a dbg.value in the middle of a sequence of dbg.values. A big no-no for DPValues. This patch replicates the order by inserting DPValues in reverse at the head-iterator of the block, to ensure the same output as dbg.value mode. Technically the order isn't important, but we're trying to ensure identical outputs from optimisation passes right now.

Add more CHECK lines for dbg.values in diundef.ll to ensure that we don't create any spurious dbg.values, and to ensure that sequences of dbg.values come out of the optimisation in the correct order.

Loop deletion identifies dbg.value intrinsics in the loop, sets them to
undef/poison, and sinks them to the exit of the loop, to ensure that any
variable assignments that happen in a deleted loop are "optimised out".
This needs to be replicated for DPValues, the non-instruction replacement
for dbg.value intrinsics.

The movement API for DPValues is (deliberately) more limited than
dbg.values, which is tricky because inserting the collection of dbg.values
at an arbitary iterator can insert a dbg.value in the middle of a sequence
of dbg.values. This patch replicates the order by inserting DPValues in
reverse at the head-iterator of the block, to ensure the same output as
dbg.value mode. Technically the order isn't important, but we're trying to
ensure identical outputs from optimisation passes right now.

Add more CHECK lines for dbg.values in diundef.ll to ensure that we don't
create any spurious dbg.values, and to ensure that sequences of dbg.values
come out of the optimisation in the correct order.
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 21, 2023

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-debuginfo

Author: Jeremy Morse (jmorse)

Changes

Loop deletion identifies dbg.value intrinsics in the loop, sets them to undef/poison, and sinks them to the exit of the loop, to ensure that any variable assignments that happen in a deleted loop are "optimised out". This needs to be replicated for DPValues, the non-instruction replacement for dbg.value intrinsics.

The movement API for DPValues is (deliberately) more limited than dbg.values, which is tricky because inserting the collection of dbg.values at an arbitary iterator can insert a dbg.value in the middle of a sequence of dbg.values. A big no-no for DPValues. This patch replicates the order by inserting DPValues in reverse at the head-iterator of the block, to ensure the same output as dbg.value mode. Technically the order isn't important, but we're trying to ensure identical outputs from optimisation passes right now.

Add more CHECK lines for dbg.values in diundef.ll to ensure that we don't create any spurious dbg.values, and to ensure that sequences of dbg.values come out of the optimisation in the correct order.


Full diff: https://github.com/llvm/llvm-project/pull/73042.diff

4 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/LoopUtils.cpp (+31-3)
  • (modified) llvm/test/Transforms/LoopDeletion/crashbc.ll (+1)
  • (modified) llvm/test/Transforms/LoopDeletion/diundef.ll (+15-3)
  • (modified) llvm/test/Transforms/LoopDeletion/over-defensive-undefing-dbg-values.ll (+1)
diff --git a/llvm/lib/Transforms/Utils/LoopUtils.cpp b/llvm/lib/Transforms/Utils/LoopUtils.cpp
index 8bb4e17b99dd629..bf1e37a97c2d542 100644
--- a/llvm/lib/Transforms/Utils/LoopUtils.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUtils.cpp
@@ -604,6 +604,7 @@ void llvm::deleteDeadLoop(Loop *L, DominatorTree *DT, ScalarEvolution *SE,
   // Use a map to unique and a vector to guarantee deterministic ordering.
   llvm::SmallDenseSet<DebugVariable, 4> DeadDebugSet;
   llvm::SmallVector<DbgVariableIntrinsic *, 4> DeadDebugInst;
+  llvm::SmallVector<DPValue *, 4> DeadDPValues;
 
   if (ExitBlock) {
     // Given LCSSA form is satisfied, we should not have users of instructions
@@ -628,6 +629,24 @@ void llvm::deleteDeadLoop(Loop *L, DominatorTree *DT, ScalarEvolution *SE,
                    "Unexpected user in reachable block");
           U.set(Poison);
         }
+
+        // RemoveDIs: do the same as below for DPValues.
+        if (Block->IsNewDbgInfoFormat) {
+          for (DPValue &DPV : llvm::make_early_inc_range(I.getDbgValueRange())) {
+            auto Key =
+                DeadDebugSet.find(DebugVariable(DPV.getVariable(), DPV.getExpression(), nullptr));
+            if (Key != DeadDebugSet.end())
+              continue;
+            // Unlinks the DPV from it's container, for later insertion.
+            DPV.removeFromParent();
+            DeadDebugSet.insert(DebugVariable(DPV.getVariable(), DPV.getExpression(), nullptr));
+            DeadDPValues.push_back(&DPV);
+          }
+        }
+
+        // For one of each variable encountered, preserve a debug intrinsic (set
+        // to Poison) and transfer it to the loop exit. This terminates any
+        // variable locations that were set during the loop.
         auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I);
         if (!DVI)
           continue;
@@ -642,12 +661,21 @@ void llvm::deleteDeadLoop(Loop *L, DominatorTree *DT, ScalarEvolution *SE,
     // be be replaced with undef. Loop invariant values will still be available.
     // Move dbg.values out the loop so that earlier location ranges are still
     // terminated and loop invariant assignments are preserved.
-    Instruction *InsertDbgValueBefore = ExitBlock->getFirstNonPHI();
-    assert(InsertDbgValueBefore &&
+    DIBuilder DIB(*ExitBlock->getModule());
+    BasicBlock::iterator InsertDbgValueBefore = ExitBlock->getFirstInsertionPt();
+    assert(InsertDbgValueBefore != ExitBlock->end() &&
            "There should be a non-PHI instruction in exit block, else these "
            "instructions will have no parent.");
+
     for (auto *DVI : DeadDebugInst)
-      DVI->moveBefore(InsertDbgValueBefore);
+      DVI->moveBefore(*ExitBlock, InsertDbgValueBefore);
+
+    // Due to the "head" bit in BasicBlock::iterator, we're going to insert
+    // each DPValue right at the start of the block, wheras dbg.values would be
+    // repeatedly inserted before the first instruction. To replicate this
+    // behaviour, do it backwards.
+    for (DPValue *DPV : llvm::reverse(DeadDPValues))
+      ExitBlock->insertDPValueBefore(DPV, InsertDbgValueBefore);
   }
 
   // Remove the block from the reference counting scheme, so that we can
diff --git a/llvm/test/Transforms/LoopDeletion/crashbc.ll b/llvm/test/Transforms/LoopDeletion/crashbc.ll
index 2ba5e2ca902a194..c01453bbda81710 100644
--- a/llvm/test/Transforms/LoopDeletion/crashbc.ll
+++ b/llvm/test/Transforms/LoopDeletion/crashbc.ll
@@ -1,5 +1,6 @@
 ; Make sure we don't crash when writing bitcode.
 ; RUN: opt < %s -passes=loop-deletion -o /dev/null
+; RUN: opt < %s -passes=loop-deletion -o /dev/null --try-experimental-debuginfo-iterators
 
 define void @f() {
   br label %bb1
diff --git a/llvm/test/Transforms/LoopDeletion/diundef.ll b/llvm/test/Transforms/LoopDeletion/diundef.ll
index 14a6e401f9c6f15..7b6178bcc2ae3dd 100644
--- a/llvm/test/Transforms/LoopDeletion/diundef.ll
+++ b/llvm/test/Transforms/LoopDeletion/diundef.ll
@@ -1,4 +1,5 @@
-; RUN: opt %s -passes=loop-deletion -S | FileCheck %s
+; RUN: opt %s -passes=loop-deletion -S | FileCheck %s --implicit-check-not=dbg.value
+; RUN: opt %s -passes=loop-deletion -S --try-experimental-debuginfo-iterators | FileCheck %s --implicit-check-not=dbg.value
 
 target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-apple-macosx10.14.0"
@@ -6,6 +7,12 @@ target triple = "x86_64-apple-macosx10.14.0"
 @a = common local_unnamed_addr global i32 0, align 4, !dbg !0
 
 define i32 @b() local_unnamed_addr !dbg !12 {
+; CHECK-LABEL: entry
+; CHECK:       call void @llvm.dbg.value(metadata i32 0, metadata ![[IVAR:[0-9]+]],
+; CHECK-LABEL: for.end:
+; CHECK-NEXT:  call void @llvm.dbg.value(metadata i32 undef, metadata ![[IVAR]], metadata !DIExpression()), !dbg !17
+; CHECK-NEXT:  call void @llvm.dbg.value(metadata i32 undef, metadata ![[JVAR:[0-9]+]], metadata !DIExpression()), !dbg !17
+; CHECK-NEXT:  %call = tail call i32 {{.*}} @patatino()
 entry:
   call void @llvm.dbg.value(metadata i32 0, metadata !16, metadata !DIExpression()), !dbg !17
   br label %for.cond, !dbg !18
@@ -15,11 +22,10 @@ for.cond:                                         ; preds = %for.cond, %entry
   call void @llvm.dbg.value(metadata i32 %i.0, metadata !16, metadata !DIExpression()), !dbg !17
   %inc = add nuw nsw i32 %i.0, 1, !dbg !21
   call void @llvm.dbg.value(metadata i32 %inc, metadata !16, metadata !DIExpression()), !dbg !17
+  call void @llvm.dbg.value(metadata i32 %inc, metadata !37, metadata !DIExpression()), !dbg !17
   %exitcond = icmp ne i32 %inc, 3, !dbg !23
   br i1 %exitcond, label %for.cond, label %for.end, !dbg !24, !llvm.loop !25
 
-; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata !16, metadata !DIExpression()), !dbg !17
-; CHECK-NEXT: %call = tail call i32 {{.*}} @patatino()
 for.end:                                          ; preds = %for.cond
   %call = tail call i32 (...) @patatino() #3, !dbg !27
   %0 = load i32, ptr @a, align 4, !dbg !28
@@ -34,6 +40,11 @@ entry:
   ret i32 0, !dbg !36
 }
 
+; CHECK: declare void @llvm.dbg.value(metadata,
+
+; CHECK: ![[IVAR]] = !DILocalVariable(name: "i",
+; CHECK: ![[JVAR]] = !DILocalVariable(name: "j",
+
 declare void @llvm.dbg.value(metadata, metadata, metadata)
 
 !llvm.dbg.cu = !{!2}
@@ -73,3 +84,4 @@ declare void @llvm.dbg.value(metadata, metadata, metadata)
 !34 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 9, type: !13, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
 !35 = !DILocation(line: 9, column: 14, scope: !34)
 !36 = !DILocation(line: 9, column: 19, scope: !34)
+!37 = !DILocalVariable(name: "j", scope: !12, file: !3, line: 3, type: !6)
diff --git a/llvm/test/Transforms/LoopDeletion/over-defensive-undefing-dbg-values.ll b/llvm/test/Transforms/LoopDeletion/over-defensive-undefing-dbg-values.ll
index 8eb1ef8ddd72a0a..6f71038a74672c4 100644
--- a/llvm/test/Transforms/LoopDeletion/over-defensive-undefing-dbg-values.ll
+++ b/llvm/test/Transforms/LoopDeletion/over-defensive-undefing-dbg-values.ll
@@ -1,4 +1,5 @@
 ; RUN: opt -S %s -passes=loop-deletion | FileCheck %s
+; RUN: opt -S %s -passes=loop-deletion --try-experimental-debuginfo-iterators | FileCheck %s
 
 ;; static int foo(int Param) __attribute__((always_inline));
 ;; static int foo(int Param) { return Param * Param * 2; }

Copy link

github-actions bot commented Nov 21, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@OCHyams OCHyams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (+ nit)

Comment on lines 636 to 643
auto Key =
DeadDebugSet.find(DebugVariable(DPV.getVariable(), DPV.getExpression(), nullptr));
if (Key != DeadDebugSet.end())
continue;
// Unlinks the DPV from it's container, for later insertion.
DPV.removeFromParent();
DeadDebugSet.insert(DebugVariable(DPV.getVariable(), DPV.getExpression(), nullptr));
DeadDPValues.push_back(&DPV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to use if(!DeadDebugSet.insert(...).second) continue;, as below in the dbg.value loop, instead of using find then insert?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that makes sense. It also looks like you fixed a past bug in 88d34d4 (only var/expr for uniqueness, no DILocation) and we haven't been noticing it when repeatedly rebasing these patches, I'll shoe-horn that in too.

// repeatedly inserted before the first instruction. To replicate this
// behaviour, do it backwards.
for (DPValue *DPV : llvm::reverse(DeadDPValues))
ExitBlock->insertDPValueBefore(DPV, InsertDbgValueBefore);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a shame we have to replicate weirdness. It would be ideal to fix the issue before hand, then apply the DPValue change to a good base. On the other hand, investigating every potential debug-info bug would probably be prohibitive to getting this finished.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas this is inherent in dbg.values having an absolute ordering and us trying to have completely identical outputs -- the reason why this reversal exists is that it can have a meaningful effect on output when building real codebases :(, and we've had to fix it up. This is something we can fix, but as part of a future "what happens when you jam two blocks of debug-info together" refactor.

@jmorse jmorse merged commit ce1b243 into llvm:main Nov 23, 2023
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants