Skip to content

Conversation

@quic-asaravan
Copy link
Contributor

@quic-asaravan quic-asaravan commented Nov 8, 2025

  • This patch detects cycles by phis and bails out if one is found.
  • It prevents to violate DAG restrictions.

Abort pipelining in the below case

%1 = phi i32 [ %a, %entry ], [ %3, %loop ]
%2 = phi i32 [ %a, %entry ], [ %1, %loop ]
%3 = phi i32 [ %b, %entry ], [ %2, %loop ]

Abort pipelining in the below case

%1 = phi i32 [ %a, %entry ], [ %3, %loop ]
%2 = phi i32 [ %a, %entry ], [ %1, %loop ]
%3 = phi i32 [ %b, %entry ], [ %2, %loop ]
@llvmbot
Copy link
Member

llvmbot commented Nov 8, 2025

@llvm/pr-subscribers-backend-hexagon

Author: Abinaya Saravanan (quic-asaravan)

Changes

Abort pipelining in the below case

%1 = phi i32 [ %a, %entry ], [ %3, %loop ]
%2 = phi i32 [ %a, %entry ], [ %1, %loop ]
%3 = phi i32 [ %b, %entry ], [ %2, %loop ]


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

2 Files Affected:

  • (modified) llvm/lib/CodeGen/MachinePipeliner.cpp (+54)
  • (added) llvm/test/CodeGen/Hexagon/swp-phi-cycle.ll (+22)
diff --git a/llvm/lib/CodeGen/MachinePipeliner.cpp b/llvm/lib/CodeGen/MachinePipeliner.cpp
index a717d9e4a618d..b327ba8900f03 100644
--- a/llvm/lib/CodeGen/MachinePipeliner.cpp
+++ b/llvm/lib/CodeGen/MachinePipeliner.cpp
@@ -485,6 +485,55 @@ void MachinePipeliner::setPragmaPipelineOptions(MachineLoop &L) {
   }
 }
 
+bool hasPHICycle(const MachineBasicBlock *LoopHeader, const MachineRegisterInfo &MRI) {
+  DenseMap<unsigned, SmallVector<unsigned, 2>> PhiDeps;
+  SmallSet<unsigned, 8> PhiRegs;
+
+  // Collect PHI nodes and their dependencies
+  for (const MachineInstr &MI : *LoopHeader) {
+    if (!MI.isPHI())
+      continue;
+
+    unsigned DefReg = MI.getOperand(0).getReg();
+    PhiRegs.insert(DefReg);
+
+    for (unsigned i = 1; i < MI.getNumOperands(); i += 2) {
+      unsigned SrcReg = MI.getOperand(i).getReg();
+      PhiDeps[DefReg].push_back(SrcReg);
+    }
+  }
+
+  // DFS to detect cycles
+  SmallSet<unsigned, 8> Visited, RecStack;
+
+  std::function<bool(unsigned)> DFS = [&](unsigned Reg) -> bool {
+  if (!PhiRegs.count(Reg))
+      return false;
+    if (RecStack.count(Reg))
+      return true;
+    if (Visited.count(Reg))
+      return false;
+
+    Visited.insert(Reg);
+    RecStack.insert(Reg);
+
+    for (unsigned Dep : PhiDeps[Reg]) {
+      if (DFS(Dep))
+        return true;
+    }
+
+    RecStack.erase(Reg);
+    return false;
+  };
+
+  for (unsigned Reg : PhiRegs) {
+    if (DFS(Reg))
+      return true;
+  }
+
+  return false;
+}
+
 /// Return true if the loop can be software pipelined.  The algorithm is
 /// restricted to loops with a single basic block.  Make sure that the
 /// branch in the loop can be analyzed.
@@ -499,6 +548,11 @@ bool MachinePipeliner::canPipelineLoop(MachineLoop &L) {
     return false;
   }
 
