[llvm] [SelectionDAG] Fix crash scalarizing `FLDEXP` with a legal `<1 x i1>` exponent (PR #224809)

Akash Manna via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 27 04:12:47 PDT 2026


https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/224809

>From 0dd02bd8789a594ea218d80319d34f347479a110 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Sat, 19 Sep 2026 11:20:32 +0530
Subject: [PATCH 1/3] [X86] Add ldexp test with a v1i1 exponent (NFC).

---
 llvm/test/CodeGen/X86/ldexp.ll | 40 ++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/llvm/test/CodeGen/X86/ldexp.ll b/llvm/test/CodeGen/X86/ldexp.ll
index b6f2793c4b1f7a..d02d3269af5e5b 100644
--- a/llvm/test/CodeGen/X86/ldexp.ll
+++ b/llvm/test/CodeGen/X86/ldexp.ll
@@ -592,5 +592,45 @@ define <4 x double> @ldexp_v4f64(<4 x double> %val, <4 x i32> %exp) nounwind {
   ret <4 x double> %1
 }
 
+define <1 x float> @ldexp_v1f32_v1i1(<1 x float> %x, <1 x i1> %e) nounwind {
+; X64-LABEL: ldexp_v1f32_v1i1:
+; X64:       # %bb.0:
+; X64-NEXT:    pushq %rax
+; X64-NEXT:    andl $1, %edi
+; X64-NEXT:    negl %edi
+; X64-NEXT:    callq ldexpf at PLT
+; X64-NEXT:    popq %rax
+; X64-NEXT:    retq
+;
+; WIN64-LABEL: ldexp_v1f32_v1i1:
+; WIN64:       # %bb.0:
+; WIN64-NEXT:    subq $40, %rsp
+; WIN64-NEXT:    andb $1, %dl
+; WIN64-NEXT:    negb %dl
+; WIN64-NEXT:    cvtss2sd %xmm0, %xmm0
+; WIN64-NEXT:    callq ldexp
+; WIN64-NEXT:    cvtsd2ss %xmm0, %xmm0
+; WIN64-NEXT:    addq $40, %rsp
+; WIN64-NEXT:    retq
+;
+; WIN32-LABEL: ldexp_v1f32_v1i1:
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    subl $16, %esp
+; WIN32-NEXT:    movzbl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    andb $1, %al
+; WIN32-NEXT:    negb %al
+; WIN32-NEXT:    flds {{[0-9]+}}(%esp)
+; WIN32-NEXT:    fstpl (%esp)
+; WIN32-NEXT:    movsbl %al, %eax
+; WIN32-NEXT:    movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT:    calll _ldexp
+; WIN32-NEXT:    fstps {{[0-9]+}}(%esp)
+; WIN32-NEXT:    flds {{[0-9]+}}(%esp)
+; WIN32-NEXT:    addl $16, %esp
+; WIN32-NEXT:    retl
+  %r = call <1 x float> @llvm.ldexp.v1f32.v1i1(<1 x float> %x, <1 x i1> %e)
+  ret <1 x float> %r
+}
+
 attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }

>From 412c1b57914d1d52c3e6ce20592831f3cdba523d Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Sat, 19 Sep 2026 11:24:36 +0530
Subject: [PATCH 2/3] [SelectionDAG] Scalarize FLDEXP without assuming its
 exponent was scalarized

ScalarizeVecRes_BinOp assumes both operands share the result's type
action. FLDEXP's exponent has its own integer vector type, which can be
legal on its own (v1i1 under AVX-512), so the lookup asserted. Give
FLDEXP a handler that legalizes the exponent by its own type action.

Fixes #219695
---
 llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h |  1 +
 .../SelectionDAG/LegalizeVectorTypes.cpp      | 22 ++++++++++++++++++-
 llvm/test/CodeGen/X86/ldexp-avx512.ll         | 14 ++++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 2ad7054e74bfa3..c5dd5f8b8bb3e1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -841,6 +841,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   SDValue ScalarizeVecRes_CONVERT_FROM_ARBITRARY_FP(SDNode *N);
   SDValue ScalarizeVecRes_CONVERT_TO_ARBITRARY_FP(SDNode *N);
   SDValue ScalarizeVecRes_UnaryOpWithExtraInput(SDNode *N);
