[llvm] [AArch64][GlobalISel] Remove fallbacks for fpcvt intrinsics with 16-bit operands (PR #179693)

Joshua Rodriguez via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 17 09:13:19 PST 2026


https://github.com/JoshdRod updated https://github.com/llvm/llvm-project/pull/179693

>From 7fe8de514703d6e31f1d4351bc8aeee357c73434 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Wed, 4 Feb 2026 15:34:46 +0000
Subject: [PATCH 01/10] [AArch64][GlobalISel] Include GlobalISel fallbacks in
 scalar f16 -> i16 intrinsics test

---
 .../CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll  | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
index ab502508fadbd..30a6488f78ecf 100644
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
@@ -1,10 +1,23 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc < %s -mtriple=aarch64 -global-isel=0 -mattr=+v8.2a,+fullfp16  | FileCheck %s
+; RUN: llc < %s -mtriple=aarch64 -mattr=+v8.2a,+fullfp16  | FileCheck %s --check-prefixes=CHECK,CHECK-SD
+; RUN: llc < %s -mtriple=aarch64 -global-isel --global-isel-abort=2 -mattr=+v8.2a,+fullfp16 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-GI
 
 ; Test f16 -> i16 NEON intrinics, currently only supported in SDAG.
 ; Should be merged with fp16_intrinsic_scalar_1op.ll once there is
 ; support in GlSel.
 
+; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtas_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtau_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtms_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtmu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtns_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtnu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtps_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtpu_intrinsic_i16
+
+
 declare i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtas.i16.f16(half)

>From 06f35e79836c4b6a85cbbecb5bfaf024532dda1f Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Fri, 6 Feb 2026 10:11:44 +0000
Subject: [PATCH 02/10] [AArch64][GloballISel] Put result of fp16 -> s16
 convert intrinsic on fpr

Previously, RegBankSelect would place the result of an fp16 -> s16 conversion intrinsic on a gpr. This would cause Instruction Selection to fail, as there are no 16-bit gprs.
Floating point convert intrinsics affected:
fcvtnu / fcvtns
fcvtau / fcvtas
fcvtmu / fcvtms
fcvtpu / fcvtps
---
 .../AArch64/GISel/AArch64RegisterBankInfo.cpp       | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
index f8b5739d1d13a..2e6e8722ee0e8 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
@@ -1240,12 +1240,13 @@ AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
       }
       TypeSize DstSize = getSizeInBits(MI.getOperand(0).getReg(), MRI, TRI);
       TypeSize SrcSize = getSizeInBits(MI.getOperand(2).getReg(), MRI, TRI);
-      if (((DstSize == SrcSize) || STI.hasFeature(AArch64::FeatureFPRCVT)) &&
-          all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
-                 [&](const MachineInstr &UseMI) {
-                   return onlyUsesFP(UseMI, MRI, TRI) ||
-                          prefersFPUse(UseMI, MRI, TRI);
-                 }))
+      if ((DstSize == 16) ||
+          (((DstSize == SrcSize) || STI.hasFeature(AArch64::FeatureFPRCVT)) &&
+           all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+                  [&](const MachineInstr &UseMI) {
+                    return onlyUsesFP(UseMI, MRI, TRI) ||
+                           prefersFPUse(UseMI, MRI, TRI);
+                  })))
         OpRegBankIdx[0] = PMI_FirstFPR;
       else
         OpRegBankIdx[0] = PMI_FirstGPR;

>From a815330f276e292d28728c58b1463f1bb4509d47 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Fri, 6 Feb 2026 11:27:55 +0000
Subject: [PATCH 03/10] [AArch64][GlobalIsel] Update test checks

---
 llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
index 30a6488f78ecf..0069edc2fcb2c 100644
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
@@ -8,15 +8,6 @@
 
 ; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
 ; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtas_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtau_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtms_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtmu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtns_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtnu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtps_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtpu_intrinsic_i16
-
 
 declare i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half)

>From eefef323dbb5fe7c84bb6d019e07a4a25b601bc0 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 10 Feb 2026 15:40:23 +0000
Subject: [PATCH 04/10] [AArch64][GlobalISel] Legalise FCVTZ<U/S> intrinsics to
 G_FPTO<U/S>I_SAT

At the Instruction Selection stage, the AArch64 patterns match this G_GPTOUI_SAT node to the FCVTZU intrinsic. Hence, we must legalise the ACLE intrinsic to this node.
---
 llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp | 4 ++++
 llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll | 3 ---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 62f8237dc3b6b..06f853354c342 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -2015,6 +2015,10 @@ bool AArch64LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
     return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_U);
   case Intrinsic::aarch64_neon_uqxtn:
     return LowerUnaryOp(TargetOpcode::G_TRUNC_USAT_U);