+  if (hasPHICycle(L.getHeader(), MF->getRegInfo())) {
+    LLVM_DEBUG(dbgs() << "Cannot pipeline loop due to PHI cycle\n");
+    return false;
+  }
+
   if (disabledByPragma) {
     ORE->emit([&]() {
       return MachineOptimizationRemarkAnalysis(DEBUG_TYPE, "canPipelineLoop",
diff --git a/llvm/test/CodeGen/Hexagon/swp-phi-cycle.ll b/llvm/test/CodeGen/Hexagon/swp-phi-cycle.ll
new file mode 100644
index 0000000000000..a92d113f01c88
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/swp-phi-cycle.ll
@@ -0,0 +1,22 @@
+; RUN: llc -mtriple=hexagon-unknown-linux-gnu -enable-pipeliner -debug-only=pipeliner < %s 2>&1 | FileCheck %s
+
+; CHECK: Cannot pipeline loop due to PHI cycle
+
+define void @phi_cycle_loop(i32 %a, i32 %b) {
+entry:
+  br label %loop
+
+loop:
+  %1 = phi i32 [ %a, %entry ], [ %3, %loop ]
+  %2 = phi i32 [ %a, %entry ], [ %1, %loop ]
+  %3 = phi i32 [ %b, %entry ], [ %2, %loop ]
+
+  ; Prevent PHI elimination by using all values
+  %add1 = add i32 %1, %2
+  %add2 = add i32 %add1, %3
+  %cmp = icmp slt i32 %add2, 100
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret void
+}

@github-actions
Copy link

github-actions bot commented Nov 8, 2025

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

Abort pipelining in the below case

%1 = phi i32 [ %a, %entry ], [ %3, %loop ]
%2 = phi i32 [ %a, %entry ], [ %1, %loop ]
%3 = phi i32 [ %b, %entry ], [ %2, %loop ]
@quic-asaravan
Copy link
Contributor Author

@iajbar @sgundapa Could you please review ?

@iajbar iajbar self-requested a review November 10, 2025 15:10
@quic-asaravan
Copy link
Contributor Author

@kasuga-fj Could you please review this patch?

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

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

Generally looks reasonable to me.

// DFS to detect cycles
SmallSet<unsigned, 8> Visited, RecStack;

std::function<bool(unsigned)> DFS = [&](unsigned Reg) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to define a function like static bool foo(unsigned Reg, ...) rather than using std::function for recursive function.

Comment on lines 499 to 504
PhiRegs.insert(DefReg);

for (unsigned i = 1; i < MI.getNumOperands(); i += 2) {
unsigned SrcReg = MI.getOperand(i).getReg();
PhiDeps[DefReg].push_back(SrcReg);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
PhiRegs.insert(DefReg);
for (unsigned i = 1; i < MI.getNumOperands(); i += 2) {
unsigned SrcReg = MI.getOperand(i).getReg();
PhiDeps[DefReg].push_back(SrcReg);
}
auto Ite = PhiDeps.try_emplace(DefReg).first;
for (unsigned i = 1; i < MI.getNumOperands(); i += 2)
Ite->second.push_back(MI.getOperand(i).getReg());

To avoid repeated hash lookup.

Comment on lines 511 to 512
if (!PhiRegs.count(Reg))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider calling PhiDeps.find(Reg) instead and remove PhiRegs.

@quic-asaravan
Copy link
Contributor Author

@kasuga-fj Could you please review? I have made the suggested changes.

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

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

The code looks good to me, but I think the PR description should be updated. At least the following should be included.

  • This patch detects cycles by phis and bails out if one is found.
  • It prevents to violate DAG restrictions.

@quic-asaravan quic-asaravan merged commit c946418 into llvm:main Nov 17, 2025
10 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Nov 17, 2025

LLVM Buildbot has detected a new failure on builder llvm-nvptx-nvidia-win running on as-builder-8 while building llvm at step 7 "test-build-unified-tree-check-llvm".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/54/builds/14754

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-llvm) failure: test (failure)
******************** TEST 'LLVM-Unit :: Support/./SupportTests.exe/79/105' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:C:\buildbot\as-builder-8\llvm-nvptx-nvidia-win\build\unittests\Support\.\SupportTests.exe-LLVM-Unit-19284-79-105.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=105 GTEST_SHARD_INDEX=79 C:\buildbot\as-builder-8\llvm-nvptx-nvidia-win\build\unittests\Support\.\SupportTests.exe
--


Note: This is test shard 80 of 105.

[==========] Running 16 tests from 16 test suites.

[----------] Global test environment set-up.

[----------] 1 test from BinaryStreamTest

[ RUN      ] BinaryStreamTest.DropOperations

[       OK ] BinaryStreamTest.DropOperations (0 ms)

[----------] 1 test from BinaryStreamTest (0 ms total)



[----------] 1 test from CommandLineTest

[ RUN      ] CommandLineTest.TokenizeAndMarkEOLs

[       OK ] CommandLineTest.TokenizeAndMarkEOLs (0 ms)

[----------] 1 test from CommandLineTest (0 ms total)



[----------] 1 test from DataExtractorTest

[ RUN      ] DataExtractorTest.LEB128_error

[       OK ] DataExtractorTest.LEB128_error (0 ms)

[----------] 1 test from DataExtractorTest (0 ms total)



[----------] 1 test from Error

[ RUN      ] Error.ForwardToExpected

[       OK ] Error.ForwardToExpected (0 ms)

[----------] 1 test from Error (0 ms total)
...

quic-asaravan added a commit to quic-asaravan/llvm-project that referenced this pull request Nov 17, 2025
aadeshps-mcw pushed a commit to aadeshps-mcw/llvm-project that referenced this pull request Nov 26, 2025
…167095)

- This patch detects cycles by phis and bails out if one is found.
- It prevents to violate DAG restrictions.

Abort pipelining in the below case

%1 = phi i32 [ %a, %entry ], [ %3, %loop ]
%2 = phi i32 [ %a, %entry ], [ %1, %loop ]
%3 = phi i32 [ %b, %entry ], [ %2, %loop ]

---------

Co-authored-by: Ryotaro Kasuga <[email protected]>
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.

4 participants