[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