[llvm] [X86] LowerFLDEXP: convert int exponent to FP before SCALEF on AVX-512F-no-VLX path (PR #199263)

via llvm-commits llvm-commits at lists.llvm.org
Fri May 22 12:28:18 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Justin Lebar (jlebar)

<details>
<summary>Changes</summary>

For sub-512-bit vector ldexp on AVX-512F without VLX, LowerFLDEXP widened
both X and Exp to 512 bits, but then constructed SINT_TO_FP from the
non-widened Exp (producing a dead node with mismatched widths) and fed
the raw integer WideExp to X86ISD::SCALEF -- which interpreted its bit
pattern as IEEE-754 floats. The result was approximately x * 1.0 instead
of x * 2^e.

Convert the widened integer exponent to FP and pass that to SCALEF. The
widening must match WideX by lane count, not by total bit width
(e.g. for v4f64 -> v8f64, Exp v4i32 widens to v8i32 -> v8f64, not v16i32).

Reproducer (clang -O2 -mavx512f repro.c -o repro && ./repro):

```
  #include <stdio.h>
  typedef float v4f __attribute__((vector_size(16)));
  typedef int v4i __attribute__((vector_size(16)));

  __attribute__((noinline))
  v4f ldexp_v4(v4f x, v4i e) {
    return __builtin_elementwise_ldexp(x, e);
  }

  int main(void) {
    v4f x = {1, 2, 4, 8};
    v4i e = {1, 2, 3, 4};
    v4f r = ldexp_v4(x, e);
    printf("got      %g %g %g %g\n", r[0], r[1], r[2], r[3]);
    printf("expected 2 8 32 128\n");
  }
```

Before this patch, ldexp_v4 lowered to (no vcvtdq2ps):

```
  vmovaps   %xmm1, %xmm1
  vmovaps   %xmm0, %xmm0
  vscalefps %zmm1, %zmm0, %zmm0  ; xmm1 still holds integer bits
```

so the program printed "got 1 2 4 8" (i.e. x * 1.0).

This bug was found by a large run of Opus 4.7 looking for bugs in LLVM.


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


2 Files Affected:

- (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+11-3) 
- (modified) llvm/test/CodeGen/X86/ldexp-avx512.ll (+30-17) 


``````````diff
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index f37d896b9c69e..edfaf131035e2 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -19834,10 +19834,18 @@ static SDValue LowerFLDEXP(SDValue Op, const X86Subtarget &Subtarget,
     return splitVectorOp(Op, DAG, DL);
   }
   SDValue WideX = widenSubVector(X, true, Subtarget, DAG, DL, 512);
-  SDValue WideExp = widenSubVector(Exp, true, Subtarget, DAG, DL, 512);
-  Exp = DAG.getNode(ISD::SINT_TO_FP, DL, WideExp.getSimpleValueType(), Exp);
+  // Widen Exp to the same *lane count* as WideX (not necessarily 512 bits) so
+  // SINT_TO_FP has matching vector lengths. For wide f64 the int exponent
+  // vector is narrower than 512 bits (e.g. v2i32 -> v8i32 to match v8f64).
+  MVT WideExpVT =
+      MVT::getVectorVT(Exp.getSimpleValueType().getVectorElementType(),
+                       WideX.getValueType().getVectorNumElements());
+  SDValue WideExp = widenSubVector(WideExpVT, Exp, /*ZeroNewElements=*/true,
+                                   Subtarget, DAG, DL);
+  SDValue WideExpFp =
+      DAG.getNode(ISD::SINT_TO_FP, DL, WideX.getValueType(), WideExp);
   SDValue Scalef =
-      DAG.getNode(X86ISD::SCALEF, DL, WideX.getValueType(), WideX, WideExp);
+      DAG.getNode(X86ISD::SCALEF, DL, WideX.getValueType(), WideX, WideExpFp);
   SDValue Final =
       DAG.getExtractSubvector(DL, X.getSimpleValueType(), Scalef, 0);
   return DAG.getFPExtendOrRound(Final, DL, XTy);
diff --git a/llvm/test/CodeGen/X86/ldexp-avx512.ll b/llvm/test/CodeGen/X86/ldexp-avx512.ll
index bb6dc3162eb1f..9ed867060a9c7 100644
--- a/llvm/test/CodeGen/X86/ldexp-avx512.ll
+++ b/llvm/test/CodeGen/X86/ldexp-avx512.ll
@@ -75,8 +75,9 @@ declare fp128 @ldexpl(fp128, i32) memory(none)
 define <8 x half> @test_ldexp_8xhalf(<8 x half> %x, <8 x i16> %exp) nounwind {
 ; AVX512F-LABEL: test_ldexp_8xhalf:
 ; AVX512F:       # %bb.0:
-; AVX512F-NEXT:    vpmovsxwd %xmm1, %ymm1
 ; AVX512F-NEXT:    vcvtph2ps %xmm0, %ymm0
+; AVX512F-NEXT:    vpmovsxwd %xmm1, %ymm1
+; AVX512F-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512F-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512F-NEXT:    vcvtps2ph $4, %ymm0, %xmm0
 ; AVX512F-NEXT:    vzeroupper
@@ -87,6 +88,7 @@ define <8 x half> @test_ldexp_8xhalf(<8 x half> %x, <8 x i16> %exp) nounwind {
 ; AVX512FP16-NEXT:    vxorps %xmm2, %xmm2, %xmm2
 ; AVX512FP16-NEXT:    vinsertf32x4 $0, %xmm0, %zmm2, %zmm0
 ; AVX512FP16-NEXT:    vmovaps %xmm1, %xmm1
+; AVX512FP16-NEXT:    vcvtw2ph %zmm1, %zmm1
 ; AVX512FP16-NEXT:    vscalefph %zmm1, %zmm0, %zmm0
 ; AVX512FP16-NEXT:    # kill: def $xmm0 killed $xmm0 killed $zmm0
 ; AVX512FP16-NEXT:    vzeroupper
@@ -94,8 +96,9 @@ define <8 x half> @test_ldexp_8xhalf(<8 x half> %x, <8 x i16> %exp) nounwind {
 ;
 ; AVX512VL-LABEL: test_ldexp_8xhalf:
 ; AVX512VL:       # %bb.0:
-; AVX512VL-NEXT:    vpmovsxwd %xmm1, %ymm1
 ; AVX512VL-NEXT:    vcvtph2ps %xmm0, %ymm0
+; AVX512VL-NEXT:    vpmovsxwd %xmm1, %ymm1
+; AVX512VL-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512VL-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512VL-NEXT:    vcvtps2ph $4, %ymm0, %xmm0
 ; AVX512VL-NEXT:    vzeroupper
@@ -114,8 +117,9 @@ declare <8 x half> @llvm.ldexp.v8f16.v8i16(<8 x half>, <8 x i16>)
 define <4 x float> @test_ldexp_4xfloat(<4 x float> %x, <4 x i32> %exp) nounwind {
 ; AVX512-LABEL: test_ldexp_4xfloat:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vmovaps %xmm1, %xmm1
 ; AVX512-NEXT:    vmovaps %xmm0, %xmm0
+; AVX512-NEXT:    vmovaps %xmm1, %xmm1
+; AVX512-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512-NEXT:    # kill: def $xmm0 killed $xmm0 killed $zmm0
 ; AVX512-NEXT:    vzeroupper
@@ -156,8 +160,9 @@ declare <2 x double> @llvm.ldexp.v2f64.v2i32(<2 x double>, <2 x i32>)
 define <16 x half> @test_ldexp_16xhalf(<16 x half> %x, <16 x i16> %exp) nounwind {
 ; AVX512F-LABEL: test_ldexp_16xhalf:
 ; AVX512F:       # %bb.0:
-; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm1
 ; AVX512F-NEXT:    vcvtph2ps %ymm0, %zmm0
+; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm1
+; AVX512F-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512F-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512F-NEXT:    vcvtps2ph $4, %zmm0, %ymm0
 ; AVX512F-NEXT:    retq
@@ -167,14 +172,16 @@ define <16 x half> @test_ldexp_16xhalf(<16 x half> %x, <16 x i16> %exp) nounwind
 ; AVX512FP16-NEXT:    vxorps %xmm2, %xmm2, %xmm2
 ; AVX512FP16-NEXT:    vinsertf64x4 $0, %ymm0, %zmm2, %zmm0
 ; AVX512FP16-NEXT:    vmovaps %ymm1, %ymm1
+; AVX512FP16-NEXT:    vcvtw2ph %zmm1, %zmm1
 ; AVX512FP16-NEXT:    vscalefph %zmm1, %zmm0, %zmm0
 ; AVX512FP16-NEXT:    # kill: def $ymm0 killed $ymm0 killed $zmm0
 ; AVX512FP16-NEXT:    retq
 ;
 ; AVX512VL-LABEL: test_ldexp_16xhalf:
 ; AVX512VL:       # %bb.0:
-; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm1
 ; AVX512VL-NEXT:    vcvtph2ps %ymm0, %zmm0
+; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm1
+; AVX512VL-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512VL-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512VL-NEXT:    vcvtps2ph $4, %zmm0, %ymm0
 ; AVX512VL-NEXT:    retq
@@ -192,8 +199,9 @@ declare <16 x half> @llvm.ldexp.v16f16.v16i16(<16 x half>, <16 x i16>)
 define <8 x float> @test_ldexp_8xfloat(<8 x float> %x, <8 x i32> %exp) nounwind {
 ; AVX512-LABEL: test_ldexp_8xfloat:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vmovaps %ymm1, %ymm1
 ; AVX512-NEXT:    vmovaps %ymm0, %ymm0
+; AVX512-NEXT:    vmovaps %ymm1, %ymm1
+; AVX512-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512-NEXT:    # kill: def $ymm0 killed $ymm0 killed $zmm0
 ; AVX512-NEXT:    retq
@@ -217,8 +225,9 @@ declare <8 x float> @llvm.ldexp.v8f32.v8i32(<8 x float>, <8 x i32>)
 define <4 x double> @test_ldexp_4xdouble(<4 x double> %x, <4 x i32> %exp) nounwind {
 ; AVX512-LABEL: test_ldexp_4xdouble:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vmovapd %xmm1, %xmm1
 ; AVX512-NEXT:    vmovapd %ymm0, %ymm0
+; AVX512-NEXT:    vmovaps %xmm1, %xmm1
+; AVX512-NEXT:    vcvtdq2pd %ymm1, %zmm1
 ; AVX512-NEXT:    vscalefpd %zmm1, %zmm0, %zmm0
 ; AVX512-NEXT:    # kill: def $ymm0 killed $ymm0 killed $zmm0
 ; AVX512-NEXT:    retq
@@ -242,14 +251,16 @@ declare <4 x double> @llvm.ldexp.v4f64.v4i32(<4 x double>, <4 x i32>)
 define <32 x half> @test_ldexp_32xhalf(<32 x half> %x, <32 x i16> %exp) nounwind {
 ; AVX512F-LABEL: test_ldexp_32xhalf:
 ; AVX512F:       # %bb.0:
-; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm2
-; AVX512F-NEXT:    vcvtph2ps %ymm0, %zmm3
-; AVX512F-NEXT:    vscalefps %zmm2, %zmm3, %zmm2
+; AVX512F-NEXT:    vcvtph2ps %ymm0, %zmm2
+; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm3
+; AVX512F-NEXT:    vcvtdq2ps %zmm3, %zmm3
+; AVX512F-NEXT:    vscalefps %zmm3, %zmm2, %zmm2
 ; AVX512F-NEXT:    vcvtps2ph $4, %zmm2, %ymm2
-; AVX512F-NEXT:    vextracti64x4 $1, %zmm1, %ymm1
-; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm1
 ; AVX512F-NEXT:    vextractf64x4 $1, %zmm0, %ymm0
 ; AVX512F-NEXT:    vcvtph2ps %ymm0, %zmm0
+; AVX512F-NEXT:    vextracti64x4 $1, %zmm1, %ymm1
+; AVX512F-NEXT:    vpmovsxwd %ymm1, %zmm1
+; AVX512F-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512F-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512F-NEXT:    vcvtps2ph $4, %zmm0, %ymm0
 ; AVX512F-NEXT:    vinsertf64x4 $1, %ymm0, %zmm2, %zmm0
@@ -263,14 +274,16 @@ define <32 x half> @test_ldexp_32xhalf(<32 x half> %x, <32 x i16> %exp) nounwind
 ;
 ; AVX512VL-LABEL: test_ldexp_32xhalf:
 ; AVX512VL:       # %bb.0:
-; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm2
-; AVX512VL-NEXT:    vcvtph2ps %ymm0, %zmm3
-; AVX512VL-NEXT:    vscalefps %zmm2, %zmm3, %zmm2
+; AVX512VL-NEXT:    vcvtph2ps %ymm0, %zmm2
+; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm3
+; AVX512VL-NEXT:    vcvtdq2ps %zmm3, %zmm3
+; AVX512VL-NEXT:    vscalefps %zmm3, %zmm2, %zmm2
 ; AVX512VL-NEXT:    vcvtps2ph $4, %zmm2, %ymm2
-; AVX512VL-NEXT:    vextracti64x4 $1, %zmm1, %ymm1
-; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm1
 ; AVX512VL-NEXT:    vextractf64x4 $1, %zmm0, %ymm0
 ; AVX512VL-NEXT:    vcvtph2ps %ymm0, %zmm0
+; AVX512VL-NEXT:    vextracti64x4 $1, %zmm1, %ymm1
+; AVX512VL-NEXT:    vpmovsxwd %ymm1, %zmm1
+; AVX512VL-NEXT:    vcvtdq2ps %zmm1, %zmm1
 ; AVX512VL-NEXT:    vscalefps %zmm1, %zmm0, %zmm0
 ; AVX512VL-NEXT:    vcvtps2ph $4, %zmm0, %ymm0
 ; AVX512VL-NEXT:    vinsertf64x4 $1, %ymm0, %zmm2, %zmm0

``````````

</details>


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


More information about the llvm-commits mailing list