This is an archive of the discontinued LLVM Phabricator instance.

[ConstantRange] Add `shlWithNoWrap()` method
AbandonedPublic

Authored by lebedev.ri on Dec 4 2019, 6:13 AM.

Details

Summary

This modelling isn't precise, much like the baseline shl() modelling,
so only conservative-correctness exaustive tests are performed.
Also, pending resolution of disscussion in D70043 whether or not
we should bother to ensure production of empty-set ranges for all-overflow inputs i've gone ahead with presumption that we will want that.

So all in all this is pretty similar to ConstantRange::mulWithNoWrap() modelling in D70043.

Diff Detail

Event Timeline

lebedev.ri created this revision.Dec 4 2019, 6:13 AM
lebedev.ri updated this revision to Diff 232121.Dec 4 2019, 6:46 AM

Simplify nsw all-wrap detection - we only need to check the .back() value
returned by getClosestToZero() - either there was a single value to begin with
and we are good either way, or there were two values and we are looking at the negative one.

nikic added a comment.Dec 8 2019, 8:41 AM

Unlike the mul case, it should be possible to efficiently compute a precise shl range. This is the implementation I came up with for the nuw case:

if (NoWrapKind == OBO::NoUnsignedWrap) {
  APInt Min = getUnsignedMin(), Max = getUnsignedMax();
  unsigned MinLeadingZeros = Max.countLeadingZeros();
  unsigned MaxLeadingZeros = Min.countLeadingZeros();
  unsigned ShAmtMax = ShAmt.getUnsignedMax().getLimitedValue();
  unsigned ShAmtMin = ShAmt.getUnsignedMin().getLimitedValue();
  if (ShAmtMin > MaxLeadingZeros)
    // All possible shifts will overflow.
    return getEmpty();

  APInt ResMin = Min.shl(ShAmtMin);
  // We'll pick the larger of two possible upper bounds, initialize to zero.
  APInt ResMax = APInt::getNullValue(BitWidth);
  // Shift the maximum as far as possible without overflowing.
  if (ShAmtMin <= MinLeadingZeros)
    ResMax = Max.shl(std::min(ShAmtMax, MinLeadingZeros));
  // Pick the largest number with all low bits sets that is both in the LHS
  // range and can be shifted without overflowing.
  if (MinLeadingZeros != MaxLeadingZeros && ShAmtMax >= MinLeadingZeros + 1) {
    unsigned Shift = std::max(ShAmtMin, MinLeadingZeros + 1);
    ResMax = APIntOps::umax(ResMax,
        APInt::getLowBitsSet(BitWidth, BitWidth - Shift).shl(Shift));
  }
  return getNonEmpty(ResMin, ResMax + 1);
}

It passes the exhaustive test without the correctness only flag.

Extending this to the nuw+nsw case should be fairly simple, as it basically just has one less usable bit. I haven't thought about the nsw case yet.

Variant with nuw+nsw support:

if (NoWrapKind & OBO::NoUnsignedWrap) {
  APInt Min = getUnsignedMin(), Max = getUnsignedMax();
  unsigned MinLeadingZeros = Max.countLeadingZeros();
  unsigned MaxLeadingZeros = Min.countLeadingZeros();

  // If additionally the nsw flag is set, we can treat this the same way as
  // nuw, but with one less bit available.
  unsigned EffectiveBitWidth = BitWidth;
  if (NoWrapKind & OBO::NoSignedWrap) {
    --EffectiveBitWidth;
    if (MinLeadingZeros != 0) --MinLeadingZeros;
    if (MaxLeadingZeros != 0) --MaxLeadingZeros;
  }

  unsigned ShAmtMax = ShAmt.getUnsignedMax().getLimitedValue();
  unsigned ShAmtMin = ShAmt.getUnsignedMin().getLimitedValue();
  if (ShAmtMin > MaxLeadingZeros)
    // All possible shifts will overflow.
    return getEmpty();

  APInt ResMin = Min.shl(ShAmtMin);
  // We'll pick the larger of two possible upper bounds, initialize to zero.
  APInt ResMax = APInt::getNullValue(BitWidth);
  // Shift the maximum as far as possible without overflowing.
  if (ShAmtMin <= MinLeadingZeros)
    ResMax = Max.shl(std::min(ShAmtMax, MinLeadingZeros));
  // Pick the largest number with all low bits sets that is both in the LHS
  // range and can be shifted without overflowing.
  if (MinLeadingZeros != MaxLeadingZeros && ShAmtMax >= MinLeadingZeros + 1) {
    unsigned Shift = std::max(ShAmtMin, MinLeadingZeros + 1);
    ResMax = APIntOps::umax(ResMax,
        APInt::getLowBitsSet(BitWidth, EffectiveBitWidth - Shift).shl(Shift));
  }
  return getNonEmpty(ResMin, ResMax + 1);
}

The nsw case is ... uh ... left as an exercise for the reader.

lebedev.ri abandoned this revision.Jan 25 2020, 5:34 AM