From b1f03c83ea37587320d835cb83c601b95ebd49c4 Mon Sep 17 00:00:00 2001 From: Grigory Pastukhov Date: Sun, 6 Oct 2024 18:38:54 -0700 Subject: [PATCH 1/5] Implementation of GuaranteedBoundsPropagator --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 158 ++++++++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 101d60525f416..0bd5f2e66d942 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -383,6 +383,78 @@ void SCCPSolver::inferArgAttributes() const { } } +// GuaranteedBoundsPropagator is a class that propagates +// guaranteed bounds for values. Typically, a ValueLatticeElement +// associated with a frequently visited Phi node is marked as "overdefined" to +// prevent excessive iterations. However, GuaranteedBoundsPropagator enhances +// this by propagating guaranteed bounds up to the Phi node, potentially +// improving precision. +// Consider a scenario where a variable 'x' is evaluated in a branch with the +// condition 'x < 10'. In this case, we can confidently assert that 'x' will not +// exceed 10. GuaranteedBoundsPropagator leverages this information by +// propagating such guaranteed bounds up to the relevant Phi node. If all +// incoming values to the Phi node have guaranteed bounds, the union of these +// bounds will represent the guaranteed bounds for the Phi node itself. Once +// these bounds are established for the Phi node, they can be propagated further +// to other values that depend on this Phi node. +// However, if not all incoming branches to the Phi node have been explored or +// are active, the bounds for the Phi node cannot be fully guaranteed. In such +// cases, the propagator may still apply the best available bounds to the Phi +// node instead of marking it as overdefined. These bounds remain valid unless +// new branches become active. If any active incoming branches lack guaranteed +// bounds, the Phi node's state need to be adjusted to overdefined. +class GuaranteedBoundsPropagator { + DenseMap GuaranteedBounds; + +public: + std::optional getGuaranteedBounds(Value *V) { + if (!V->getType()->isIntegerTy()) + return {}; + if (Constant *C = dyn_cast(V)) + return C->toConstantRange(); + auto It = GuaranteedBounds.find(V); + if (It == GuaranteedBounds.end()) + return {}; + auto &Range = It->second; + if (Range.isFullSet()) + return {}; + return Range; + } + + void insertOrUpdate(Value *V, const ConstantRange &CR) { + if (CR.isFullSet()) + return; + auto It = GuaranteedBounds.find(V); + if (It == GuaranteedBounds.end()) { + GuaranteedBounds.insert({V, CR}); + } else { + It->second = CR; + } + } + + // If ImposedCR is not full set, then we update guaranteed bounds + // of OutputValue. If in addition InputValue has guaranteed bounds, + // we update the guaranteed bounds of OutputValue to be the intersection + // of the two. + void processConditionalBranch(Value *OutputValue, Value *InputValue, + const ConstantRange &ImposedCR); + + // Updates the guaranteed bounds of the corresponding value if the + // operands have guaranteed bounds. + void processBinaryOp(Instruction *I); + + // If guaranteed bounds from all incoming edges are known, the union of all + // of the bounds is returned. The flag \p IsBoundGuaranteed is set to true. + // If all the incoming edges were not explored yet, but the ones that were + // all have guaranteed bounds, the union of the bounds is returned and the + // flag \p IsBoundGuaranteed is set to false. If some of the incoming edges + // do not have guaranteed bounds (or we failed to calculate union), + // the function returns std::nullopt. + std::optional processPhiNode( + PHINode *PN, + const SmallVector &IncomingValuesFromActiveBranches); +}; + /// Helper class for SCCPSolver. This implements the instruction visitor and /// holds all the state. class SCCPInstVisitor : public InstVisitor { @@ -450,6 +522,8 @@ class SCCPInstVisitor : public InstVisitor { DenseMap> AdditionalUsers; + GuaranteedBoundsPropagator BoundsPropagator; + LLVMContext &Ctx; private: @@ -1255,6 +1329,7 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { return (void)markOverdefined(&PN); unsigned NumActiveIncoming = 0; + SmallVector IncomingValuesFromActiveBranches; // Look at all of the executable operands of the PHI node. If any of them // are overdefined, the PHI becomes overdefined as well. If they are all @@ -1265,7 +1340,7 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) { if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent())) continue; - + IncomingValuesFromActiveBranches.push_back(PN.getIncomingValue(i)); ValueLatticeElement IV = getValueState(PN.getIncomingValue(i)); PhiState.mergeIn(IV); NumActiveIncoming++; @@ -1273,6 +1348,27 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { break; } + // If we have visited this PHI node too many times, we first check + // if there is a known bounds we could use. If not, the state will + // be maked as overdefined. + auto OptionalBestBounds = + BoundsPropagator.processPhiNode(&PN, IncomingValuesFromActiveBranches); + auto &OldState = getValueState(&PN); + if (OptionalBestBounds && PhiState.isConstantRange() && + OldState.isConstantRange()) { + ConstantRange OldStateRange = OldState.getConstantRange(); + ConstantRange NewStateRange = PhiState.getConstantRange(); + if (OldStateRange != NewStateRange && + OldState.getNumRangeExtensions() > NumActiveIncoming) { + PhiState = ValueLatticeElement::getRange(*OptionalBestBounds); + mergeInValue(&PN, PhiState); + ValueLatticeElement &PhiStateRef = getValueState(&PN); + PhiStateRef.setNumRangeExtensions( + std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions())); + return; + } + } + // We allow up to 1 range extension per active incoming value and one // additional extension. Note that we manually adjust the number of range // extensions to match the number of active incoming values. This helps to @@ -1280,7 +1376,7 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { // incoming values are equal. mergeInValue(&PN, PhiState, ValueLatticeElement::MergeOptions().setMaxWidenSteps( - NumActiveIncoming + 1)); + NumActiveIncoming + 2)); ValueLatticeElement &PhiStateRef = getValueState(&PN); PhiStateRef.setNumRangeExtensions( std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions())); @@ -1585,6 +1681,8 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) { R = A.overflowingBinaryOp(BO->getOpcode(), B, OBO->getNoWrapKind()); else R = A.binaryOp(BO->getOpcode(), B); + + BoundsPropagator.processBinaryOp(&I); mergeInValue(&I, ValueLatticeElement::getRange(R)); // TODO: Currently we do not exploit special values that produce something @@ -1880,9 +1978,12 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) { ConstantRange::getFull(DL.getTypeSizeInBits(CopyOf->getType())); // Get the range imposed by the condition. - if (CondVal.isConstantRange()) + if (CondVal.isConstantRange()) { ImposedCR = ConstantRange::makeAllowedICmpRegion( Pred, CondVal.getConstantRange()); + if (BoundsPropagator.getGuaranteedBounds(OtherOp)) + BoundsPropagator.processConditionalBranch(&CB, CopyOf, ImposedCR); + } // Combine range info for the original value with the new range from the // condition. @@ -2252,3 +2353,54 @@ void SCCPSolver::markFunctionUnreachable(Function *F) { void SCCPSolver::visit(Instruction *I) { Visitor->visit(I); } void SCCPSolver::visitCall(CallInst &I) { Visitor->visitCall(I); } + +void GuaranteedBoundsPropagator::processConditionalBranch( + Value *OutputValue, Value *InputValue, const ConstantRange &ImposedCR) { + auto OptionalInputValueBounds = getGuaranteedBounds(InputValue); + if (OptionalInputValueBounds) + insertOrUpdate(OutputValue, + ImposedCR.intersectWith(*OptionalInputValueBounds)); + else + insertOrUpdate(OutputValue, ImposedCR); +} + +void GuaranteedBoundsPropagator::processBinaryOp(Instruction *I) { + auto *BO = cast(I); + assert(BO && "Expected binary op"); + auto OptionalLHSBounds = getGuaranteedBounds(BO->getOperand(0)); + auto OptionalRHSBounds = getGuaranteedBounds(BO->getOperand(1)); + if (!OptionalLHSBounds || !OptionalRHSBounds) + return; + ConstantRange R = + ConstantRange::getEmpty(I->getType()->getScalarSizeInBits()); + if (auto *OBO = dyn_cast(BO)) + R = OptionalLHSBounds->overflowingBinaryOp( + BO->getOpcode(), *OptionalRHSBounds, OBO->getNoWrapKind()); + else + R = OptionalLHSBounds->binaryOp(BO->getOpcode(), *OptionalRHSBounds); + insertOrUpdate(I, R); +} + +std::optional GuaranteedBoundsPropagator::processPhiNode( + PHINode *PN, + const SmallVector &IncomingValuesFromActiveBranches) { + auto OptionalExistingBounds = getGuaranteedBounds(PN); + if (OptionalExistingBounds) + return *OptionalExistingBounds; + + ConstantRange R = + ConstantRange::getEmpty(PN->getType()->getScalarSizeInBits()); + for (Value *IncomingValue : IncomingValuesFromActiveBranches) { + auto OptionalIncomingBounds = getGuaranteedBounds(IncomingValue); + if (!OptionalIncomingBounds) + return {}; + // TODO: Handle disjoint ranges in the future, if needed. + auto OptionalUnion = R.exactUnionWith(*OptionalIncomingBounds); + if (!OptionalUnion) + return {}; + R = *OptionalUnion; + } + if (PN->getNumIncomingValues() == IncomingValuesFromActiveBranches.size()) + insertOrUpdate(PN, R); + return R; +} From 634ff93559ae0b9569518a15645863fd0e11c444 Mon Sep 17 00:00:00 2001 From: Grigory Pastukhov Date: Sun, 6 Oct 2024 18:39:52 -0700 Subject: [PATCH 2/5] Adding regression test file --- llvm/test/Transforms/SCCP/loop-removal.ll | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 llvm/test/Transforms/SCCP/loop-removal.ll diff --git a/llvm/test/Transforms/SCCP/loop-removal.ll b/llvm/test/Transforms/SCCP/loop-removal.ll new file mode 100644 index 0000000000000..05f7a096734ef --- /dev/null +++ b/llvm/test/Transforms/SCCP/loop-removal.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=ipsccp -S | FileCheck %s + +define i32 @foo() { +; CHECK-LABEL: @foo( +; CHECK-NEXT: init: +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] +; CHECK: outer.loop.control: +; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 +; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] +; CHECK: inner.loop.control: +; CHECK-NEXT: br label [[OUTER_LOOP_INC]] +; CHECK: outer.loop.inc: +; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_0]], 2 +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[X_0]] +; +init: + br label %outer.loop.control + +outer.loop.control: ; preds = %init, %outer.loop.inc + %x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] + %0 = icmp slt i32 %x.0, 10 + br i1 %0, label %inner.loop.control, label %exit + +inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body + %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] + %1 = icmp sgt i32 %x.1, 20 + br i1 %1, label %inner.loop.body, label %outer.loop.inc + +inner.loop.body: ; preds = %inner.loop.control + %x.inner = sub nsw i32 %x.1, 1 + br label %inner.loop.control + +outer.loop.inc: ; preds = %inner.loop.control + %x.outer = add nsw i32 %x.1, 2 + br label %outer.loop.control + +exit: ; preds = %1 + ret i32 %x.0 +} From e059c6ab2811d0924933d7972ddad2fe4cd35acd Mon Sep 17 00:00:00 2001 From: Grigory Pastukhov Date: Sun, 6 Oct 2024 19:57:30 -0700 Subject: [PATCH 3/5] Fixed failing test: the pass now eliminates more branches --- llvm/test/Transforms/SCCP/undef-resolve.ll | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/llvm/test/Transforms/SCCP/undef-resolve.ll b/llvm/test/Transforms/SCCP/undef-resolve.ll index 8bb2baa82dce3..7f8842f31f037 100644 --- a/llvm/test/Transforms/SCCP/undef-resolve.ll +++ b/llvm/test/Transforms/SCCP/undef-resolve.ll @@ -38,20 +38,13 @@ define i32 @test2() nounwind readnone ssp { ; CHECK-NEXT: br label [[CONTROL_US:%.*]] ; CHECK: bb3.us: ; CHECK-NEXT: br label [[CONTROL_OUTER_US]] -; CHECK: bb0.us: -; CHECK-NEXT: br label [[CONTROL_US]] ; CHECK: control.us: -; CHECK-NEXT: [[SWITCHCOND_0_US]] = phi i32 [ [[A_0_PH_US]], [[BB0_US:%.*]] ], [ [[SWITCHCOND_0_PH_US]], [[CONTROL_OUTER_US]] ] -; CHECK-NEXT: switch i32 [[SWITCHCOND_0_US]], label [[CONTROL_OUTER_LOOPEXIT_US_LCSSA_US:%.*]] [ -; CHECK-NEXT: i32 0, label [[BB0_US]] -; CHECK-NEXT: i32 1, label [[BB1_US_LCSSA_US:%.*]] -; CHECK-NEXT: i32 3, label [[BB3_US]] +; CHECK-NEXT: switch i32 [[SWITCHCOND_0_PH_US]], label [[CONTROL_OUTER_LOOPEXIT_US_LCSSA_US:%.*]] [ ; CHECK-NEXT: i32 4, label [[BB4_US_LCSSA_US:%.*]] +; CHECK-NEXT: i32 3, label [[BB3_US]] ; CHECK-NEXT: ] ; CHECK: control.outer.loopexit.us-lcssa.us: ; CHECK-NEXT: br label [[CONTROL_OUTER_LOOPEXIT]] -; CHECK: bb1.us-lcssa.us: -; CHECK-NEXT: br label [[BB1:%.*]] ; CHECK: bb4.us-lcssa.us: ; CHECK-NEXT: br label [[BB4:%.*]] ; CHECK: control.outer: @@ -79,7 +72,7 @@ define i32 @test2() nounwind readnone ssp { ; CHECK: bb0: ; CHECK-NEXT: br label [[CONTROL]] ; CHECK: bb1.us-lcssa: -; CHECK-NEXT: br label [[BB1]] +; CHECK-NEXT: br label [[BB1:%.*]] ; CHECK: bb1: ; CHECK-NEXT: ret i32 0 ; From c67a05934b127af6faf383e84f594d7196ff951e Mon Sep 17 00:00:00 2001 From: Grigory Pastukhov Date: Tue, 8 Oct 2024 22:26:10 -0700 Subject: [PATCH 4/5] Implemented tracking monotonicity --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 223 ++++++++++++++++-- .../test/Transforms/SCCP/conditions-ranges.ll | 4 +- llvm/test/Transforms/SCCP/loop-removal.ll | 166 ++++++++++++- llvm/test/Transforms/SCCP/widening.ll | 4 +- 4 files changed, 375 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 0bd5f2e66d942..f76638a8c56fd 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -455,6 +455,53 @@ class GuaranteedBoundsPropagator { const SmallVector &IncomingValuesFromActiveBranches); }; +// The MonotonicityTracker class evaluates whether a variable x, which is +// subject to various functional transformations within a loop, maintains a +// consistent pattern of monotonicity. Monotonicity, in this context, refers +// to the property of a sequence where its elements consistently increase +// or decrease. When a variable x enters a loop and is transformed by a series +// of operations before it merges back at a PHI node, this class checks if all +// transformations applied to x preserve the same type of monotonicity +// (either increasing or decreasing). If the monotonicity is consistent and +// increasing, for instance, it can be inferred that the value of x at the end +// of the loop will always be greater than its initial value at the start +// of the loop. +class MonotonicityTracker { +public: + enum class Monotonicity { + UNKNOWN, + CONSTANT, + INCREASING, + DECREASING, + }; + + Monotonicity getMonotonicity(Value *V) const { + if (isa(V)) + return Monotonicity::CONSTANT; + auto It = MonotonicityMap.find(V); + if (It == MonotonicityMap.end()) + return Monotonicity::UNKNOWN; + return It->second; + } + + void processSSACopyIntrinsic(CallBase &CB) { + Value *Op = CB.getOperand(0); + Monotonicity OpMonotonicity = getMonotonicity(Op); + if (OpMonotonicity == Monotonicity::UNKNOWN) + return; + MonotonicityMap[&CB] = OpMonotonicity; + } + + void processPhiNode( + PHINode &PN, + const SmallVector &IncomingValuesFromActiveBranches); + + void processBinaryOp(Instruction &I); + +private: + DenseMap MonotonicityMap; +}; + /// Helper class for SCCPSolver. This implements the instruction visitor and /// holds all the state. class SCCPInstVisitor : public InstVisitor { @@ -524,6 +571,8 @@ class SCCPInstVisitor : public InstVisitor { GuaranteedBoundsPropagator BoundsPropagator; + MonotonicityTracker MonotonicityTrackerObj; + LLVMContext &Ctx; private: @@ -989,6 +1038,11 @@ class SCCPInstVisitor : public InstVisitor { } Invalidated.clear(); } + + bool mergeInWithBoundsOverrideForPhi( + PHINode &PN, + const SmallVector &IncomingValuesFromActiveBranches, + ValueLatticeElement &PhiState); }; } // namespace llvm @@ -1297,6 +1351,65 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const { return KnownFeasibleEdges.count(Edge(From, To)); } +bool SCCPInstVisitor::mergeInWithBoundsOverrideForPhi( + PHINode &PN, + const SmallVector &IncomingValuesFromActiveBranches, + ValueLatticeElement &PhiState) { + if (!PhiState.isConstantRange(false)) + return false; + auto &OldState = getValueState(&PN); + if (!OldState.isConstantRange(false)) + return false; + ConstantRange OldStateRange = OldState.getConstantRange(); + ConstantRange NewStateRange = PhiState.getConstantRange(); + unsigned NumActiveIncoming = IncomingValuesFromActiveBranches.size(); + if (OldStateRange == NewStateRange || + OldState.getNumRangeExtensions() <= NumActiveIncoming) + return false; + + // At this point we visited Phi node too many times and need + // to extend the range + MonotonicityTracker::Monotonicity Monotonicity = + MonotonicityTrackerObj.getMonotonicity(&PN); + + // Monotonicity cannot be CONSTANT, otherwise the range should not + // have changed + if (Monotonicity == MonotonicityTracker::Monotonicity::INCREASING && + OldStateRange.getUpper() == NewStateRange.getUpper()) { + mergeInValue(&PN, PhiState); + return true; + } + + if (Monotonicity == MonotonicityTracker::Monotonicity::DECREASING && + OldStateRange.getLower() == NewStateRange.getLower()) { + mergeInValue(&PN, PhiState); + return true; + } + + auto OptionalBestBounds = + BoundsPropagator.processPhiNode(&PN, IncomingValuesFromActiveBranches); + if (!OptionalBestBounds) + return false; + if (Monotonicity == MonotonicityTracker::Monotonicity::INCREASING) { + mergeInValue( + &PN, ValueLatticeElement::getRange(ConstantRange( + NewStateRange.getLower(), OptionalBestBounds->getUpper()))); + return true; + } + + if (Monotonicity == MonotonicityTracker::Monotonicity::DECREASING && + OptionalBestBounds) { + mergeInValue( + &PN, ValueLatticeElement::getRange(ConstantRange( + OptionalBestBounds->getLower(), NewStateRange.getUpper()))); + return true; + } + + PhiState = ValueLatticeElement::getRange(*OptionalBestBounds); + mergeInValue(&PN, PhiState); + return true; +} + // visit Implementations - Something changed in this instruction, either an // operand made a transition, or the instruction is newly executable. Change // the value type of I to reflect these changes if appropriate. This method @@ -1348,26 +1461,14 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { break; } + MonotonicityTrackerObj.processPhiNode(PN, IncomingValuesFromActiveBranches); + // If we have visited this PHI node too many times, we first check // if there is a known bounds we could use. If not, the state will // be maked as overdefined. - auto OptionalBestBounds = - BoundsPropagator.processPhiNode(&PN, IncomingValuesFromActiveBranches); - auto &OldState = getValueState(&PN); - if (OptionalBestBounds && PhiState.isConstantRange() && - OldState.isConstantRange()) { - ConstantRange OldStateRange = OldState.getConstantRange(); - ConstantRange NewStateRange = PhiState.getConstantRange(); - if (OldStateRange != NewStateRange && - OldState.getNumRangeExtensions() > NumActiveIncoming) { - PhiState = ValueLatticeElement::getRange(*OptionalBestBounds); - mergeInValue(&PN, PhiState); - ValueLatticeElement &PhiStateRef = getValueState(&PN); - PhiStateRef.setNumRangeExtensions( - std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions())); - return; - } - } + if (mergeInWithBoundsOverrideForPhi(PN, IncomingValuesFromActiveBranches, + PhiState)) + return; // We allow up to 1 range extension per active incoming value and one // additional extension. Note that we manually adjust the number of range @@ -1642,6 +1743,8 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) { if (V1State.isOverdefined() && V2State.isOverdefined()) return (void)markOverdefined(&I); + MonotonicityTrackerObj.processBinaryOp(I); + // If either of the operands is a constant, try to fold it to a constant. // TODO: Use information from notconstant better. if ((V1State.isConstant() || V2State.isConstant())) { @@ -1947,6 +2050,7 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) { if (auto *II = dyn_cast(&CB)) { if (II->getIntrinsicID() == Intrinsic::ssa_copy) { + MonotonicityTrackerObj.processSSACopyIntrinsic(CB); if (ValueState[&CB].isOverdefined()) return; @@ -2404,3 +2508,88 @@ std::optional GuaranteedBoundsPropagator::processPhiNode( insertOrUpdate(PN, R); return R; } + +void MonotonicityTracker::processPhiNode( + PHINode &PN, + const SmallVector &IncomingValuesFromActiveBranches) { + Monotonicity MI = Monotonicity::UNKNOWN; + // If all incoming values have the same monotonicity, then the phi + // node will be assinged the same monotonicity as well. However, if + // there are different monotonicities or unknown for some incoming values, + // then the phi node will be assigned UNKNOWN. + for (Value *IncomingValue : IncomingValuesFromActiveBranches) { + auto IncomingMonotonicity = getMonotonicity(IncomingValue); + if (IncomingMonotonicity == Monotonicity::UNKNOWN) { + MI = IncomingMonotonicity; + break; + } + if (IncomingMonotonicity == Monotonicity::CONSTANT && + MI != Monotonicity::UNKNOWN) + continue; + if (MI != IncomingMonotonicity && MI != Monotonicity::UNKNOWN && + MI != Monotonicity::CONSTANT) { + MI = Monotonicity::UNKNOWN; + break; + } + if (MI == Monotonicity::UNKNOWN || MI == Monotonicity::CONSTANT) + MI = IncomingMonotonicity; + } + MonotonicityMap[&PN] = MI; +} + +static bool increasingOrConst(MonotonicityTracker::Monotonicity M) { + return M == MonotonicityTracker::Monotonicity::INCREASING || + M == MonotonicityTracker::Monotonicity::CONSTANT; +} + +static bool decreasingOrConst(MonotonicityTracker::Monotonicity M) { + return M == MonotonicityTracker::Monotonicity::DECREASING || + M == MonotonicityTracker::Monotonicity::CONSTANT; +} + +void MonotonicityTracker::processBinaryOp(Instruction &I) { + auto *BO = dyn_cast(&I); + if (!I.getType()->isIntegerTy()) + return; + if (!BO || !(BO->hasNoSignedWrap() || BO->hasNoUnsignedWrap())) + return; + if (isa(BO->getOperand(0)) && isa(BO->getOperand(1))) { + MonotonicityMap[&I] = Monotonicity::CONSTANT; + return; + } + if (!isa(BO->getOperand(0)) && !isa(BO->getOperand(1))) { + MonotonicityMap[&I] = Monotonicity::UNKNOWN; + return; + } + // Here one operand must be a constant and the other must be a variable. + ConstantInt *C; + Value *NonConstOp; + if (isa(BO->getOperand(0))) { + C = cast(BO->getOperand(0)); + NonConstOp = BO->getOperand(1); + } else { + C = cast(BO->getOperand(1)); + NonConstOp = BO->getOperand(0); + } + Monotonicity NonConstOpMonotonicity = getMonotonicity(NonConstOp); + if (BO->getOpcode() == Instruction::Add) { + if (increasingOrConst(NonConstOpMonotonicity) && !C->isNegative()) { + MonotonicityMap[&I] = Monotonicity::INCREASING; + return; + } else if (decreasingOrConst(NonConstOpMonotonicity) && C->isNegative()) { + MonotonicityMap[&I] = Monotonicity::DECREASING; + return; + } + } + // Handle subtraction only if it is in the form of "x - constant". + if (BO->getOpcode() == Instruction::Sub && + !isa(BO->getOperand(0))) { + if (decreasingOrConst(NonConstOpMonotonicity) && !C->isNegative()) { + MonotonicityMap[&I] = Monotonicity::DECREASING; + return; + } else if (increasingOrConst(NonConstOpMonotonicity) && C->isNegative()) { + MonotonicityMap[&I] = Monotonicity::INCREASING; + return; + } + } +} diff --git a/llvm/test/Transforms/SCCP/conditions-ranges.ll b/llvm/test/Transforms/SCCP/conditions-ranges.ll index bb3764160f724..829598c7b14f6 100644 --- a/llvm/test/Transforms/SCCP/conditions-ranges.ll +++ b/llvm/test/Transforms/SCCP/conditions-ranges.ll @@ -675,7 +675,7 @@ define void @loop_1() { ; CHECK: for.cond11: ; CHECK-NEXT: br label [[FOR_COND_CLEANUP13]] ; CHECK: for.cond.cleanup13: -; CHECK-NEXT: [[INC27]] = add nsw i32 [[I_0]], 1 +; CHECK-NEXT: [[INC27]] = add nuw nsw i32 [[I_0]], 1 ; CHECK-NEXT: br label [[FOR_COND]] ; entry: @@ -718,7 +718,7 @@ define void @loop() { ; CHECK-NEXT: [[CMP12:%.*]] = icmp slt i32 [[J_0]], 2 ; CHECK-NEXT: br i1 [[CMP12]], label [[FOR_BODY14]], label [[FOR_COND_CLEANUP13]] ; CHECK: for.cond.cleanup13: -; CHECK-NEXT: [[INC27]] = add nsw i32 [[I_0]], 1 +; CHECK-NEXT: [[INC27]] = add nuw nsw i32 [[I_0]], 1 ; CHECK-NEXT: br label [[FOR_COND]] ; CHECK: for.body14: ; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[J_0]], 1 diff --git a/llvm/test/Transforms/SCCP/loop-removal.ll b/llvm/test/Transforms/SCCP/loop-removal.ll index 05f7a096734ef..5a933d22742c9 100644 --- a/llvm/test/Transforms/SCCP/loop-removal.ll +++ b/llvm/test/Transforms/SCCP/loop-removal.ll @@ -12,7 +12,7 @@ define i32 @foo() { ; CHECK: inner.loop.control: ; CHECK-NEXT: br label [[OUTER_LOOP_INC]] ; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_0]], 2 +; CHECK-NEXT: [[X_OUTER]] = add nuw nsw i32 [[X_0]], 2 ; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] ; CHECK: exit: ; CHECK-NEXT: ret i32 [[X_0]] @@ -41,3 +41,167 @@ outer.loop.inc: ; preds = %inner. exit: ; preds = %1 ret i32 %x.0 } + +define i32 @test_monotonicity_add() { +; CHECK-LABEL: @test_monotonicity_add( +; CHECK-NEXT: init: +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] +; CHECK: outer.loop.control: +; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 +; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] +; CHECK: inner.loop.control: +; CHECK-NEXT: br label [[OUTER_LOOP_INC]] +; CHECK: outer.loop.inc: +; CHECK-NEXT: [[X_OUTER]] = add nuw nsw i32 [[X_0]], 2 +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[X_0]] +; +init: + br label %outer.loop.control + +outer.loop.control: ; preds = %init, %outer.loop.inc + %x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] + %0 = icmp slt i32 %x.0, 10 + br i1 %0, label %inner.loop.control, label %exit + +inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body + %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] + %1 = icmp slt i32 %x.1, -5 + br i1 %1, label %inner.loop.body, label %outer.loop.inc + +inner.loop.body: ; preds = %inner.loop.control + %x.inner = sub nsw i32 %x.1, 1 + br label %inner.loop.control + +outer.loop.inc: ; preds = %inner.loop.control + %x.outer = add nsw i32 %x.1, 2 + br label %outer.loop.control + +exit: ; preds = %1 + ret i32 %x.0 +} + +define i32 @test_monotonicity_add_negative() { +; CHECK-LABEL: @test_monotonicity_add_negative( +; CHECK-NEXT: init: +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] +; CHECK: outer.loop.control: +; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 100, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X_0]], 10 +; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] +; CHECK: inner.loop.control: +; CHECK-NEXT: br label [[OUTER_LOOP_INC]] +; CHECK: outer.loop.inc: +; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_0]], -2 +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[X_0]] +; +init: + br label %outer.loop.control + +outer.loop.control: ; preds = %init, %outer.loop.inc + %x.0 = phi i32 [ 100, %init ], [ %x.outer, %outer.loop.inc ] + %0 = icmp sgt i32 %x.0, 10 + br i1 %0, label %inner.loop.control, label %exit + +inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body + %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] + %1 = icmp sgt i32 %x.1, 150 + br i1 %1, label %inner.loop.body, label %outer.loop.inc + +inner.loop.body: ; preds = %inner.loop.control + %x.inner = sub nsw i32 %x.1, 1 + br label %inner.loop.control + +outer.loop.inc: ; preds = %inner.loop.control + %x.outer = add nsw i32 %x.1, -2 + br label %outer.loop.control + +exit: ; preds = %1 + ret i32 %x.0 +} + +define i32 @test_monotonicity_sub() { +; CHECK-LABEL: @test_monotonicity_sub( +; CHECK-NEXT: init: +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] +; CHECK: outer.loop.control: +; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 100, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X_0]], 10 +; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] +; CHECK: inner.loop.control: +; CHECK-NEXT: br label [[OUTER_LOOP_INC]] +; CHECK: outer.loop.inc: +; CHECK-NEXT: [[X_OUTER]] = sub nuw nsw i32 [[X_0]], 2 +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[X_0]] +; +init: + br label %outer.loop.control + +outer.loop.control: ; preds = %init, %outer.loop.inc + %x.0 = phi i32 [ 100, %init ], [ %x.outer, %outer.loop.inc ] + %0 = icmp sgt i32 %x.0, 10 + br i1 %0, label %inner.loop.control, label %exit + +inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body + %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] + %1 = icmp sgt i32 %x.1, 150 + br i1 %1, label %inner.loop.body, label %outer.loop.inc + +inner.loop.body: ; preds = %inner.loop.control + %x.inner = sub nsw i32 %x.1, 1 + br label %inner.loop.control + +outer.loop.inc: ; preds = %inner.loop.control + %x.outer = sub nsw i32 %x.1, 2 + br label %outer.loop.control + +exit: ; preds = %1 + ret i32 %x.0 +} + +define i32 @test_monotonicity_sub_negative() { +; CHECK-LABEL: @test_monotonicity_sub_negative( +; CHECK-NEXT: init: +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] +; CHECK: outer.loop.control: +; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 +; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] +; CHECK: inner.loop.control: +; CHECK-NEXT: br label [[OUTER_LOOP_INC]] +; CHECK: outer.loop.inc: +; CHECK-NEXT: [[X_OUTER]] = sub nsw i32 [[X_0]], -2 +; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[X_0]] +; +init: + br label %outer.loop.control + +outer.loop.control: ; preds = %init, %outer.loop.inc + %x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] + %0 = icmp slt i32 %x.0, 10 + br i1 %0, label %inner.loop.control, label %exit + +inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body + %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] + %1 = icmp slt i32 %x.1, -5 + br i1 %1, label %inner.loop.body, label %outer.loop.inc + +inner.loop.body: ; preds = %inner.loop.control + %x.inner = sub nsw i32 %x.1, 1 + br label %inner.loop.control + +outer.loop.inc: ; preds = %inner.loop.control + %x.outer = sub nsw i32 %x.1, -2 + br label %outer.loop.control + +exit: ; preds = %1 + ret i32 %x.0 +} diff --git a/llvm/test/Transforms/SCCP/widening.ll b/llvm/test/Transforms/SCCP/widening.ll index 144c3fd54e75b..3679a93ac385c 100644 --- a/llvm/test/Transforms/SCCP/widening.ll +++ b/llvm/test/Transforms/SCCP/widening.ll @@ -406,7 +406,7 @@ define void @loop_with_header_2(i32 %x) { ; IPSCCP-NEXT: br i1 [[C_1]], label [[LOOP_BODY]], label [[EXIT:%.*]] ; IPSCCP: loop.body: ; IPSCCP-NEXT: call void @use(i1 true) -; IPSCCP-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 +; IPSCCP-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 ; IPSCCP-NEXT: br label [[LOOP_HEADER]] ; IPSCCP: exit: ; IPSCCP-NEXT: ret void @@ -706,7 +706,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; IPSCCP-NEXT: [[TMP28:%.*]] = icmp eq i32 [[TMP27]], [[TMP3]] ; IPSCCP-NEXT: br i1 [[TMP28]], label [[BB31]], label [[BB29]] ; IPSCCP: bb29: -; IPSCCP-NEXT: [[TMP30]] = add nsw i32 [[TMP11]], 1 +; IPSCCP-NEXT: [[TMP30]] = add nuw nsw i32 [[TMP11]], 1 ; IPSCCP-NEXT: br label [[BB8]] ; IPSCCP: bb31: ; IPSCCP-NEXT: [[TMP32:%.*]] = phi ptr [ [[TMP17]], [[BB23]] ], [ [[TMP17]], [[BB25]] ], [ [[TMP9]], [[BB8]] ] From d83f18ca0e7b3f7634c82327662ed57b24ec5590 Mon Sep 17 00:00:00 2001 From: Grigory Pastukhov Date: Wed, 9 Oct 2024 16:20:35 -0700 Subject: [PATCH 5/5] Reverting: Implement tracking monotonicity --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 223 ++---------------- .../test/Transforms/SCCP/conditions-ranges.ll | 4 +- llvm/test/Transforms/SCCP/loop-removal.ll | 166 +------------ llvm/test/Transforms/SCCP/widening.ll | 4 +- 4 files changed, 22 insertions(+), 375 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index f76638a8c56fd..0bd5f2e66d942 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -455,53 +455,6 @@ class GuaranteedBoundsPropagator { const SmallVector &IncomingValuesFromActiveBranches); }; -// The MonotonicityTracker class evaluates whether a variable x, which is -// subject to various functional transformations within a loop, maintains a -// consistent pattern of monotonicity. Monotonicity, in this context, refers -// to the property of a sequence where its elements consistently increase -// or decrease. When a variable x enters a loop and is transformed by a series -// of operations before it merges back at a PHI node, this class checks if all -// transformations applied to x preserve the same type of monotonicity -// (either increasing or decreasing). If the monotonicity is consistent and -// increasing, for instance, it can be inferred that the value of x at the end -// of the loop will always be greater than its initial value at the start -// of the loop. -class MonotonicityTracker { -public: - enum class Monotonicity { - UNKNOWN, - CONSTANT, - INCREASING, - DECREASING, - }; - - Monotonicity getMonotonicity(Value *V) const { - if (isa(V)) - return Monotonicity::CONSTANT; - auto It = MonotonicityMap.find(V); - if (It == MonotonicityMap.end()) - return Monotonicity::UNKNOWN; - return It->second; - } - - void processSSACopyIntrinsic(CallBase &CB) { - Value *Op = CB.getOperand(0); - Monotonicity OpMonotonicity = getMonotonicity(Op); - if (OpMonotonicity == Monotonicity::UNKNOWN) - return; - MonotonicityMap[&CB] = OpMonotonicity; - } - - void processPhiNode( - PHINode &PN, - const SmallVector &IncomingValuesFromActiveBranches); - - void processBinaryOp(Instruction &I); - -private: - DenseMap MonotonicityMap; -}; - /// Helper class for SCCPSolver. This implements the instruction visitor and /// holds all the state. class SCCPInstVisitor : public InstVisitor { @@ -571,8 +524,6 @@ class SCCPInstVisitor : public InstVisitor { GuaranteedBoundsPropagator BoundsPropagator; - MonotonicityTracker MonotonicityTrackerObj; - LLVMContext &Ctx; private: @@ -1038,11 +989,6 @@ class SCCPInstVisitor : public InstVisitor { } Invalidated.clear(); } - - bool mergeInWithBoundsOverrideForPhi( - PHINode &PN, - const SmallVector &IncomingValuesFromActiveBranches, - ValueLatticeElement &PhiState); }; } // namespace llvm @@ -1351,65 +1297,6 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const { return KnownFeasibleEdges.count(Edge(From, To)); } -bool SCCPInstVisitor::mergeInWithBoundsOverrideForPhi( - PHINode &PN, - const SmallVector &IncomingValuesFromActiveBranches, - ValueLatticeElement &PhiState) { - if (!PhiState.isConstantRange(false)) - return false; - auto &OldState = getValueState(&PN); - if (!OldState.isConstantRange(false)) - return false; - ConstantRange OldStateRange = OldState.getConstantRange(); - ConstantRange NewStateRange = PhiState.getConstantRange(); - unsigned NumActiveIncoming = IncomingValuesFromActiveBranches.size(); - if (OldStateRange == NewStateRange || - OldState.getNumRangeExtensions() <= NumActiveIncoming) - return false; - - // At this point we visited Phi node too many times and need - // to extend the range - MonotonicityTracker::Monotonicity Monotonicity = - MonotonicityTrackerObj.getMonotonicity(&PN); - - // Monotonicity cannot be CONSTANT, otherwise the range should not - // have changed - if (Monotonicity == MonotonicityTracker::Monotonicity::INCREASING && - OldStateRange.getUpper() == NewStateRange.getUpper()) { - mergeInValue(&PN, PhiState); - return true; - } - - if (Monotonicity == MonotonicityTracker::Monotonicity::DECREASING && - OldStateRange.getLower() == NewStateRange.getLower()) { - mergeInValue(&PN, PhiState); - return true; - } - - auto OptionalBestBounds = - BoundsPropagator.processPhiNode(&PN, IncomingValuesFromActiveBranches); - if (!OptionalBestBounds) - return false; - if (Monotonicity == MonotonicityTracker::Monotonicity::INCREASING) { - mergeInValue( - &PN, ValueLatticeElement::getRange(ConstantRange( - NewStateRange.getLower(), OptionalBestBounds->getUpper()))); - return true; - } - - if (Monotonicity == MonotonicityTracker::Monotonicity::DECREASING && - OptionalBestBounds) { - mergeInValue( - &PN, ValueLatticeElement::getRange(ConstantRange( - OptionalBestBounds->getLower(), NewStateRange.getUpper()))); - return true; - } - - PhiState = ValueLatticeElement::getRange(*OptionalBestBounds); - mergeInValue(&PN, PhiState); - return true; -} - // visit Implementations - Something changed in this instruction, either an // operand made a transition, or the instruction is newly executable. Change // the value type of I to reflect these changes if appropriate. This method @@ -1461,14 +1348,26 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { break; } - MonotonicityTrackerObj.processPhiNode(PN, IncomingValuesFromActiveBranches); - // If we have visited this PHI node too many times, we first check // if there is a known bounds we could use. If not, the state will // be maked as overdefined. - if (mergeInWithBoundsOverrideForPhi(PN, IncomingValuesFromActiveBranches, - PhiState)) - return; + auto OptionalBestBounds = + BoundsPropagator.processPhiNode(&PN, IncomingValuesFromActiveBranches); + auto &OldState = getValueState(&PN); + if (OptionalBestBounds && PhiState.isConstantRange() && + OldState.isConstantRange()) { + ConstantRange OldStateRange = OldState.getConstantRange(); + ConstantRange NewStateRange = PhiState.getConstantRange(); + if (OldStateRange != NewStateRange && + OldState.getNumRangeExtensions() > NumActiveIncoming) { + PhiState = ValueLatticeElement::getRange(*OptionalBestBounds); + mergeInValue(&PN, PhiState); + ValueLatticeElement &PhiStateRef = getValueState(&PN); + PhiStateRef.setNumRangeExtensions( + std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions())); + return; + } + } // We allow up to 1 range extension per active incoming value and one // additional extension. Note that we manually adjust the number of range @@ -1743,8 +1642,6 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) { if (V1State.isOverdefined() && V2State.isOverdefined()) return (void)markOverdefined(&I); - MonotonicityTrackerObj.processBinaryOp(I); - // If either of the operands is a constant, try to fold it to a constant. // TODO: Use information from notconstant better. if ((V1State.isConstant() || V2State.isConstant())) { @@ -2050,7 +1947,6 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) { if (auto *II = dyn_cast(&CB)) { if (II->getIntrinsicID() == Intrinsic::ssa_copy) { - MonotonicityTrackerObj.processSSACopyIntrinsic(CB); if (ValueState[&CB].isOverdefined()) return; @@ -2508,88 +2404,3 @@ std::optional GuaranteedBoundsPropagator::processPhiNode( insertOrUpdate(PN, R); return R; } - -void MonotonicityTracker::processPhiNode( - PHINode &PN, - const SmallVector &IncomingValuesFromActiveBranches) { - Monotonicity MI = Monotonicity::UNKNOWN; - // If all incoming values have the same monotonicity, then the phi - // node will be assinged the same monotonicity as well. However, if - // there are different monotonicities or unknown for some incoming values, - // then the phi node will be assigned UNKNOWN. - for (Value *IncomingValue : IncomingValuesFromActiveBranches) { - auto IncomingMonotonicity = getMonotonicity(IncomingValue); - if (IncomingMonotonicity == Monotonicity::UNKNOWN) { - MI = IncomingMonotonicity; - break; - } - if (IncomingMonotonicity == Monotonicity::CONSTANT && - MI != Monotonicity::UNKNOWN) - continue; - if (MI != IncomingMonotonicity && MI != Monotonicity::UNKNOWN && - MI != Monotonicity::CONSTANT) { - MI = Monotonicity::UNKNOWN; - break; - } - if (MI == Monotonicity::UNKNOWN || MI == Monotonicity::CONSTANT) - MI = IncomingMonotonicity; - } - MonotonicityMap[&PN] = MI; -} - -static bool increasingOrConst(MonotonicityTracker::Monotonicity M) { - return M == MonotonicityTracker::Monotonicity::INCREASING || - M == MonotonicityTracker::Monotonicity::CONSTANT; -} - -static bool decreasingOrConst(MonotonicityTracker::Monotonicity M) { - return M == MonotonicityTracker::Monotonicity::DECREASING || - M == MonotonicityTracker::Monotonicity::CONSTANT; -} - -void MonotonicityTracker::processBinaryOp(Instruction &I) { - auto *BO = dyn_cast(&I); - if (!I.getType()->isIntegerTy()) - return; - if (!BO || !(BO->hasNoSignedWrap() || BO->hasNoUnsignedWrap())) - return; - if (isa(BO->getOperand(0)) && isa(BO->getOperand(1))) { - MonotonicityMap[&I] = Monotonicity::CONSTANT; - return; - } - if (!isa(BO->getOperand(0)) && !isa(BO->getOperand(1))) { - MonotonicityMap[&I] = Monotonicity::UNKNOWN; - return; - } - // Here one operand must be a constant and the other must be a variable. - ConstantInt *C; - Value *NonConstOp; - if (isa(BO->getOperand(0))) { - C = cast(BO->getOperand(0)); - NonConstOp = BO->getOperand(1); - } else { - C = cast(BO->getOperand(1)); - NonConstOp = BO->getOperand(0); - } - Monotonicity NonConstOpMonotonicity = getMonotonicity(NonConstOp); - if (BO->getOpcode() == Instruction::Add) { - if (increasingOrConst(NonConstOpMonotonicity) && !C->isNegative()) { - MonotonicityMap[&I] = Monotonicity::INCREASING; - return; - } else if (decreasingOrConst(NonConstOpMonotonicity) && C->isNegative()) { - MonotonicityMap[&I] = Monotonicity::DECREASING; - return; - } - } - // Handle subtraction only if it is in the form of "x - constant". - if (BO->getOpcode() == Instruction::Sub && - !isa(BO->getOperand(0))) { - if (decreasingOrConst(NonConstOpMonotonicity) && !C->isNegative()) { - MonotonicityMap[&I] = Monotonicity::DECREASING; - return; - } else if (increasingOrConst(NonConstOpMonotonicity) && C->isNegative()) { - MonotonicityMap[&I] = Monotonicity::INCREASING; - return; - } - } -} diff --git a/llvm/test/Transforms/SCCP/conditions-ranges.ll b/llvm/test/Transforms/SCCP/conditions-ranges.ll index 829598c7b14f6..bb3764160f724 100644 --- a/llvm/test/Transforms/SCCP/conditions-ranges.ll +++ b/llvm/test/Transforms/SCCP/conditions-ranges.ll @@ -675,7 +675,7 @@ define void @loop_1() { ; CHECK: for.cond11: ; CHECK-NEXT: br label [[FOR_COND_CLEANUP13]] ; CHECK: for.cond.cleanup13: -; CHECK-NEXT: [[INC27]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: [[INC27]] = add nsw i32 [[I_0]], 1 ; CHECK-NEXT: br label [[FOR_COND]] ; entry: @@ -718,7 +718,7 @@ define void @loop() { ; CHECK-NEXT: [[CMP12:%.*]] = icmp slt i32 [[J_0]], 2 ; CHECK-NEXT: br i1 [[CMP12]], label [[FOR_BODY14]], label [[FOR_COND_CLEANUP13]] ; CHECK: for.cond.cleanup13: -; CHECK-NEXT: [[INC27]] = add nuw nsw i32 [[I_0]], 1 +; CHECK-NEXT: [[INC27]] = add nsw i32 [[I_0]], 1 ; CHECK-NEXT: br label [[FOR_COND]] ; CHECK: for.body14: ; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[J_0]], 1 diff --git a/llvm/test/Transforms/SCCP/loop-removal.ll b/llvm/test/Transforms/SCCP/loop-removal.ll index 5a933d22742c9..05f7a096734ef 100644 --- a/llvm/test/Transforms/SCCP/loop-removal.ll +++ b/llvm/test/Transforms/SCCP/loop-removal.ll @@ -12,7 +12,7 @@ define i32 @foo() { ; CHECK: inner.loop.control: ; CHECK-NEXT: br label [[OUTER_LOOP_INC]] ; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = add nuw nsw i32 [[X_0]], 2 +; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_0]], 2 ; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] ; CHECK: exit: ; CHECK-NEXT: ret i32 [[X_0]] @@ -41,167 +41,3 @@ outer.loop.inc: ; preds = %inner. exit: ; preds = %1 ret i32 %x.0 } - -define i32 @test_monotonicity_add() { -; CHECK-LABEL: @test_monotonicity_add( -; CHECK-NEXT: init: -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] -; CHECK: outer.loop.control: -; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 -; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] -; CHECK: inner.loop.control: -; CHECK-NEXT: br label [[OUTER_LOOP_INC]] -; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = add nuw nsw i32 [[X_0]], 2 -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] -; CHECK: exit: -; CHECK-NEXT: ret i32 [[X_0]] -; -init: - br label %outer.loop.control - -outer.loop.control: ; preds = %init, %outer.loop.inc - %x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] - %0 = icmp slt i32 %x.0, 10 - br i1 %0, label %inner.loop.control, label %exit - -inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body - %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] - %1 = icmp slt i32 %x.1, -5 - br i1 %1, label %inner.loop.body, label %outer.loop.inc - -inner.loop.body: ; preds = %inner.loop.control - %x.inner = sub nsw i32 %x.1, 1 - br label %inner.loop.control - -outer.loop.inc: ; preds = %inner.loop.control - %x.outer = add nsw i32 %x.1, 2 - br label %outer.loop.control - -exit: ; preds = %1 - ret i32 %x.0 -} - -define i32 @test_monotonicity_add_negative() { -; CHECK-LABEL: @test_monotonicity_add_negative( -; CHECK-NEXT: init: -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] -; CHECK: outer.loop.control: -; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 100, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X_0]], 10 -; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] -; CHECK: inner.loop.control: -; CHECK-NEXT: br label [[OUTER_LOOP_INC]] -; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_0]], -2 -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] -; CHECK: exit: -; CHECK-NEXT: ret i32 [[X_0]] -; -init: - br label %outer.loop.control - -outer.loop.control: ; preds = %init, %outer.loop.inc - %x.0 = phi i32 [ 100, %init ], [ %x.outer, %outer.loop.inc ] - %0 = icmp sgt i32 %x.0, 10 - br i1 %0, label %inner.loop.control, label %exit - -inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body - %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] - %1 = icmp sgt i32 %x.1, 150 - br i1 %1, label %inner.loop.body, label %outer.loop.inc - -inner.loop.body: ; preds = %inner.loop.control - %x.inner = sub nsw i32 %x.1, 1 - br label %inner.loop.control - -outer.loop.inc: ; preds = %inner.loop.control - %x.outer = add nsw i32 %x.1, -2 - br label %outer.loop.control - -exit: ; preds = %1 - ret i32 %x.0 -} - -define i32 @test_monotonicity_sub() { -; CHECK-LABEL: @test_monotonicity_sub( -; CHECK-NEXT: init: -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] -; CHECK: outer.loop.control: -; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 100, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[X_0]], 10 -; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] -; CHECK: inner.loop.control: -; CHECK-NEXT: br label [[OUTER_LOOP_INC]] -; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = sub nuw nsw i32 [[X_0]], 2 -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] -; CHECK: exit: -; CHECK-NEXT: ret i32 [[X_0]] -; -init: - br label %outer.loop.control - -outer.loop.control: ; preds = %init, %outer.loop.inc - %x.0 = phi i32 [ 100, %init ], [ %x.outer, %outer.loop.inc ] - %0 = icmp sgt i32 %x.0, 10 - br i1 %0, label %inner.loop.control, label %exit - -inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body - %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] - %1 = icmp sgt i32 %x.1, 150 - br i1 %1, label %inner.loop.body, label %outer.loop.inc - -inner.loop.body: ; preds = %inner.loop.control - %x.inner = sub nsw i32 %x.1, 1 - br label %inner.loop.control - -outer.loop.inc: ; preds = %inner.loop.control - %x.outer = sub nsw i32 %x.1, 2 - br label %outer.loop.control - -exit: ; preds = %1 - ret i32 %x.0 -} - -define i32 @test_monotonicity_sub_negative() { -; CHECK-LABEL: @test_monotonicity_sub_negative( -; CHECK-NEXT: init: -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] -; CHECK: outer.loop.control: -; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], [[OUTER_LOOP_INC:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 -; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] -; CHECK: inner.loop.control: -; CHECK-NEXT: br label [[OUTER_LOOP_INC]] -; CHECK: outer.loop.inc: -; CHECK-NEXT: [[X_OUTER]] = sub nsw i32 [[X_0]], -2 -; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] -; CHECK: exit: -; CHECK-NEXT: ret i32 [[X_0]] -; -init: - br label %outer.loop.control - -outer.loop.control: ; preds = %init, %outer.loop.inc - %x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] - %0 = icmp slt i32 %x.0, 10 - br i1 %0, label %inner.loop.control, label %exit - -inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body - %x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] - %1 = icmp slt i32 %x.1, -5 - br i1 %1, label %inner.loop.body, label %outer.loop.inc - -inner.loop.body: ; preds = %inner.loop.control - %x.inner = sub nsw i32 %x.1, 1 - br label %inner.loop.control - -outer.loop.inc: ; preds = %inner.loop.control - %x.outer = sub nsw i32 %x.1, -2 - br label %outer.loop.control - -exit: ; preds = %1 - ret i32 %x.0 -} diff --git a/llvm/test/Transforms/SCCP/widening.ll b/llvm/test/Transforms/SCCP/widening.ll index 3679a93ac385c..144c3fd54e75b 100644 --- a/llvm/test/Transforms/SCCP/widening.ll +++ b/llvm/test/Transforms/SCCP/widening.ll @@ -406,7 +406,7 @@ define void @loop_with_header_2(i32 %x) { ; IPSCCP-NEXT: br i1 [[C_1]], label [[LOOP_BODY]], label [[EXIT:%.*]] ; IPSCCP: loop.body: ; IPSCCP-NEXT: call void @use(i1 true) -; IPSCCP-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 +; IPSCCP-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 ; IPSCCP-NEXT: br label [[LOOP_HEADER]] ; IPSCCP: exit: ; IPSCCP-NEXT: ret void @@ -706,7 +706,7 @@ define ptr @wobble(ptr %arg, i32 %arg1) align 2 { ; IPSCCP-NEXT: [[TMP28:%.*]] = icmp eq i32 [[TMP27]], [[TMP3]] ; IPSCCP-NEXT: br i1 [[TMP28]], label [[BB31]], label [[BB29]] ; IPSCCP: bb29: -; IPSCCP-NEXT: [[TMP30]] = add nuw nsw i32 [[TMP11]], 1 +; IPSCCP-NEXT: [[TMP30]] = add nsw i32 [[TMP11]], 1 ; IPSCCP-NEXT: br label [[BB8]] ; IPSCCP: bb31: ; IPSCCP-NEXT: [[TMP32:%.*]] = phi ptr [ [[TMP17]], [[BB23]] ], [ [[TMP17]], [[BB25]] ], [ [[TMP9]], [[BB8]] ]