Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -212,6 +212,14 @@ const Instruction *CxtI = nullptr, const DominatorTree *DT = nullptr); +/// Return true if V1 and V2 are known to mutually wrap - that is they wrap at +/// the same time. For example i8's (a+1) and (a+2) would mutually signed wrap +/// if the a%3==1, as they either both do not wrap or both wrap at the same +/// time. NSWWrap specifies whether it checks for signed (true) or unsigned +/// (false) wrapping. +bool isKnownMutuallyWrapping(const Value *V1, const Value *V2, bool NSWWrap, + const Instruction *ContextI = nullptr); + /// Map a call instruction to an intrinsic ID. Libcalls which have equivalent /// intrinsics are treated as-if they were intrinsics. Intrinsic::ID getIntrinsicForCallSite(const CallBase &CB, Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -3685,6 +3685,54 @@ return Intrinsic::not_intrinsic; } +static std::pair +getDomPredecessorCondition(const Instruction *ContextI); + +bool llvm::isKnownMutuallyWrapping(const Value *V1, const Value *V2, + bool NSWWrap, const Instruction *ContextI) { + if (!match(V1, m_Add(m_Value(), m_Value())) || + !match(V2, m_Add(m_Value(), m_Value()))) + return false; + + // Check the easy case where both Adds are NSW/NUW. + if (NSWWrap && cast(V1)->hasNoSignedWrap() && + cast(V2)->hasNoSignedWrap()) + return true; + if (!NSWWrap && cast(V1)->hasNoUnsignedWrap() && + cast(V2)->hasNoUnsignedWrap()) + return true; + + // Check for X+C1 and X+C2 + Value *X; + const APInt *C1, *C2, *Rem, *Denom; + if (ContextI && match(V1, m_Add(m_Value(X), m_APInt(C1))) && + match(V2, m_Add(m_Specific(X), m_APInt(C2))) && C1->isNonNegative() && + C2->isNonNegative()) { + auto PredCond = getDomPredecessorCondition(ContextI); + if (PredCond.first) { + ICmpInst::Predicate Pred; + // X+C1 and X+C2 will mutually wrap at the same time if we know the range + // of X is a given by a modulo and that the two adds will always fall on + // the same side of wrap: + // Limit = signed/unsigned max + 1 + // (Rem - (Limit % Denom) + C1) / Denom = (Rem - (Limit % Denom) + C2) / Denom + if (match(PredCond.first, + m_ICmp(Pred, m_SRem(m_Specific(X), m_APInt(Denom)), + m_APInt(Rem))) && + Pred == ICmpInst::ICMP_EQ && !Denom->isNegative()) { + unsigned BW = C1->getBitWidth(); + APInt Limit = + NSWWrap ? APInt::getSignedMaxValue(BW) : APInt::getAllOnes(BW); + Limit = (Limit.zext(BW + 1) + 1).urem(Denom->zext(BW + 1)).trunc(BW); + APInt Start = *Rem - Limit; + return (Start + *C1).udiv(*Denom) == (Start + *C2).udiv(*Denom); + } + } + } + + return false; +} + /// Deprecated, use computeKnownFPClass instead. /// /// If \p SignBitOnly is true, test for a known 0 sign bit rather than a Index: llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -2524,32 +2524,33 @@ } } - // sext(X + Y) - sext(X + Z) -> sext(Y) - sext(Z) - if ((match(Op0, m_SExt(m_NSWAdd(m_Value(X), m_Value(Y)))) && - match(Op1, m_SExt(m_c_Add(m_Specific(X), m_Value(Z))))) || - (match(Op0, m_SExt(m_NSWAdd(m_Value(Y), m_Value(X)))) && - match(Op1, m_SExt(m_c_Add(m_Specific(X), m_Value(Z)))))) { - Value *Add1 = cast(Op1)->getOperand(0); - if ((Op0->hasOneUse() || Op1->hasOneUse() || - (isa(Y) && isa(Z))) && - cast(Add1)->hasNoSignedWrap()) { - Value *S1 = Builder.CreateSExt(Y, Ty); - Value *S2 = Builder.CreateSExt(Z, Ty); - return BinaryOperator::CreateSub(S1, S2); - } - } - if ((match(Op0, m_ZExt(m_NUWAdd(m_Value(X), m_Value(Y)))) && - match(Op1, m_ZExt(m_c_Add(m_Specific(X), m_Value(Z))))) || - (match(Op0, m_ZExt(m_NUWAdd(m_Value(Y), m_Value(X)))) && - match(Op1, m_ZExt(m_c_Add(m_Specific(X), m_Value(Z)))))) { - Value *Add1 = cast(Op1)->getOperand(0); - if ((Op0->hasOneUse() || Op1->hasOneUse() || - (isa(Y) && isa(Z))) && - cast(Add1)->hasNoUnsignedWrap()) { - Value *S1 = Builder.CreateZExt(Y, Ty); - Value *S2 = Builder.CreateZExt(Z, Ty); - return BinaryOperator::CreateSub(S1, S2); - } + // sext(X + Y) - sext(X + Z) -> sext(Y) - sext(Z) if X+Y and X+Z are known to + // both wrap at the same time. + if (((match(Op0, m_SExt(m_Add(m_Value(X), m_Value(Y)))) && + match(Op1, m_SExt(m_c_Add(m_Specific(X), m_Value(Z))))) || + (match(Op0, m_SExt(m_Add(m_Value(Y), m_Value(X)))) && + match(Op1, m_SExt(m_c_Add(m_Specific(X), m_Value(Z)))))) && + (Op0->hasOneUse() || Op1->hasOneUse() || + (isa(Y) && isa(Z))) && + isKnownMutuallyWrapping(cast(Op0)->getOperand(0), + cast(Op1)->getOperand(0), true, + &I)) { + Value *S1 = Builder.CreateSExt(Y, Ty); + Value *S2 = Builder.CreateSExt(Z, Ty); + return BinaryOperator::CreateSub(S1, S2); + } + if (((match(Op0, m_ZExt(m_Add(m_Value(X), m_Value(Y)))) && + match(Op1, m_ZExt(m_c_Add(m_Specific(X), m_Value(Z))))) || + (match(Op0, m_ZExt(m_Add(m_Value(Y), m_Value(X)))) && + match(Op1, m_ZExt(m_c_Add(m_Specific(X), m_Value(Z)))))) && + (Op0->hasOneUse() || Op1->hasOneUse() || + (isa(Y) && isa(Z))) && + isKnownMutuallyWrapping(cast(Op0)->getOperand(0), + cast(Op1)->getOperand(0), false, + &I)) { + Value *S1 = Builder.CreateZExt(Y, Ty); + Value *S2 = Builder.CreateZExt(Z, Ty); + return BinaryOperator::CreateSub(S1, S2); } if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I)) Index: llvm/test/Transforms/InstCombine/sub-ext-add.ll =================================================================== --- llvm/test/Transforms/InstCombine/sub-ext-add.ll +++ llvm/test/Transforms/InstCombine/sub-ext-add.ll @@ -243,12 +243,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[B]], 1 ; CHECK-NEXT: br i1 [[C]], label [[BB:%.*]], label [[END:%.*]] ; CHECK: bb: -; CHECK-NEXT: [[A1:%.*]] = add i8 [[A]], 1 -; CHECK-NEXT: [[Z1:%.*]] = sext i8 [[A1]] to i32 -; CHECK-NEXT: [[A2:%.*]] = add i8 [[A]], 2 -; CHECK-NEXT: [[Z2:%.*]] = sext i8 [[A2]] to i32 -; CHECK-NEXT: [[S:%.*]] = sub nsw i32 [[Z2]], [[Z1]] -; CHECK-NEXT: ret i32 [[S]] +; CHECK-NEXT: ret i32 1 ; CHECK: end: ; CHECK-NEXT: ret i32 0 ; @@ -339,12 +334,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[B]], 1 ; CHECK-NEXT: br i1 [[C]], label [[BB:%.*]], label [[END:%.*]] ; CHECK: bb: -; CHECK-NEXT: [[A1:%.*]] = add i8 [[A]], 1 -; CHECK-NEXT: [[Z1:%.*]] = zext i8 [[A1]] to i32 -; CHECK-NEXT: [[A2:%.*]] = add i8 [[A]], 2 -; CHECK-NEXT: [[Z2:%.*]] = zext i8 [[A2]] to i32 -; CHECK-NEXT: [[S:%.*]] = sub nsw i32 [[Z2]], [[Z1]] -; CHECK-NEXT: ret i32 [[S]] +; CHECK-NEXT: ret i32 1 ; CHECK: end: ; CHECK-NEXT: ret i32 0 ; @@ -403,12 +393,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[B]], 0 ; CHECK-NEXT: br i1 [[C]], label [[BB:%.*]], label [[END:%.*]] ; CHECK: bb: -; CHECK-NEXT: [[A1:%.*]] = add i8 [[A]], 1 -; CHECK-NEXT: [[Z1:%.*]] = zext i8 [[A1]] to i32 -; CHECK-NEXT: [[A2:%.*]] = add i8 [[A]], 2 -; CHECK-NEXT: [[Z2:%.*]] = zext i8 [[A2]] to i32 -; CHECK-NEXT: [[S:%.*]] = sub nsw i32 [[Z2]], [[Z1]] -; CHECK-NEXT: ret i32 [[S]] +; CHECK-NEXT: ret i32 1 ; CHECK: end: ; CHECK-NEXT: ret i32 0 ; @@ -435,12 +420,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[B]], 1 ; CHECK-NEXT: br i1 [[C]], label [[BB:%.*]], label [[END:%.*]] ; CHECK: bb: -; CHECK-NEXT: [[A1:%.*]] = add i8 [[A]], 1 -; CHECK-NEXT: [[Z1:%.*]] = sext i8 [[A1]] to i32 -; CHECK-NEXT: [[A2:%.*]] = add i8 [[A]], 2 -; CHECK-NEXT: [[Z2:%.*]] = sext i8 [[A2]] to i32 -; CHECK-NEXT: [[S:%.*]] = sub nsw i32 [[Z2]], [[Z1]] -; CHECK-NEXT: ret i32 [[S]] +; CHECK-NEXT: ret i32 1 ; CHECK: end: ; CHECK-NEXT: ret i32 0 ;