[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