[llvm] [AArch64] Mark GPR64arg register class as non-allocatable (PR #212457)

Cullen Rhodes via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 28 04:32:12 PDT 2026


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

>From 829e6f93abbaf67de8084316b0092f841ebf2f9c Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Wed, 22 Jul 2026 15:40:11 +0000
Subject: [PATCH 1/2] [AArch64] Mark GPR64arg register class as non-allocatable

GPR64arg was added in fcbec02ea6fb to describe X0-X7 for the reserved
argument-register check. It is an ABI register set rather than an
allocation constraint and should not be used for register allocation.

This exposes that GlobalISel recomputes the minimal register class when
a matching inline asm input is tied to a fixed physical-register output.
For X2 that class is GPR64arg, triggering an assertion when it is used
to create a virtual register after becoming non-allocatable.

getRegistersForValue already asks the target which class implements the
output constraint. Retain that class and reuse it for the matching
input.

Assisted-by: codex
---
 llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp  | 14 +++++++++-----
 llvm/lib/Target/AArch64/AArch64RegisterInfo.td     |  4 +++-
 .../AArch64/GlobalISel/irtranslator-inline-asm.ll  |  2 +-
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index b5bbcc193b6b7..8830494fc9681 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -46,6 +46,9 @@ class GISelAsmOperandInfo : public TargetLowering::AsmOperandInfo {
   /// contains the set of assigned registers corresponding to the operand.
   SmallVector<Register, 1> Regs;
 
+  /// The register class selected for this operand's constraint.
+  const TargetRegisterClass *RegClass = nullptr;
+
   explicit GISelAsmOperandInfo(const TargetLowering::AsmOperandInfo &Info)
       : TargetLowering::AsmOperandInfo(Info) {}
 };
@@ -112,6 +115,7 @@ static void getRegistersForValue(MachineFunction &MF,
   // RC is unset only on failure. Return immediately.
   if (!RC)
     return;
+  OpInfo.RegClass = RC;
 
   // No need to allocate a matching input constraint since the constraint it's
   // matching to has already been allocated.
@@ -425,14 +429,14 @@ bool InlineAsmLowering::lowerInlineAsm(
         // We need the tied input to live in the same register class as the def.
         //
         // - if Def is a vreg, we can just use its regclass.
-        // - if Def is a physreg, create a vreg in the minimal regclass for that
-        //   physreg.
+        // - if Def is a physreg, create a vreg in the regclass selected for its
+        //   constraint.
         //
         // Otherwise RegBankSelect may leave it in the wrong bank (e.g. GPR even
         // though it's tied to an FP physreg).
-        const TargetRegisterClass *RC = Def.isVirtual()
-                                            ? MRI->getRegClass(Def)
-                                            : TRI->getMinimalPhysRegClass(Def);
+        const TargetRegisterClass *RC =
+            Def.isVirtual() ? MRI->getRegClass(Def) : OpInfo.RegClass;
+        assert(RC && "Expected a register class for matching constraint");
 
         // Materialize `In` in a new vreg that has a register class that matches
         // the register class of `Def`.
diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
index 5dd8171853fd3..2964357343d02 100644
--- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
@@ -248,7 +248,9 @@ def GPR64z : RegisterOperand<GPR64> {
   let GIZeroRegister = XZR;
 }
 
-// GPR argument registers.
+// GPR argument registers. This is an ABI register set, not an allocation
+// constraint.
+let isAllocatable = 0 in
 def GPR64arg : RegisterClass<"AArch64", [i64], 64, (sequence "X%u", 0, 7)>;
 
 // GPR register classes which include WZR/XZR AND SP/WSP. This is not a
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll
index d627e11f4aa0c..5b783e44a66ec 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll
@@ -270,7 +270,7 @@ define i64 @test_input_with_matching_constraint_to_physical_register() {
   ; CHECK-LABEL: name: test_input_with_matching_constraint_to_physical_register
   ; CHECK: bb.1 (%ir-block.0):
   ; CHECK-NEXT:   [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
-  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gpr64arg = COPY [[C]](i64)
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gpr64all = COPY [[C]](i64)
   ; CHECK-NEXT:   INLINEASM &"", attdialect, regdef, implicit-def $x2, reguse tiedto:$0, [[COPY]](tied-def 3)
   ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:_(i64) = COPY $x2
   ; CHECK-NEXT:   $x0 = COPY [[COPY1]](i64)

>From 360668449b6e79bb3d225b09e159fc461d758ba1 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Tue, 28 Jul 2026 11:25:58 +0000
Subject: [PATCH 2/2] address comments

---
 llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 8830494fc9681..10d2d99138e8f 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -429,13 +429,14 @@ bool InlineAsmLowering::lowerInlineAsm(
         // We need the tied input to live in the same register class as the def.
         //
         // - if Def is a vreg, we can just use its regclass.
-        // - if Def is a physreg, create a vreg in the regclass selected for its
-        //   constraint.
+        // - if Def is a physreg, create a vreg in an allocatable subclass of
+        //   the regclass selected for its constraint.
         //
         // Otherwise RegBankSelect may leave it in the wrong bank (e.g. GPR even
         // though it's tied to an FP physreg).
         const TargetRegisterClass *RC =
-            Def.isVirtual() ? MRI->getRegClass(Def) : OpInfo.RegClass;
+            Def.isVirtual() ? MRI->getRegClass(Def)
+                            : TRI->getAllocatableClass(OpInfo.RegClass);
         assert(RC && "Expected a register class for matching constraint");
 
         // Materialize `In` in a new vreg that has a register class that matches



More information about the llvm-commits mailing list