+  case Intrinsic::aarch64_neon_fcvtzu:
+    return LowerUnaryOp(TargetOpcode::G_FPTOUI_SAT);
+  case Intrinsic::aarch64_neon_fcvtzs:
+    return LowerUnaryOp(TargetOpcode::G_FPTOSI_SAT);
 
   case Intrinsic::vector_reverse:
     // TODO: Add support for vector_reverse
diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
index 0069edc2fcb2c..a7640e79997cc 100644
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
@@ -6,9 +6,6 @@
 ; Should be merged with fp16_intrinsic_scalar_1op.ll once there is
 ; support in GlSel.
 
-; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16
-
 declare i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtas.i16.f16(half)

>From 7855a95d9981254f7eaff8fba561cfab46442839 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Wed, 4 Feb 2026 15:34:46 +0000
Subject: [PATCH 05/10] [AArch64][GloballISel] Put result of fp16 -> s16
 convert intrinsic on fpr

Previously, RegBankSelect would place the result of an fp16 -> s16 conversion intrinsic on a gpr. This would cause Instruction Selection to fail, as there are no 16-bit gprs.
Floating point convert intrinsics affected:
fcvtnu / fcvtns
fcvtau / fcvtas
fcvtmu / fcvtms
fcvtpu / fcvtps
---
 .../CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll     | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
index a7640e79997cc..30a6488f78ecf 100644
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
@@ -6,6 +6,18 @@
 ; Should be merged with fp16_intrinsic_scalar_1op.ll once there is
 ; support in GlSel.
 
+; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtas_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtau_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtms_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtmu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtns_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtnu_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtps_intrinsic_i16
+; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtpu_intrinsic_i16
+
+
 declare i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half)
 declare i16 @llvm.aarch64.neon.fcvtas.i16.f16(half)

>From c9ca2195ea91b5a96985a7f5ce9ab8201468c1b8 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 17 Feb 2026 10:01:23 +0000
Subject: [PATCH 06/10] [AArch64][GlobalISel] Merge SDAG check into GlobalISel
 check

As GlobalISel no longer fails to lower these intrinsics, and SDAG and GI generated code is identical, the two test checks can safely be merged into one.
---
 llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
index 30a6488f78ecf..9260695d851fe 100644
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
@@ -1,10 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc < %s -mtriple=aarch64 -mattr=+v8.2a,+fullfp16  | FileCheck %s --check-prefixes=CHECK,CHECK-SD
-; RUN: llc < %s -mtriple=aarch64 -global-isel --global-isel-abort=2 -mattr=+v8.2a,+fullfp16 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-GI
-
-; Test f16 -> i16 NEON intrinics, currently only supported in SDAG.
-; Should be merged with fp16_intrinsic_scalar_1op.ll once there is
-; support in GlSel.
+; RUN: llc < %s -mtriple=aarch64 -global-isel --global-isel-abort=2 -mattr=+v8.2a,+fullfp16 2>&1 | FileCheck %s --check-prefixes=CHECK
 
 ; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
 ; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16

>From a83ecd5603fcdedf93ebff008430f390d52852a0 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 17 Feb 2026 11:56:29 +0000
Subject: [PATCH 07/10] [AArch64][GlobalISel] Move fp16 -> i16 test checks into
 fp16 test file

fp16 -> i16 checks were previously placed in a separate file due to GlobalISel being unable to select the intrinsics.
Now, place these tests into the master file.
---
 .../AArch64/fp16_i16_intrinsic_scalar.ll      | 136 ------------------
 .../AArch64/fp16_intrinsic_scalar_1op.ll      | 110 ++++++++++++++
 2 files changed, 110 insertions(+), 136 deletions(-)
 delete mode 100644 llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll

