[llvm] [AArch64][GlobalISel] Select unmerging s128 FPR value into two s64 GPR values (PR #215995)

Cullen Rhodes via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 23:33:58 PDT 2026


https://github.com/c-rhodes updated https://github.com/llvm/llvm-project/pull/215995

>From 0e8bb00eca7beb1eac39e7e3569952a31ba197ef Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Thu, 11 Jun 2026 11:39:50 +0000
Subject: [PATCH 1/2] [AArch64][GlobalISel] Select unmerging s128 FPR value
 into two s64 GPR values

RegBankSelect treats s128s like vectors when unmerging and assigns the
s64 results to FPR. Prototyping an alternative type-based RBS (#199040)
that assigns 64-bit scalar values to GPR exposed a gap in the
instruction selector.

Select the two s64 results directly with UMOVvi64.

Assisted-by: codex
---
 .../GISel/AArch64InstructionSelector.cpp      | 44 ++++++++++++++-----
 .../AArch64/GlobalISel/select-unmerge.mir     | 24 ++++++++++
 2 files changed, 57 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index ad8c1eb728e7b..1c6abf3777a62 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -4029,22 +4029,44 @@ bool AArch64InstructionSelector::selectUnmergeValues(MachineInstr &I,
   assert(I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES &&
          "unexpected opcode");
 
-  // TODO: Handle unmerging into GPRs and from scalars to scalars.
-  if (RBI.getRegBank(I.getOperand(0).getReg(), MRI, TRI)->getID() !=
-          AArch64::FPRRegBankID ||
-      RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI)->getID() !=
-          AArch64::FPRRegBankID) {
-    LLVM_DEBUG(dbgs() << "Unmerging vector-to-gpr and scalar-to-scalar "
-                         "currently unsupported.\n");
-    return false;
-  }
-
   // The last operand is the vector source register, and every other operand is
   // a register to unpack into.
   unsigned NumElts = I.getNumOperands() - 1;
   Register SrcReg = I.getOperand(NumElts).getReg();
-  const LLT NarrowTy = MRI.getType(I.getOperand(0).getReg());
+  Register LoReg = I.getOperand(0).getReg();
+  Register HiReg = I.getOperand(1).getReg();
+  const LLT NarrowTy = MRI.getType(LoReg);
   const LLT WideTy = MRI.getType(SrcReg);
+  const RegisterBank &LoRB = *RBI.getRegBank(LoReg, MRI, TRI);
+  const RegisterBank &HiRB = *RBI.getRegBank(HiReg, MRI, TRI);
+  const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI);
+
+  // Handle unmerging an s128 FPR value into two s64 GPR values.
+  if (NarrowTy == LLT::scalar(64) && WideTy == LLT::scalar(128) &&
+      LoRB.getID() == AArch64::GPRRegBankID &&
+      HiRB.getID() == AArch64::GPRRegBankID &&
+      SrcRB.getID() == AArch64::FPRRegBankID) {
+    MachineInstr &Lo = *BuildMI(*I.getParent(), I, I.getDebugLoc(),
+                                TII.get(AArch64::UMOVvi64), LoReg)
+                            .addUse(SrcReg)
+                            .addImm(0);
+    MachineInstr &Hi = *BuildMI(*I.getParent(), I, I.getDebugLoc(),
+                                TII.get(AArch64::UMOVvi64), HiReg)
+                            .addUse(SrcReg)
+                            .addImm(1);
+    constrainSelectedInstRegOperands(Lo, TII, TRI, RBI);
+    constrainSelectedInstRegOperands(Hi, TII, TRI, RBI);
+    I.eraseFromParent();
+    return true;
+  }
+
+  // TODO: Handle other unmerges into GPRs and from scalars to scalars.
+  if (LoRB.getID() != AArch64::FPRRegBankID ||
+      HiRB.getID() != AArch64::FPRRegBankID) {
+    LLVM_DEBUG(dbgs() << "Unmerging vector-to-gpr and scalar-to-scalar "
+                         "currently unsupported.\n");
+    return false;
+  }
 
   assert(WideTy.getSizeInBits() > NarrowTy.getSizeInBits() &&
          "source register size too small!");
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
index da58fa4815d94..09a6504a17a83 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
@@ -461,6 +461,30 @@ body:             |
     RET_ReallyLR implicit $d0, implicit $d1
 ...
 ---
+name:            test_s128_fpr_to_gpr
+alignment:       4
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1:
+    liveins: $q0
+    ; CHECK-LABEL: name: test_s128_fpr_to_gpr
+    ; CHECK: liveins: $q0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:fpr128 = COPY $q0
+    ; CHECK-NEXT: [[UMOVvi64_:%[0-9]+]]:gpr64 = UMOVvi64 [[COPY]], 0
+    ; CHECK-NEXT: [[UMOVvi64_1:%[0-9]+]]:gpr64 = UMOVvi64 [[COPY]], 1
+    ; CHECK-NEXT: $x0 = COPY [[UMOVvi64_]]
+    ; CHECK-NEXT: $x1 = COPY [[UMOVvi64_1]]
+    ; CHECK-NEXT: RET_ReallyLR implicit $x0, implicit $x1
+    %0:fpr(s128) = COPY $q0
+    %1:gpr(s64), %2:gpr(s64) = G_UNMERGE_VALUES %0(s128)
+    $x0 = COPY %1(s64)
+    $x1 = COPY %2(s64)
+    RET_ReallyLR implicit $x0, implicit $x1
+...
+---
 name:            test_s32_to_2s16_unmerge
 alignment:       4
 legalized:       true

>From 76db03418606feabf7f344c9e2d4553c96099fc9 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Fri, 21 Aug 2026 06:33:43 +0000
Subject: [PATCH 2/2] address comments

---
 .../AArch64/GISel/AArch64InstructionSelector.cpp     |  2 +-
 .../CodeGen/AArch64/GlobalISel/select-unmerge.mir    | 12 ++++++------
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 1c6abf3777a62..6761d5dfb586b 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -4041,7 +4041,7 @@ bool AArch64InstructionSelector::selectUnmergeValues(MachineInstr &I,
   const RegisterBank &HiRB = *RBI.getRegBank(HiReg, MRI, TRI);
   const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI);
 
-  // Handle unmerging an s128 FPR value into two s64 GPR values.
+  // Handle unmerging a 128-bit FPR value into two 64-bit GPR values.
   if (NarrowTy == LLT::scalar(64) && WideTy == LLT::scalar(128) &&
       LoRB.getID() == AArch64::GPRRegBankID &&
       HiRB.getID() == AArch64::GPRRegBankID &&
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
index 09a6504a17a83..4a9ce40982f61 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-unmerge.mir
@@ -461,7 +461,7 @@ body:             |
     RET_ReallyLR implicit $d0, implicit $d1
 ...
 ---
-name:            test_s128_fpr_to_gpr
+name:            test_i128_fpr_to_gpr
 alignment:       4
 legalized:       true
 regBankSelected: true
@@ -469,7 +469,7 @@ tracksRegLiveness: true
 body:             |
   bb.1:
     liveins: $q0
-    ; CHECK-LABEL: name: test_s128_fpr_to_gpr
+    ; CHECK-LABEL: name: test_i128_fpr_to_gpr
     ; CHECK: liveins: $q0
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:fpr128 = COPY $q0
@@ -478,10 +478,10 @@ body:             |
     ; CHECK-NEXT: $x0 = COPY [[UMOVvi64_]]
     ; CHECK-NEXT: $x1 = COPY [[UMOVvi64_1]]
     ; CHECK-NEXT: RET_ReallyLR implicit $x0, implicit $x1
-    %0:fpr(s128) = COPY $q0
-    %1:gpr(s64), %2:gpr(s64) = G_UNMERGE_VALUES %0(s128)
-    $x0 = COPY %1(s64)
-    $x1 = COPY %2(s64)
+    %0:fpr(i128) = COPY $q0
+    %1:gpr(i64), %2:gpr(i64) = G_UNMERGE_VALUES %0(i128)
+    $x0 = COPY %1(i64)
+    $x1 = COPY %2(i64)
     RET_ReallyLR implicit $x0, implicit $x1
 ...
 ---



More information about the llvm-commits mailing list