[llvm] [AArch64] Fix lowering of non-power2 uitofp (PR #190921)

David Green via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 01:13:01 PDT 2026


https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/190921

>From 632755a7dfec76e87758169354e76469feb66ce3 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Wed, 8 Apr 2026 08:15:44 +0100
Subject: [PATCH 1/3] [AArch64] Fix lowering of non-power2 uitofp

The code in DAGTypeLegalizer::SplitVecOp_TruncateHelper attempts to use
getFloatingPointVT(InElementSize/2), which is invalid for non-power2 type
sizes. Fall back to the existing SplitVecOp_UnaryOp in this case.
---
 .../SelectionDAG/LegalizeVectorTypes.cpp      |  3 +
 llvm/test/CodeGen/AArch64/itofp.ll            | 72 +++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index a96c77bc6a4e9..005e2384b7260 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4727,6 +4727,9 @@ SDValue DAGTypeLegalizer::SplitVecOp_TruncateHelper(SDNode *N) {
   if (getTypeAction(FinalVT) == TargetLowering::TypeScalarizeVector)
     return SplitVecOp_UnaryOp(N);
 
+  if (IsFloat && !isPowerOf2_32(InElementSize))
+    return SplitVecOp_UnaryOp(N);
+
   // Get the split input vector.
   SDValue InLoVec, InHiVec;
   GetSplitVector(InVec, InLoVec, InHiVec);
diff --git a/llvm/test/CodeGen/AArch64/itofp.ll b/llvm/test/CodeGen/AArch64/itofp.ll
index 2bb0559f870ad..7a963cceb8119 100644
--- a/llvm/test/CodeGen/AArch64/itofp.ll
+++ b/llvm/test/CodeGen/AArch64/itofp.ll
@@ -8230,3 +8230,75 @@ entry:
   %c = uitofp <2 x i128> %a to <2 x fp128>
   ret <2 x fp128> %c
 }
+
+define <4 x half> @utofp_v4i48_v4f16(<4 x i48> %a) {
+; CHECK-NOFP16-SD-LABEL: utofp_v4i48_v4f16:
+; CHECK-NOFP16-SD:       // %bb.0:
+; CHECK-NOFP16-SD-NEXT:    and x8, x1, #0xffffffffffff
+; CHECK-NOFP16-SD-NEXT:    and x9, x0, #0xffffffffffff
+; CHECK-NOFP16-SD-NEXT:    ucvtf s0, x8
+; CHECK-NOFP16-SD-NEXT:    ucvtf s1, x9
+; CHECK-NOFP16-SD-NEXT:    and x8, x2, #0xffffffffffff
+; CHECK-NOFP16-SD-NEXT:    fcvt h2, s0
+; CHECK-NOFP16-SD-NEXT:    fcvt h0, s1
+; CHECK-NOFP16-SD-NEXT:    ucvtf s1, x8
+; CHECK-NOFP16-SD-NEXT:    and x8, x3, #0xffffffffffff
+; CHECK-NOFP16-SD-NEXT:    mov v0.h[1], v2.h[0]
+; CHECK-NOFP16-SD-NEXT:    fcvt h1, s1
+; CHECK-NOFP16-SD-NEXT:    ucvtf s2, x8
+; CHECK-NOFP16-SD-NEXT:    mov v0.h[2], v1.h[0]
+; CHECK-NOFP16-SD-NEXT:    fcvt h1, s2
+; CHECK-NOFP16-SD-NEXT:    mov v0.h[3], v1.h[0]
+; CHECK-NOFP16-SD-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; CHECK-NOFP16-SD-NEXT:    ret
+;
+; CHECK-FP16-SD-LABEL: utofp_v4i48_v4f16:
+; CHECK-FP16-SD:       // %bb.0:
+; CHECK-FP16-SD-NEXT:    and x8, x1, #0xffffffffffff
+; CHECK-FP16-SD-NEXT:    and x9, x0, #0xffffffffffff
+; CHECK-FP16-SD-NEXT:    ucvtf h1, x8
+; CHECK-FP16-SD-NEXT:    ucvtf h0, x9
+; CHECK-FP16-SD-NEXT:    and x8, x2, #0xffffffffffff
+; CHECK-FP16-SD-NEXT:    mov v0.h[1], v1.h[0]
+; CHECK-FP16-SD-NEXT:    ucvtf h1, x8
+; CHECK-FP16-SD-NEXT:    and x8, x3, #0xffffffffffff
+; CHECK-FP16-SD-NEXT:    mov v0.h[2], v1.h[0]
+; CHECK-FP16-SD-NEXT:    ucvtf h1, x8
+; CHECK-FP16-SD-NEXT:    mov v0.h[3], v1.h[0]
+; CHECK-FP16-SD-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; CHECK-FP16-SD-NEXT:    ret
+;
+; CHECK-NOFP16-GI-LABEL: utofp_v4i48_v4f16:
+; CHECK-NOFP16-GI:       // %bb.0:
+; CHECK-NOFP16-GI-NEXT:    fmov d0, x0
+; CHECK-NOFP16-GI-NEXT:    movi v1.2d, #0x00ffffffffffff
+; CHECK-NOFP16-GI-NEXT:    fmov d2, x2
+; CHECK-NOFP16-GI-NEXT:    mov v0.d[1], x1
+; CHECK-NOFP16-GI-NEXT:    mov v2.d[1], x3
+; CHECK-NOFP16-GI-NEXT:    and v0.16b, v0.16b, v1.16b
+; CHECK-NOFP16-GI-NEXT:    and v1.16b, v2.16b, v1.16b
+; CHECK-NOFP16-GI-NEXT:    ucvtf v0.2d, v0.2d
+; CHECK-NOFP16-GI-NEXT:    ucvtf v1.2d, v1.2d
+; CHECK-NOFP16-GI-NEXT:    fcvtn v0.2s, v0.2d
+; CHECK-NOFP16-GI-NEXT:    fcvtn2 v0.4s, v1.2d
+; CHECK-NOFP16-GI-NEXT:    fcvtn v0.4h, v0.4s
+; CHECK-NOFP16-GI-NEXT:    ret
+;
+; CHECK-FP16-GI-LABEL: utofp_v4i48_v4f16:
+; CHECK-FP16-GI:       // %bb.0:
+; CHECK-FP16-GI-NEXT:    fmov d0, x0
+; CHECK-FP16-GI-NEXT:    movi v1.2d, #0x00ffffffffffff
+; CHECK-FP16-GI-NEXT:    fmov d2, x2
+; CHECK-FP16-GI-NEXT:    mov v0.d[1], x1
+; CHECK-FP16-GI-NEXT:    mov v2.d[1], x3
+; CHECK-FP16-GI-NEXT:    and v0.16b, v0.16b, v1.16b
+; CHECK-FP16-GI-NEXT:    and v1.16b, v2.16b, v1.16b
+; CHECK-FP16-GI-NEXT:    ucvtf v0.2d, v0.2d
+; CHECK-FP16-GI-NEXT:    ucvtf v1.2d, v1.2d
+; CHECK-FP16-GI-NEXT:    fcvtn v0.2s, v0.2d
+; CHECK-FP16-GI-NEXT:    fcvtn2 v0.4s, v1.2d
+; CHECK-FP16-GI-NEXT:    fcvtn v0.4h, v0.4s
+; CHECK-FP16-GI-NEXT:    ret
+  %r = uitofp <4 x i48> %a to <4 x half>
+  ret <4 x half> %r
+}

>From 7fcbb0503401b70bfd8a9e1014d2c93ab2b7842f Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Thu, 16 Apr 2026 22:22:05 +0100
Subject: [PATCH 2/3] Add bf16 test and formatting

---
 .../SelectionDAG/LegalizeVectorTypes.cpp      |  3 +-
 llvm/test/CodeGen/AArch64/itofp-bf16.ll       | 77 +++++++++++++++++++
 2 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index 005e2384b7260..3dc3e17f6467b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4714,8 +4714,7 @@ SDValue DAGTypeLegalizer::SplitVecOp_TruncateHelper(SDNode *N) {
   // If the input elements are only 1/2 the width of the result elements,
   // just use the normal splitting. Our trick only work if there's room
   // to split more than once.
-  if (isTypeLegal(LoOutVT) ||
-      InElementSize <= OutElementSize * 2)
+  if (isTypeLegal(LoOutVT) || InElementSize <= OutElementSize * 2)
     return SplitVecOp_UnaryOp(N);
   SDLoc DL(N);
 
diff --git a/llvm/test/CodeGen/AArch64/itofp-bf16.ll b/llvm/test/CodeGen/AArch64/itofp-bf16.ll
index 415e4f3c6d39d..910f6d5f4571f 100644
--- a/llvm/test/CodeGen/AArch64/itofp-bf16.ll
+++ b/llvm/test/CodeGen/AArch64/itofp-bf16.ll
@@ -60,6 +60,7 @@
 ; CHECK-NOFP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v16i8_v16bf16
 ; CHECK-NOFP16-GI-NEXT:  warning: Instruction selection used fallback path for stofp_v32i8_v32bf16
 ; CHECK-NOFP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v32i8_v32bf16
+; CHECK-NOFP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v4i48_v4f16
 ;
 ; CHECK-FP16-GI:       warning: Instruction selection used fallback path for stofp_i64_bf16
 ; CHECK-FP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_i64_bf16
@@ -117,6 +118,8 @@
 ; CHECK-FP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v16i8_v16bf16
 ; CHECK-FP16-GI-NEXT:  warning: Instruction selection used fallback path for stofp_v32i8_v32bf16
 ; CHECK-FP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v32i8_v32bf16
+; CHECK-FP16-GI-NEXT:  warning: Instruction selection used fallback path for utofp_v4i48_v4f16
+;
 
 define bfloat @stofp_i64_bf16(i64 %a) {
 ; CHECK-NOFP16-LABEL: stofp_i64_bf16:
@@ -3163,6 +3166,80 @@ entry:
   %c = uitofp <32 x i8> %a to <32 x bfloat>
   ret <32 x bfloat> %c
 }
+
+define <4 x bfloat> @utofp_v4i48_v4f16(<4 x i48> %a) {
+; CHECK-NOFP16-LABEL: utofp_v4i48_v4f16:
+; CHECK-NOFP16:       // %bb.0:
+; CHECK-NOFP16-NEXT:    and x8, x1, #0xffffffffffff
+; CHECK-NOFP16-NEXT:    and x9, x0, #0xffffffffffff
+; CHECK-NOFP16-NEXT:    ucvtf d0, x8
+; CHECK-NOFP16-NEXT:    ucvtf d1, x9
+; CHECK-NOFP16-NEXT:    and x8, x2, #0xffffffffffff
+; CHECK-NOFP16-NEXT:    ucvtf d2, x8
+; CHECK-NOFP16-NEXT:    and x8, x3, #0xffffffffffff
+; CHECK-NOFP16-NEXT:    mov w9, #32767 // =0x7fff
+; CHECK-NOFP16-NEXT:    fcvtxn s0, d0
+; CHECK-NOFP16-NEXT:    fcvtxn s1, d1
+; CHECK-NOFP16-NEXT:    fcvtxn s2, d2
+; CHECK-NOFP16-NEXT:    fmov w10, s0
+; CHECK-NOFP16-NEXT:    ucvtf d0, x8
+; CHECK-NOFP16-NEXT:    fmov w8, s1
+; CHECK-NOFP16-NEXT:    ubfx w11, w10, #16, #1
+; CHECK-NOFP16-NEXT:    add w10, w10, w9
+; CHECK-NOFP16-NEXT:    ubfx w12, w8, #16, #1
+; CHECK-NOFP16-NEXT:    add w8, w8, w9
+; CHECK-NOFP16-NEXT:    fcvtxn s1, d0
+; CHECK-NOFP16-NEXT:    add w10, w11, w10
+; CHECK-NOFP16-NEXT:    fmov w11, s2
+; CHECK-NOFP16-NEXT:    add w8, w12, w8
+; CHECK-NOFP16-NEXT:    lsr w10, w10, #16
+; CHECK-NOFP16-NEXT:    lsr w8, w8, #16
+; CHECK-NOFP16-NEXT:    ubfx w12, w11, #16, #1
+; CHECK-NOFP16-NEXT:    fmov s2, w10
+; CHECK-NOFP16-NEXT:    fmov s0, w8
+; CHECK-NOFP16-NEXT:    add w8, w11, w9
+; CHECK-NOFP16-NEXT:    fmov w10, s1
+; CHECK-NOFP16-NEXT:    add w8, w12, w8
+; CHECK-NOFP16-NEXT:    lsr w8, w8, #16
+; CHECK-NOFP16-NEXT:    mov v0.h[1], v2.h[0]
+; CHECK-NOFP16-NEXT:    ubfx w11, w10, #16, #1
+; CHECK-NOFP16-NEXT:    fmov s1, w8
+; CHECK-NOFP16-NEXT:    add w8, w10, w9
+; CHECK-NOFP16-NEXT:    add w8, w11, w8
+; CHECK-NOFP16-NEXT:    lsr w8, w8, #16
+; CHECK-NOFP16-NEXT:    mov v0.h[2], v1.h[0]
+; CHECK-NOFP16-NEXT:    fmov s1, w8
+; CHECK-NOFP16-NEXT:    mov v0.h[3], v1.h[0]
+; CHECK-NOFP16-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; CHECK-NOFP16-NEXT:    ret
+;
+; CHECK-FP16-LABEL: utofp_v4i48_v4f16:
+; CHECK-FP16:       // %bb.0:
+; CHECK-FP16-NEXT:    and x8, x1, #0xffffffffffff
+; CHECK-FP16-NEXT:    and x9, x0, #0xffffffffffff
+; CHECK-FP16-NEXT:    ucvtf d0, x8
+; CHECK-FP16-NEXT:    ucvtf d1, x9
+; CHECK-FP16-NEXT:    and x8, x2, #0xffffffffffff
+; CHECK-FP16-NEXT:    ucvtf d2, x8
+; CHECK-FP16-NEXT:    and x8, x3, #0xffffffffffff
+; CHECK-FP16-NEXT:    fcvtxn s0, d0
+; CHECK-FP16-NEXT:    fcvtxn s1, d1
+; CHECK-FP16-NEXT:    bfcvt h3, s0
+; CHECK-FP16-NEXT:    bfcvt h0, s1
+; CHECK-FP16-NEXT:    fcvtxn s1, d2
+; CHECK-FP16-NEXT:    ucvtf d2, x8
+; CHECK-FP16-NEXT:    mov v0.h[1], v3.h[0]
+; CHECK-FP16-NEXT:    bfcvt h1, s1
+; CHECK-FP16-NEXT:    fcvtxn s2, d2
+; CHECK-FP16-NEXT:    mov v0.h[2], v1.h[0]
+; CHECK-FP16-NEXT:    bfcvt h1, s2
+; CHECK-FP16-NEXT:    mov v0.h[3], v1.h[0]
+; CHECK-FP16-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; CHECK-FP16-NEXT:    ret
+  %r = uitofp <4 x i48> %a to <4 x bfloat>
+  ret <4 x bfloat> %r
+}
+
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; CHECK: {{.*}}
 ; CHECK-FP16-GI: {{.*}}

>From 6152237ca7413b52b049679f3e81bc280d054d34 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Fri, 17 Apr 2026 09:12:43 +0100
Subject: [PATCH 3/3] Move into the existing return

---
 llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index 3dc3e17f6467b..e33f45cf426b3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4714,7 +4714,8 @@ SDValue DAGTypeLegalizer::SplitVecOp_TruncateHelper(SDNode *N) {
   // If the input elements are only 1/2 the width of the result elements,
   // just use the normal splitting. Our trick only work if there's room
   // to split more than once.
-  if (isTypeLegal(LoOutVT) || InElementSize <= OutElementSize * 2)
+  if (isTypeLegal(LoOutVT) || InElementSize <= OutElementSize * 2 ||
+      (IsFloat && !isPowerOf2_32(InElementSize)))
     return SplitVecOp_UnaryOp(N);
   SDLoc DL(N);
 
@@ -4726,9 +4727,6 @@ SDValue DAGTypeLegalizer::SplitVecOp_TruncateHelper(SDNode *N) {
   if (getTypeAction(FinalVT) == TargetLowering::TypeScalarizeVector)
     return SplitVecOp_UnaryOp(N);
 
-  if (IsFloat && !isPowerOf2_32(InElementSize))
-    return SplitVecOp_UnaryOp(N);
-
   // Get the split input vector.
   SDValue InLoVec, InHiVec;
   GetSplitVector(InVec, InLoVec, InHiVec);



More information about the llvm-commits mailing list