diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp index 349a0a1a2d3c42..20f69a0955f51c 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -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(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 LazyValueInfoImpl::getValueFromICmpCondition( Value *Val, ICmpInst *ICI, bool isTrueDest, bool UseBlockValue) { Value *LHS = ICI->getOperand(0); @@ -1192,6 +1213,9 @@ std::optional LazyValueInfoImpl::getValueFromICmpCondition( return getValueFromSimpleICmpCondition(SwappedPred, LHS, Offset, ICI, UseBlockValue); + if (match(LHS, m_Intrinsic(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))) { diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/ctpop-range.ll b/llvm/test/Transforms/CorrelatedValuePropagation/ctpop-range.ll new file mode 100644 index 00000000000000..7101244dff4c45 --- /dev/null +++ b/llvm/test/Transforms/CorrelatedValuePropagation/ctpop-range.ll @@ -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) + %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 +}