[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