diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h index bddd2cf78354b..68406c90dd27e 100644 --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -505,6 +505,17 @@ class ScalarEvolution { /// Erase Value from ValueExprMap and ExprValueMap. void eraseValueFromMap(Value *V); + /// Is operation \p BinOp between \p LHS and \p RHS provably does not have + /// a signed/unsigned overflow (\p Signed)? + bool willNotOverflow(Instruction::BinaryOps BinOp, bool Signed, + const SCEV *LHS, const SCEV *RHS); + + /// Parse NSW/NUW flags from add/sub/mul IR binary operation \p Op into + /// SCEV no-wrap flags, and deduce flag[s] that aren't known yet. + /// Does not mutate the original instruction. + std::pair + getStrengthenedNoWrapFlagsFromBinOp(const OverflowingBinaryOperator *OBO); + /// Return a SCEV expression for the full generality of the specified /// expression. const SCEV *getSCEV(Value *V); diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 0d3d781d6ff46..7a8369b9ea013 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -2244,6 +2244,81 @@ CollectAddOperandsWithScales(DenseMap &M, return Interesting; } +bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed, + const SCEV *LHS, const SCEV *RHS) { + const SCEV *(ScalarEvolution::*Operation)(const SCEV *, const SCEV *, + SCEV::NoWrapFlags, unsigned); + switch (BinOp) { + default: + llvm_unreachable("Unsupported binary op"); + case Instruction::Add: + Operation = &ScalarEvolution::getAddExpr; + break; + case Instruction::Sub: + Operation = &ScalarEvolution::getMinusSCEV; + break; + case Instruction::Mul: + Operation = &ScalarEvolution::getMulExpr; + break; + } + + const SCEV *(ScalarEvolution::*Extension)(const SCEV *, Type *, unsigned) = + Signed ? &ScalarEvolution::getSignExtendExpr + : &ScalarEvolution::getZeroExtendExpr; + + // Check ext(LHS op RHS) == ext(LHS) op ext(RHS) + auto *NarrowTy = cast(LHS->getType()); + auto *WideTy = + IntegerType::get(NarrowTy->getContext(), NarrowTy->getBitWidth() * 2); + + const SCEV *A = (this->*Extension)( + (this->*Operation)(LHS, RHS, SCEV::FlagAnyWrap, 0), WideTy, 0); + const SCEV *B = (this->*Operation)((this->*Extension)(LHS, WideTy, 0), + (this->*Extension)(RHS, WideTy, 0), + SCEV::FlagAnyWrap, 0); + return A == B; +} + +std::pair +ScalarEvolution::getStrengthenedNoWrapFlagsFromBinOp( + const OverflowingBinaryOperator *OBO) { + SCEV::NoWrapFlags Flags = SCEV::NoWrapFlags::FlagAnyWrap; + + if (OBO->hasNoUnsignedWrap()) + Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNUW); + if (OBO->hasNoSignedWrap()) + Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNSW); + + bool Deduced = false; + + if (OBO->hasNoUnsignedWrap() && OBO->hasNoSignedWrap()) + return {Flags, Deduced}; + + if (OBO->getOpcode() != Instruction::Add && + OBO->getOpcode() != Instruction::Sub && + OBO->getOpcode() != Instruction::Mul) + return {Flags, Deduced}; + + const SCEV *LHS = getSCEV(OBO->getOperand(0)); + const SCEV *RHS = getSCEV(OBO->getOperand(1)); + + if (!OBO->hasNoUnsignedWrap() && + willNotOverflow((Instruction::BinaryOps)OBO->getOpcode(), + /* Signed */ false, LHS, RHS)) { + Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNUW); + Deduced = true; + } + + if (!OBO->hasNoSignedWrap() && + willNotOverflow((Instruction::BinaryOps)OBO->getOpcode(), + /* Signed */ true, LHS, RHS)) { + Flags = ScalarEvolution::setFlags(Flags, SCEV::FlagNSW); + Deduced = true; + } + + return {Flags, Deduced}; +} + // We're trying to construct a SCEV of type `Type' with `Ops' as operands and // `OldFlags' as can't-wrap behavior. Infer a more aggressive set of // can't-overflow flags for the operation if possible. diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp index 246bc0adaf6c8..ff60667e72b3c 100644 --- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -422,46 +422,10 @@ void SimplifyIndvar::simplifyIVRemainder(BinaryOperator *Rem, Value *IVOperand, replaceSRemWithURem(Rem); } -static bool willNotOverflow(ScalarEvolution *SE, Instruction::BinaryOps BinOp, - bool Signed, const SCEV *LHS, const SCEV *RHS) { - const SCEV *(ScalarEvolution::*Operation)(const SCEV *, const SCEV *, - SCEV::NoWrapFlags, unsigned); - switch (BinOp) { - default: - llvm_unreachable("Unsupported binary op"); - case Instruction::Add: - Operation = &ScalarEvolution::getAddExpr; - break; - case Instruction::Sub: - Operation = &ScalarEvolution::getMinusSCEV; - break; - case Instruction::Mul: - Operation = &ScalarEvolution::getMulExpr; - break; - } - - const SCEV *(ScalarEvolution::*Extension)(const SCEV *, Type *, unsigned) = - Signed ? &ScalarEvolution::getSignExtendExpr - : &ScalarEvolution::getZeroExtendExpr; - - // Check ext(LHS op RHS) == ext(LHS) op ext(RHS) - auto *NarrowTy = cast(LHS->getType()); - auto *WideTy = - IntegerType::get(NarrowTy->getContext(), NarrowTy->getBitWidth() * 2); - - const SCEV *A = - (SE->*Extension)((SE->*Operation)(LHS, RHS, SCEV::FlagAnyWrap, 0), - WideTy, 0); - const SCEV *B = - (SE->*Operation)((SE->*Extension)(LHS, WideTy, 0), - (SE->*Extension)(RHS, WideTy, 0), SCEV::FlagAnyWrap, 0); - return A == B; -} - bool SimplifyIndvar::eliminateOverflowIntrinsic(WithOverflowInst *WO) { const SCEV *LHS = SE->getSCEV(WO->getLHS()); const SCEV *RHS = SE->getSCEV(WO->getRHS()); - if (!willNotOverflow(SE, WO->getBinaryOp(), WO->isSigned(), LHS, RHS)) + if (!SE->willNotOverflow(WO->getBinaryOp(), WO->isSigned(), LHS, RHS)) return false; // Proved no overflow, nuke the overflow check and, if possible, the overflow @@ -502,7 +466,7 @@ bool SimplifyIndvar::eliminateOverflowIntrinsic(WithOverflowInst *WO) { bool SimplifyIndvar::eliminateSaturatingIntrinsic(SaturatingInst *SI) { const SCEV *LHS = SE->getSCEV(SI->getLHS()); const SCEV *RHS = SE->getSCEV(SI->getRHS()); - if (!willNotOverflow(SE, SI->getBinaryOp(), SI->isSigned(), LHS, RHS)) + if (!SE->willNotOverflow(SI->getBinaryOp(), SI->isSigned(), LHS, RHS)) return false; BinaryOperator *BO = BinaryOperator::Create( @@ -756,37 +720,25 @@ bool SimplifyIndvar::eliminateIdentitySCEV(Instruction *UseInst, /// unsigned-overflow. Returns true if anything changed, false otherwise. bool SimplifyIndvar::strengthenOverflowingOperation(BinaryOperator *BO, Value *IVOperand) { - // Fastpath: we don't have any work to do if `BO` is `nuw` and `nsw`. - if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap()) - return false; - - if (BO->getOpcode() != Instruction::Add && - BO->getOpcode() != Instruction::Sub && - BO->getOpcode() != Instruction::Mul) - return false; - - const SCEV *LHS = SE->getSCEV(BO->getOperand(0)); - const SCEV *RHS = SE->getSCEV(BO->getOperand(1)); - bool Changed = false; - - if (!BO->hasNoUnsignedWrap() && - willNotOverflow(SE, BO->getOpcode(), /* Signed */ false, LHS, RHS)) { - BO->setHasNoUnsignedWrap(); - Changed = true; - } - - if (!BO->hasNoSignedWrap() && - willNotOverflow(SE, BO->getOpcode(), /* Signed */ true, LHS, RHS)) { - BO->setHasNoSignedWrap(); - Changed = true; - } - - // The willNotOverflow() check might infer additional nowrap flags on addrecs - // while performing zero/sign extensions. We could call forgetValue() here - // to make sure those flags also propagate to any other SCEV expressions - // based on the addrec. However, this can have pathological compile-time - // impact, see https://bugs.llvm.org/show_bug.cgi?id=50384. - return Changed; + SCEV::NoWrapFlags Flags; + bool Deduced; + std::tie(Flags, Deduced) = SE->getStrengthenedNoWrapFlagsFromBinOp( + cast(BO)); + + if (!Deduced) + return Deduced; + + BO->setHasNoUnsignedWrap(ScalarEvolution::maskFlags(Flags, SCEV::FlagNUW) == + SCEV::FlagNUW); + BO->setHasNoSignedWrap(ScalarEvolution::maskFlags(Flags, SCEV::FlagNSW) == + SCEV::FlagNSW); + + // The getStrengthenedNoWrapFlagsFromBinOp() check inferred additional nowrap + // flags on addrecs while performing zero/sign extensions. We could call + // forgetValue() here to make sure those flags also propagate to any other + // SCEV expressions based on the addrec. However, this can have pathological + // compile-time impact, see https://bugs.llvm.org/show_bug.cgi?id=50384. + return Deduced; } /// Annotate the Shr in (X << IVOperand) >> C as exact using the