[llvm] [CodeGen] Expand power-of-2 div/rem at IR level in ExpandIRInsts. (PR #180654)

Daniil Fukalov via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 16 09:51:40 PST 2026


================
@@ -85,6 +85,120 @@ bool isSigned(unsigned int Opcode) {
   return Opcode == Instruction::SDiv || Opcode == Instruction::SRem;
 }
 
+/// For signed div/rem by a power of 2, compute the bias-adjusted dividend:
+///   Sign = ashr X, (BitWidth - 1)          -- 0 or -1
+///   Bias = lshr Sign, (BitWidth - ShiftAmt) -- 0 or 2^ShiftAmt - 1
+///   Adjusted = add X, Bias
+/// The bias adds (2^ShiftAmt - 1) for negative X, correcting rounding towards
+/// zero (instead of towards -inf that a plain ashr would give).
+/// The lshr form is used instead of 'and' to avoid large immediate constants.
+static Value *addSignedBias(IRBuilder<> &Builder, Value *X, unsigned BitWidth,
+                            unsigned ShiftAmt) {
+  assert(ShiftAmt > 0 && ShiftAmt < BitWidth &&
+         "ShiftAmt out of range; callers should handle ShiftAmt == 0");
+  Value *Sign = Builder.CreateAShr(X, BitWidth - 1, "sign");
+  Value *Bias = Builder.CreateLShr(Sign, BitWidth - ShiftAmt, "bias");
+  return Builder.CreateAdd(X, Bias, "adjusted");
+}
+
+/// Expand division by a power-of-2 constant.
+/// udiv X, 2^C  ->  lshr X, C
+/// sdiv X, 2^C  ->  ashr (add X, Bias), C  (Bias corrects rounding)
+/// sdiv exact X, 2^C  ->  ashr exact X, C  (no bias needed)
+/// For negative power-of-2 divisors, the result is negated.
+static void expandPow2Division(BinaryOperator *Div) {
+  bool IsSigned = isSigned(Div->getOpcode());
+  bool IsExact = Div->isExact();
+  Value *X = Div->getOperand(0);
+  auto *C = cast<ConstantInt>(Div->getOperand(1));
+  Type *Ty = Div->getType();
+  unsigned BitWidth = Ty->getIntegerBitWidth();
+
+  APInt DivisorVal = C->getValue();
+  bool IsNegativeDivisor = IsSigned && DivisorVal.isNegative();
+  // Use countr_zero() to get the shift amount directly from the bit pattern.
+  // This works correctly for both positive and negative powers of 2, including
+  // INT_MIN, without needing to negate the value first.
+  unsigned ShiftAmt = DivisorVal.countr_zero();
+
+  IRBuilder<> Builder(Div);
+  Value *Result;
+
+  if (ShiftAmt == 0) {
+    // Division by 1 or -1.
+    // X / 1 = X, X / -1 = -X.
+    Result = IsNegativeDivisor ? Builder.CreateNeg(X) : X;
+  } else if (IsSigned) {
+    // The signed expansion uses X multiple times (in the bias computation and
+    // the shift). Freeze X to ensure consistent behavior if it is undef/poison,
+    // matching the approach in IntegerDivision.cpp and expandFPToI.
+    if (!IsExact && !isGuaranteedNotToBeUndefOrPoison(X))
+      X = Builder.CreateFreeze(X, X->getName() + ".fr");
+    // For exact division, no bias is needed since there's no rounding.
+    Value *Dividend =
+        IsExact ? X : addSignedBias(Builder, X, BitWidth, ShiftAmt);
+    Result = Builder.CreateAShr(Dividend, ShiftAmt,
+                                IsNegativeDivisor ? "pre.neg" : "", IsExact);
+    if (IsNegativeDivisor)
+      Result = Builder.CreateNeg(Result);
+  } else {
+    Result = Builder.CreateLShr(X, ShiftAmt, "", IsExact);
+  }
+
+  Div->replaceAllUsesWith(Result);
+  // Transfer the name of the original instruction to its replacement,
+  // unless the result is the original dividend itself (div by 1).
+  if (Result != X)
+    if (auto *RI = dyn_cast<Instruction>(Result))
+      RI->takeName(Div);
+  Div->dropAllReferences();
+  Div->eraseFromParent();
+}
+
+/// Expand remainder by a power-of-2 constant (let C = log2(|divisor|)).
+/// urem X, 2^C  ->  and X, (2^C - 1)
+/// srem X, 2^C  ->  sub X, (shl (ashr (add X, Bias), C), C)
+static void expandPow2Remainder(BinaryOperator *Rem) {
+  bool IsSigned = isSigned(Rem->getOpcode());
+  Value *X = Rem->getOperand(0);
+  auto *C = cast<ConstantInt>(Rem->getOperand(1));
+  Type *Ty = Rem->getType();
+  unsigned BitWidth = Ty->getIntegerBitWidth();
+
+  // Use countr_zero() to get the shift amount directly from the bit pattern.
+  // This works for both positive and negative powers of 2, including INT_MIN.
+  unsigned ShiftAmt = C->getValue().countr_zero();
+
+  IRBuilder<> Builder(Rem);
+  Value *Result;
+
+  if (ShiftAmt == 0) {
+    // Remainder by 1 or -1 is always 0.
+    Result = ConstantInt::get(Ty, 0);
+  } else if (IsSigned) {
+    // The signed expansion uses X multiple times (bias computation, shift, and
----------------
dfukalov wrote:

Thanks for suggestion, sounds reasonable. Refactored two functions into one.

https://github.com/llvm/llvm-project/pull/180654


More information about the llvm-commits mailing list