-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[MachinePipeliner] Detect a cycle in PHI dependencies early on #167095
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
[MachinePipeliner] Detect a cycle in PHI dependencies early on #167095
Conversation
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 ]
|
@llvm/pr-subscribers-backend-hexagon Author: Abinaya Saravanan (quic-asaravan) ChangesAbort pipelining in the below case %1 = phi i32 [ %a, %entry ], [ %3, %loop ] Full diff: https://github.com/llvm/llvm-project/pull/167095.diff 2 Files Affected:
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
+}
|
|
✅ 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 ]
…ravan/llvm-project into Special-case-PHIs_cycle
|
@kasuga-fj Could you please review this patch? |
kasuga-fj
left a comment
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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.
| PhiRegs.insert(DefReg); | ||
|
|
||
| for (unsigned i = 1; i < MI.getNumOperands(); i += 2) { | ||
| unsigned SrcReg = MI.getOperand(i).getReg(); | ||
| PhiDeps[DefReg].push_back(SrcReg); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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.
| if (!PhiRegs.count(Reg)) | ||
| return false; |
There was a problem hiding this comment.
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.
Co-authored-by: Ryotaro Kasuga <[email protected]>
Co-authored-by: Ryotaro Kasuga <[email protected]>
Co-authored-by: Ryotaro Kasuga <[email protected]>
|
@kasuga-fj Could you please review? I have made the suggested changes. |
kasuga-fj
left a comment
There was a problem hiding this 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.
Co-authored-by: Ryotaro Kasuga <[email protected]>
|
LLVM Buildbot has detected a new failure on builder 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 |
handled early on - llvm#167095
…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]>
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 ]