[llvm] [DFAJumpThreading] Try harder to avoid cycles in paths. (PR #169151)

Usman Nadeem via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 22 09:55:35 PST 2025


https://github.com/UsmanNadeem updated https://github.com/llvm/llvm-project/pull/169151

>From eddb26adc25c8b9b289c6306913f7d883e55d6b4 Mon Sep 17 00:00:00 2001
From: Usman Nadeem <mnadeem at qti.qualcomm.com>
Date: Fri, 21 Nov 2025 23:19:45 -0800
Subject: [PATCH 1/3] Precommit tests.

Change-Id: I410868c905e5d88a9ac3cbe6b8a829947377cafd
---
 .../dfa-jump-threading-analysis.ll            | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
index 5076517d92c74..b22740c9318f1 100644
--- a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
@@ -246,5 +246,92 @@ exit:
   ret i32 0
 }
 
+define i8 @cyclesInPaths1(i1 %call12, i1 %cmp18) {
+; CHECK-LABEL: DFA Jump threading: cyclesInPaths1
+; CHECK: < switchPhiDef.for.body, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 2, detBB1 ]
+; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 2, detBB1 ]
+; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 2, detBB1 ]
+; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 2, detBB1 ]
+; CHECK-NEXT: < if.else, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 4, detBB1 ]
+; CHECK-NEXT: < if.else, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 4, detBB1 ]
+; CHECK-NEXT: < if.else, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 4, detBB1 ]
+; CHECK-NEXT: < if.else, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 4, detBB1 ]
+; CHECK-NEXT: < detBB1, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 0, detBB2 ]
+; CHECK-NEXT: < detBB1, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 0, detBB2 ]
+
+entry:
+  br label %switchPhiDef.for.body
+
+switchPhiDef.for.body:                                         ; preds = %detBB2, %entry
+  %switchPhi.curState = phi i32 [ %curState.1, %detBB2 ], [ 0, %entry ]
+  br i1 %call12, label %detBB1, label %if.else
+
+if.else:                                          ; preds = %switchPhiDef.for.body
+  br label %detBB1
+
+detBB1:                                         ; preds = %if.else, %switchPhiDef.for.body
+  %newState.02 = phi i32 [ 2, %switchPhiDef.for.body ], [ 4, %if.else ]
+  br i1 %cmp18, label %detBB2, label %if.end20
+
+if.end20:                                         ; preds = %detBB1
+  br i1 %call12, label %if.end23, label %switchBB
+
+switchBB:                                        ; preds = %if.end20
+  switch i32 %switchPhi.curState, label %if.end23 [
+    i32 4, label %ret1
+    i32 0, label %ret2
+  ]
+
+ret1:                                       ; preds = %switchBB
+  ret i8 1
+
+ret2:                                       ; preds = %switchBB
+  ret i8 2
+
+if.end23:                                         ; preds = %switchBB, %if.end20
+  br label %detBB2
+
+detBB2:                                          ; preds = %if.end23, %detBB1
+  %curState.1 = phi i32 [ %newState.02, %if.end23 ], [ 0, %detBB1 ]
+  br label %switchPhiDef.for.body
+}
+
+define void @cyclesInPaths2(i1 %tobool5.not.i, ptr %P.sroa.0.050) {
+; CHECK-LABEL: DFA Jump threading: cyclesInPaths2
+; CHECK: < sw.bb.i, bb.exit, if.end5, if.end.i > [ 1, bb.exit ]
+; CHECK-NEXT: < if.end5, bb.exit, if.end5, if.end.i > [ 0, bb.exit ]
+; CHECK-NEXT: < bb.exit, if.end5, if.end.i > [ 0, bb.exit ]
+
+entry:
+  br label %if.end5
+
+if.end5:                                          ; preds = %bb.exit, %entry
+  %P.sroa.8.051 = phi i16 [ %retval.sroa.6.0.i, %bb.exit ], [ 0, %entry ]
+  call void @foo3()
+  br i1 %tobool5.not.i, label %if.end.i, label %bb.exit
+
+if.end.i:                                         ; preds = %if.end5
+  switch i16 %P.sroa.8.051, label %sw.default.i [
+    i16 1, label %sw.bb.i
+    i16 0, label %bb.exit
+  ]
+
+sw.bb.i:                                          ; preds = %if.end.i
+  call void @foo1()
+  br label %bb.exit
+
+sw.default.i:                                     ; preds = %if.end.i
+  unreachable
+
+bb.exit: ; preds = %sw.bb.i, %if.end.i, %if.end5
+  %retval.sroa.6.0.i = phi i16 [ 1, %sw.bb.i ], [ 0, %if.end5 ], [ 0, %if.end.i ]
+  call void @foo2()
+  br label %if.end5
+}
+
+declare void @foo1()
+declare void @foo2()
+declare void @foo3()
+
 !0 = !{!"function_entry_count", i32 10}
 !1 = !{!"branch_weights", i32 3, i32 5}

