[llvm] [VectorCombine] fold fptoui(fdiv(uitofp(x),uitofp(y))) into udiv(x,y) (PR #214844)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 20 04:48:49 PDT 2026
https://github.com/Xylecrack updated https://github.com/llvm/llvm-project/pull/214844
>From ddd313a9c0b3a8a65c2edd3a7ad5656c79a9918d Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Sat, 8 Aug 2026 01:34:18 +0530
Subject: [PATCH 1/7] Add baseline tests
---
.../AArch64/fold-fdiv-to-udiv.ll | 117 ++++++++++++++++++
1 file changed, 117 insertions(+)
create mode 100644 llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
new file mode 100644
index 0000000000000..3107dc21999a5
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
@@ -0,0 +1,117 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=vector-combine -mtriple=aarch64 -mattr=sve2 < %s | FileCheck %s
+
+; ValueTracking cannot prove divisor isKnwonNonZero. Should not fold
+define <4 x i32> @no_fold_v4i32_to_v4f64(<4 x i32> %x, <4 x i32> %y) {
+; CHECK-LABEL: define <4 x i32> @no_fold_v4i32_to_v4f64(
+; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> [[Y]] to <4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> %y to <4 x double>
+ %r = fdiv <4 x double> %a, %b
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
+define <4 x i32> @fold_v4i32_constant(<4 x i32> %x) {
+; CHECK-LABEL: define <4 x i32> @fold_v4i32_constant(
+; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+ %r = fdiv <4 x double> %a, %b
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
+; ValueTracking proves %nz is non-zero via bitwise OR. Should fold.
+define <4 x i32> @fold_value_tracking(<4 x i32> %x, <4 x i32> %y) {
+; CHECK-LABEL: define <4 x i32> @fold_value_tracking(
+; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[NZ:%.*]] = or <4 x i32> [[Y]], splat (i32 1)
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> [[NZ]] to <4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %nz = or <4 x i32> %y, <i32 1, i32 1, i32 1, i32 1>
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> %nz to <4 x double>
+ %r = fdiv <4 x double> %a, %b
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
+; IntWidth(32) > Precision(24). Should not fold.
+define <4 x i32> @no_fold_precision(<4 x i32> %x) {
+; CHECK-LABEL: define <4 x i32> @no_fold_precision(
+; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x float>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x float>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x float> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x float> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %a = uitofp <4 x i32> %x to <4 x float>
+ %b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x float>
+ %r = fdiv <4 x float> %a, %b
+ %res = fptoui <4 x float> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
+define <4 x i32> @no_fold_constant_zero(<4 x i32> %x) {
+; CHECK-LABEL: define <4 x i32> @no_fold_constant_zero(
+; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 0, i32 3, i32 4> to <4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> <i32 1, i32 0, i32 3, i32 4> to <4 x double>
+ %r = fdiv <4 x double> %a, %b
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
+define <4 x i32> @no_fold_multiuse(<4 x i32> %x, ptr %p) {
+; CHECK-LABEL: define <4 x i32> @no_fold_multiuse(
+; CHECK-SAME: <4 x i32> [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: store <4 x double> [[R]], ptr [[P]], align 32
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+entry:
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+ %r = fdiv <4 x double> %a, %b
+ store <4 x double> %r, ptr %p
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
>From 5bc968ba046f54b5d388c57986b66ab40154169e Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Sat, 8 Aug 2026 01:35:30 +0530
Subject: [PATCH 2/7] Implement fold
---
.../Transforms/Vectorize/VectorCombine.cpp | 48 +++++++++++++++++++
.../AArch64/fold-fdiv-to-udiv.ll | 10 +---
2 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 726f564b1aad9..7e213a6a322b3 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -136,6 +136,7 @@ class VectorCombine {
bool scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy, Value *Ptr);
bool scalarizeLoadBitcast(LoadInst *LI, VectorType *VecTy, Value *Ptr);
bool scalarizeExtExtract(Instruction &I);
+ bool foldFDivToUDiv(Instruction &I);
bool foldConcatOfBoolMasks(Instruction &I);
bool foldPermuteOfBinops(Instruction &I);
bool foldShuffleOfBinops(Instruction &I);
@@ -2332,6 +2333,50 @@ bool VectorCombine::scalarizeExtExtract(Instruction &I) {
return true;
}
+/// Fold fptoui(fdiv(uitofp(x),uitofp(y))) --> udiv(x,y)
+bool VectorCombine::foldFDivToUDiv(Instruction &I) {
+ Instruction *X, *Y;
+ if (!match(&I, m_FPToUI(m_OneUse(m_FDiv(m_OneUse(m_Instruction(X)),
+ m_OneUse(m_Instruction(Y)))))))
+ return false;
+ Value *SrcX;
+ if (!match(X, m_UIToFP(m_Value(SrcX))))
+ return false;
+ Value *SrcY;
+ if (!match(Y, m_UIToFP(m_Value(SrcY))))
+ return false;
+
+ Type *IntTy = SrcX->getType();
+ Type *FloatTy = X->getType();
+
+ if (IntTy != SrcY->getType() || IntTy != I.getType())
+ return false;
+
+ unsigned IntWidth = IntTy->getScalarSizeInBits();
+ unsigned Precision =
+ APFloat::semanticsPrecision(FloatTy->getScalarType()->getFltSemantics());
+
+ if (IntWidth > Precision)
+ return false;
+ if (!isKnownNonZero(SrcY, SQ.getWithInstruction(&I)))
+ return false;
+
+ // OldCost = fptoui + fdiv + 2*uitofp
+ InstructionCost OldCost =
+ TTI.getInstructionCost(&I, CostKind) +
+ TTI.getArithmeticInstrCost(Instruction::FDiv, FloatTy, CostKind) +
+ 2 * TTI.getInstructionCost(X, CostKind);
+ // NewCost = udiv
+ InstructionCost NewCost =
+ TTI.getArithmeticInstrCost(Instruction::UDiv, IntTy, CostKind);
+
+ if (NewCost > OldCost)
+ return false;
+
+ Value *NewInst = Builder.CreateUDiv(SrcX, SrcY);
+ replaceValue(I, *NewInst);
+ return true;
+}
/// Try to fold "(or (zext (bitcast X)), (shl (zext (bitcast Y)), C))"
/// to "(bitcast (concat X, Y))"
/// where X/Y are bitcasted from i1 mask vectors.
@@ -6542,6 +6587,9 @@ bool VectorCombine::run() {
if (Opcode == Instruction::BitCast)
if (foldBitOrderReverseAndSwap(I))
return true;
+ if (Opcode == Instruction::FPToUI)
+ if (foldFDivToUDiv(I))
+ return true;
// Otherwise, try folds that improve codegen but may interfere with
// early IR canonicalizations.
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
index 3107dc21999a5..8b701a879bed7 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
@@ -24,10 +24,7 @@ define <4 x i32> @fold_v4i32_constant(<4 x i32> %x) {
; CHECK-LABEL: define <4 x i32> @fold_v4i32_constant(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
-; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
-; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
-; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: [[RES:%.*]] = udiv <4 x i32> [[X]], <i32 1, i32 2, i32 3, i32 4>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
entry:
@@ -44,10 +41,7 @@ define <4 x i32> @fold_value_tracking(<4 x i32> %x, <4 x i32> %y) {
; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[NZ:%.*]] = or <4 x i32> [[Y]], splat (i32 1)
-; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
-; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> [[NZ]] to <4 x double>
-; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
-; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: [[RES:%.*]] = udiv <4 x i32> [[X]], [[NZ]]
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
entry:
>From 791cd3f2a69024f934b82f6c7f615786e8d921ba Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Thu, 13 Aug 2026 22:44:00 +0530
Subject: [PATCH 3/7] Add debug statement and explain constraints
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 7e213a6a322b3..4eb6cb53ac98e 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -2333,7 +2333,7 @@ bool VectorCombine::scalarizeExtExtract(Instruction &I) {
return true;
}
-/// Fold fptoui(fdiv(uitofp(x),uitofp(y))) --> udiv(x,y)
+/// Try to fold fptoui(fdiv(uitofp(x),uitofp(y))) to udiv(x,y)
bool VectorCombine::foldFDivToUDiv(Instruction &I) {
Instruction *X, *Y;
if (!match(&I, m_FPToUI(m_OneUse(m_FDiv(m_OneUse(m_Instruction(X)),
@@ -2352,12 +2352,16 @@ bool VectorCombine::foldFDivToUDiv(Instruction &I) {
if (IntTy != SrcY->getType() || IntTy != I.getType())
return false;
+ // Require uitofp(x) and uitofp(y) to be exact conversions, i.e. IntWidth
+ // must fit within the float type's mantissa precision.
unsigned IntWidth = IntTy->getScalarSizeInBits();
unsigned Precision =
APFloat::semanticsPrecision(FloatTy->getScalarType()->getFltSemantics());
-
if (IntWidth > Precision)
return false;
+
+ // Integer division by zero is UB. We must prove the divisor
+ // is known non-zero to safely transform fdiv into udiv.
if (!isKnownNonZero(SrcY, SQ.getWithInstruction(&I)))
return false;
@@ -2370,6 +2374,10 @@ bool VectorCombine::foldFDivToUDiv(Instruction &I) {
InstructionCost NewCost =
TTI.getArithmeticInstrCost(Instruction::UDiv, IntTy, CostKind);
+ LLVM_DEBUG(dbgs() << "Found division of vector float to unsigned integer: "
+ << I << "\n OldCost: " << OldCost
+ << " vs NewCost: " << NewCost << "\n");
+
if (NewCost > OldCost)
return false;
@@ -2377,6 +2385,7 @@ bool VectorCombine::foldFDivToUDiv(Instruction &I) {
replaceValue(I, *NewInst);
return true;
}
+
/// Try to fold "(or (zext (bitcast X)), (shl (zext (bitcast Y)), C))"
/// to "(bitcast (concat X, Y))"
/// where X/Y are bitcasted from i1 mask vectors.
>From 556ec6ec2982bd3ce2496535c0757fbfcff1e58f Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Thu, 13 Aug 2026 22:47:38 +0530
Subject: [PATCH 4/7] Clean up labels and add vscale tests
---
.../AArch64/fold-fdiv-to-udiv.ll | 42 +++++++++++++------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
index 8b701a879bed7..d8412e39e2e72 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
@@ -5,14 +5,12 @@
define <4 x i32> @no_fold_v4i32_to_v4f64(<4 x i32> %x, <4 x i32> %y) {
; CHECK-LABEL: define <4 x i32> @no_fold_v4i32_to_v4f64(
; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> [[Y]] to <4 x double>
; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%a = uitofp <4 x i32> %x to <4 x double>
%b = uitofp <4 x i32> %y to <4 x double>
%r = fdiv <4 x double> %a, %b
@@ -23,11 +21,9 @@ entry:
define <4 x i32> @fold_v4i32_constant(<4 x i32> %x) {
; CHECK-LABEL: define <4 x i32> @fold_v4i32_constant(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[RES:%.*]] = udiv <4 x i32> [[X]], <i32 1, i32 2, i32 3, i32 4>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%a = uitofp <4 x i32> %x to <4 x double>
%b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
%r = fdiv <4 x double> %a, %b
@@ -39,12 +35,10 @@ entry:
define <4 x i32> @fold_value_tracking(<4 x i32> %x, <4 x i32> %y) {
; CHECK-LABEL: define <4 x i32> @fold_value_tracking(
; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[NZ:%.*]] = or <4 x i32> [[Y]], splat (i32 1)
; CHECK-NEXT: [[RES:%.*]] = udiv <4 x i32> [[X]], [[NZ]]
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%nz = or <4 x i32> %y, <i32 1, i32 1, i32 1, i32 1>
%a = uitofp <4 x i32> %x to <4 x double>
%b = uitofp <4 x i32> %nz to <4 x double>
@@ -57,32 +51,27 @@ entry:
define <4 x i32> @no_fold_precision(<4 x i32> %x) {
; CHECK-LABEL: define <4 x i32> @no_fold_precision(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x float>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x float>
; CHECK-NEXT: [[R:%.*]] = fdiv <4 x float> [[A]], [[B]]
; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x float> [[R]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%a = uitofp <4 x i32> %x to <4 x float>
%b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x float>
%r = fdiv <4 x float> %a, %b
%res = fptoui <4 x float> %r to <4 x i32>
ret <4 x i32> %res
}
-
define <4 x i32> @no_fold_constant_zero(<4 x i32> %x) {
; CHECK-LABEL: define <4 x i32> @no_fold_constant_zero(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 0, i32 3, i32 4> to <4 x double>
; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%a = uitofp <4 x i32> %x to <4 x double>
%b = uitofp <4 x i32> <i32 1, i32 0, i32 3, i32 4> to <4 x double>
%r = fdiv <4 x double> %a, %b
@@ -93,7 +82,6 @@ entry:
define <4 x i32> @no_fold_multiuse(<4 x i32> %x, ptr %p) {
; CHECK-LABEL: define <4 x i32> @no_fold_multiuse(
; CHECK-SAME: <4 x i32> [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
@@ -101,7 +89,6 @@ define <4 x i32> @no_fold_multiuse(<4 x i32> %x, ptr %p) {
; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
-entry:
%a = uitofp <4 x i32> %x to <4 x double>
%b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
%r = fdiv <4 x double> %a, %b
@@ -109,3 +96,32 @@ entry:
%res = fptoui <4 x double> %r to <4 x i32>
ret <4 x i32> %res
}
+
+define <vscale x 4 x i32> @no_fold_vscale_nonconstant(<vscale x 4 x i32> %x, <vscale x 4 x i32> %y) {
+; CHECK-LABEL: define <vscale x 4 x i32> @no_fold_vscale_nonconstant(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]], <vscale x 4 x i32> [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[A:%.*]] = uitofp <vscale x 4 x i32> [[X]] to <vscale x 4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <vscale x 4 x i32> [[Y]] to <vscale x 4 x double>
+; CHECK-NEXT: [[R:%.*]] = fdiv <vscale x 4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <vscale x 4 x double> [[R]] to <vscale x 4 x i32>
+; CHECK-NEXT: ret <vscale x 4 x i32> [[RES]]
+;
+ %a = uitofp <vscale x 4 x i32> %x to <vscale x 4 x double>
+ %b = uitofp <vscale x 4 x i32> %y to <vscale x 4 x double>
+ %r = fdiv <vscale x 4 x double> %a, %b
+ %res = fptoui <vscale x 4 x double> %r to <vscale x 4 x i32>
+ ret <vscale x 4 x i32> %res
+}
+
+define <vscale x 4 x i32> @fold_vscale_constant(<vscale x 4 x i32> %x) {
+; CHECK-LABEL: define <vscale x 4 x i32> @fold_vscale_constant(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RES:%.*]] = udiv <vscale x 4 x i32> [[X]], splat (i32 2)
+; CHECK-NEXT: ret <vscale x 4 x i32> [[RES]]
+;
+ %a = uitofp <vscale x 4 x i32> %x to <vscale x 4 x double>
+ %b = uitofp <vscale x 4 x i32> splat (i32 2) to <vscale x 4 x double>
+ %r = fdiv <vscale x 4 x double> %a, %b
+ %res = fptoui <vscale x 4 x double> %r to <vscale x 4 x i32>
+ ret <vscale x 4 x i32> %res
+}
>From 5899994e860016dfdbb74ed31c0a7512ca68a02d Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Fri, 14 Aug 2026 20:41:44 +0530
Subject: [PATCH 5/7] Calculate cost of X and Y separately
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 4eb6cb53ac98e..c3b512861735b 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -2365,11 +2365,11 @@ bool VectorCombine::foldFDivToUDiv(Instruction &I) {
if (!isKnownNonZero(SrcY, SQ.getWithInstruction(&I)))
return false;
- // OldCost = fptoui + fdiv + 2*uitofp
+ // OldCost = fptoui + fdiv + uitofp(x) + uitofp(y)
InstructionCost OldCost =
TTI.getInstructionCost(&I, CostKind) +
TTI.getArithmeticInstrCost(Instruction::FDiv, FloatTy, CostKind) +
- 2 * TTI.getInstructionCost(X, CostKind);
+ TTI.getInstructionCost(X, CostKind) + TTI.getInstructionCost(Y, CostKind);
// NewCost = udiv
InstructionCost NewCost =
TTI.getArithmeticInstrCost(Instruction::UDiv, IntTy, CostKind);
>From 64f924976622aaf3f9457bc280ecde78fedb9fff Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Wed, 19 Aug 2026 20:57:42 +0530
Subject: [PATCH 6/7] Add sclar tests
---
.../AArch64/fold-fdiv-to-udiv.ll | 77 +++++++++++++++++--
1 file changed, 69 insertions(+), 8 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
index d8412e39e2e72..ba05dd4ef2cb9 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
@@ -32,8 +32,8 @@ define <4 x i32> @fold_v4i32_constant(<4 x i32> %x) {
}
; ValueTracking proves %nz is non-zero via bitwise OR. Should fold.
-define <4 x i32> @fold_value_tracking(<4 x i32> %x, <4 x i32> %y) {
-; CHECK-LABEL: define <4 x i32> @fold_value_tracking(
+define <4 x i32> @fold_vector_value_tracking(<4 x i32> %x, <4 x i32> %y) {
+; CHECK-LABEL: define <4 x i32> @fold_vector_value_tracking(
; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[NZ:%.*]] = or <4 x i32> [[Y]], splat (i32 1)
; CHECK-NEXT: [[RES:%.*]] = udiv <4 x i32> [[X]], [[NZ]]
@@ -48,8 +48,8 @@ define <4 x i32> @fold_value_tracking(<4 x i32> %x, <4 x i32> %y) {
}
; IntWidth(32) > Precision(24). Should not fold.
-define <4 x i32> @no_fold_precision(<4 x i32> %x) {
-; CHECK-LABEL: define <4 x i32> @no_fold_precision(
+define <4 x i32> @no_fold_vector_precision(<4 x i32> %x) {
+; CHECK-LABEL: define <4 x i32> @no_fold_vector_precision(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x float>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x float>
@@ -63,8 +63,8 @@ define <4 x i32> @no_fold_precision(<4 x i32> %x) {
%res = fptoui <4 x float> %r to <4 x i32>
ret <4 x i32> %res
}
-define <4 x i32> @no_fold_constant_zero(<4 x i32> %x) {
-; CHECK-LABEL: define <4 x i32> @no_fold_constant_zero(
+define <4 x i32> @no_fold_vector_constant_zero(<4 x i32> %x) {
+; CHECK-LABEL: define <4 x i32> @no_fold_vector_constant_zero(
; CHECK-SAME: <4 x i32> [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 0, i32 3, i32 4> to <4 x double>
@@ -79,8 +79,8 @@ define <4 x i32> @no_fold_constant_zero(<4 x i32> %x) {
ret <4 x i32> %res
}
-define <4 x i32> @no_fold_multiuse(<4 x i32> %x, ptr %p) {
-; CHECK-LABEL: define <4 x i32> @no_fold_multiuse(
+define <4 x i32> @no_fold_vector_multiuse(<4 x i32> %x, ptr %p) {
+; CHECK-LABEL: define <4 x i32> @no_fold_vector_multiuse(
; CHECK-SAME: <4 x i32> [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
@@ -125,3 +125,64 @@ define <vscale x 4 x i32> @fold_vscale_constant(<vscale x 4 x i32> %x) {
%res = fptoui <vscale x 4 x double> %r to <vscale x 4 x i32>
ret <vscale x 4 x i32> %res
}
+
+define i32 @fold_scalar_constant(i32 %x) {
+; CHECK-LABEL: define i32 @fold_scalar_constant(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[RES:%.*]] = udiv i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %a = uitofp i32 %x to double
+ %b = uitofp i32 3 to double
+ %r = fdiv double %a, %b
+ %res = fptoui double %r to i32
+ ret i32 %res
+}
+
+define i32 @no_fold_scalar_unknown(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @no_fold_scalar_unknown(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT: [[B:%.*]] = uitofp i32 [[Y]] to double
+; CHECK-NEXT: [[R:%.*]] = fdiv double [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui double [[R]] to i32
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %a = uitofp i32 %x to double
+ %b = uitofp i32 %y to double
+ %r = fdiv double %a, %b
+ %res = fptoui double %r to i32
+ ret i32 %res
+}
+
+define i32 @no_fold_scalar_precision(i32 %x) {
+; CHECK-LABEL: define i32 @no_fold_scalar_precision(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[A:%.*]] = uitofp i32 [[X]] to float
+; CHECK-NEXT: [[B:%.*]] = uitofp i32 2 to float
+; CHECK-NEXT: [[R:%.*]] = fdiv float [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui float [[R]] to i32
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %a = uitofp i32 %x to float
+ %b = uitofp i32 2 to float
+ %r = fdiv float %a, %b
+ %res = fptoui float %r to i32
+ ret i32 %res
+}
+
+define i32 @no_fold_scalar_constant_zero(i32 %x) {
+; CHECK-LABEL: define i32 @no_fold_scalar_constant_zero(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT: [[B:%.*]] = uitofp i32 0 to double
+; CHECK-NEXT: [[R:%.*]] = fdiv double [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui double [[R]] to i32
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %a = uitofp i32 %x to double
+ %b = uitofp i32 0 to double
+ %r = fdiv double %a, %b
+ %res = fptoui double %r to i32
+ ret i32 %res
+}
>From d7090e38456b8486a1054de8884dc82202ec42e1 Mon Sep 17 00:00:00 2001
From: Dhruva Narayan <dhruvakodiadka at gmail.com>
Date: Thu, 20 Aug 2026 17:18:31 +0530
Subject: [PATCH 7/7] Add uitofp-multiuse check
---
.../AArch64/fold-fdiv-to-udiv.ll | 22 +++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
index ba05dd4ef2cb9..a1ae9bc02130f 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/fold-fdiv-to-udiv.ll
@@ -79,8 +79,8 @@ define <4 x i32> @no_fold_vector_constant_zero(<4 x i32> %x) {
ret <4 x i32> %res
}
-define <4 x i32> @no_fold_vector_multiuse(<4 x i32> %x, ptr %p) {
-; CHECK-LABEL: define <4 x i32> @no_fold_vector_multiuse(
+define <4 x i32> @no_fold_vector_multiuse_fdiv(<4 x i32> %x, ptr %p) {
+; CHECK-LABEL: define <4 x i32> @no_fold_vector_multiuse_fdiv(
; CHECK-SAME: <4 x i32> [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
@@ -97,6 +97,24 @@ define <4 x i32> @no_fold_vector_multiuse(<4 x i32> %x, ptr %p) {
ret <4 x i32> %res
}
+define <4 x i32> @no_fold_vector_multiuse_uitofp(<4 x i32> %x, ptr %p) {
+; CHECK-LABEL: define <4 x i32> @no_fold_vector_multiuse_uitofp(
+; CHECK-SAME: <4 x i32> [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[A:%.*]] = uitofp <4 x i32> [[X]] to <4 x double>
+; CHECK-NEXT: [[B:%.*]] = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+; CHECK-NEXT: store <4 x double> [[B]], ptr [[P]], align 32
+; CHECK-NEXT: [[R:%.*]] = fdiv <4 x double> [[A]], [[B]]
+; CHECK-NEXT: [[RES:%.*]] = fptoui <4 x double> [[R]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[RES]]
+;
+ %a = uitofp <4 x i32> %x to <4 x double>
+ %b = uitofp <4 x i32> <i32 1, i32 2, i32 3, i32 4> to <4 x double>
+ store <4 x double> %b, ptr %p
+ %r = fdiv <4 x double> %a, %b
+ %res = fptoui <4 x double> %r to <4 x i32>
+ ret <4 x i32> %res
+}
+
define <vscale x 4 x i32> @no_fold_vscale_nonconstant(<vscale x 4 x i32> %x, <vscale x 4 x i32> %y) {
; CHECK-LABEL: define <vscale x 4 x i32> @no_fold_vscale_nonconstant(
; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]], <vscale x 4 x i32> [[Y:%.*]]) #[[ATTR0]] {
More information about the llvm-commits
mailing list