Skip to content

[ValueTracking] Infer NonEqual from dominating conditions/assumptions #117442

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 4 commits into from
Feb 12, 2025
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
49 changes: 47 additions & 2 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3857,6 +3857,50 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2,
match(V2, m_PtrToIntSameSize(Q.DL, m_Value(B))))
return isKnownNonEqual(A, B, DemandedElts, Depth + 1, Q);

if (!Q.CxtI)
return false;

// Try to infer NonEqual based on information from dominating conditions.
if (Q.DC && Q.DT) {
for (BranchInst *BI : Q.DC->conditionsFor(V1)) {
Value *Cond = BI->getCondition();
BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0));
if (Q.DT->dominates(Edge0, Q.CxtI->getParent()) &&
isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
/*LHSIsTrue=*/true, Depth)
.value_or(false))
return true;

BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1));
if (Q.DT->dominates(Edge1, Q.CxtI->getParent()) &&
isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
/*LHSIsTrue=*/false, Depth)
.value_or(false))
return true;
}
}

if (!Q.AC)
return false;

// Try to infer NonEqual based on information from assumptions.
for (auto &AssumeVH : Q.AC->assumptionsFor(V1)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and above, any value in iterating through V2 as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

We add both V1 and V2 in findValuesAffectedByCondition. So it is ok to iterate on conditions related to V1.

if (!AssumeVH)
continue;
CallInst *I = cast<CallInst>(AssumeVH);

assert(I->getFunction() == Q.CxtI->getFunction() &&
"Got assumption for the wrong function!");
assert(I->getIntrinsicID() == Intrinsic::assume &&
"must be an assume intrinsic");

if (isImpliedCondition(I->getArgOperand(0), ICmpInst::ICMP_NE, V1, V2, Q.DL,
/*LHSIsTrue=*/true, Depth)
.value_or(false) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT))
Copy link
Contributor

Choose a reason for hiding this comment

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

Check that assume is valid before recursive function?

Copy link
Member Author

Choose a reason for hiding this comment

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

return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Maybe nicer to have a helper isKnownNonEqualFromContext?


return false;
}

Expand Down Expand Up @@ -10231,10 +10275,10 @@ void llvm::findValuesAffectedByCondition(
Worklist.push_back(B);
}
} else if (match(V, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
AddCmpOperands(A, B);

bool HasRHSC = match(B, m_ConstantInt());
if (ICmpInst::isEquality(Pred)) {
AddAffected(A);
AddAffected(B);
Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Is the impact here mainly from adding more affected values or from using them in isKnownNonEqual?

Something we could do is add a bitfield for what the affected value is for (e.g. knownbits, etc), so we can quickly skip ones that are not relevant.

Copy link
Member Author

Choose a reason for hiding this comment

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

It is mainly from adding more affected values.

Copy link
Member Author

Choose a reason for hiding this comment

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

Something we could do is add a bitfield for what the affected value is for (e.g. knownbits, etc), so we can quickly skip ones that are not relevant.

Are you working on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, I'm not working on this.

Copy link
Member Author

Choose a reason for hiding this comment

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

if (HasRHSC) {
Value *Y;
// (X & C) or (X | C).
Expand All @@ -10248,6 +10292,7 @@ void llvm::findValuesAffectedByCondition(
}
}
} else {
AddCmpOperands(A, B);
if (HasRHSC) {
// Handle (A + C1) u< C2, which is the canonical form of
// A > C3 && A < C4.
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Analysis/BasicAA/fallback-mayalias.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
; Check that BasicAA falls back to MayAlias (instead of PartialAlias) when none
; of its little tricks are applicable.

; CHECK: MayAlias: float* %arrayidxA, float* %arrayidxB
; CHECK: NoAlias: float* %arrayidxA, float* %arrayidxB

define void @fallback_mayalias(ptr noalias nocapture %C, i64 %i, i64 %j) local_unnamed_addr {
entry:
Expand Down
200 changes: 200 additions & 0 deletions llvm/test/Transforms/InstCombine/icmp-dom.ll
Original file line number Diff line number Diff line change
Expand Up @@ -534,3 +534,203 @@ else:
%cmp1 = icmp eq i32 %and1, 0
ret i1 %cmp1
}

; TODO: X != Y implies X | Y != 0
define i1 @or_nonzero_from_nonequal(i8 %x, i8 %y) {
; CHECK-LABEL: @or_nonzero_from_nonequal(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND:%.*]] = icmp eq i8 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[OR:%.*]] = or i8 [[X]], [[Y]]
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[OR]], 0
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK: if.else:
; CHECK-NEXT: ret i1 false
;
entry:
%cond = icmp eq i8 %x, %y
br i1 %cond, label %if.else, label %if.then

if.then:
%or = or i8 %x, %y
%cmp = icmp eq i8 %or, 0
ret i1 %cmp

if.else:
ret i1 false
}

define i1 @test_nonequal_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_domcond1(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
; CHECK-NEXT: br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 false
; CHECK: if.end:
; CHECK-NEXT: ret i1 false
;
entry:
%cond1 = icmp eq i64 %y, %x
%cond2 = icmp eq i64 %w, %z
%or.cond = select i1 %cond1, i1 true, i1 %cond2
br i1 %or.cond, label %if.end, label %if.then

if.then:
%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0
ret i1 %cmp

if.end:
ret i1 false
}

define i1 @test_nonequal_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_domcond2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[COND1]], i1 [[COND2]], i1 false
; CHECK-NEXT: br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 false
; CHECK: if.end:
; CHECK-NEXT: ret i1 false
;
entry:
%cond1 = icmp ne i64 %y, %x
%cond2 = icmp ne i64 %w, %z
%or.cond = select i1 %cond1, i1 %cond2, i1 false
br i1 %or.cond, label %if.then, label %if.end

