[llvm] r225282 - This patch teaches IndVarSimplify to add nuw and nsw to certain kinds

Sanjoy Das sanjoy at playingwithpointers.com
Tue Jan 6 12:16:51 PST 2015


That is what I tried initially, but OverflowingBinaryOperator is not a
BinaryOperator (AFAICT OverflowingBinaryOperator is an Operator while
BinaryOperator is an Instruction) or vice versa, so I'd have to check
for both independently anyway -- being one does not imply being the
other.

Operationally, checking for an OBO is pointless, because
strengthenOverflowingOperation will bail out if the opcode isn't Add
or Sub.  But semantically I wanted to preserve the invariant that
strengthenOverflowingOperation is called only with instructions that
may overflow.

On Tue, Jan 6, 2015 at 11:42 AM, David Majnemer
<david.majnemer at gmail.com> wrote:
>
>
> On Tue, Jan 6, 2015 at 11:02 AM, Sanjoy Das <sanjoy at playingwithpointers.com>
> wrote:
>>
>> Author: sanjoy
>> Date: Tue Jan  6 13:02:56 2015
>> New Revision: 225282
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=225282&view=rev
>> Log:
>> This patch teaches IndVarSimplify to add nuw and nsw to certain kinds
>> of operations that provably don't overflow. For example, we can prove
>> %civ.inc below does not sign-overflow. With this change,
>> IndVarSimplify changes %civ.inc to an add nsw.
>>
>>   define i32 @foo(i32* %array, i32* %length_ptr, i32 %init) {
>>    entry:
>>     %length = load i32* %length_ptr, !range !0
>>     %len.sub.1 = sub i32 %length, 1
>>     %upper = icmp slt i32 %init, %len.sub.1
>>     br i1 %upper, label %loop, label %exit
>>
>>    loop:
>>     %civ = phi i32 [ %init, %entry ], [ %civ.inc, %latch ]
>>     %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, %len.sub.1
>>     br i1 %check, label %loop, label %break
>>
>>    break:
>>     ret i32 %civ.inc
>>
>>    exit:
>>     ret i32 42
>>   }
>>
>> Differential Revision: http://reviews.llvm.org/D6748
>>
>>
>> Added:
>>     llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll
>> Modified:
>>     llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp
>>     llvm/trunk/test/Transforms/BBVectorize/loop1.ll
>>     llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll
>>
>> Modified: llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp?rev=225282&r1=225281&r2=225282&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp (original)
>> +++ llvm/trunk/lib/Transforms/Utils/SimplifyIndVar.cpp Tue Jan  6 13:02:56
>> 2015
>> @@ -80,6 +80,7 @@ namespace {
>>      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 @@ bool SimplifyIndvar::eliminateIVUser(Ins
>>    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 <indvar>
>> <value>"
>> +  // and "sub <indvar> <value>".
>> +  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<IntegerType>(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 <indvar>, <op>", then
>> pretend it
>> +    // is "add <indvar>, -<op>" 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 @@ void SimplifyIndvar::simplifyUsers(PHINo
>>        pushIVUsers(IVOperand, Simplified, SimpleIVUsers);
>>        continue;
>>      }
>> +
>> +    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(UseOper.first)) {
>> +      if (isa<OverflowingBinaryOperator>(BO) &&
>> +          strengthenOverflowingOperation(BO, IVOperand)) {
>
>
> Any reason why you don't just do:
> if (const auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO))
>   if (strengthenOverflowingOperation(OBO, 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<CastInst>(UseOper.first);
>>      if (V && Cast) {
>>        V->visitCast(Cast);
>>
>> Modified: llvm/trunk/test/Transforms/BBVectorize/loop1.ll
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/BBVectorize/loop1.ll?rev=225282&r1=225281&r2=225282&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/test/Transforms/BBVectorize/loop1.ll (original)
>> +++ llvm/trunk/test/Transforms/BBVectorize/loop1.ll Tue Jan  6 13:02:56
>> 2015
>> @@ -83,7 +83,7 @@ for.body:
>>  ; 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
>>
>> Modified:
>> llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll?rev=225282&r1=225281&r2=225282&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll
>> (original)
>> +++ llvm/trunk/test/Transforms/IndVarSimplify/2011-09-10-widen-nsw.ll Tue
>> Jan  6 13:02:56 2015
>> @@ -17,7 +17,7 @@ for.body11:
>>  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
>>
>> Added: llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll?rev=225282&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll
>> (added)
>> +++ llvm/trunk/test/Transforms/IndVarSimplify/strengthen-overflow.ll Tue
>> Jan  6 13:02:56 2015
>> @@ -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}
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
>



More information about the llvm-commits mailing list