Skip to content

[LVI] Add trunc to i1 handling. #124480

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
Feb 2, 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
26 changes: 26 additions & 0 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ class LazyValueInfoImpl {
std::optional<ValueLatticeElement>
getValueFromICmpCondition(Value *Val, ICmpInst *ICI, bool isTrueDest,
bool UseBlockValue);
ValueLatticeElement getValueFromTrunc(Value *Val, TruncInst *Trunc,
bool IsTrueDest);

std::optional<ValueLatticeElement>
getValueFromCondition(Value *Val, Value *Cond, bool IsTrueDest,
Expand Down Expand Up @@ -1283,6 +1285,27 @@ std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
return ValueLatticeElement::getOverdefined();
}

ValueLatticeElement LazyValueInfoImpl::getValueFromTrunc(Value *Val,
TruncInst *Trunc,
bool IsTrueDest) {
assert(Trunc->getType()->isIntOrIntVectorTy(1));

if (Trunc->getOperand(0) != Val)
return ValueLatticeElement::getOverdefined();

Type *Ty = Val->getType();

if (Trunc->hasNoUnsignedWrap()) {
if (IsTrueDest)
return ValueLatticeElement::get(ConstantInt::get(Ty, 1));
return ValueLatticeElement::get(Constant::getNullValue(Ty));
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use ConstantInt::getBool() here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do not think it is not possible to use ConstantInt::getBool() as the type of val is not i1.
it is possible to swap the true case to ConstantInt::get(Ty, 1)

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right, I got confused with the types here. And yes, using ConstantInt::get is preferable. (getIntegerValue is a specialized API if you need to potentially create an inttoptr expression...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}

if (IsTrueDest)
return ValueLatticeElement::getNot(Constant::getNullValue(Ty));
return ValueLatticeElement::getNot(Constant::getAllOnesValue(Ty));
Copy link
Contributor

Choose a reason for hiding this comment

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

We could further improve these by taking the nowrap flags into account. I think in a lot of cases we'll have trunc nuw %x to i1, in which case if (trunc nuw %x to i1) implies %x == 1, not just %x != 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added handling for nuw and update the proof

}

// Handle conditions of the form
// extractvalue(op.with.overflow(%x, C), 1).
static ValueLatticeElement getValueFromOverflowCondition(
Expand Down Expand Up @@ -1312,6 +1335,9 @@ LazyValueInfoImpl::getValueFromCondition(Value *Val, Value *Cond,
if (ICmpInst *ICI = dyn_cast<ICmpInst>(Cond))
return getValueFromICmpCondition(Val, ICI, IsTrueDest, UseBlockValue);

if (auto *Trunc = dyn_cast<TruncInst>(Cond))
return getValueFromTrunc(Val, Trunc, IsTrueDest);

if (auto *EVI = dyn_cast<ExtractValueInst>(Cond))
if (auto *WO = dyn_cast<WithOverflowInst>(EVI->getAggregateOperand()))
if (EVI->getNumIndices() == 1 && *EVI->idx_begin() == 1)
Expand Down
10 changes: 4 additions & 6 deletions llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll
Original file line number Diff line number Diff line change
Expand Up @@ -557,9 +557,8 @@ define i16 @and_elide(i16 noundef %x) {

define i16 @and_elide_trunc_cond(i16 noundef %x) {
; CHECK-LABEL: @and_elide_trunc_cond(
; CHECK-NEXT: [[AND:%.*]] = and i16 [[X:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = trunc nuw i16 [[X]] to i1
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 [[AND]], i16 24
; CHECK-NEXT: [[CMP:%.*]] = trunc nuw i16 [[X:%.*]] to i1
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 [[X]], i16 24
; CHECK-NEXT: ret i16 [[SEL]]
;
%and = and i16 %x, 1
Expand All @@ -570,9 +569,8 @@ define i16 @and_elide_trunc_cond(i16 noundef %x) {

define <2 x i8> @and_elide_trunc_cond_vec(<2 x i8> noundef %x) {
; CHECK-LABEL: @and_elide_trunc_cond_vec(
; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], splat (i8 1)
; CHECK-NEXT: [[CMP:%.*]] = trunc nuw <2 x i8> [[X]] to <2 x i1>
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[CMP]], <2 x i8> [[AND]], <2 x i8> splat (i8 24)
; CHECK-NEXT: [[CMP:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[CMP]], <2 x i8> [[X]], <2 x i8> splat (i8 24)
; CHECK-NEXT: ret <2 x i8> [[SEL]]
;
%and = and <2 x i8> %x, splat (i8 1)
Expand Down
36 changes: 12 additions & 24 deletions llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1515,10 +1515,8 @@ define void @test_trunc_bittest(i8 %a) {
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[A:%.*]] to i1
; CHECK-NEXT: br i1 [[TRUNC]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
; CHECK: if.true:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP1]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP2]])
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: call void @check1(i1 false)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also have negative tests, e.g. to show that it does not imply == 1.

; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[A]], 1
; CHECK-NEXT: call void @check1(i1 [[CMP3]])
; CHECK-NEXT: [[CMP4:%.*]] = icmp eq i8 [[A]], 1
Expand Down Expand Up @@ -1559,10 +1557,8 @@ define void @test_trunc_not_bittest(i8 %a) {
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[TRUNC]], true
; CHECK-NEXT: br i1 [[NOT]], label [[IF_FALSE:%.*]], label [[IF_TRUE:%.*]]
; CHECK: if.true:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[A]], -1
; CHECK-NEXT: call void @check1(i1 [[CMP1]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[A]], -1
; CHECK-NEXT: call void @check1(i1 [[CMP2]])
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: call void @check1(i1 false)
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP3]])
; CHECK-NEXT: [[CMP4:%.*]] = icmp eq i8 [[A]], 0
Expand Down Expand Up @@ -1603,14 +1599,10 @@ define void @test_trunc_nuw_bittest(i8 %a) {
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[A:%.*]] to i1
; CHECK-NEXT: br i1 [[TRUNC]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
; CHECK: if.true:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP1]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP2]])
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[A]], 1
; CHECK-NEXT: call void @check1(i1 [[CMP3]])
; CHECK-NEXT: [[CMP4:%.*]] = icmp eq i8 [[A]], 1
; CHECK-NEXT: call void @check1(i1 [[CMP4]])
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: call void @check1(i1 false)
; CHECK-NEXT: call void @check1(i1 false)
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: ret void
; CHECK: if.false:
; CHECK-NEXT: ret void
Expand Down Expand Up @@ -1639,14 +1631,10 @@ define void @test_trunc_nuw_not_bittest(i8 %a) {
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[TRUNC]], true
; CHECK-NEXT: br i1 [[NOT]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
; CHECK: if.true:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP1]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[A]], 0
; CHECK-NEXT: call void @check1(i1 [[CMP2]])
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[A]], 1
; CHECK-NEXT: call void @check1(i1 [[CMP3]])
; CHECK-NEXT: [[CMP4:%.*]] = icmp eq i8 [[A]], 1
; CHECK-NEXT: call void @check1(i1 [[CMP4]])
; CHECK-NEXT: call void @check1(i1 false)
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: call void @check1(i1 true)
; CHECK-NEXT: call void @check1(i1 false)
; CHECK-NEXT: ret void
; CHECK: if.false:
; CHECK-NEXT: ret void
Expand Down