>From 719418082e3f456c3a7960a45cd2720d118963e5 Mon Sep 17 00:00:00 2001
From: Usman Nadeem <mnadeem at qti.qualcomm.com>
Date: Fri, 21 Nov 2025 23:22:24 -0800
Subject: [PATCH 2/3] [DFAJumpThreading] Try harder to avoid cycles in paths.

If a threading path has cycles within it then the transfomation
is not correct. This patch fixes a couple of cases that create
such cycles.

Fixes https://github.com/llvm/llvm-project/issues/166868

Change-Id: I8aeb7bc5e25c7b9565c60da937aea14c58d74e3e
---
 llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp      | 11 ++++++++++-
 .../DFAJumpThreading/dfa-jump-threading-analysis.ll  | 12 +-----------
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
index e84ca819b93d8..2f1f59c1ff2a8 100644
--- a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
@@ -625,8 +625,12 @@ struct AllSwitchPaths {
         NewPath.setDeterminator(PhiBB);
         NewPath.setExitValue(C);
         // Don't add SwitchBlock at the start, this is handled later.
-        if (IncomingBB != SwitchBlock)
+        if (IncomingBB != SwitchBlock) {
+          // Don't add a cycle to the path.
+          if (VB.contains(IncomingBB))
+            continue;
           NewPath.push_back(IncomingBB);
+        }
         NewPath.push_back(PhiBB);
         Res.push_back(NewPath);
         continue;
@@ -815,7 +819,12 @@ struct AllSwitchPaths {
 
     std::vector<ThreadingPath> TempList;
     for (const ThreadingPath &Path : PathsToPhiDef) {
+      SmallPtrSet<BasicBlock *, 32> PathSet(Path.getPath().begin(),
+                                            Path.getPath().end());
       for (const PathType &PathToSw : PathsToSwitchBB) {
+        if (any_of(llvm::drop_begin(PathToSw),
+                   [&](const BasicBlock *BB) { return PathSet.contains(BB); }))
+          continue;
         ThreadingPath PathCopy(Path);
         PathCopy.appendExcludingFirst(PathToSw);
         TempList.push_back(PathCopy);
diff --git a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
index b22740c9318f1..7b8794ec22af5 100644
--- a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
@@ -248,16 +248,7 @@ exit:
 
 define i8 @cyclesInPaths1(i1 %call12, i1 %cmp18) {
 ; CHECK-LABEL: DFA Jump threading: cyclesInPaths1
-; CHECK: < switchPhiDef.for.body, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 2, detBB1 ]
-; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 2, detBB1 ]
-; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 2, detBB1 ]
-; CHECK-NEXT: < switchPhiDef.for.body, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 2, detBB1 ]
-; CHECK-NEXT: < if.else, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 4, detBB1 ]
-; CHECK-NEXT: < if.else, detBB1, if.end20, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 4, detBB1 ]
-; CHECK-NEXT: < if.else, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 4, detBB1 ]
-; CHECK-NEXT: < if.else, detBB1, if.end20, switchBB, if.end23, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 4, detBB1 ]
-; CHECK-NEXT: < detBB1, detBB2, switchPhiDef.for.body, detBB1, if.end20, switchBB > [ 0, detBB2 ]
-; CHECK-NEXT: < detBB1, detBB2, switchPhiDef.for.body, if.else, detBB1, if.end20, switchBB > [ 0, detBB2 ]
+; CHECK-NOT: <{{.*}}> [{{.*}}]
 
 entry:
   br label %switchPhiDef.for.body
@@ -299,7 +290,6 @@ detBB2:                                          ; preds = %if.end23, %detBB1
 define void @cyclesInPaths2(i1 %tobool5.not.i, ptr %P.sroa.0.050) {
 ; CHECK-LABEL: DFA Jump threading: cyclesInPaths2
 ; CHECK: < sw.bb.i, bb.exit, if.end5, if.end.i > [ 1, bb.exit ]
-; CHECK-NEXT: < if.end5, bb.exit, if.end5, if.end.i > [ 0, bb.exit ]
 ; CHECK-NEXT: < bb.exit, if.end5, if.end.i > [ 0, bb.exit ]
 
 entry:

>From 333c74242a75a45ac9561f0a1b87c6a0b7113298 Mon Sep 17 00:00:00 2001
From: Usman Nadeem <mnadeem at qti.qualcomm.com>
Date: Sat, 22 Nov 2025 09:55:20 -0800
Subject: [PATCH 3/3] Add transformation tests.

Change-Id: I23a291c3ef2ab221065aa5759ea13a5d2cfc20bf
---
 .../dfa-jump-threading-analysis.ll            |   2 +-
 .../dfa-jump-threading-transform.ll           | 136 ++++++++++++++++++
 2 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
index 7b8794ec22af5..cc76d22a1990c 100644
--- a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-analysis.ll
@@ -287,7 +287,7 @@ detBB2:                                          ; preds = %if.end23, %detBB1
   br label %switchPhiDef.for.body
 }
 
