[llvm] [X86] Lower scalar integer div/rem to FP division (PR #216589)

via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 11:02:34 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Ankit Kumar Tiwari (ankit-cybertron)

<details>
<summary>Changes</summary>

PR aims to extend the vector int-to-FP division combine (#<!-- -->205263) to scalar i8/i16/i32 sdiv/udiv/srem/urem, per #<!-- -->214938. Covers signed and unsigned div/rem, FP division is correctly-rounded and never traps, so it is safe for values that fit the target float type exactly, and still working on  f16 lowering for i8.



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


2 Files Affected:

- (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+24-10) 
- (added) llvm/test/CodeGen/X86/scalar-intdiv-to-fp.ll (+145) 


``````````diff
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 0dc3340a13c73..1f10178fabd72 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -50720,8 +50720,8 @@ static SDValue combineIntDivRem(SDNode *N, SelectionDAG &DAG,
   EVT VT = N->getValueType(0);
   SDLoc DL(N);
 
-  // Vector division never survives op legalization to reach later rounds.
-  if (!VT.isVector() || !Subtarget.hasSSE2())
+  
+  if (!Subtarget.hasSSE2())
     return SDValue();
 
   SDValue Dividend = N->getOperand(0);
@@ -50735,9 +50735,17 @@ static SDValue combineIntDivRem(SDNode *N, SelectionDAG &DAG,
   bool IsRem = Opc == ISD::UREM || Opc == ISD::SREM;
   bool IsSigned = Opc == ISD::SDIV || Opc == ISD::SREM;
 
+  // Scalar functions that do not use XMM registers must not get this combine.
+  if (!VT.isVector() &&
+      (Subtarget.useSoftFloat() ||
+       DAG.getMachineFunction().getFunction().hasFnAttribute(
+           Attribute::NoImplicitFloat)))
+    return SDValue();
+
   // If the result is only read back as scalar extracts, scalarization computes
   // just the demanded lanes.
-  if (all_of(N->users(), [](const SDNode *U) {
+  if (VT.isVector() &&
+      all_of(N->users(), [](const SDNode *U) {
         return U->getOpcode() == ISD::EXTRACT_VECTOR_ELT;
       }))
     return SDValue();
@@ -50764,16 +50772,21 @@ static SDValue combineIntDivRem(SDNode *N, SelectionDAG &DAG,
   MVT FPSclVT = MVT::f64;
   if (EltBits <= 16 || BothFitFP(APFloat::IEEEsingle()))
     FPSclVT = MVT::f32;
-  EVT FPVT = VT.changeVectorElementType(*DAG.getContext(), FPSclVT);
+  EVT FPVT = VT.isVector()
+                 ? VT.changeVectorElementType(*DAG.getContext(), FPSclVT)
+                 : EVT(FPSclVT);
 
   bool IsStrict = DAG.getMachineFunction().getFunction().hasFnAttribute(
       Attribute::StrictFP);
   if (IsStrict) {
+    // Scalar strictfp support is not implemented yet.
+    if (!VT.isVector())
+      return SDValue();
     // The SAE forms are 512-bit only. Inputs widen into a zmm below, which
     // requires 512-bit types to be legal and a power of 2 lane count.
     if (!Subtarget.useAVX512Regs() || !isPowerOf2_32(VT.getVectorNumElements()))
       return SDValue();
-  } else if (!IsSigned && VT.getScalarSizeInBits() == 32 &&
+  } else if (VT.isVector() && !IsSigned && VT.getScalarSizeInBits() == 32 &&
              !Subtarget.hasAVX2()) {
     // Unsigned i32 needs FP_TO_UINT(f64->u32) which is emulated and a loss
     // for latency and code size before AVX2.
@@ -50781,11 +50794,12 @@ static SDValue combineIntDivRem(SDNode *N, SelectionDAG &DAG,
   }
 
   // Nothing will split an illegal FP type after type legalization and the
-  // strict SAE divide is 512-bit only.
-  bool FPVTUsable = IsStrict
-                        ? FPVT.getSizeInBits() <= 512
-                        : DCI.isBeforeLegalize() ||
-                              DAG.getTargetLoweringInfo().isTypeLegal(FPVT);
+  // strict SAE divide is 512-bit only. Scalar f32/f64 are always legal.
+  bool FPVTUsable = !VT.isVector() ||
+                     (IsStrict
+                          ? FPVT.getSizeInBits() <= 512
+                          : DCI.isBeforeLegalize() ||
+                                DAG.getTargetLoweringInfo().isTypeLegal(FPVT));
 
   // Halve the divide while the integer halves stay legal.
   if (!FPVTUsable) {
diff --git a/llvm/test/CodeGen/X86/scalar-intdiv-to-fp.ll b/llvm/test/CodeGen/X86/scalar-intdiv-to-fp.ll
new file mode 100644
index 0000000000000..c752a26703275
--- /dev/null
+++ b/llvm/test/CodeGen/X86/scalar-intdiv-to-fp.ll
@@ -0,0 +1,145 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx2 | FileCheck %s
+
+define i32 @sdiv_i32(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: sdiv_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vcvtsi2sd %esi, %xmm15, %xmm0
+; CHECK-NEXT:    vcvtsi2sd %edi, %xmm15, %xmm1
+; CHECK-NEXT:    vdivsd %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttsd2si %xmm0, %eax
+; CHECK-NEXT:    retq
+  %r = sdiv i32 %a, %b
+  ret i32 %r
+}
+
+define i16 @sdiv_i16(i16 %a, i16 %b) nounwind {
+; CHECK-LABEL: sdiv_i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movswl %si, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm0
+; CHECK-NEXT:    movswl %di, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm1
+; CHECK-NEXT:    vdivss %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttss2si %xmm0, %eax
+; CHECK-NEXT:    # kill: def $ax killed $ax killed $eax
+; CHECK-NEXT:    retq
+  %r = sdiv i16 %a, %b
+  ret i16 %r
+}
+
+define i8 @sdiv_i8(i8 %a, i8 %b) nounwind {
+; CHECK-LABEL: sdiv_i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movsbl %sil, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm0
+; CHECK-NEXT:    movsbl %dil, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm1
+; CHECK-NEXT:    vdivss %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttss2si %xmm0, %eax
+; CHECK-NEXT:    # kill: def $al killed $al killed $eax
+; CHECK-NEXT:    retq
+  %r = sdiv i8 %a, %b
+  ret i8 %r
+}
+
+define i32 @udiv_i32(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: udiv_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %eax
+; CHECK-NEXT:    vcvtsi2sd %rax, %xmm15, %xmm0
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    vcvtsi2sd %rax, %xmm15, %xmm1
+; CHECK-NEXT:    vdivsd %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttsd2si %xmm0, %rax
+; CHECK-NEXT:    # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT:    retq
+  %r = udiv i32 %a, %b
+  ret i32 %r
+}
+
+define i16 @udiv_i16(i16 %a, i16 %b) nounwind {
+; CHECK-LABEL: udiv_i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movzwl %si, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm0
+; CHECK-NEXT:    movzwl %di, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm1
+; CHECK-NEXT:    vdivss %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttss2si %xmm0, %eax
+; CHECK-NEXT:    # kill: def $ax killed $ax killed $eax
+; CHECK-NEXT:    retq
+  %r = udiv i16 %a, %b
+  ret i16 %r
+}
+
+define i8 @udiv_i8(i8 %a, i8 %b) nounwind {
+; CHECK-LABEL: udiv_i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movzbl %sil, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm0
+; CHECK-NEXT:    movzbl %dil, %eax
+; CHECK-NEXT:    vcvtsi2ss %eax, %xmm15, %xmm1
+; CHECK-NEXT:    vdivss %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttss2si %xmm0, %eax
+; CHECK-NEXT:    # kill: def $al killed $al killed $eax
+; CHECK-NEXT:    retq
+  %r = udiv i8 %a, %b
+  ret i8 %r
+}
+
+define i32 @urem_i32(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: urem_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    vcvtsi2sd %rcx, %xmm15, %xmm0
+; CHECK-NEXT:    movl %edi, %ecx
+; CHECK-NEXT:    vcvtsi2sd %rcx, %xmm15, %xmm1
+; CHECK-NEXT:    vdivsd %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vcvttsd2si %xmm0, %rcx
+; CHECK-NEXT:    imull %esi, %ecx
+; CHECK-NEXT:    subl %ecx, %eax
+; CHECK-NEXT:    retq
+  %r = urem i32 %a, %b
+  ret i32 %r
+}
+
+define i64 @sdiv_i64(i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: sdiv_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movq %rdi, %rax
+; CHECK-NEXT:    cqto
+; CHECK-NEXT:    idivq %rsi
+; CHECK-NEXT:    retq
+  %r = sdiv i64 %a, %b
+  ret i64 %r
+}
+
+define i32 @sdiv_i32_const(i32 %a) nounwind {
+; CHECK-LABEL: sdiv_i32_const:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movslq %edi, %rax
+; CHECK-NEXT:    imulq $-1840700269, %rax, %rcx # imm = 0x92492493
+; CHECK-NEXT:    shrq $32, %rcx
+; CHECK-NEXT:    addl %ecx, %eax
+; CHECK-NEXT:    movl %eax, %ecx
+; CHECK-NEXT:    shrl $31, %ecx
+; CHECK-NEXT:    sarl $2, %eax
+; CHECK-NEXT:    addl %ecx, %eax
+; CHECK-NEXT:    # kill: def $eax killed $eax killed $rax
+; CHECK-NEXT:    retq
+  %r = sdiv i32 %a, 7
+  ret i32 %r
+}
+
+define i32 @sdiv_i32_noimplicitfloat(i32 %a, i32 %b) nounwind noimplicitfloat {
+; CHECK-LABEL: sdiv_i32_noimplicitfloat:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    cltd
+; CHECK-NEXT:    idivl %esi
+; CHECK-NEXT:    retq
+  %r = sdiv i32 %a, %b
+  ret i32 %r
+}

``````````

</details>


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


More information about the llvm-commits mailing list