[PATCH] D151911: [LVI] Handle icmp of ashr.

Amara Emerson via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 21 12:41:51 PDT 2023


aemerson added inline comments.


================
Comment at: llvm/lib/Analysis/LazyValueInfo.cpp:1152
+  // Recognize:
+  // icmp sgt (ashr X, ShAmtC), C --> icmp sgt X, ((C + 1) << ShAmtC) - 1
+  // and friends.
----------------
nikic wrote:
> I think the better way to explain this is `icmp slt (ashr X, ShAmtC), C --> icmp slt X, C << ShAmtC` (https://alive2.llvm.org/ce/z/ww5u4i). In this case the only precondition is `((C << ShAmtC) >> ShAmtC) == C`. Same for sge.
> 
> All the +1/-1 get introduced by using sgt/sle because we're effectively trying to reduce them to that base case. This also introduces the extra preconditions to avoid overflow on +/-1.
> 
> With this in mind, I think we can introduce an abstraction like this to transparently handle all the signed predicates while only specifying the fold for slt (without knowledge of anything about ashr in particular):
> 
> ```
> static std::optional<ConstantRange> getRangeViaSLT(
>     CmpInst::Predicate Pred, APInt RHS,
>     function_ref<std::optional<ConstantRange>(const APInt &)> Fn) {
>   bool Invert = false;
>   if (Pred == ICmpInst::SGT || Pred == ICmpInst::SGE) {
>     Pred = ICmpInst::getInversePredicate(Pred);
>     Invert = true;
>   }
>   if (Pred == ICmpInst::SLE) {
>     Pred = ICmpInst::SLT;
>     if (RHS.isSignedMax())
>       return std::nullopt; // Could also return full/empty here, if we wanted.
>     ++RHS;
>   }
>   assert(Pred == ICmpInst::SLT && "Must be signed predicate");
>   if (auto CR = Fn(RHS))
>     return Invert ? CR->inverse() : CR;
>   return std::nullopt;
> }
> ```
> 
> And then do something like this:
> ```
>   const APInt *ShAmtC;
>   if (CmpInst::isSigned(EdgePred) &&
>       match(LHS, m_AShr(m_Specific(Val), m_APInt(ShAmtC))) &&
>       match(RHS, m_APInt(C))) {
>     return getRangeViaSLT(EdgePred, *C, [&](const APInt &RHS) {
>       APInt New = RHS << *ShAmtC;
>       if ((New.ashr(*ShAmtC)) != New)
>         return std::nullopt;
>       return ConstantRange::getNonEmpty(APInt:.getSignedMinValue(BW), New + 1);
>     });
>   }
> ```
> 
> Code is untested, but I think the general idea should work.
I've been looking at this and despite my efforts, I can't get this approach to generate the same constant ranges as my patch. Here's your implementation with some fixes and modifications to make it work for `sgt` but I can't see how you can just use one implementation that covers the permutations needed.

```
static std::optional<ConstantRange>
getRangeViaSLT(CmpInst::Predicate Pred, APInt RHS,
               function_ref<std::optional<ConstantRange>(const APInt &)> Fn) {
  bool Invert = false;
  if (Pred == ICmpInst::ICMP_SGT || Pred == ICmpInst::ICMP_SGE) {
    Pred = ICmpInst::getInversePredicate(Pred);
    Invert = true;
  }
  if (Pred == ICmpInst::ICMP_SLE) {
    Pred = ICmpInst::ICMP_SLT;
    if (RHS.isMaxSignedValue())
      return std::nullopt;
    ++RHS;
  }
  assert(Pred == ICmpInst::ICMP_SLT && "Must be signed predicate");
  if (auto CR = Fn(RHS)) {
    dbgs() << "Init CR: " << *CR << "\n";
    if (Invert) {
      CR = ConstantRange(CR->getUpper(), ~CR->getLower());
    }
    return CR;
  }
  return std::nullopt;
}
```

```
  // Recognize:
  // icmp sgt (ashr X, ShAmtC), C --> icmp sgt X, ((C + 1) << ShAmtC) - 1
  // and friends.
  // Preconditions: (C != SIGNED_MAX) &&
  //                ((C+1) << ShAmtC != SIGNED_MIN) &&
  //                (((C+1) << ShAmtC) >> ShAmtC) == (C+1)
  const APInt *ShAmtC;
  if (CmpInst::isSigned(EdgePred) &&
      match(LHS, m_AShr(m_Specific(Val), m_APInt(ShAmtC))) &&
      match(RHS, m_APInt(C))) {
    auto CR = getRangeViaSLT(
        EdgePred, *C, [&](const APInt &RHS) -> std::optional<ConstantRange> {
          APInt New = RHS << *ShAmtC;
          if ((New.ashr(*ShAmtC)) != RHS)
            return std::nullopt;
          return ConstantRange::getNonEmpty(
              APInt::getSignedMinValue(C->getBitWidth()), New/* + 1*/);
        });
    if (CR) {
      dbgs() << ICI->getFunction()->getName() << ": " << *ICI << "\n";
      dbgs() << "CR is " << *CR << "\n";
      return ValueLatticeElement::getRange(*CR);
    }
  }
```

I may have just misunderstood completely though. But from what I can tell, with the changes needed to make this correct, you'd just end up with code that's harder to follow and just as verbose as before?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D151911/new/

https://reviews.llvm.org/D151911



More information about the llvm-commits mailing list