[llvm] [BypassSlowDivision] Add option to bypass div/rem with constant divisors (PR #230885)

via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 10 21:26:20 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: yinghao (andreww0210)

<details>
<summary>Changes</summary>

A div/rem with a constant divisor is normally lowered to a multiplication by a magic constant, so bypassSlowDivision() currently skips constant divisors.

However, on targets where 64-bit multiplication is slow (e.g. software emulation), the magic-multiply lowering can be more expensive than a narrower division with extra control flow. Add a flag to enable bypassing constant divisors in that case.

Divisors that are powers of two are still never bypassed, since they lower to a simple shift. The flag defaults to false, so no in-tree target changes behavior.

---
Full diff: https://github.com/llvm/llvm-project/pull/230885.diff


5 Files Affected:

- (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+24) 
- (modified) llvm/include/llvm/Transforms/Utils/BypassSlowDivision.h (+4-5) 
- (modified) llvm/lib/CodeGen/CodeGenPrepare.cpp (+3-1) 
- (modified) llvm/lib/CodeGen/TargetLoweringBase.cpp (+1) 
- (modified) llvm/lib/Transforms/Utils/BypassSlowDivision.cpp (+16-6) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 33ed5d49e9d82..8e76b21dd9094 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -646,6 +646,12 @@ class LLVM_ABI TargetLoweringBase {
     return BypassSlowDivWidths;
   }
 
+  /// Returns true if the target wants to bypass div/rem instructions with
+  /// a constant divisor.
+  bool shouldBypassConstantDivisor() const {
+    return ShouldBypassConstantDivisor;
+  }
+
   /// Return true if Flow Control is an expensive operation that should be
   /// avoided.
   bool isJumpExpensive() const { return JumpIsExpensive; }
@@ -2735,6 +2741,16 @@ class LLVM_ABI TargetLoweringBase {
     HasExtractBitsInsn = hasExtractInsn;
   }
 
+  /// Tells the code generator to bypass a div/rem with a constant divisor.
+  /// A div/rem with a constant divisor is normally converted to a
+  /// multiplication by a magic constant. Bypassing it may introduce a more
+  /// complex CFG. Set this to true if the target's multiplication is slow
+  /// enough that it is worth introducing branches to use a narrower
+  /// division.
+  void setShouldBypassConstantDivisor(bool ShouldBypassConstantDivisor = true) {
+    this->ShouldBypassConstantDivisor = ShouldBypassConstantDivisor;
+  }
+
   /// Tells the code generator not to expand logic operations on comparison
   /// predicates into separate sequences that increase the amount of flow
   /// control.
@@ -3831,6 +3847,14 @@ class LLVM_ABI TargetLoweringBase {
   /// div/rem when the operands are positive and less than 256.
   DenseMap <unsigned int, unsigned int> BypassSlowDivWidths;
 
+  /// Tells the code generator to bypass a div/rem with a constant divisor.
+  /// A div/rem with a constant divisor is normally converted to a
+  /// multiplication by a magic constant. Bypassing it may introduce a more
+  /// complex CFG. Set this to true if the target's multiplication is slow
+  /// enough that it is worth introducing branches to use a narrower
+  /// division.
+  bool ShouldBypassConstantDivisor;
+
   /// Tells the code generator that it shouldn't generate extra flow control
   /// instructions and should attempt to combine flow control instructions via
   /// predication.
diff --git a/llvm/include/llvm/Transforms/Utils/BypassSlowDivision.h b/llvm/include/llvm/Transforms/Utils/BypassSlowDivision.h
index 7b4b728dc6a8d..88d4d93f54961 100644
--- a/llvm/include/llvm/Transforms/Utils/BypassSlowDivision.h
+++ b/llvm/include/llvm/Transforms/Utils/BypassSlowDivision.h
@@ -61,11 +61,10 @@ template <> struct DenseMapInfo<DivRemMapKey> {
 ///
 /// This optimization may add basic blocks immediately after BB; for obvious
 /// reasons, you shouldn't pass those blocks to bypassSlowDivision.
-LLVM_ABI bool
-bypassSlowDivision(BasicBlock *BB,
-                   const DenseMap<unsigned int, unsigned int> &BypassWidth,
-                   DomTreeUpdater *DTU = nullptr, LoopInfo *LI = nullptr,
-                   BranchProbabilityInfo *BPI = nullptr);
+LLVM_ABI bool bypassSlowDivision(
+    BasicBlock *BB, const DenseMap<unsigned int, unsigned int> &BypassWidth,
+    bool ShouldBypassConstantDivisor, DomTreeUpdater *DTU = nullptr,
+    LoopInfo *LI = nullptr, BranchProbabilityInfo *BPI = nullptr);
 
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index e00b32770b401..25fc874c658a2 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -475,13 +475,15 @@ bool CodeGenPrepare::_run(Function &F) {
   if (!OptSize && !PSI->hasHugeWorkingSetSize() && TLI->isSlowDivBypassed()) {
     const DenseMap<unsigned int, unsigned int> &BypassWidths =
         TLI->getBypassSlowDivWidths();
+    bool ShouldBypassConstantDivisor = TLI->shouldBypassConstantDivisor();
     BasicBlock *BB = &*F.begin();
     while (BB != nullptr) {
       // bypassSlowDivision may create new BBs, but we don't want to reapply the
       // optimization to those blocks.
       BasicBlock *Next = BB->getNextNode();
       if (!llvm::shouldOptimizeForSize(BB, PSI, BFI))
-        EverMadeChange |= bypassSlowDivision(BB, BypassWidths, DTU, LI, BPI);
+        EverMadeChange |= bypassSlowDivision(
+            BB, BypassWidths, ShouldBypassConstantDivisor, DTU, LI, BPI);
       BB = Next;
     }
   }
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp
index b6397d4551c3d..270a60e6cb173 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -732,6 +732,7 @@ TargetLoweringBase::TargetLoweringBase(const TargetMachine &tm,
   MaxStoresPerMemsetOptSize = MaxStoresPerMemcpyOptSize =
       MaxStoresPerMemmoveOptSize = MaxLoadsPerMemcmpOptSize = 4;
   HasExtractBitsInsn = false;
+  ShouldBypassConstantDivisor = false;
   JumpIsExpensive = JumpIsExpensiveOverride;
   PredictableSelectIsExpensive = false;
   EnableExtLdPromotion = false;
diff --git a/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp b/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp
index 54d3790ee3442..c848612befc77 100644
--- a/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp
+++ b/llvm/lib/Transforms/Utils/BypassSlowDivision.cpp
@@ -76,6 +76,7 @@ enum ValueRange {
 
 class FastDivInsertionTask {
   bool IsValidTask = false;
+  bool ShouldBypassConstantDivisor = false;
   Instruction *SlowDivOrRem = nullptr;
   IntegerType *BypassType = nullptr;
   BasicBlock *MainBB = nullptr;
@@ -107,8 +108,8 @@ class FastDivInsertionTask {
 
 public:
   FastDivInsertionTask(Instruction *I, const BypassWidthsTy &BypassWidths,
-                       DomTreeUpdater *DTU, LoopInfo *LI,
-                       BranchProbabilityInfo *BPI);
+                       bool ShouldBypassConstantDivisor, DomTreeUpdater *DTU,
+                       LoopInfo *LI, BranchProbabilityInfo *BPI);
 
   Value *getReplacement(DivCacheTy &Cache);
 };
@@ -117,9 +118,11 @@ class FastDivInsertionTask {
 
 FastDivInsertionTask::FastDivInsertionTask(Instruction *I,
                                            const BypassWidthsTy &BypassWidths,
+                                           bool ShouldBypassConstantDivisor,
                                            DomTreeUpdater *DTU, LoopInfo *LI,
                                            BranchProbabilityInfo *BPI)
-    : DTU(DTU), LI(LI), BPI(BPI) {
+    : ShouldBypassConstantDivisor(ShouldBypassConstantDivisor), DTU(DTU),
+      LI(LI), BPI(BPI) {
   switch (I->getOpcode()) {
   case Instruction::UDiv:
   case Instruction::SDiv:
@@ -410,11 +413,16 @@ std::optional<QuotRemPair> FastDivInsertionTask::insertFastDivAndRem() {
     return QuotRemPair(ExtDiv, ExtRem);
   }
 
-  if (isa<ConstantInt>(Divisor)) {
+  if (auto *CDivisor = dyn_cast<ConstantInt>(Divisor)) {
     // If the divisor is not a constant, DAGCombiner will convert it to a
     // multiplication by a magic constant.  It isn't clear if it is worth
     // introducing control flow to get a narrower multiply.
-    return std::nullopt;
+    //
+    // Setting ShouldBypassConstantDivisor to true enables the bypass
+    // optimization for constant divisors. However, powers of 2 should
+    // never be bypassed: they lower to a simple shift.
+    if (!ShouldBypassConstantDivisor || CDivisor->getValue().isPowerOf2())
+      return std::nullopt;
   }
 
   // After Constant Hoisting pass, long constants may be represented as
@@ -493,6 +501,7 @@ std::optional<QuotRemPair> FastDivInsertionTask::insertFastDivAndRem() {
 /// profitably bypassed and carried out with a shorter, faster divide.
 bool llvm::bypassSlowDivision(BasicBlock *BB,
                               const BypassWidthsTy &BypassWidths,
+                              bool ShouldBypassConstantDivisor,
                               DomTreeUpdater *DTU, LoopInfo *LI,
                               BranchProbabilityInfo *BPI) {
   DivCacheTy PerBBDivCache;
@@ -509,7 +518,8 @@ bool llvm::bypassSlowDivision(BasicBlock *BB,
     if (I->use_empty())
       continue;
 
-    FastDivInsertionTask Task(I, BypassWidths, DTU, LI, BPI);
+    FastDivInsertionTask Task(I, BypassWidths, ShouldBypassConstantDivisor, DTU,
+                              LI, BPI);
     if (Value *Replacement = Task.getReplacement(PerBBDivCache)) {
       I->replaceAllUsesWith(Replacement);
       I->eraseFromParent();

``````````

</details>


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


More information about the llvm-commits mailing list