[llvm] [X86] Lower vector integer division and remainder through float division (PR #205263)
Adam Scott via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 19:30:38 PDT 2026
================
@@ -50516,6 +50524,190 @@ static SDValue combineMulToPMADD52(SDNode *N, const SDLoc &DL,
return SDValue();
}
+// x86 has no vector integer divide instructions. Lower vector
+// UDIV/SDIV/UREM/SREM through float division instead of scalarizing into N
+// scalar hardware divides.
+static SDValue combineIntDivRem(SDNode *N, SelectionDAG &DAG,
+ TargetLowering::DAGCombinerInfo &DCI,
+ const X86Subtarget &Subtarget) {
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+
+ // Run before the legalizer expands the division.
+ if (!VT.isVector() || !Subtarget.hasSSE2() || !DCI.isBeforeLegalizeOps())
+ return SDValue();
+
+ SDValue Dividend = N->getOperand(0);
+ SDValue Divisor = N->getOperand(1);
+ unsigned Opc = N->getOpcode();
+
+ // Disabled lanes are poison and fdiv never traps, so ignore the mask.
+ if (Opc == ISD::MASKED_UDIV || Opc == ISD::MASKED_SDIV ||
+ Opc == ISD::MASKED_UREM || Opc == ISD::MASKED_SREM)
+ Opc = ISD::getUnmaskedBinOpOpcode(Opc);
+ bool IsRem = Opc == ISD::UREM || Opc == ISD::SREM;
+ bool IsSigned = Opc == ISD::SDIV || Opc == ISD::SREM;
+
+ // If the result is only read back as scalar extracts, scalarization computes
+ // just the demanded lanes.
+ if (all_of(N->users(), [](const SDNode *U) {
+ return U->getOpcode() == ISD::EXTRACT_VECTOR_ELT;
+ }))
+ return SDValue();
+
+ // Magic multiply lowers constant divisors cheaper than a divide.
+ if (DAG.isConstantIntBuildVectorOrConstantInt(Divisor))
+ return SDValue();
+
+ // i8/i16/i32: the operands fit the float mantissa exactly so one float
+ // divide recovers the exact quotient.
+ if (VT.getScalarSizeInBits() <= 32) {
+ // f32 recovers the quotient exactly when both operands fit in 24 bits
+ unsigned EltBits = VT.getScalarSizeInBits();
+ unsigned Precision = APFloat::semanticsPrecision(APFloat::IEEEsingle());
+ auto FitsF32 = [&](SDValue V) {
----------------
as4230 wrote:
Actually we can drop the DQ requirement for the narrow i64 case with a 2^52 magic constant trick: https://stackoverflow.com/questions/41144668/how-to-efficiently-perform-double-int64-conversions-with-sse-avx
The gist is that doubles in [2^52, 2^53) hold the integer verbatim in the mantissa so when both operands provably fit 52 bits, int64 to f64 is just OR with 0x4330000000000000 and subtract 2^52, and the way back is add 2^52 and mask the mantissa. Two instructions each way and it works down to SSE2. For signed you add a 2^51 bias first and subtract it back at the end so negatives fit the window. The convert back also rounds instead of truncating so the quotient needs a compare and subtract to fix the cases that rounded up. LLVM already uses the full range variant of this in LowerUINT_TO_FP but this would be the short form of it.
It comes out kind of ugly in my draft implementation though. I will play with it but it might be better as a follow-up so this one stays reviewable.
https://github.com/llvm/llvm-project/pull/205263
More information about the llvm-commits
mailing list