diff --git a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll b/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
deleted file mode 100644
index 9260695d851fe..0000000000000
--- a/llvm/test/CodeGen/AArch64/fp16_i16_intrinsic_scalar.ll
+++ /dev/null
@@ -1,136 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc < %s -mtriple=aarch64 -global-isel --global-isel-abort=2 -mattr=+v8.2a,+fullfp16 2>&1 | FileCheck %s --check-prefixes=CHECK
-
-; CHECK-GI:    warning: Instruction selection used fallback path for fcvtzs_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtzu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtas_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtau_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtms_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtmu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtns_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtnu_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtps_intrinsic_i16
-; CHECK-GI NEXT:    warning: Instruction selection used fallback path for fcvtpu_intrinsic_i16
-
-
-declare i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtas.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtau.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtms.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtmu.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtns.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtnu.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtps.i16.f16(half)
-declare i16 @llvm.aarch64.neon.fcvtpu.i16.f16(half)
-
-
-define i16 @fcvtzs_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtzs_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtzs h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtzu_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtzu_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtzu h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtas_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtas_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtas h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtas.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtau_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtau_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtau h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtau.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtms_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtms_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtms h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtms.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtmu_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtmu_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtmu h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtmu.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtns_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtns_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtns h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtns.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtnu_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtnu_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtnu h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtnu.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtps_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtps_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtps h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtps.i16.f16(half %a)
-  ret i16 %fcvt
-}
-
-define i16 @fcvtpu_intrinsic_i16(half %a) {
-; CHECK-LABEL: fcvtpu_intrinsic_i16:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    fcvtpu h0, h0
-; CHECK-NEXT:    fmov w0, s0
-; CHECK-NEXT:    ret
-entry:
-  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtpu.i16.f16(half %a)
-  ret i16 %fcvt
-}
diff --git a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_1op.ll b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_1op.ll
index b056460153b29..5ccf7b1adc3a7 100644
--- a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_1op.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_1op.ll
@@ -196,6 +196,17 @@ entry:
   ret i64 %0
 }
 
+define i16 @fcvtzu_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtzu_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtzu h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtzu.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define i32 @fcvtzu_intrinsic_i32(half %a) {
 ; CHECK-LABEL: fcvtzu_intrinsic_i32:
 ; CHECK:       // %bb.0: // %entry
@@ -216,6 +227,17 @@ entry:
   ret i64 %fcvt
 }
 
+define i16 @fcvtzs_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtzs_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtzs h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtzs.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define i32 @fcvtzs_intrinsic_i32(half %a) {
 ; CHECK-LABEL: fcvtzs_intrinsic_i32:
 ; CHECK:       // %bb.0: // %entry
@@ -236,6 +258,17 @@ entry:
   ret i64 %fcvt
 }
 
+define i16 @fcvtas_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtas_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtas h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtas.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t19(half %a) {
 ; CHECK-LABEL: t19:
 ; CHECK:       // %bb.0: // %entry
@@ -257,6 +290,17 @@ entry:
   ret i64 %vcvtah_s64_f16
 }
 
+define i16 @fcvtau_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtau_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtau h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtau.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t22(half %a) {
 ; CHECK-LABEL: t22:
 ; CHECK:       // %bb.0: // %entry
@@ -278,6 +322,17 @@ entry:
   ret i64 %vcvtah_u64_f16
 }
 
+define i16 @fcvtms_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtms_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtms h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtms.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t25(half %a) {
 ; CHECK-LABEL: t25:
 ; CHECK:       // %bb.0: // %entry
@@ -299,6 +354,17 @@ entry:
   ret i64 %vcvtmh_s64_f16
 }
 
+define i16 @fcvtmu_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtmu_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtmu h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtmu.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t28(half %a) {
 ; CHECK-LABEL: t28:
 ; CHECK:       // %bb.0: // %entry
@@ -320,6 +386,17 @@ entry:
   ret i64 %vcvtmh_u64_f16
 }
 
+define i16 @fcvtns_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtns_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtns h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtns.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t31(half %a) {
 ; CHECK-LABEL: t31:
 ; CHECK:       // %bb.0: // %entry
@@ -341,6 +418,17 @@ entry:
   ret i64 %vcvtnh_s64_f16
 }
 
+define i16 @fcvtnu_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtnu_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtnu h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtnu.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t34(half %a) {
 ; CHECK-LABEL: t34:
 ; CHECK:       // %bb.0: // %entry
@@ -362,6 +450,17 @@ entry:
   ret i64 %vcvtnh_u64_f16
 }
 
+define i16 @fcvtps_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtps_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtps h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtps.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t37(half %a) {
 ; CHECK-LABEL: t37:
 ; CHECK:       // %bb.0: // %entry
@@ -383,6 +482,17 @@ entry:
   ret i64 %vcvtph_s64_f16
 }
 
