Index: llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp +++ llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -80,6 +80,7 @@ void eliminateIVComparison(ICmpInst *ICmp, Value *IVOperand); void eliminateIVRemainder(BinaryOperator *Rem, Value *IVOperand, bool IsSigned); + bool strengthenOverflowingOperation(BinaryOperator *OBO, Value *IVOperand); Instruction *splitOverflowIntrinsic(Instruction *IVUser, const DominatorTree *DT); @@ -271,6 +272,120 @@ return true; } +/// Annotate BO with nsw / nuw if it provably does not signed-overflow / +/// unsigned-overflow. Returns true if anything changed, false otherwise. +bool SimplifyIndvar::strengthenOverflowingOperation(BinaryOperator *BO, + Value *IVOperand) { + + // Currently we only handle instructions of the form "add " + // and "sub ". + unsigned Op = BO->getOpcode(); + if (!(Op == Instruction::Add || Op == Instruction::Sub)) + return false; + + // If BO is already both nuw and nsw then there is nothing left to do + if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap()) + return false; + + IntegerType *IT = cast(IVOperand->getType()); + Value *OtherOperand = nullptr; + int OtherOperandIdx = -1; + if (BO->getOperand(0) == IVOperand) { + OtherOperand = BO->getOperand(1); + OtherOperandIdx = 1; + } else { + assert(BO->getOperand(1) == IVOperand && "only other use!"); + OtherOperand = BO->getOperand(0); + OtherOperandIdx = 0; + } + + bool Changed = false; + const SCEV *OtherOpSCEV = SE->getSCEV(OtherOperand); + if (OtherOpSCEV == SE->getCouldNotCompute()) + return false; + + if (Op == Instruction::Sub) { + // If the subtraction is of the form "sub , ", then pretend it + // is "add , -" and continue, else bail out. + if (OtherOperandIdx != 1) + return false; + + OtherOpSCEV = SE->getNegativeSCEV(OtherOpSCEV); + } + + const SCEV *IVOpSCEV = SE->getSCEV(IVOperand); + const SCEV *ZeroSCEV = SE->getConstant(IVOpSCEV->getType(), 0); + + if (!BO->hasNoSignedWrap()) { + // Upgrade the add to an "add nsw" if we can prove that it will never + // sign-overflow or sign-underflow. + + const SCEV *SignedMax = + SE->getConstant(APInt::getSignedMaxValue(IT->getBitWidth())); + const SCEV *SignedMin = + SE->getConstant(APInt::getSignedMinValue(IT->getBitWidth())); + + // The addition "IVOperand + OtherOp" does not sign-overflow if the result + // is sign-representable in 2's complement in the given bit-width. + // + // If OtherOp is SLT 0, then for an IVOperand in [SignedMin - OtherOp, + // SignedMax], "IVOperand + OtherOp" is in [SignedMin, SignedMax + OtherOp]. + // Everything in [SignedMin, SignedMax + OtherOp] is representable since + // SignedMax + OtherOp is at least -1. + // + // If OtherOp is SGE 0, then for an IVOperand in [SignedMin, SignedMax - + // OtherOp], "IVOperand + OtherOp" is in [SignedMin + OtherOp, SignedMax]. + // Everything in [SignedMin + OtherOp, SignedMax] is representable since + // SignedMin + OtherOp is at most -1. + // + // It follows that for all values of IVOperand in [SignedMin - smin(0, + // OtherOp), SignedMax - smax(0, OtherOp)] the result of the add is + // representable (i.e. there is no sign-overflow). + + const SCEV *UpperDelta = SE->getSMaxExpr(ZeroSCEV, OtherOpSCEV); + const SCEV *UpperLimit = SE->getMinusSCEV(SignedMax, UpperDelta); + + bool NeverSignedOverflows = + SE->isKnownPredicate(ICmpInst::ICMP_SLE, IVOpSCEV, UpperLimit); + + if (NeverSignedOverflows) { + const SCEV *LowerDelta = SE->getSMinExpr(ZeroSCEV, OtherOpSCEV); + const SCEV *LowerLimit = SE->getMinusSCEV(SignedMin, LowerDelta); + + bool NeverSignedUnderflows = + SE->isKnownPredicate(ICmpInst::ICMP_SGE, IVOpSCEV, LowerLimit); + if (NeverSignedUnderflows) { + BO->setHasNoSignedWrap(true); + Changed = true; + } + } + } + + if (!BO->hasNoUnsignedWrap()) { + // Upgrade the add computing "IVOperand + OtherOp" to an "add nuw" if we can + // prove that it will never unsigned-overflow (i.e. the result will always + // be representable in the given bit-width). + // + // "IVOperand + OtherOp" is unsigned-representable in 2's complement iff it + // does not produce a carry. "IVOperand + OtherOp" produces no carry iff + // IVOperand ULE (UnsignedMax - OtherOp). + + const SCEV *UnsignedMax = + SE->getConstant(APInt::getMaxValue(IT->getBitWidth())); + const SCEV *UpperLimit = SE->getMinusSCEV(UnsignedMax, OtherOpSCEV); + + bool NeverUnsignedOverflows = + SE->isKnownPredicate(ICmpInst::ICMP_ULE, IVOpSCEV, UpperLimit); + + if (NeverUnsignedOverflows) { + BO->setHasNoUnsignedWrap(true); + Changed = true; + } + } + + return Changed; +} + /// \brief Split sadd.with.overflow into add + sadd.with.overflow to allow /// analysis and optimization. /// @@ -430,6 +545,16 @@ pushIVUsers(IVOperand, Simplified, SimpleIVUsers); continue; } + + if (BinaryOperator *BO = dyn_cast(UseOper.first)) { + if (isa(BO) && + strengthenOverflowingOperation(BO, IVOperand)) { + // re-queue uses of the now modified binary operator and fall + // through to the checks that remain. + pushIVUsers(IVOperand, Simplified, SimpleIVUsers); + } + } + CastInst *Cast = dyn_cast(UseOper.first); if (V && Cast) { V->visitCast(Cast); Index: llvm/trunk/test/Transforms/BBVectorize/loop1.ll =================================================================== --- llvm/trunk/test/Transforms/BBVectorize/loop1.ll +++ llvm/trunk/test/Transforms/BBVectorize/loop1.ll @@ -83,7 +83,7 @@ ; CHECK-UNRL: %add12 = fadd <2 x double> %add7, %mul11 ; CHECK-UNRL: %4 = bitcast double* %arrayidx14 to <2 x double>* ; CHECK-UNRL: store <2 x double> %add12, <2 x double>* %4, align 8 -; CHECK-UNRL: %indvars.iv.next.1 = add i64 %indvars.iv, 2 +; CHECK-UNRL: %indvars.iv.next.1 = add nsw i64 %indvars.iv, 2 ; CHECK-UNRL: %lftr.wideiv.1 = trunc i64 %indvars.iv.next.1 to i32 ; CHECK-UNRL: %exitcond.1 = icmp eq i32 %lftr.wideiv.1, 10 ; CHECK-UNRL: br i1 %exitcond.1, label %for.end, label %for.body Index: llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll =================================================================== --- llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll +++ llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll @@ -17,7 +17,7 @@ for.body153: ; preds = %for.body153, %for.body11 br i1 undef, label %for.body170, label %for.body153 -; CHECK: add nsw i64 %indvars.iv, 1 +; CHECK: add nuw nsw i64 %indvars.iv, 1 ; CHECK: sub nsw i64 %indvars.iv, 2 ; CHECK: sub nsw i64 4, %indvars.iv ; CHECK: mul nsw i64 %indvars.iv, 8 Index: llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll =================================================================== --- llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll +++ llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll @@ -0,0 +1,214 @@ +; RUN: opt < %s -indvars -S | FileCheck %s + +define i32 @test.signed.add.0(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.signed.add.0 + entry: + %upper = icmp slt i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = add i32 %civ, 1 +; CHECK: %civ.inc = add nsw i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp slt i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.signed.add.1(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.signed.add.1 + entry: + %upper = icmp sle i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = add i32 %civ, 1 +; CHECK: %civ.inc = add i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp slt i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.signed.sub.0(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.signed.sub.0 + entry: + %upper = icmp sgt i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = sub i32 %civ, 1 +; CHECK: %civ.inc = sub nsw i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp sgt i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.signed.sub.1(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.signed.sub.1 + entry: + %upper = icmp sgt i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = sub i32 %civ, 1 +; CHECK: %civ.inc = sub i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp sge i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.unsigned.add.0(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.unsigned.add.0 + entry: + %upper = icmp ult i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = add i32 %civ, 1 +; CHECK: %civ.inc = add nuw i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp ult i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.unsigned.add.1(i32* %array, i32 %length, i32 %init) { +; CHECK-LABEL: @test.unsigned.add.1 + entry: + %upper = icmp ule i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = add i32 %civ, 1 +; CHECK: %civ.inc = add i32 %civ, 1 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp ult i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.unsigned.sub.0(i32* %array, i32* %length_ptr, i32 %init) { +; CHECK-LABEL: @test.unsigned.sub.0 + entry: + %length = load i32* %length_ptr, !range !0 + %upper = icmp ult i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = sub i32 %civ, 2 +; CHECK: %civ.inc = sub nuw i32 %civ, 2 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp ult i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +define i32 @test.unsigned.sub.1(i32* %array, i32* %length_ptr, i32 %init) { +; CHECK-LABEL: @test.unsigned.sub.1 + entry: + %length = load i32* %length_ptr, !range !1 + %upper = icmp ult i32 %init, %length + br i1 %upper, label %loop, label %exit + + loop: +; CHECK-LABEL: loop + %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ] + %civ.inc = sub i32 %civ, 2 +; CHECK: %civ.inc = sub i32 %civ, 2 + %cmp = icmp slt i32 %civ.inc, %length + br i1 %cmp, label %latch, label %break + + latch: + store i32 0, i32* %array + %check = icmp ult i32 %civ.inc, %length + br i1 %check, label %loop, label %break + + break: + ret i32 %civ.inc + + exit: + ret i32 42 +} + +!0 = !{i32 0, i32 2} +!1 = !{i32 0, i32 42}