+  SDValue ScalarizeVecRes_FPOp_MultiType(SDNode *N);
   SDValue ScalarizeVecRes_INSERT_VECTOR_ELT(SDNode *N);
   SDValue ScalarizeVecRes_LOAD(LoadSDNode *N);
   SDValue ScalarizeVecRes_ATOMIC_LOAD(AtomicSDNode *N);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index dc3b5890874217..e29872a9642309 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -184,7 +184,6 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
   case ISD::FMAXIMUM:
   case ISD::FMINIMUMNUM:
   case ISD::FMAXIMUMNUM:
-  case ISD::FLDEXP:
   case ISD::ABDS:
   case ISD::ABDU:
   case ISD::SMIN:
@@ -233,6 +232,10 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
     R = ScalarizeVecRes_MaskedBinOp(N);
     break;
 
+  case ISD::FLDEXP:
+    R = ScalarizeVecRes_FPOp_MultiType(N);
+    break;
+
   case ISD::SCMP:
   case ISD::UCMP:
     R = ScalarizeVecRes_CMP(N);
@@ -547,6 +550,23 @@ SDValue DAGTypeLegalizer::ScalarizeVecRes_UnaryOpWithExtraInput(SDNode *N) {
                      N->getOperand(1));
 }
 
+SDValue DAGTypeLegalizer::ScalarizeVecRes_FPOp_MultiType(SDNode *N) {
+  SDLoc DL(N);
+  SDValue LHS = GetScalarizedVector(N->getOperand(0));
+  SDValue RHS = N->getOperand(1);
+  EVT RHSVT = RHS.getValueType();
+  // The second operand has its own type and may not need scalarizing itself,
+  // e.g. v1i1 is legal under AVX-512. See ScalarizeVecRes_UnaryOp.
+  if (RHSVT.isVector()) {
+    if (getTypeAction(RHSVT) == TargetLowering::TypeScalarizeVector)
+      RHS = GetScalarizedVector(RHS);
+    else
+      RHS = DAG.getExtractVectorElt(DL, RHSVT.getVectorElementType(), RHS, 0);
+  }
+  return DAG.getNode(N->getOpcode(), DL, LHS.getValueType(), LHS, RHS,
+                     N->getFlags());
+}
+
 SDValue DAGTypeLegalizer::ScalarizeVecRes_INSERT_VECTOR_ELT(SDNode *N) {
   // The value to insert may have a wider type than the vector element type,
   // so be sure to truncate it to the element type if necessary.
diff --git a/llvm/test/CodeGen/X86/ldexp-avx512.ll b/llvm/test/CodeGen/X86/ldexp-avx512.ll
index cce76e969ffffa..3abac6b5f26d98 100644
--- a/llvm/test/CodeGen/X86/ldexp-avx512.ll
+++ b/llvm/test/CodeGen/X86/ldexp-avx512.ll
@@ -357,5 +357,19 @@ define <8 x double> @test_ldexp_8xdouble_prefer256(<8 x double> %x, <8 x i32> %e
   ret <8 x double> %r
 }
 
+define <1 x float> @test_ldexp_v1f32_v1i1(<1 x float> %x, <1 x i1> %e) nounwind {
+; CHECK-LABEL: test_ldexp_v1f32_v1i1:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    andl $1, %edi
+; CHECK-NEXT:    negl %edi
+; CHECK-NEXT:    callq ldexpf at PLT
+; CHECK-NEXT:    popq %rax
+; CHECK-NEXT:    retq
+  %r = call <1 x float> @llvm.ldexp.v1f32.v1i1(<1 x float> %x, <1 x i1> %e)
+  ret <1 x float> %r
+}
+declare <1 x float> @llvm.ldexp.v1f32.v1i1(<1 x float>, <1 x i1>)
+
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; AVX512VLF: {{.*}}

>From 715b7c3fabb12bc7ddddc00eb0457f9b6c8bd6c8 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Tue, 22 Sep 2026 21:52:47 +0530
Subject: [PATCH 3/3] [SelectionDAG] Add an AArch64 test with a widened FLDEXP
 exponent

The same assertion fires on AArch64 with an ordinary i32 exponent:
v1f16 is scalarized while v1i32 is widened, so the exponent was never
scalarized either.
---
 .../SelectionDAG/LegalizeVectorTypes.cpp      |  4 +--
 llvm/test/CodeGen/AArch64/ldexp.ll            | 36 +++++++++++++++++++
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index e29872a9642309..00943b194631ce 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -555,8 +555,8 @@ SDValue DAGTypeLegalizer::ScalarizeVecRes_FPOp_MultiType(SDNode *N) {
   SDValue LHS = GetScalarizedVector(N->getOperand(0));
   SDValue RHS = N->getOperand(1);
   EVT RHSVT = RHS.getValueType();
-  // The second operand has its own type and may not need scalarizing itself,
-  // e.g. v1i1 is legal under AVX-512. See ScalarizeVecRes_UnaryOp.
+  // The exponent has its own type action and may not have been scalarized:
+  // v1i1 is legal on AVX-512, v1i32 is widened on AArch64.
   if (RHSVT.isVector()) {
     if (getTypeAction(RHSVT) == TargetLowering::TypeScalarizeVector)
       RHS = GetScalarizedVector(RHS);
diff --git a/llvm/test/CodeGen/AArch64/ldexp.ll b/llvm/test/CodeGen/AArch64/ldexp.ll
index fed1489b1bcff2..edcdf1c4414b8a 100644
--- a/llvm/test/CodeGen/AArch64/ldexp.ll
+++ b/llvm/test/CodeGen/AArch64/ldexp.ll
@@ -363,3 +363,39 @@ entry:
   %0 = tail call fast bfloat @llvm.ldexp.bf16.i32(bfloat %val, i32 %a)
   ret bfloat %0
 }
+
+; The v1f16 result is scalarized but the v1i32 exponent is widened, so the
+; exponent was never scalarized.
+define <1 x half> @test_ldexp_v1f16_v1i32(<1 x half> %val, <1 x i32> %exp) nounwind {
+; SVE-LABEL: test_ldexp_v1f16_v1i32:
+; SVE:       // %bb.0:
+; SVE-NEXT:    fcvt s0, h0
+; SVE-NEXT:    ptrue p0.s
+; SVE-NEXT:    // kill: def $d1 killed $d1 def $z1
+; SVE-NEXT:    fscale z0.s, p0/m, z0.s, z1.s
+; SVE-NEXT:    fcvt h0, s0
+; SVE-NEXT:    ret
+;
+; GISEL-LABEL: test_ldexp_v1f16_v1i32:
+; GISEL:       // %bb.0:
+; GISEL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; GISEL-NEXT:    fcvt s0, h0
+; GISEL-NEXT:    fmov w0, s1
+; GISEL-NEXT:    bl ldexpf
+; GISEL-NEXT:    fcvt h0, s0
+; GISEL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; GISEL-NEXT:    ret
+;
+; WINDOWS-LABEL: test_ldexp_v1f16_v1i32:
+; WINDOWS:       // %bb.0:
+; WINDOWS-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; WINDOWS-NEXT:    fcvt d0, h0
+; WINDOWS-NEXT:    // kill: def $d1 killed $d1 def $q1
+; WINDOWS-NEXT:    fmov w0, s1
+; WINDOWS-NEXT:    bl ldexp
+; WINDOWS-NEXT:    fcvt h0, d0
+; WINDOWS-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; WINDOWS-NEXT:    ret
+  %result = call <1 x half> @llvm.ldexp.v1f16.v1i32(<1 x half> %val, <1 x i32> %exp)
+  ret <1 x half> %result
+}



More information about the llvm-commits mailing list