+define i16 @fcvtpu_intrinsic_i16(half %a) {
+; CHECK-LABEL: fcvtpu_intrinsic_i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    fcvtpu h0, h0
+; CHECK-NEXT:    fmov w0, s0
+; CHECK-NEXT:    ret
+entry:
+  %fcvt = tail call i16 @llvm.aarch64.neon.fcvtpu.i16.f16(half %a)
+  ret i16 %fcvt
+}
+
 define dso_local i16 @t40(half %a) {
 ; CHECK-LABEL: t40:
 ; CHECK:       // %bb.0: // %entry

>From 93cf43207b6542a25d83ae5631adccfd9c8d04a6 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 17 Feb 2026 12:03:47 +0000
Subject: [PATCH 08/10] [AArch64][GlobalISel] Remove unnecessary brackets

---
 .../AArch64/GISel/AArch64RegisterBankInfo.cpp   | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
index 2e6e8722ee0e8..299b8c11143b8 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
@@ -1240,13 +1240,16 @@ AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
       }
       TypeSize DstSize = getSizeInBits(MI.getOperand(0).getReg(), MRI, TRI);
       TypeSize SrcSize = getSizeInBits(MI.getOperand(2).getReg(), MRI, TRI);
-      if ((DstSize == 16) ||
-          (((DstSize == SrcSize) || STI.hasFeature(AArch64::FeatureFPRCVT)) &&
-           all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
-                  [&](const MachineInstr &UseMI) {
-                    return onlyUsesFP(UseMI, MRI, TRI) ||
-                           prefersFPUse(UseMI, MRI, TRI);
-                  })))
+      if (DstSize ==
+              16 || // Fp conversions to i16 must be kept on fp register banks
+                    // to ensure proper saturation, as there are no 16-bit gprs
+          (DstSize == SrcSize ||
+           STI.hasFeature(AArch64::FeatureFPRCVT) &&
+               all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+                      [&](const MachineInstr &UseMI) {
+                        return onlyUsesFP(UseMI, MRI, TRI) ||
+                               prefersFPUse(UseMI, MRI, TRI);
+                      })))
         OpRegBankIdx[0] = PMI_FirstFPR;
       else
         OpRegBankIdx[0] = PMI_FirstGPR;

>From f7263799881ff143bffe2e2d859c82d104201062 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 17 Feb 2026 13:56:28 +0000
Subject: [PATCH 09/10] [AArch64][GlobalISel] Move comment to above if
 statement for readability

---
 llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
index 299b8c11143b8..7f13ae1ab202b 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
@@ -1240,9 +1240,9 @@ AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
       }
       TypeSize DstSize = getSizeInBits(MI.getOperand(0).getReg(), MRI, TRI);
       TypeSize SrcSize = getSizeInBits(MI.getOperand(2).getReg(), MRI, TRI);
-      if (DstSize ==
-              16 || // Fp conversions to i16 must be kept on fp register banks
-                    // to ensure proper saturation, as there are no 16-bit gprs
+      // Fp conversions to i16 must be kept on fp register banks to ensure
+      // proper saturation, as there are no 16-bit gprs
+      if (DstSize == 16 ||
           (DstSize == SrcSize ||
            STI.hasFeature(AArch64::FeatureFPRCVT) &&
                all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),

>From c52f2b62d120a9198128e08bd1f744ea26e34cf4 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 17 Feb 2026 17:12:49 +0000
Subject: [PATCH 10/10] [AArch64][GlobalISel] Re-add necessary brackets

Some brackets needed to allow "Build and Test Linux" CI test to pass.
This is because some configurations of clang see the order of operations in A || B && C as ambigious. Add the brackets in to avoid this.
---
 .../AArch64/GISel/AArch64RegisterBankInfo.cpp       | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
index 7f13ae1ab202b..1e0a8a8767c8b 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp
@@ -1243,13 +1243,12 @@ AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
       // Fp conversions to i16 must be kept on fp register banks to ensure
       // proper saturation, as there are no 16-bit gprs
       if (DstSize == 16 ||
-          (DstSize == SrcSize ||
-           STI.hasFeature(AArch64::FeatureFPRCVT) &&
-               all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
-                      [&](const MachineInstr &UseMI) {
-                        return onlyUsesFP(UseMI, MRI, TRI) ||
-                               prefersFPUse(UseMI, MRI, TRI);
-                      })))
+          ((DstSize == SrcSize || STI.hasFeature(AArch64::FeatureFPRCVT)) &&
+           all_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()),
+                  [&](const MachineInstr &UseMI) {
+                    return onlyUsesFP(UseMI, MRI, TRI) ||
+                           prefersFPUse(UseMI, MRI, TRI);
+                  })))
         OpRegBankIdx[0] = PMI_FirstFPR;
       else
         OpRegBankIdx[0] = PMI_FirstGPR;



More information about the llvm-commits mailing list