[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
Fri Sep 18 23:06:08 PDT 2026
https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/224809
Fixes #219695
When the result of an `FLDEXP` node is scalarized, we went through the generic `ScalarizeVecRes_BinOp` handler, which asks for the scalarized form of both operands. That only works when both operands share the result's type action. `ldexp` is not a true binop: its exponent has its own integer vector type, and under AVX-512 `<1 x i1>` is a legal mask type. It was never scalarized, so it never made it into the table, and the lookup hit `TableId should be non-zero`. Without AVX-512 the same `<1 x i1>` happens to get scalarized like any other one-element vector, which is why the default target didn't crash.
`FLDEXP` now has its own scalarization handler. The FP operand is scalarized as before, while the exponent is legalized according to its own type action: its scalarized value is used when one exists, otherwise the single element is extracted from the still-legal vector. This is the same approach `ScalarizeVecRes_UnaryOp` and `ScalarizeVecRes_SETCC` already take for operands that don't need scalarizing, and mirrors what `SplitVecRes_FPOp_MultiType` does for the split case.
>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/2] [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 b6f2793c4b1f7..d02d3269af5e5 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/2] [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 2ad7054e74bfa..c5dd5f8b8bb3e 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 dc3b589087421..e29872a964230 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 cce76e969ffff..3abac6b5f26d9 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: {{.*}}
More information about the llvm-commits
mailing list