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][SimpleLoopUnswitch] Fix missing debug location updates #97662

Merged
merged 2 commits into from
Jul 15, 2024

Conversation

Apochens
Copy link
Contributor

@Apochens Apochens commented Jul 4, 2024

Fix #97559 .

For the change at line 1253, I propagate the debug location of the terminator (i.e., the insertion point) to the new phi. because MergeBB is generated by splitting ExitBB several lines above, it only has the terminator, which could provide a reasonable debug location.

For the change at line 2348, I switch the order of moving and cloning TI. Because NewTI cloned from TI is inserted into the original place where TI is, NewTI should preserve the origianl debug location. At the same time, doing this allows us to propagate the debug location to the new branch instruction replacing NewTI (the change at line 2446).

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 4, 2024

@llvm/pr-subscribers-debuginfo

Author: Shan Huang (Apochens)

Changes

Fix #97559 .

For the change at line 1253, I propagate the debug location of the terminator (i.e., the insertion point) to the new phi. because MergeBB is generated by splitting ExitBB several lines above, it only has the terminator, which could provide a reasonable debug location.

For the change at line 2348, I switch the order of moving and cloning TI. Because NewTI cloned from TI is inserted into the original place where TI is, NewTI should preserve the origianl debug location. At the same time, doing this allows us to propagate the debug location to the new branch instruction replacing NewTI (the change at line 2446).


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp (+20-10)
  • (added) llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll (+75)
diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index fdb3211b4a438..caa1f7a156a3f 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -1245,9 +1245,12 @@ static BasicBlock *buildClonedLoopBlocks(
       if (SE && isa<PHINode>(I))
         SE->forgetValue(&I);
 
+      Instruction *InsertPt = &*MergeBB->getFirstInsertionPt();
+
       auto *MergePN =
           PHINode::Create(I.getType(), /*NumReservedValues*/ 2, ".us-phi");
-      MergePN->insertBefore(MergeBB->getFirstInsertionPt());
+      MergePN->insertBefore(InsertPt);
+      MergePN->setDebugLoc(InsertPt->getDebugLoc());
       I.replaceAllUsesWith(MergePN);
       MergePN->addIncoming(&I, ExitBB);
       MergePN->addIncoming(&ClonedI, ClonedExitBB);
@@ -1306,8 +1309,9 @@ static BasicBlock *buildClonedLoopBlocks(
   else if (auto *SI = dyn_cast<SwitchInst>(ClonedTerminator))
     ClonedConditionToErase = SI->getCondition();
 
+  Instruction *BI = BranchInst::Create(ClonedSuccBB, ClonedParentBB);
+  BI->setDebugLoc(ClonedTerminator->getDebugLoc());
   ClonedTerminator->eraseFromParent();
-  BranchInst::Create(ClonedSuccBB, ClonedParentBB);
 
   if (ClonedConditionToErase)
     RecursivelyDeleteTriviallyDeadInstructions(ClonedConditionToErase, nullptr,
@@ -2334,14 +2338,15 @@ static void unswitchNontrivialInvariants(
   // nuke the initial terminator placed in the split block.
   SplitBB->getTerminator()->eraseFromParent();
   if (FullUnswitch) {
-    // Splice the terminator from the original loop and rewrite its
-    // successors.
-    TI.moveBefore(*SplitBB, SplitBB->end());
-
     // Keep a clone of the terminator for MSSA updates.
     Instruction *NewTI = TI.clone();
     NewTI->insertInto(ParentBB, ParentBB->end());
 
+    // Splice the terminator from the original loop and rewrite its
+    // successors.
+    TI.moveBefore(*SplitBB, SplitBB->end());
+    TI.dropLocation();
+
     // First wire up the moved terminator to the preheaders.
     if (BI) {
       BasicBlock *ClonedPH = ClonedPHs.begin()->second;
@@ -2349,6 +2354,9 @@ static void unswitchNontrivialInvariants(
       BI->setSuccessor(1 - ClonedSucc, LoopPH);
       Value *Cond = skipTrivialSelect(BI->getCondition());
       if (InsertFreeze)
+        // We don't give any debug location to the new freeze, because the
+        // BI (`dyn_cast<BranchInst>(TI)`) is an in-loop instruction hoisted
+        // out of the loop.
         Cond = new FreezeInst(Cond, Cond->getName() + ".fr", BI->getIterator());
       BI->setCondition(Cond);
       DTUpdates.push_back({DominatorTree::Insert, SplitBB, ClonedPH});
@@ -2432,12 +2440,13 @@ static void unswitchNontrivialInvariants(
         DTUpdates.push_back({DominatorTree::Delete, ParentBB, SuccBB});
     }
 
-    // After MSSAU update, remove the cloned terminator instruction NewTI.
-    ParentBB->getTerminator()->eraseFromParent();
-
     // Create a new unconditional branch to the continuing block (as opposed to
     // the one cloned).
-    BranchInst::Create(RetainedSuccBB, ParentBB);
+    Instruction *NewBI = BranchInst::Create(RetainedSuccBB, ParentBB);
+    NewBI->setDebugLoc(NewTI->getDebugLoc());
+
+    // After MSSAU update, remove the cloned terminator instruction NewTI.
+    NewTI->eraseFromParent();
   } else {
     assert(BI && "Only branches have partial unswitching.");
     assert(UnswitchedSuccBBs.size() == 1 &&
@@ -2710,6 +2719,7 @@ static BranchInst *turnSelectIntoBranch(SelectInst *SI, DominatorTree &DT,
       PHINode::Create(SI->getType(), 2, "unswitched.select", SI->getIterator());
   Phi->addIncoming(SI->getTrueValue(), ThenBB);
   Phi->addIncoming(SI->getFalseValue(), HeadBB);
+  Phi->setDebugLoc(SI->getDebugLoc());
   SI->replaceAllUsesWith(Phi);
   SI->eraseFromParent();
 
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll b/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll
new file mode 100644
index 0000000000000..bdb97ff220608
--- /dev/null
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll
@@ -0,0 +1,75 @@
+; RUN: opt -passes='simple-loop-unswitch<nontrivial>' -S < %s | FileCheck %s
+
+define i32 @basic(i32 %N, i1 %cond, i32 %select_input) !dbg !5 {
+; CHECK-LABEL: define i32 @basic(
+
+; Check that SimpleLoopUnswitch's unswitchNontrivialInvariants() drops the
+; debug location of the hoisted terminator and doesn't give any debug location
+; to the new freeze, since it's inserted in a hoist block.
+; Also check that in unswitchNontrivialInvariants(), the new br instruction
+; inherits the debug location of the old terminator in the same block.
+
+; CHECK:       entry:
+; CHECK-NEXT:    [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
+; CHECK-NEXT:    br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
+; CHECK:       for.body.us:
+; CHECK-NEXT:    br label %0, !dbg [[DBG13:![0-9]+]]
+
+; Check that in turnSelectIntoBranch(), the new phi inherits the debug
+; location of the old select instruction replaced.
+
+; CHECK:       1:
+; CHECK-NEXT:    [[UNSWITCHED_SELECT_US:%.*]] = phi i32 [ [[SELECT_INPUT:%.*]], %0 ], !dbg [[DBG13]]
+
+; Check that in BuildClonedLoopBlocks(), the new phi inherits the debug
+; location of the instruction at the insertion point and the new br
+; instruction inherits the debug location of the old terminator.
+
+; CHECK:       for.body:
+; CHECK-NEXT:    br label %2, !dbg [[DBG13]]
+; CHECK:       for.cond.cleanup:
+; CHECK:         [[DOTUS_PHI:%.*]] = phi i32 [ [[RES_LCSSA:%.*]], %[[FOR_COND_CLEANUP_SPLIT:.*]] ], [ [[RES_LCSSA_US:%.*]], %[[FOR_COND_CLEANUP_SPLIT_US:.*]] ], !dbg [[DBG17:![0-9]+]]
+entry:
+  br label %for.cond, !dbg !8
+
+for.cond:                                         ; preds = %for.body, %entry
+  %res = phi i32 [ 0, %entry ], [ %add, %for.body ], !dbg !9
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ], !dbg !10
+  %cmp = icmp slt i32 %i, %N, !dbg !11
+  br i1 %cmp, label %for.body, label %for.cond.cleanup, !dbg !12
+
+for.body:                                         ; preds = %for.cond
+  %cond1 = select i1 %cond, i32 %select_input, i32 42, !dbg !13
+  %add = add nuw nsw i32 %cond1, %res, !dbg !14
+  %inc = add nuw nsw i32 %i, 1, !dbg !15
+  br label %for.cond, !dbg !16
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  ret i32 %res, !dbg !17
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.debugify = !{!2, !3}
+!llvm.module.flags = !{!4}
+
+; CHECK: [[DBG13]] = !DILocation(line: 6,
+; CHECK: [[DBG17]] = !DILocation(line: 10,
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "main.ll", directory: "/")
+!2 = !{i32 10}
+!3 = !{i32 0}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "basic", linkageName: "basic", scope: null, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!6 = !DISubroutineType(types: !7)
+!7 = !{}
+!8 = !DILocation(line: 1, column: 1, scope: !5)
+!9 = !DILocation(line: 2, column: 1, scope: !5)
+!10 = !DILocation(line: 3, column: 1, scope: !5)
+!11 = !DILocation(line: 4, column: 1, scope: !5)
+!12 = !DILocation(line: 5, column: 1, scope: !5)
+!13 = !DILocation(line: 6, column: 1, scope: !5)
+!14 = !DILocation(line: 7, column: 1, scope: !5)
+!15 = !DILocation(line: 8, column: 1, scope: !5)
+!16 = !DILocation(line: 9, column: 1, scope: !5)
+!17 = !DILocation(line: 10, column: 1, scope: !5)

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 4, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Shan Huang (Apochens)

Changes

Fix #97559 .

For the change at line 1253, I propagate the debug location of the terminator (i.e., the insertion point) to the new phi. because MergeBB is generated by splitting ExitBB several lines above, it only has the terminator, which could provide a reasonable debug location.

For the change at line 2348, I switch the order of moving and cloning TI. Because NewTI cloned from TI is inserted into the original place where TI is, NewTI should preserve the origianl debug location. At the same time, doing this allows us to propagate the debug location to the new branch instruction replacing NewTI (the change at line 2446).


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp (+20-10)
  • (added) llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll (+75)
diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index fdb3211b4a438..caa1f7a156a3f 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -1245,9 +1245,12 @@ static BasicBlock *buildClonedLoopBlocks(
       if (SE && isa<PHINode>(I))
         SE->forgetValue(&I);
 
+      Instruction *InsertPt = &*MergeBB->getFirstInsertionPt();
+
       auto *MergePN =
           PHINode::Create(I.getType(), /*NumReservedValues*/ 2, ".us-phi");
-      MergePN->insertBefore(MergeBB->getFirstInsertionPt());
+      MergePN->insertBefore(InsertPt);
+      MergePN->setDebugLoc(InsertPt->getDebugLoc());
       I.replaceAllUsesWith(MergePN);
       MergePN->addIncoming(&I, ExitBB);
       MergePN->addIncoming(&ClonedI, ClonedExitBB);
@@ -1306,8 +1309,9 @@ static BasicBlock *buildClonedLoopBlocks(
   else if (auto *SI = dyn_cast<SwitchInst>(ClonedTerminator))
     ClonedConditionToErase = SI->getCondition();
 
+  Instruction *BI = BranchInst::Create(ClonedSuccBB, ClonedParentBB);
+  BI->setDebugLoc(ClonedTerminator->getDebugLoc());
   ClonedTerminator->eraseFromParent();
-  BranchInst::Create(ClonedSuccBB, ClonedParentBB);
 
   if (ClonedConditionToErase)
     RecursivelyDeleteTriviallyDeadInstructions(ClonedConditionToErase, nullptr,
@@ -2334,14 +2338,15 @@ static void unswitchNontrivialInvariants(
   // nuke the initial terminator placed in the split block.
   SplitBB->getTerminator()->eraseFromParent();
   if (FullUnswitch) {
-    // Splice the terminator from the original loop and rewrite its
-    // successors.
-    TI.moveBefore(*SplitBB, SplitBB->end());
-
     // Keep a clone of the terminator for MSSA updates.
     Instruction *NewTI = TI.clone();
     NewTI->insertInto(ParentBB, ParentBB->end());
 
+    // Splice the terminator from the original loop and rewrite its
+    // successors.
+    TI.moveBefore(*SplitBB, SplitBB->end());
+    TI.dropLocation();
+
     // First wire up the moved terminator to the preheaders.
     if (BI) {
       BasicBlock *ClonedPH = ClonedPHs.begin()->second;
@@ -2349,6 +2354,9 @@ static void unswitchNontrivialInvariants(
       BI->setSuccessor(1 - ClonedSucc, LoopPH);
       Value *Cond = skipTrivialSelect(BI->getCondition());
       if (InsertFreeze)
+        // We don't give any debug location to the new freeze, because the
+        // BI (`dyn_cast<BranchInst>(TI)`) is an in-loop instruction hoisted
+        // out of the loop.
         Cond = new FreezeInst(Cond, Cond->getName() + ".fr", BI->getIterator());
       BI->setCondition(Cond);
       DTUpdates.push_back({DominatorTree::Insert, SplitBB, ClonedPH});
@@ -2432,12 +2440,13 @@ static void unswitchNontrivialInvariants(
         DTUpdates.push_back({DominatorTree::Delete, ParentBB, SuccBB});
     }
 
-    // After MSSAU update, remove the cloned terminator instruction NewTI.
-    ParentBB->getTerminator()->eraseFromParent();
-
     // Create a new unconditional branch to the continuing block (as opposed to
     // the one cloned).
-    BranchInst::Create(RetainedSuccBB, ParentBB);
+    Instruction *NewBI = BranchInst::Create(RetainedSuccBB, ParentBB);
+    NewBI->setDebugLoc(NewTI->getDebugLoc());
+
+    // After MSSAU update, remove the cloned terminator instruction NewTI.
+    NewTI->eraseFromParent();
   } else {
     assert(BI && "Only branches have partial unswitching.");
     assert(UnswitchedSuccBBs.size() == 1 &&
@@ -2710,6 +2719,7 @@ static BranchInst *turnSelectIntoBranch(SelectInst *SI, DominatorTree &DT,
       PHINode::Create(SI->getType(), 2, "unswitched.select", SI->getIterator());
   Phi->addIncoming(SI->getTrueValue(), ThenBB);
   Phi->addIncoming(SI->getFalseValue(), HeadBB);
+  Phi->setDebugLoc(SI->getDebugLoc());
   SI->replaceAllUsesWith(Phi);
   SI->eraseFromParent();
 
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll b/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll
new file mode 100644
index 0000000000000..bdb97ff220608
--- /dev/null
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/preserving-dropping-debugloc-nontrivial.ll
@@ -0,0 +1,75 @@
+; RUN: opt -passes='simple-loop-unswitch<nontrivial>' -S < %s | FileCheck %s
+
+define i32 @basic(i32 %N, i1 %cond, i32 %select_input) !dbg !5 {
+; CHECK-LABEL: define i32 @basic(
+
+; Check that SimpleLoopUnswitch's unswitchNontrivialInvariants() drops the
+; debug location of the hoisted terminator and doesn't give any debug location
+; to the new freeze, since it's inserted in a hoist block.
+; Also check that in unswitchNontrivialInvariants(), the new br instruction
+; inherits the debug location of the old terminator in the same block.
+
+; CHECK:       entry:
+; CHECK-NEXT:    [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
+; CHECK-NEXT:    br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
+; CHECK:       for.body.us:
+; CHECK-NEXT:    br label %0, !dbg [[DBG13:![0-9]+]]
+
+; Check that in turnSelectIntoBranch(), the new phi inherits the debug
+; location of the old select instruction replaced.
+
+; CHECK:       1:
+; CHECK-NEXT:    [[UNSWITCHED_SELECT_US:%.*]] = phi i32 [ [[SELECT_INPUT:%.*]], %0 ], !dbg [[DBG13]]
+
+; Check that in BuildClonedLoopBlocks(), the new phi inherits the debug
+; location of the instruction at the insertion point and the new br
+; instruction inherits the debug location of the old terminator.
+
+; CHECK:       for.body:
+; CHECK-NEXT:    br label %2, !dbg [[DBG13]]
+; CHECK:       for.cond.cleanup:
+; CHECK:         [[DOTUS_PHI:%.*]] = phi i32 [ [[RES_LCSSA:%.*]], %[[FOR_COND_CLEANUP_SPLIT:.*]] ], [ [[RES_LCSSA_US:%.*]], %[[FOR_COND_CLEANUP_SPLIT_US:.*]] ], !dbg [[DBG17:![0-9]+]]
+entry:
+  br label %for.cond, !dbg !8
+
+for.cond:                                         ; preds = %for.body, %entry
+  %res = phi i32 [ 0, %entry ], [ %add, %for.body ], !dbg !9
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ], !dbg !10
+  %cmp = icmp slt i32 %i, %N, !dbg !11
+  br i1 %cmp, label %for.body, label %for.cond.cleanup, !dbg !12
+
+for.body:                                         ; preds = %for.cond
+  %cond1 = select i1 %cond, i32 %select_input, i32 42, !dbg !13
+  %add = add nuw nsw i32 %cond1, %res, !dbg !14
+  %inc = add nuw nsw i32 %i, 1, !dbg !15
+  br label %for.cond, !dbg !16
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  ret i32 %res, !dbg !17
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.debugify = !{!2, !3}
+!llvm.module.flags = !{!4}
+
+; CHECK: [[DBG13]] = !DILocation(line: 6,
+; CHECK: [[DBG17]] = !DILocation(line: 10,
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "main.ll", directory: "/")
+!2 = !{i32 10}
+!3 = !{i32 0}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "basic", linkageName: "basic", scope: null, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!6 = !DISubroutineType(types: !7)
+!7 = !{}
+!8 = !DILocation(line: 1, column: 1, scope: !5)
+!9 = !DILocation(line: 2, column: 1, scope: !5)
+!10 = !DILocation(line: 3, column: 1, scope: !5)
+!11 = !DILocation(line: 4, column: 1, scope: !5)
+!12 = !DILocation(line: 5, column: 1, scope: !5)
+!13 = !DILocation(line: 6, column: 1, scope: !5)
+!14 = !DILocation(line: 7, column: 1, scope: !5)
+!15 = !DILocation(line: 8, column: 1, scope: !5)
+!16 = !DILocation(line: 9, column: 1, scope: !5)
+!17 = !DILocation(line: 10, column: 1, scope: !5)

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.

This patch involves some slightly more complex updates than other recent fixes so I've just got a few questions inline to make sure I understand the it and to check that we're doing the right thing.

@@ -1245,9 +1245,12 @@ static BasicBlock *buildClonedLoopBlocks(
if (SE && isa<PHINode>(I))
SE->forgetValue(&I);

Instruction *InsertPt = &*MergeBB->getFirstInsertionPt();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we keep this as an iterator rather than dereference it? It has variable location implications in Instruction::insertBefore (more info here -TL;DR, there's an extra bit in iterators that determines whether the iterator points before this instruction only or additionally the debug records attached to it too).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, thanks for pointing out this. I'll fix it.

// Splice the terminator from the original loop and rewrite its
// successors.
TI.moveBefore(*SplitBB, SplitBB->end());
TI.dropLocation();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why move this code down from where it was before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TI is moved out of the loop and its debug location should be dropped. However, NewTI (the code moved up) which is cloned from TI should preserve TI's debugl location, because NewTI is inserted into the original place where TI is at. In addition, NewTI will be replaced by a new branch instruction, which should inherit the debug location of NewTI.

So, I move the code down after the clone to preserve the debug location so that NewTI can preserve the right debug location while the debug location of TI can be correctly dropped.

// First wire up the moved terminator to the preheaders.
if (BI) {
BasicBlock *ClonedPH = ClonedPHs.begin()->second;
BI->setSuccessor(ClonedSucc, ClonedPH);
BI->setSuccessor(1 - ClonedSucc, LoopPH);
Value *Cond = skipTrivialSelect(BI->getCondition());
if (InsertFreeze)
// We don't give any debug location to the new freeze, because the
// BI (`dyn_cast<BranchInst>(TI)`) is an in-loop instruction hoisted
// out of the loop.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Please either add braces around this or hoist the comment out the if statement (see second para here).


; CHECK: entry:
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
; CHECK-NEXT: br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Glancing at the IR, the old behaviour (new cond branch gets uncond branch location) looks fine, but I think this change does make sense - just to make sure I'm reading this right, is the argument here that the conditional branch above is essentially a select from within the loop that's been hoisted out? And that in this case the new branch would get the select's source location, but because it has been hoisted out the loop we should actually drop it.

Copy link
Contributor Author

@Apochens Apochens Jul 8, 2024

Choose a reason for hiding this comment

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

This dropping is corresponding to the newly added update TI.dropLocation(). I traced the origin of TI:

(1) it is the argument of function unswitchNontrivialInvariants();

static void unswitchNontrivialInvariants(
    Loop &L, Instruction &TI, ...)

(2) The function unswitchNontrivialInvariants() is called in function unswitchBestCondition(), and the TI argument takes *Best.TI. Best is the result of the function call to findBestNonTrivialUnswitchCandidate(), which return the best candidate from UnswitchCandidates. And UnswitchCandidates are collected by calling the function collectUnswitchCandidates();

static bool unswitchBestCondition(...) {
    ...
    collectUnswitchCandidates(UnswitchCandidates, ...);
    ...
    NonTrivialUnswitchCandidate Best = 
        findBestNonTrivialUnswitchCandidate(UnswitchCandidates, ...);
    ...
->  unswitchNontrivialInvariants(L, *Best.TI, ... );
}

(3) In function collectUnswitchCandidates(), instructions satisfying certain conditions will be added into UnswitchCandidates. According to the code, instructions added into UnswitchCandidates are all in the loop, including instructions in the header of the loop (the code it too long to paste it here, see L2912-2964).

To sum up, the conditional branch is essentially the hoisted instruction in the loop (it could be a select, a branch, or a switch according to the code).

Glancing at the IR, the old behaviour (new cond branch gets uncond branch location) looks fine

Acctually, the new cond branch did not get the location of uncond branch. Before the dropping, there is no location propagation from the uncond branch to the new cond branch. See the following snippets (godbolt). (Hope I understand the old branch right.)

entry:
  br label %for.cond, !dbg !8

!8 = !DILocation(line: 1, column: 1, scope: !5)
entry:
  %cond.fr = freeze i1 %cond
  br i1 %cond.fr, label %entry.split.us, label %entry.split, !dbg !8

!8 = !DILocation(line: 6, column: 1, scope: !5)

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for digging in and confirming - this all makes sense and sounds right to me.

Just one thing:

Actually, the new cond branch did not get the location of uncond branch. Before the dropping, there is no location propagation from the uncond branch to the new cond branch. See the following snippets (godbolt). (Hope I understand the old branch right.)

In the two snippets you posted at the end of the last comment it looks like the unconditional branch's location !8 (br label %for.cond, !dbg !8) gets propagated to the conditional branch br i1 %cond.fr, label %entry.split.us, label %entry.split, !dbg !8 which seems to oppose what you're saying - I must've missed something here, are we talking about a different unconditional branch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I think we are talking about the same unconditional branch and I did not state it clearly. The conditional branch in the second code snippet just has the same number of !dbg with the unconditional branch in the first code snippet, i.e., !dbg !8. However, !dbg !8 in the first snippet points to line 1 while it points to line 6 in the second snippet. So, the debug location of the unconditional branch did not get propagated to the conditional branch (they just have the same metadata number).

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh that makes sense, thanks for the clarification.

; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
; CHECK-NEXT: br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
; CHECK: for.body.us:
; CHECK-NEXT: br label %0, !dbg [[DBG13:![0-9]+]]
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not 100% what's the correct thing to do for this one - if I'm reading it right this source location is from the select. What's the thinking behind you change for this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This debug location comes from the debug location update:

  Instruction *BI = BranchInst::Create(ClonedSuccBB, ClonedParentBB);
  BI->setDebugLoc(ClonedTerminator->getDebugLoc());
  ClonedTerminator->eraseFromParent();

In the update, BI points the new br instruction. So, the debug location of select is propagated from ClonedTerminator to BI. To illustrate the instruction that ClonedTerminator points to, I paste the IR program before the remove of ClonedTerminator in the above code:

define i32 @basic(i32 %N, i1 %cond, i32 %select_input) !dbg !5 {
entry:
  br label %entry.split, !dbg !8

entry.split.us:                                   ; No predecessors!
  br label %for.cond.us, !dbg !8

for.cond.us:                                      ; preds = %1, %entry.split.us
  %res.us = phi i32 [ 0, %entry.split.us ], [ %add.us, %1 ], !dbg !9
  %i.us = phi i32 [ 0, %entry.split.us ], [ %inc.us, %1 ], !dbg !10
  %cmp.us = icmp slt i32 %i.us, %N, !dbg !11
  br i1 %cmp.us, label %for.body.us, label %for.cond.cleanup.split.us, !dbg !12

for.body.us:                                      ; preds = %for.cond.us
->br i1 %cond, label %0, label %1, !dbg !13    (ClonedTerminator)
  br label %0, !dbg !13.                                       (BI)

0:                                                ; preds = %for.body.us, %for.body.us
  br label %1, !dbg !13

1:                                                ; preds = %0, %for.body.us
  %unswitched.select.us = phi i32 [ %select_input, %0 ], !dbg !13
  %add.us = add nuw nsw i32 %unswitched.select.us, %res.us, !dbg !14
  %inc.us = add nuw nsw i32 %i.us, 1, !dbg !15
  br label %for.cond.us, !dbg !16

for.cond.cleanup.split.us:                        ; preds = %for.cond.us
  ...
}

I just propagate the debug location of the instruction pointed by ClonedTerminator to BI, and the reason why ClonedTerminator has the debug location of the select could be resulted by spliting the cloned for body (as the contents of block %1 is actually the contents of %for.body).

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, thanks, yep that sounds ok.

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, thanks for your patience and detailed explanations.


; CHECK: entry:
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
; CHECK-NEXT: br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh that makes sense, thanks for the clarification.

; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND:%.*]]{{$}}
; CHECK-NEXT: br i1 [[COND_FR]], label %[[ENTRY_SPLIT_US:.*]], label %[[ENTRY_SPLIT:.*]]{{$}}
; CHECK: for.body.us:
; CHECK-NEXT: br label %0, !dbg [[DBG13:![0-9]+]]
Copy link
Contributor

Choose a reason for hiding this comment

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

I see, thanks, yep that sounds ok.

@Apochens Apochens merged commit 33bdb87 into llvm:main Jul 15, 2024
7 checks passed
@Apochens Apochens deleted the #97559_simpleloopunswitch_part_1 branch July 15, 2024 01:46
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.

[DebugInfo][SimpleLoopUnswitch] Missing debug location updates (Part 1)
3 participants