Skip to content

[LVI] Learn value ranges from ctpop results #121945

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
Jan 15, 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
24 changes: 24 additions & 0 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,27 @@ getRangeViaSLT(CmpInst::Predicate Pred, APInt RHS,
return std::nullopt;
}

/// Get value range for a "ctpop(Val) Pred RHS" condition.
static ValueLatticeElement getValueFromICmpCtpop(ICmpInst::Predicate Pred,
Value *RHS) {
unsigned BitWidth = RHS->getType()->getScalarSizeInBits();

auto *RHSConst = dyn_cast<ConstantInt>(RHS);
if (!RHSConst)
return ValueLatticeElement::getOverdefined();

ConstantRange ResValRange =
ConstantRange::makeExactICmpRegion(Pred, RHSConst->getValue());

unsigned ResMin = ResValRange.getUnsignedMin().getLimitedValue(BitWidth);
unsigned ResMax = ResValRange.getUnsignedMax().getLimitedValue(BitWidth);

APInt ValMin = APInt::getLowBitsSet(BitWidth, ResMin);
APInt ValMax = APInt::getHighBitsSet(BitWidth, ResMax);
return ValueLatticeElement::getRange(
ConstantRange::getNonEmpty(std::move(ValMin), ValMax + 1));
}

std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
Value *Val, ICmpInst *ICI, bool isTrueDest, bool UseBlockValue) {
Value *LHS = ICI->getOperand(0);
Expand Down Expand Up @@ -1192,6 +1213,9 @@ std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
return getValueFromSimpleICmpCondition(SwappedPred, LHS, Offset, ICI,
UseBlockValue);

if (match(LHS, m_Intrinsic<Intrinsic::ctpop>(m_Specific(Val))))
return getValueFromICmpCtpop(EdgePred, RHS);

const APInt *Mask, *C;
if (match(LHS, m_And(m_Specific(Val), m_APInt(Mask))) &&
match(RHS, m_APInt(C))) {
Expand Down
142 changes: 142 additions & 0 deletions llvm/test/Transforms/CorrelatedValuePropagation/ctpop-range.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=correlated-propagation %s | FileCheck %s

declare void @use(i1)

define void @ctpop1(i8 %v) {
; CHECK-LABEL: define void @ctpop1(
; CHECK-SAME: i8 [[V:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[RES:%.*]] = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 [[V]])
; CHECK-NEXT: [[C0_0:%.*]] = icmp samesign uge i8 [[RES]], 3
; CHECK-NEXT: [[C0_1:%.*]] = icmp samesign ule i8 [[RES]], 7
; CHECK-NEXT: [[C0:%.*]] = and i1 [[C0_0]], [[C0_1]]
; CHECK-NEXT: br i1 [[C0]], label %[[RANGE_3_8:.*]], label %[[ED:.*]]
; CHECK: [[RANGE_3_8]]:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[CMP1:%.*]] = icmp uge i8 [[V]], 8
; CHECK-NEXT: call void @use(i1 [[CMP1]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[CMP3:%.*]] = icmp ule i8 [[V]], -3
; CHECK-NEXT: call void @use(i1 [[CMP3]])
; CHECK-NEXT: ret void
; CHECK: [[ED]]:
; CHECK-NEXT: ret void
;
entry:
%res = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 %v)
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
%res = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 %v)
%res = call i8 @llvm.ctpop.i8(i8 %v)

I don't think these range annotations are really relevant to the test?

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

%c0.0 = icmp uge i8 %res, 3
%c0.1 = icmp ule i8 %res, 7
%c0 = and i1 %c0.0, %c0.1
br i1 %c0, label %range.3.8, label %ed

range.3.8:
%cmp0 = icmp uge i8 %v, 7
call void @use(i1 %cmp0) ; true
%cmp1 = icmp uge i8 %v, 8
call void @use(i1 %cmp1) ; unknown
%cmp2 = icmp ule i8 %v, 254
call void @use(i1 %cmp2) ; true
%cmp3 = icmp ule i8 %v, 253
call void @use(i1 %cmp3) ; unknown
ret void

ed:
ret void
}

define void @ctpop2(i8 %v) {
; CHECK-LABEL: define void @ctpop2(
; CHECK-SAME: i8 [[V:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[RES:%.*]] = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 [[V]])
; CHECK-NEXT: [[C2_0:%.*]] = icmp samesign uge i8 [[RES]], 1
; CHECK-NEXT: [[C2_1:%.*]] = icmp samesign ule i8 [[RES]], 4
; CHECK-NEXT: [[C2:%.*]] = and i1 [[C2_0]], [[C2_1]]
; CHECK-NEXT: br i1 [[C2]], label %[[RANGE_1_5:.*]], label %[[ED:.*]]
; CHECK: [[RANGE_1_5]]:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[CMP9:%.*]] = icmp uge i8 [[V]], 2
; CHECK-NEXT: call void @use(i1 [[CMP9]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[CMP11:%.*]] = icmp ule i8 [[V]], -17
; CHECK-NEXT: call void @use(i1 [[CMP11]])
; CHECK-NEXT: ret void
; CHECK: [[ED]]:
; CHECK-NEXT: ret void
;
entry:
%res = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 %v)
%c2.0 = icmp uge i8 %res, 1
%c2.1 = icmp ule i8 %res, 4
%c2 = and i1 %c2.0, %c2.1
br i1 %c2, label %range.1.5, label %ed

range.1.5:
%cmp8 = icmp uge i8 %v, 1
call void @use(i1 %cmp8) ; true
%cmp9 = icmp uge i8 %v, 2
call void @use(i1 %cmp9) ; unknown
%cmp10 = icmp ule i8 %v, 240
call void @use(i1 %cmp10) ; true
%cmp11 = icmp ule i8 %v, 239
call void @use(i1 %cmp11) ; unknown
ret void

ed:
ret void
}

define void @ctpop3(i8 %v) {
; CHECK-LABEL: define void @ctpop3(
; CHECK-SAME: i8 [[V:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ctpop.i8(i8 [[V]])
; CHECK-NEXT: [[C3:%.*]] = icmp samesign uge i8 [[RES]], 8
; CHECK-NEXT: br i1 [[C3]], label %[[RANGE_8_9:.*]], label %[[ED:.*]]
; CHECK: [[RANGE_8_9]]:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
; CHECK: [[ED]]:
; CHECK-NEXT: ret void
;
entry:
%res = call i8 @llvm.ctpop.i8(i8 %v)
%c3 = icmp uge i8 %res, 8
br i1 %c3, label %range.8.9, label %ed

range.8.9:
%cmp4 = icmp eq i8 %v, -1
call void @use(i1 %cmp4) ; true
ret void

ed:
ret void
}

define void @ctpop4(i8 %v) {
; CHECK-LABEL: define void @ctpop4(
; CHECK-SAME: i8 [[V:%.*]]) {
; CHECK-NEXT: [[TEST4:.*:]]
; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ctpop.i8(i8 [[V]])
; CHECK-NEXT: [[C4:%.*]] = icmp eq i8 [[RES]], 0
; CHECK-NEXT: br i1 [[C4]], label %[[RANGE_0_1:.*]], label %[[ED:.*]]
; CHECK: [[RANGE_0_1]]:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
; CHECK: [[ED]]:
; CHECK-NEXT: ret void
;
test4:
%res = call i8 @llvm.ctpop.i8(i8 %v)
%c4 = icmp eq i8 %res, 0
br i1 %c4, label %range.0.1, label %ed

range.0.1:
%cmp5 = icmp eq i8 %v, 0
call void @use(i1 %cmp5) ; true
ret void

ed:
ret void
}
Loading