[llvm] [SelectionDAG] Do not use illegal type when expanding `CONVERT_FROM_ARBITRARY_FP` (PR #219597)

Min-Yih Hsu via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 16:04:54 PDT 2026


https://github.com/mshockwave created https://github.com/llvm/llvm-project/pull/219597

During `CONVERT_FROM_ARBITRARY_FP`'s expansion, it'll try to create intermediate integer values with the same width as the floating point result. However, that integer type might not be legal, and would cause problem when dealing with scalar version of `CONVERT_FROM_ARBITRARY_FP`.

For example, in the attached LIT tests, it'll generate something like
```
t58: f32 = convert_from_arbitrary_fp t57, TargetConstant:i32<7>
```
after type legalization. While f32 is a legal type, its integer counterpart with the same width, i32, is not a legal type in RV64.

This patch fixes such problem by using the legal type for those intermediate values, if the legal type is wider.

------
I ran into this problem when trying to lower this ISD with the Zvfofp8min RISC-V extension.

This problem shows up in `CONVERT_TO_ARBITRARY_FP` too, which I'm still fixing.

>From d6eb845bb018dd64a74c64dfa63a44576180863c Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Wed, 26 Aug 2026 10:27:34 -0700
Subject: [PATCH] [Legalizer] Do not use illegal type when expanding
 CONVERT_FROM_ARBITRARY_FP

---
 .../CodeGen/SelectionDAG/TargetLowering.cpp   |  53 +++++--
 .../RISCV/convert-from-arbitrary-fp.ll        |  70 +++++++++
 .../rvv/fixed-convert-from-arbitrary-fp.ll    | 137 ++++++++++++++++++
 3 files changed, 249 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/convert-from-arbitrary-fp.ll
 create mode 100644 llvm/test/CodeGen/RISCV/rvv/fixed-convert-from-arbitrary-fp.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index cca7cdad0e2c8..1001f3d58bde6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -9810,10 +9810,20 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
 
   // Work in an integer type matching the destination float width.
   EVT IntScalarVT = EVT::getIntegerVT(*DAG.getContext(), DstBits);
-  EVT IntVT = DstVT.isVector()
-                  ? EVT::getVectorVT(*DAG.getContext(), IntScalarVT,
-                                     DstVT.getVectorElementCount())
-                  : IntScalarVT;
+  EVT IntVT = IntScalarVT;
+  if (DstVT.isVector()) {
+    IntVT = EVT::getVectorVT(*DAG.getContext(), IntScalarVT,
+                             DstVT.getVectorElementCount());
+  } else if (!isTypeLegal(IntScalarVT)) {
+    // Avoid generating illegal type as there is no other places that'll
+    // legalize it. Vector types don't have this problem because they
+    // are subject to LegalizeVectorOps and another type legalization phase
+    // will follow.
+    if (getTypeAction(*DAG.getContext(), IntScalarVT) != TypePromoteInteger)
+      // We only know how to handle situations where the legal type is wider.
+      return SDValue();
+    IntVT = getTypeToTransformTo(*DAG.getContext(), IntScalarVT);
+  }
 
   SDValue Src = DAG.getZExtOrTrunc(IntVal, dl, IntVT);
 
@@ -9881,9 +9891,10 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
 
   // Normal value conversion.
   const int BiasAdjust = DstBias - SrcBias;
-  SDValue NormDstExp =
-      DAG.getNode(ISD::ADD, dl, IntVT, ExpField,
-                  DAG.getConstant(APInt(DstBits, BiasAdjust, true), dl, IntVT));
+  SDValue NormDstExp = DAG.getNode(
+      ISD::ADD, dl, IntVT, ExpField,
+      DAG.getConstant(APInt(IntVT.getScalarSizeInBits(), BiasAdjust, true), dl,
+                      IntVT));
 
   SDValue NormDstMant;
   if (DstMant > SrcMant) {
@@ -9911,10 +9922,12 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
 
     const int DenormExpConst =
         (int)IntVTBits + DstBias - SrcBias - (int)SrcMant;
-    SDValue DenormDstExp = DAG.getNode(
-        ISD::SUB, dl, IntVT,
-        DAG.getConstant(APInt(DstBits, DenormExpConst, true), dl, IntVT),
-        LeadingZeros);
+    SDValue DenormDstExp =
+        DAG.getNode(ISD::SUB, dl, IntVT,
+                    DAG.getConstant(APInt(IntVT.getScalarSizeInBits(),
+                                          DenormExpConst, true),
+                                    dl, IntVT),
+                    LeadingZeros);
 
     SDValue MantMSB =
         DAG.getNode(ISD::SUB, dl, IntVT,
@@ -9955,6 +9968,24 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
   Result = DAG.getSelect(dl, IntVT, IsInf, InfResult, Result);
   Result = DAG.getSelect(dl, IntVT, IsNaN, NaNResult, Result);
 
+  if (!DstVT.bitsEq(IntVT)) {
+    // Store to stack before loading it back.
+    assert(!IntVT.isVector() && IntVT.bitsGT(DstVT));
+    // IntScalarVT is the original type that has the same width as DstVT.
+    Align Alignment = DAG.getReducedAlign(IntScalarVT, /*UseABI=*/false);
+    SDValue StackPtr =
+        DAG.CreateStackTemporary(IntScalarVT.getStoreSize(), Alignment);
+    auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+    MachineFunction &MF = DAG.getMachineFunction();
+    MachinePointerInfo PtrInfo =
+        MachinePointerInfo::getFixedStack(MF, FrameIndex);
+    SDValue Store = DAG.getTruncStore(DAG.getEntryNode(), dl, Result, StackPtr,
+                                      PtrInfo, IntScalarVT, Alignment);
+
+    SDValue Load = DAG.getLoad(DstVT, dl, Store, StackPtr, PtrInfo, Alignment);
+    return DAG.getMergeValues({Load, Load.getValue(1)}, dl);
+  }
+
   return DAG.getNode(ISD::BITCAST, dl, DstVT, Result);
 }
 
diff --git a/llvm/test/CodeGen/RISCV/convert-from-arbitrary-fp.ll b/llvm/test/CodeGen/RISCV/convert-from-arbitrary-fp.ll
new file mode 100644
index 0000000000000..109a8f8a48981
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/convert-from-arbitrary-fp.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+b,+f,+m < %s | FileCheck %s
+
+define bfloat @fp8e5m2_to_bf16(i8 signext %x) nounwind {
+; CHECK-LABEL: fp8e5m2_to_bf16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a4, 524288
+; CHECK-NEXT:    slli a3, a0, 57
+; CHECK-NEXT:    andi a2, a0, 3
+; CHECK-NEXT:    srli a3, a3, 59
+; CHECK-NEXT:    snez a1, a2
+; CHECK-NEXT:    seqz a5, a3
+; CHECK-NEXT:    and a5, a5, a1
+; CHECK-NEXT:    and a0, a0, a4
+; CHECK-NEXT:    bnez a5, .LBB0_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    addi a4, a3, 112
+; CHECK-NEXT:    slli a5, a2, 21
+; CHECK-NEXT:    slli a4, a4, 23
+; CHECK-NEXT:    or a5, a0, a5
+; CHECK-NEXT:    j .LBB0_3
+; CHECK-NEXT:  .LBB0_2:
+; CHECK-NEXT:    clz a4, a2
+; CHECK-NEXT:    li a5, 142
+; CHECK-NEXT:    li a6, 31
+; CHECK-NEXT:    sub a5, a5, a4
+; CHECK-NEXT:    sub a6, a6, a4
+; CHECK-NEXT:    slli a5, a5, 23
+; CHECK-NEXT:    binv a6, a2, a6
+; CHECK-NEXT:    addi a4, a4, -8
+; CHECK-NEXT:    or a5, a0, a5
+; CHECK-NEXT:    sll a4, a6, a4
+; CHECK-NEXT:  .LBB0_3:
+; CHECK-NEXT:    or a5, a5, a4
+; CHECK-NEXT:    or a6, a3, a2
+; CHECK-NEXT:    mv a4, a0
+; CHECK-NEXT:    beqz a6, .LBB0_5
+; CHECK-NEXT:  # %bb.4:
+; CHECK-NEXT:    mv a4, a5
+; CHECK-NEXT:  .LBB0_5:
+; CHECK-NEXT:    addi a3, a3, -31
+; CHECK-NEXT:    seqz a5, a2
+; CHECK-NEXT:    seqz a2, a3
+; CHECK-NEXT:    and a5, a2, a5
+; CHECK-NEXT:    beqz a5, .LBB0_7
+; CHECK-NEXT:  # %bb.6:
+; CHECK-NEXT:    lui a4, 522240
+; CHECK-NEXT:    or a4, a0, a4
+; CHECK-NEXT:  .LBB0_7:
+; CHECK-NEXT:    and a1, a2, a1
+; CHECK-NEXT:    lui a0, 523264
+; CHECK-NEXT:    bnez a1, .LBB0_9
+; CHECK-NEXT:  # %bb.8:
+; CHECK-NEXT:    mv a0, a4
+; CHECK-NEXT:  .LBB0_9:
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    sw a0, 4(sp)
+; CHECK-NEXT:    flw fa0, 4(sp)
+; CHECK-NEXT:    call __truncsfbf2
+; CHECK-NEXT:    fmv.x.w a0, fa0
+; CHECK-NEXT:    lui a1, 1048560
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    fmv.w.x fa0, a0
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %r = call bfloat @llvm.convert.from.arbitrary.fp(i8 %x, metadata !"Float8E5M2")
+  ret bfloat %r
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-convert-from-arbitrary-fp.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-convert-from-arbitrary-fp.ll
new file mode 100644
index 0000000000000..94397be6ea416
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/fixed-convert-from-arbitrary-fp.ll
@@ -0,0 +1,137 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+v,+b < %s | FileCheck %s
+
+define <2 x bfloat> @v2fp8e5m2_to_v2bf16(<2 x i8> %x) nounwind {
+; CHECK-LABEL: v2fp8e5m2_to_v2bf16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a0, 524288
+; CHECK-NEXT:    vsetivli zero, 1, e8, m1, ta, ma
+; CHECK-NEXT:    vmv.x.s a5, v8
+; CHECK-NEXT:    slli a1, a5, 57
+; CHECK-NEXT:    andi a4, a5, 3
+; CHECK-NEXT:    srli a6, a1, 59
+; CHECK-NEXT:    snez a3, a4
+; CHECK-NEXT:    seqz a1, a6
+; CHECK-NEXT:    and a5, a5, a0
+; CHECK-NEXT:    and a7, a1, a3
+; CHECK-NEXT:    li a2, 142
+; CHECK-NEXT:    li a1, 31
+; CHECK-NEXT:    bnez a7, .LBB0_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    addi a7, a6, 112
+; CHECK-NEXT:    slli t0, a4, 21
+; CHECK-NEXT:    slli a7, a7, 23
+; CHECK-NEXT:    or t0, a5, t0
+; CHECK-NEXT:    j .LBB0_3
+; CHECK-NEXT:  .LBB0_2:
+; CHECK-NEXT:    clz a7, a4
+; CHECK-NEXT:    sub t0, a2, a7
+; CHECK-NEXT:    sub t1, a1, a7
+; CHECK-NEXT:    slli t0, t0, 23
+; CHECK-NEXT:    binv t1, a4, t1
+; CHECK-NEXT:    addi a7, a7, -8
+; CHECK-NEXT:    or t0, a5, t0
+; CHECK-NEXT:    sll a7, t1, a7
+; CHECK-NEXT:  .LBB0_3:
+; CHECK-NEXT:    or t0, t0, a7
+; CHECK-NEXT:    or t1, a6, a4
+; CHECK-NEXT:    mv a7, a5
+; CHECK-NEXT:    beqz t1, .LBB0_5
+; CHECK-NEXT:  # %bb.4:
+; CHECK-NEXT:    mv a7, t0
+; CHECK-NEXT:  .LBB0_5:
+; CHECK-NEXT:    addi a6, a6, -31
+; CHECK-NEXT:    seqz a4, a4
+; CHECK-NEXT:    seqz a6, a6
+; CHECK-NEXT:    and t0, a6, a4
+; CHECK-NEXT:    lui a4, 522240
+; CHECK-NEXT:    beqz t0, .LBB0_7
+; CHECK-NEXT:  # %bb.6:
+; CHECK-NEXT:    or a7, a5, a4
+; CHECK-NEXT:  .LBB0_7:
+; CHECK-NEXT:    and a5, a6, a3
+; CHECK-NEXT:    lui a3, 523264
+; CHECK-NEXT:    lui t0, 523264
+; CHECK-NEXT:    bnez a5, .LBB0_9
+; CHECK-NEXT:  # %bb.8:
+; CHECK-NEXT:    mv t0, a7
+; CHECK-NEXT:  .LBB0_9:
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    sd s0, 16(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    vsetivli zero, 1, e8, mf8, ta, ma
+; CHECK-NEXT:    vslidedown.vi v8, v8, 1
+; CHECK-NEXT:    vmv.x.s t1, v8
+; CHECK-NEXT:    slli a5, t1, 57
+; CHECK-NEXT:    andi a6, t1, 3
+; CHECK-NEXT:    srli a7, a5, 59
+; CHECK-NEXT:    snez a5, a6
+; CHECK-NEXT:    seqz t2, a7
+; CHECK-NEXT:    and t2, t2, a5
+; CHECK-NEXT:    sw t0, 8(sp)
+; CHECK-NEXT:    and a0, t1, a0
+; CHECK-NEXT:    bnez t2, .LBB0_11
+; CHECK-NEXT:  # %bb.10:
+; CHECK-NEXT:    addi a1, a7, 112
+; CHECK-NEXT:    slli a2, a6, 21
+; CHECK-NEXT:    slli a1, a1, 23
+; CHECK-NEXT:    or a2, a0, a2
+; CHECK-NEXT:    j .LBB0_12
+; CHECK-NEXT:  .LBB0_11:
+; CHECK-NEXT:    clz t0, a6
+; CHECK-NEXT:    sub a2, a2, t0
+; CHECK-NEXT:    sub a1, a1, t0
+; CHECK-NEXT:    slli a2, a2, 23
+; CHECK-NEXT:    binv a1, a6, a1
+; CHECK-NEXT:    addi t0, t0, -8
+; CHECK-NEXT:    or a2, a0, a2
+; CHECK-NEXT:    sll a1, a1, t0
+; CHECK-NEXT:  .LBB0_12:
+; CHECK-NEXT:    or a2, a2, a1
+; CHECK-NEXT:    or t0, a7, a6
+; CHECK-NEXT:    mv a1, a0
+; CHECK-NEXT:    beqz t0, .LBB0_14
+; CHECK-NEXT:  # %bb.13:
+; CHECK-NEXT:    mv a1, a2
+; CHECK-NEXT:  .LBB0_14:
+; CHECK-NEXT:    addi a7, a7, -31
+; CHECK-NEXT:    seqz a6, a6
+; CHECK-NEXT:    seqz a2, a7
+; CHECK-NEXT:    and a6, a2, a6
+; CHECK-NEXT:    beqz a6, .LBB0_16
+; CHECK-NEXT:  # %bb.15:
+; CHECK-NEXT:    or a1, a0, a4
+; CHECK-NEXT:  .LBB0_16:
+; CHECK-NEXT:    and a2, a2, a5
+; CHECK-NEXT:    bnez a2, .LBB0_18
+; CHECK-NEXT:  # %bb.17:
+; CHECK-NEXT:    mv a3, a1
+; CHECK-NEXT:  .LBB0_18:
+; CHECK-NEXT:    sw a3, 12(sp)
+; CHECK-NEXT:    flw fa0, 8(sp)
+; CHECK-NEXT:    call __truncsfbf2
+; CHECK-NEXT:    fmv.x.w a0, fa0
+; CHECK-NEXT:    flw fa0, 12(sp)
+; CHECK-NEXT:    zext.h s0, a0
+; CHECK-NEXT:    call __truncsfbf2
+; CHECK-NEXT:    lui a0, 1048560
+; CHECK-NEXT:    fmv.x.w a1, fa0
+; CHECK-NEXT:    slli a1, a1, 16
+; CHECK-NEXT:    or a1, s0, a1
+; CHECK-NEXT:    vsetivli zero, 1, e32, m1, ta, ma
+; CHECK-NEXT:    vmv.s.x v8, a1
+; CHECK-NEXT:    vsetivli zero, 1, e16, mf4, ta, ma
+; CHECK-NEXT:    vmv.x.s a1, v8
+; CHECK-NEXT:    vslidedown.vi v8, v8, 1
+; CHECK-NEXT:    vmv.x.s a2, v8
+; CHECK-NEXT:    or a1, a1, a0
+; CHECK-NEXT:    or a0, a2, a0
+; CHECK-NEXT:    fmv.w.x fa0, a1
+; CHECK-NEXT:    fmv.w.x fa1, a0
+; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    ld s0, 16(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    ret
+  %r = call <2 x bfloat> @llvm.convert.from.arbitrary.fp(<2 x i8> %x, metadata !"Float8E5M2")
+  ret <2 x bfloat> %r
+}



More information about the llvm-commits mailing list