[llvm] [CVP] Implement type narrowing for LShr (PR #119577)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 11 16:16:52 PST 2024
================
@@ -1067,6 +1074,124 @@ static bool processSDivOrSRem(BinaryOperator *Instr, LazyValueInfo *LVI) {
return narrowSDivOrSRem(Instr, LCR, RCR);
}
+/**
+ * @brief Narrows type of the LShr instruction if the range of the possible
+ * values fits into a smaller type. Since LShr is a relatively cheap
+ * instruction, the narrowing should not happen too frequently. Performance
+ * testing and compatibility with other passes indicate that the narrowing is
+ * beneficial under the following circumstances:
+ *
+ * i) the narrowing occurs only if all the users of the LShr instruction are
+ * already TruncInst;
+ *
+ * ii) the narrowing is carried out to the largest TruncInst following the LShr
+ * instruction.
+ *
+ * Additionally, the function optimizes the cases where the result of the LShr
+ * instruction is guaranteed to vanish or be equal to poison.
+ */
+static bool narrowLShr(BinaryOperator *LShr, LazyValueInfo *LVI) {
+
+ IntegerType *RetTy = dyn_cast<IntegerType>(LShr->getType());
+ if (!RetTy)
+ return false;
+
+ ConstantRange ArgRange = LVI->getConstantRangeAtUse(LShr->getOperandUse(0),
+ /*UndefAllowed*/ false);
+ ConstantRange ShiftRange = LVI->getConstantRangeAtUse(LShr->getOperandUse(1),
+ /*UndefAllowed*/ false);
+
+ unsigned OrigWidth = RetTy->getScalarSizeInBits();
+ unsigned MaxActiveBitsInArg = ArgRange.getActiveBits();
+ uint64_t MinShiftValue64 = ShiftRange.getUnsignedMin().getZExtValue();
+ unsigned MinShiftValue =
+ MinShiftValue64 < std::numeric_limits<unsigned>::max()
+ ? static_cast<unsigned>(MinShiftValue64)
+ : std::numeric_limits<unsigned>::max();
+
+ // First we deal with the cases where the result is guaranteed to vanish or be
+ // equal to posion.
+
+ auto replaceWith = [&](Value *V) -> void {
+ LShr->replaceAllUsesWith(V);
+ LShr->eraseFromParent();
+ ++NumLShrsRemoved;
+ };
+
+ // If the shift is larger or equal to the bit width of the argument,
+ // the instruction returns a poison value.
+ if (MinShiftValue >= OrigWidth) {
+ replaceWith(PoisonValue::get(RetTy));
+ return true;
+ }
+
+ // If we are guaranteed to shift away all bits,
+ // we replace the shift by the null value.
+ // We should not apply the optimization if LShr is exact,
+ // as the result may be poison.
+ if (!LShr->isExact() && MinShiftValue >= MaxActiveBitsInArg) {
+ replaceWith(Constant::getNullValue(RetTy));
+ return true;
+ }
+
+ // That's how many bits we need.
+ unsigned MaxActiveBits =
+ std::max(MaxActiveBitsInArg, ShiftRange.getActiveBits());
+
+ // We could do better, but is it worth it?
+ // With the first argument being the n-bit integer, we may limit the value of
+ // the second argument to be less than n, as larger shifts would lead to a
+ // vanishing result or poison. Thus the number of bits in the second argument
+ // is limited by Log2(n). Unfortunately, this would require an introduction of
+ // a select instruction (or llvm.min) to make sure every argument larger than
+ // n is mapped to n and not just truncated. We do not implement it here.
+
+ // What is the smallest bit width that can accommodate the entire value ranges
+ // of both of the operands? Don't shrink below 8 bits wide.
+ unsigned NewWidth = std::max<unsigned>(PowerOf2Ceil(MaxActiveBits), 8);
----------------
dtcxzyw wrote:
We should avoid narrowing the type to an illegal type. See also `InstCombinerImpl::shouldChangeType`.
https://github.com/llvm/llvm-project/pull/119577
More information about the llvm-commits
mailing list