[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