if.then:
%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0
ret i1 %cmp

if.end:
ret i1 false
}

define i1 @test_nonequal_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_assume(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT: ret i1 false
;
entry:
%cond1 = icmp ne i64 %y, %x
call void @llvm.assume(i1 %cond1)
%cond2 = icmp ne i64 %w, %z
call void @llvm.assume(i1 %cond2)

%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0
ret i1 %cmp
}

; Negative tests

define i1 @test_nonequal_invalid_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_invalid_domcond1(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
; CHECK-NEXT: br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: ret i1 true
; CHECK: if.end:
; CHECK-NEXT: ret i1 false
;
entry:
%cond1 = icmp ne i64 %y, %x
%cond2 = icmp eq i64 %w, %z
%or.cond = select i1 %cond1, i1 true, i1 %cond2
br i1 %or.cond, label %if.end, label %if.then

if.then:
%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0
ret i1 %cmp

if.end:
ret i1 false
}

define i1 @test_nonequal_invalid_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_invalid_domcond2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
; CHECK-NEXT: br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[SUB1:%.*]] = sub i64 [[W]], [[Z]]
; CHECK-NEXT: [[SUB2:%.*]] = sub i64 [[Y]], [[X]]
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
entry:
%cond1 = icmp eq i64 %y, %x
%cond2 = icmp eq i64 %w, %z
%or.cond = select i1 %cond1, i1 true, i1 %cond2
br i1 %or.cond, label %if.then, label %if.end

if.then:
br label %if.end

if.end:
%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0
ret i1 %cmp
}

define i1 @test_nonequal_invalid_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
; CHECK-LABEL: @test_nonequal_invalid_assume(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SUB1:%.*]] = sub i64 [[W:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[SUB2:%.*]] = sub i64 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
; CHECK-NEXT: call void @side_effect()
; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[Y]], [[X]]
; CHECK-NEXT: call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[W]], [[Z]]
; CHECK-NEXT: call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT: ret i1 [[CMP]]
;
entry:
%sub1 = sub i64 %w, %z
%sub2 = sub i64 %y, %x
%umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
%cmp = icmp eq i64 %umin, 0

call void @side_effect()
%cond1 = icmp ne i64 %y, %x
call void @llvm.assume(i1 %cond1)
%cond2 = icmp ne i64 %w, %z
call void @llvm.assume(i1 %cond2)
ret i1 %cmp
}

declare void @side_effect()