[LLVMdev] confusion w.r.t. scalar evolution and nuw

Andrew Trick atrick at apple.com
Fri Jan 16 15:40:57 PST 2015


> On Jan 14, 2015, at 8:05 PM, Sanjoy Das <sanjoy at playingwithpointers.com> wrote:
> 
> I've been doing some digging in this area (scev, wrapping arithmetic),
> learning as much as I can, and have reached a point where I'm fairly
> confused about the semantics of nuw in scalar evolution expressions.
> Consider the following program:
> 
> define void @foo(i32 %begin) {
> entry:
>  br label %loop
> 
> loop:
>  %idx = phi i32 [ %begin, %entry ], [ %idx.dec, %loop ]
>  %idx.dec = sub nuw i32 %idx, 1
>  %exit.condition = icmp eq i32 %idx.dec, 0
>  br i1 %exit.condition, label %loop.exit, label %loop
> 
> loop.exit:
>  ret void
> }
> 
> As long as %begin is positive, %idx.dec is not poison and the function
> has defined behavior.
> 
> When I run opt -analyze -scalar-evolution on the IR, I get
> 
> Printing analysis 'Scalar Evolution Analysis' for function 'foo':
> Classifying expressions for: @foo
>  %idx = phi i32 [ %begin, %entry ], [ %idx.dec, %loop ]
>  -->  {%begin,+,-1}<nuw><%loop>                Exits: 1
>  %idx.dec = sub nuw i32 %idx, 1
>  -->  {(-1 + %begin),+,-1}<nuw><%loop>         Exits: 0
> Determining loop execution counts for: @foo
> Loop %loop: backedge-taken count is (-1 + %begin)
> Loop %loop: max backedge-taken count is -1
> 
> SCEV has mapped `%idx.dec' to `{(-1 + %begin),+,-1}<nuw>'!  This means
> SCEV thinks `%idx.dec' will be poison in the very first iteration if
> %begin is ult 1.
> 
> As an example on how this could blow up, if %begin was constant
> (or LLVM was got smarter about ranges someday and learned to handle
> non-constant ranges) in ScalarEvolution::getUnsignedRange, the
> following code could fire:
> 
>  if (const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S)) {
>    // If there's no unsigned wrap, the value will never be less than its
>    // initial value.
>    if (AddRec->getNoWrapFlags(SCEV::FlagNUW))
>      if (const SCEVConstant *C = dyn_cast<SCEVConstant>(AddRec->getStart()))
>        if (!C->getValue()->isZero())
>          ConservativeResult =
>            ConservativeResult.intersectWith(
>              ConstantRange(C->getValue()->getValue(), APInt(BitWidth, 0)));
> 
> and conclude that %idx.dec is always UGE %begin.  This is a real bug
> waiting to happen.
> 
> As far as I can tell, in the presence of no-wrap flags, 'sub X Y' is
> not the same as 'add X -Y', and subtractions can be expressed as
> additions only if they don't have any no-wrap flags on them.  While
> I have not thought about nsw in depth, but it is possible there are
> similar issues with nsw as well.
> 
> Is my reasoning broken at some point?  What gives?
> 
> -- Sanjoy


It looks like this change was incorrect:

@@ -3102,6 +3102,12 @@ const SCEV *ScalarEvolution::createNodeForPHI(PHINode *PN) {
                   if (isKnownPositive(getMinusSCEV(getSCEV(GEP), Ptr)))
                     Flags = setFlags(Flags, SCEV::FlagNUW);
                 }
+              } else if (const SubOperator *OBO =
+                           dyn_cast<SubOperator>(BEValueV)) {
+                if (OBO->hasNoUnsignedWrap())
+                  Flags = setFlags(Flags, SCEV::FlagNUW);
+                if (OBO->hasNoSignedWrap())
+                  Flags = setFlags(Flags, SCEV::FlagNSW);

I missed this during review, possibly because it was a small part of a larger patch. I should have caught it because I'm well aware of exactly the same problem that occurs with IR canonicalization in Reassociate.

To answer your question about general semantics, nsw/nuw flags are fundamentally unsound in SCEV. They should only be used in very specific IR patterns where we can prove safety (phi-add-phi). If those patterns weren't so important, we could just eliminate them and have a clean design. The hardest part about this is developers repeatedly want to improve SCEV by handling more nsw/nuw awareness. Naturally, there's a tendancy to build on the existing logic. Those improvements are likely wrong in subtle ways, and the burden is on a reviewer to spot a problem with it.

To improve nsw/nuw awareness, we need a different approach. I think we should have some IR-level analysis of nsw/nuw to augment trip count computation. At the moment, you're the best person to propose an improved design. I'm looking forward to seeing it ;)

-Andy



More information about the llvm-dev mailing list