Skip to content

[InstCombine] Expand redundant phi cycle elimination #67968

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

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,8 +996,8 @@ static bool isDeadPHICycle(PHINode *PN,
/// Return true if this phi node is always equal to NonPhiInVal.
/// This happens with mutually cyclic phi nodes like:
/// z = some value; x = phi (y, z); y = phi (x, z)
static bool PHIsEqualValue(PHINode *PN, Value *NonPhiInVal,
SmallPtrSetImpl<PHINode*> &ValueEqualPHIs) {
static bool PHIsEqualValue(PHINode *PN, Value *&NonPhiInVal,
SmallPtrSetImpl<PHINode *> &ValueEqualPHIs) {
// See if we already saw this PHI node.
if (!ValueEqualPHIs.insert(PN).second)
return true;
Expand All @@ -1010,8 +1010,11 @@ static bool PHIsEqualValue(PHINode *PN, Value *NonPhiInVal,
// the value.
for (Value *Op : PN->incoming_values()) {
if (PHINode *OpPN = dyn_cast<PHINode>(Op)) {
if (!PHIsEqualValue(OpPN, NonPhiInVal, ValueEqualPHIs))
return false;
if (!PHIsEqualValue(OpPN, NonPhiInVal, ValueEqualPHIs)) {
if (NonPhiInVal)
return false;
NonPhiInVal = OpPN;
}
} else if (Op != NonPhiInVal)
return false;
}
Expand Down Expand Up @@ -1478,33 +1481,35 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
// z = some value; x = phi (y, z); y = phi (x, z)
// where the phi nodes don't necessarily need to be in the same block. Do a
// quick check to see if the PHI node only contains a single non-phi value, if
// so, scan to see if the phi cycle is actually equal to that value.
// so, scan to see if the phi cycle is actually equal to that value. If the
// phi has no non-phi values then allow the "NonPhiInVal" to be set later if
// one of the phis itself does not have a single input.
{
unsigned InValNo = 0, NumIncomingVals = PN.getNumIncomingValues();
// Scan for the first non-phi operand.
while (InValNo != NumIncomingVals &&
isa<PHINode>(PN.getIncomingValue(InValNo)))
++InValNo;

if (InValNo != NumIncomingVals) {
Value *NonPhiInVal = PN.getIncomingValue(InValNo);
Value *NonPhiInVal =
InValNo != NumIncomingVals ? PN.getIncomingValue(InValNo) : nullptr;

// Scan the rest of the operands to see if there are any conflicts, if so
// there is no need to recursively scan other phis.
// Scan the rest of the operands to see if there are any conflicts, if so
// there is no need to recursively scan other phis.
if (NonPhiInVal)
for (++InValNo; InValNo != NumIncomingVals; ++InValNo) {
Value *OpVal = PN.getIncomingValue(InValNo);
if (OpVal != NonPhiInVal && !isa<PHINode>(OpVal))
break;
}

// If we scanned over all operands, then we have one unique value plus
// phi values. Scan PHI nodes to see if they all merge in each other or
// the value.
if (InValNo == NumIncomingVals) {
SmallPtrSet<PHINode*, 16> ValueEqualPHIs;
if (PHIsEqualValue(&PN, NonPhiInVal, ValueEqualPHIs))
return replaceInstUsesWith(PN, NonPhiInVal);
}
// If we scanned over all operands, then we have one unique value plus
// phi values. Scan PHI nodes to see if they all merge in each other or
// the value.
if (InValNo == NumIncomingVals) {
SmallPtrSet<PHINode *, 16> ValueEqualPHIs;
if (PHIsEqualValue(&PN, NonPhiInVal, ValueEqualPHIs))
return replaceInstUsesWith(PN, NonPhiInVal);
}
}

Expand Down
54 changes: 54 additions & 0 deletions llvm/test/Transforms/InstCombine/phi.ll
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,60 @@ end:
ret i1 %z
}

; Same as above, but the input is also a phi
define i1 @test25b(i1 %ci, i64 %ai, i64 %bi) {
; CHECK-LABEL: @test25b(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[CI:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: br label [[ELSE]]
; CHECK: else:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[AI:%.*]], [[ENTRY:%.*]] ], [ [[BI:%.*]], [[THEN]] ]
; CHECK-NEXT: [[B:%.*]] = call i1 @test25a()
; CHECK-NEXT: br i1 [[B]], label [[ONE:%.*]], label [[TWO:%.*]]
; CHECK: one:
; CHECK-NEXT: [[C:%.*]] = call i1 @test25a()
; CHECK-NEXT: br i1 [[C]], label [[TWO]], label [[END:%.*]]
; CHECK: two:
; CHECK-NEXT: [[D:%.*]] = call i1 @test25a()
; CHECK-NEXT: br i1 [[D]], label [[ONE]], label [[END]]
; CHECK: end:
; CHECK-NEXT: [[G:%.*]] = inttoptr i64 [[I]] to ptr
; CHECK-NEXT: store i32 10, ptr [[G]], align 4
; CHECK-NEXT: [[Z:%.*]] = call i1 @test25a()
; CHECK-NEXT: ret i1 [[Z]]
;
entry:
br i1 %ci, label %then, label %else

then:
br label %else

else:
%i = phi i64 [ %ai, %entry ], [ %bi, %then ]
%b = call i1 @test25a()
br i1 %b, label %one, label %two

one:
%x = phi i64 [%y, %two], [%i, %else]
%c = call i1 @test25a()
br i1 %c, label %two, label %end

two:
%y = phi i64 [%x, %one], [%i, %else]
%d = call i1 @test25a()
br i1 %d, label %one, label %end

end:
%f = phi i64 [ %x, %one], [%y, %two]
; Change the %f to %i, and the optimizer suddenly becomes a lot smarter
; even though %f must equal %i at this point
%g = inttoptr i64 %f to ptr
store i32 10, ptr %g
%z = call i1 @test25a()
ret i1 %z
}

declare i1 @test26a()

define i1 @test26(i32 %n) {
Expand Down