[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