-define void @cyclesInPaths2(i1 %tobool5.not.i, ptr %P.sroa.0.050) {
+define void @cyclesInPaths2(i1 %tobool5.not.i) {
 ; CHECK-LABEL: DFA Jump threading: cyclesInPaths2
 ; CHECK: < sw.bb.i, bb.exit, if.end5, if.end.i > [ 1, bb.exit ]
 ; CHECK-NEXT: < bb.exit, if.end5, if.end.i > [ 0, bb.exit ]
diff --git a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-transform.ll b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-transform.ll
index 426b51edd2099..8df3d5ad62f6b 100644
--- a/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-transform.ll
+++ b/llvm/test/Transforms/DFAJumpThreading/dfa-jump-threading-transform.ll
@@ -499,6 +499,142 @@ cleanup:                                       ; preds = %for.inc, %switchblock,
   ret i16 0
 }
 
+define i8 @cyclesInPaths1(i1 %call12, i1 %cmp18) {
+; CHECK-LABEL: @cyclesInPaths1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[SWITCHPHIDEF_FOR_BODY:%.*]]
+; CHECK:       switchPhiDef.for.body:
+; CHECK-NEXT:    [[SWITCHPHI_CURSTATE:%.*]] = phi i32 [ [[CURSTATE_1:%.*]], [[DETBB2:%.*]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    br i1 [[CALL12:%.*]], label [[DETBB1:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.else:
+; CHECK-NEXT:    br label [[DETBB1]]
+; CHECK:       detBB1:
+; CHECK-NEXT:    [[NEWSTATE_02:%.*]] = phi i32 [ 2, [[SWITCHPHIDEF_FOR_BODY]] ], [ 4, [[IF_ELSE]] ]
+; CHECK-NEXT:    br i1 [[CMP18:%.*]], label [[DETBB2]], label [[IF_END20:%.*]]
+; CHECK:       if.end20:
+; CHECK-NEXT:    br i1 [[CALL12]], label [[IF_END23:%.*]], label [[SWITCHBB:%.*]]
+; CHECK:       switchBB:
+; CHECK-NEXT:    switch i32 [[SWITCHPHI_CURSTATE]], label [[IF_END23]] [
+; CHECK-NEXT:      i32 4, label [[RET1:%.*]]
+; CHECK-NEXT:      i32 0, label [[RET2:%.*]]
+; CHECK-NEXT:    ]
+; CHECK:       ret1:
+; CHECK-NEXT:    ret i8 1
+; CHECK:       ret2:
+; CHECK-NEXT:    ret i8 2
+; CHECK:       if.end23:
+; CHECK-NEXT:    br label [[DETBB2]]
+; CHECK:       detBB2:
+; CHECK-NEXT:    [[CURSTATE_1]] = phi i32 [ [[NEWSTATE_02]], [[IF_END23]] ], [ 0, [[DETBB1]] ]
+; CHECK-NEXT:    br label [[SWITCHPHIDEF_FOR_BODY]]
+;
+entry:
+  br label %switchPhiDef.for.body
+
+switchPhiDef.for.body:                                         ; preds = %detBB2, %entry
+  %switchPhi.curState = phi i32 [ %curState.1, %detBB2 ], [ 0, %entry ]
+  br i1 %call12, label %detBB1, label %if.else
+
+if.else:                                          ; preds = %switchPhiDef.for.body
+  br label %detBB1
+
+detBB1:                                         ; preds = %if.else, %switchPhiDef.for.body
+  %newState.02 = phi i32 [ 2, %switchPhiDef.for.body ], [ 4, %if.else ]
+  br i1 %cmp18, label %detBB2, label %if.end20
+
+if.end20:                                         ; preds = %detBB1
+  br i1 %call12, label %if.end23, label %switchBB
+
+switchBB:                                        ; preds = %if.end20
+  switch i32 %switchPhi.curState, label %if.end23 [
+  i32 4, label %ret1
+  i32 0, label %ret2
+  ]
+
+ret1:                                       ; preds = %switchBB
+  ret i8 1
+
+ret2:                                       ; preds = %switchBB
+  ret i8 2
+
+if.end23:                                         ; preds = %switchBB, %if.end20
+  br label %detBB2
+
+detBB2:                                          ; preds = %if.end23, %detBB1
+  %curState.1 = phi i32 [ %newState.02, %if.end23 ], [ 0, %detBB1 ]
+  br label %switchPhiDef.for.body
+}
+
+define void @cyclesInPaths2(i1 %tobool) {
+; CHECK-LABEL: @cyclesInPaths2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[IF_END5:%.*]]
+; CHECK:       if.end5:
+; CHECK-NEXT:    [[P_SROA_8_051:%.*]] = phi i16 [ [[RETVAL_SROA_6_0_I:%.*]], [[BB_EXIT:%.*]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 3)
+; CHECK-NEXT:    br i1 [[TOBOOL:%.*]], label [[IF_END_I:%.*]], label [[BB_EXIT]]
+; CHECK:       if.end5.jt0:
+; CHECK-NEXT:    [[P_SROA_8_051_JT0:%.*]] = phi i16 [ [[RETVAL_SROA_6_0_I_JT0:%.*]], [[BB_EXIT_JT0:%.*]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 3)
+; CHECK-NEXT:    br i1 [[TOBOOL]], label [[IF_END_I_JT0:%.*]], label [[BB_EXIT]]
+; CHECK:       if.end5.jt1:
+; CHECK-NEXT:    [[P_SROA_8_051_JT1:%.*]] = phi i16 [ [[RETVAL_SROA_6_0_I_JT1:%.*]], [[BB_EXIT_JT1:%.*]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 3)
+; CHECK-NEXT:    br i1 [[TOBOOL]], label [[IF_END_I_JT1:%.*]], label [[BB_EXIT]]
+; CHECK:       if.end.i:
+; CHECK-NEXT:    switch i16 [[P_SROA_8_051]], label [[SW_DEFAULT_I:%.*]] [
+; CHECK-NEXT:      i16 1, label [[SW_BB_I:%.*]]
+; CHECK-NEXT:      i16 0, label [[BB_EXIT_JT0]]
+; CHECK-NEXT:    ]
+; CHECK:       if.end.i.jt0:
+; CHECK-NEXT:    br label [[BB_EXIT_JT0]]
+; CHECK:       if.end.i.jt1:
+; CHECK-NEXT:    br label [[SW_BB_I]]
+; CHECK:       sw.bb.i:
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 1)
+; CHECK-NEXT:    br label [[BB_EXIT_JT1]]
+; CHECK:       sw.default.i:
+; CHECK-NEXT:    unreachable
+; CHECK:       bb.exit:
+; CHECK-NEXT:    [[RETVAL_SROA_6_0_I]] = phi i16 [ 0, [[IF_END5]] ], [ 0, [[IF_END5_JT1:%.*]] ], [ 0, [[IF_END5_JT0:%.*]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 2)
+; CHECK-NEXT:    br label [[IF_END5]]
+; CHECK:       bb.exit.jt0:
+; CHECK-NEXT:    [[RETVAL_SROA_6_0_I_JT0]] = phi i16 [ 0, [[IF_END_I]] ], [ 0, [[IF_END_I_JT0]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 2)
+; CHECK-NEXT:    br label [[IF_END5_JT0]]
+; CHECK:       bb.exit.jt1:
+; CHECK-NEXT:    [[RETVAL_SROA_6_0_I_JT1]] = phi i16 [ 1, [[SW_BB_I]] ]
+; CHECK-NEXT:    call void (...) @llvm.fake.use(i32 2)
+; CHECK-NEXT:    br label [[IF_END5_JT1]]
+;
+entry:
+  br label %if.end5
+
+if.end5:                                          ; preds = %bb.exit, %entry
+  %P.sroa.8.051 = phi i16 [ %retval.sroa.6.0.i, %bb.exit ], [ 0, %entry ]
+  call void (...) @llvm.fake.use(i32 3)
+  br i1 %tobool, label %if.end.i, label %bb.exit
+
+if.end.i:                                         ; preds = %if.end5
+  switch i16 %P.sroa.8.051, label %sw.default.i [
+  i16 1, label %sw.bb.i
+  i16 0, label %bb.exit
+  ]
+
+sw.bb.i:                                          ; preds = %if.end.i
+  call void (...) @llvm.fake.use(i32 1)
+  br label %bb.exit
+
+sw.default.i:                                     ; preds = %if.end.i
+  unreachable
+
+bb.exit: ; preds = %sw.bb.i, %if.end.i, %if.end5
+  %retval.sroa.6.0.i = phi i16 [ 1, %sw.bb.i ], [ 0, %if.end5 ], [ 0, %if.end.i ]
+  call void (...) @llvm.fake.use(i32 2)
+  br label %if.end5
+}
+
 declare void @llvm.fake.use(...)
 
 !0 = !{!"function_entry_count", i32 10}



More information about the llvm-commits mailing list