diff --git a/llvm/include/llvm/Analysis/LazyValueInfo.h b/llvm/include/llvm/Analysis/LazyValueInfo.h --- a/llvm/include/llvm/Analysis/LazyValueInfo.h +++ b/llvm/include/llvm/Analysis/LazyValueInfo.h @@ -95,6 +95,10 @@ ConstantRange getConstantRange(Value *V, Instruction *CxtI, bool UndefAllowed = true); + /// Return the ConstantRange constraint that is known to hold for the value + /// at a specific use-site. + ConstantRange getConstantRangeAtUse(const Use &U, bool UndefAllowed = true); + /// Determine whether the specified value is known to be a /// constant on the specified edge. Return null if not. Constant *getConstantOnEdge(Value *V, BasicBlock *FromBB, BasicBlock *ToBB, diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -1650,6 +1650,43 @@ return ConstantRange::getFull(Width); } +ConstantRange LazyValueInfo::getConstantRangeAtUse(const Use &U, + bool UndefAllowed) { + Value *V = U.get(); + ConstantRange CR = + getConstantRange(V, cast(U.getUser()), UndefAllowed); + + // Check whether the only (possibly transitive) use of the value is in a + // position where V can be constrained by a select or branch condition. + const Use *CurrU = &U; + // TODO: Increase limit? + const unsigned MaxUsesToInspect = 2; + for (unsigned I = 0; I < MaxUsesToInspect; ++I) { + std::optional CondVal; + auto *CurrI = cast(CurrU->getUser()); + if (auto *SI = dyn_cast(CurrI)) { + if (CurrU->getOperandNo() == 1) + CondVal = getValueFromCondition(V, SI->getCondition(), true); + else if (CurrU->getOperandNo() == 2) + CondVal = getValueFromCondition(V, SI->getCondition(), false); + } else if (auto *PHI = dyn_cast(CurrI)) { + // TODO: Use non-local query? + CondVal = + getEdgeValueLocal(V, PHI->getIncomingBlock(*CurrU), PHI->getParent()); + } + if (CondVal && CondVal->isConstantRange()) + CR = CR.intersectWith(CondVal->getConstantRange()); + + // Only follow one-use chain, to allow direct intersection of conditions. + // If there are multiple uses, we would have to intersect with the union of + // all conditions at different uses. + if (!CurrI->hasOneUse()) + break; + CurrU = &*CurrI->use_begin(); + } + return CR; +} + /// Determine whether the specified value is known to be a /// constant on the specified edge. Return null if not. Constant *LazyValueInfo::getConstantOnEdge(Value *V, BasicBlock *FromBB, diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp --- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -438,8 +438,8 @@ // See if we can prove that the given binary op intrinsic will not overflow. static bool willNotOverflow(BinaryOpIntrinsic *BO, LazyValueInfo *LVI) { - ConstantRange LRange = LVI->getConstantRange(BO->getLHS(), BO); - ConstantRange RRange = LVI->getConstantRange(BO->getRHS(), BO); + ConstantRange LRange = LVI->getConstantRangeAtUse(BO->getOperandUse(0)); + ConstantRange RRange = LVI->getConstantRangeAtUse(BO->getOperandUse(1)); ConstantRange NWRegion = ConstantRange::makeGuaranteedNoWrapRegion( BO->getBinaryOp(), RRange, BO->getNoWrapKind()); return NWRegion.contains(LRange); diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll b/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll --- a/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll @@ -7,9 +7,9 @@ define i16 @sel_true_cond(i16 %x) { ; CHECK-LABEL: @sel_true_cond( -; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[X:%.*]], i16 10) +; CHECK-NEXT: [[SUB1:%.*]] = sub nuw i16 [[X:%.*]], 10 ; CHECK-NEXT: [[CMP:%.*]] = icmp uge i16 [[X]], 10 -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 [[SUB]], i16 42 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 [[SUB1]], i16 42 ; CHECK-NEXT: ret i16 [[SEL]] ; %sub = call i16 @llvm.usub.sat.i16(i16 %x, i16 10) @@ -72,6 +72,8 @@ ret i16 %sel } +; TODO: We could handle this case by raising the limit on the number of +; instructions we look through. define i16 @sel_true_cond_longer_chain(i16 %x) { ; CHECK-LABEL: @sel_true_cond_longer_chain( ; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[X:%.*]], i16 10) @@ -89,9 +91,9 @@ define i16 @sel_false_cond(i16 %x) { ; CHECK-LABEL: @sel_false_cond( -; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[X:%.*]], i16 10) +; CHECK-NEXT: [[SUB1:%.*]] = sub nuw i16 [[X:%.*]], 10 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[X]], 10 -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 42, i16 [[SUB]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i16 42, i16 [[SUB1]] ; CHECK-NEXT: ret i16 [[SEL]] ; %sub = call i16 @llvm.usub.sat.i16(i16 %x, i16 10) @@ -116,13 +118,13 @@ define i16 @phi_true_cond(i16 %x) { ; CHECK-LABEL: @phi_true_cond( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[X:%.*]], i16 10) +; CHECK-NEXT: [[SUB1:%.*]] = sub nuw i16 [[X:%.*]], 10 ; CHECK-NEXT: [[CMP:%.*]] = icmp uge i16 [[X]], 10 ; CHECK-NEXT: br i1 [[CMP]], label [[JOIN:%.*]], label [[SPLIT:%.*]] ; CHECK: split: ; CHECK-NEXT: br label [[JOIN]] ; CHECK: join: -; CHECK-NEXT: [[PHI:%.*]] = phi i16 [ [[SUB]], [[ENTRY:%.*]] ], [ 42, [[SPLIT]] ] +; CHECK-NEXT: [[PHI:%.*]] = phi i16 [ [[SUB1]], [[ENTRY:%.*]] ], [ 42, [[SPLIT]] ] ; CHECK-NEXT: ret i16 [[PHI]] ; entry: @@ -163,6 +165,8 @@ ret i16 %phi } +; TODO: We could handle this by using conditions that are not directly on the +; phi edge. define i16 @phi_true_cond_non_local(i16 %x) { ; CHECK-LABEL: @phi_true_cond_non_local( ; CHECK-NEXT: entry: @@ -191,13 +195,13 @@ define i16 @phi_false_cond(i16 %x) { ; CHECK-LABEL: @phi_false_cond( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[X:%.*]], i16 10) +; CHECK-NEXT: [[SUB1:%.*]] = sub nuw i16 [[X:%.*]], 10 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[X]], 10 ; CHECK-NEXT: br i1 [[CMP]], label [[SPLIT:%.*]], label [[JOIN:%.*]] ; CHECK: split: ; CHECK-NEXT: br label [[JOIN]] ; CHECK: join: -; CHECK-NEXT: [[PHI:%.*]] = phi i16 [ [[SUB]], [[ENTRY:%.*]] ], [ 42, [[SPLIT]] ] +; CHECK-NEXT: [[PHI:%.*]] = phi i16 [ [[SUB1]], [[ENTRY:%.*]] ], [ 42, [[SPLIT]] ] ; CHECK-NEXT: ret i16 [[PHI]] ; entry: @@ -238,6 +242,8 @@ ret i16 %phi } +; TODO: We could handle this by using conditions that are not directly on the +; phi edge. define i16 @phi_false_cond_non_local(i16 %x) { ; CHECK-LABEL: @phi_false_cond_non_local( ; CHECK-NEXT: entry: @@ -268,10 +274,10 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: -; CHECK-NEXT: [[IV:%.*]] = phi i16 [ 1000, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[IV:%.*]] = phi i16 [ 1000, [[ENTRY:%.*]] ], [ [[IV_NEXT1:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[COUNT:%.*]] = phi i16 [ 0, [[ENTRY]] ], [ [[COUNT_NEXT:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i16 [[IV]], 0 -; CHECK-NEXT: [[IV_NEXT]] = call i16 @llvm.usub.sat.i16(i16 [[IV]], i16 1) +; CHECK-NEXT: [[IV_NEXT1]] = sub nuw i16 [[IV]], 1 ; CHECK-NEXT: [[COUNT_NEXT]] = add i16 [[COUNT]], 1 ; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]] ; CHECK: exit: