[llvm] [AArch64] Avoid getMinimalPhysRegClass in callee-save frame lowering (PR #193438)

Cullen Rhodes via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 27 22:56:47 PDT 2026


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

>From 9a4b2befdd2525ac92ae21139b5d96787ca688a6 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Wed, 22 Apr 2026 07:19:31 +0000
Subject: [PATCH 1/2] [AArch64] Avoid getMinimalPhysRegClass in callee-save
 frame lowering

getMinimalPhysRegClass is slow and the callee-save paths in frame
lowering that call it are hot. Which registers are callee-saved is
well-defined, selecting the register classes directly is a -0.22%
geomean improvement on stage1-aarch64-O0-g CTMark.
---
 .../Target/AArch64/AArch64FrameLowering.cpp   | 22 ++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 2c68e379205ce..012857356b033 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -345,6 +345,23 @@ static unsigned getStackHazardSize(const MachineFunction &MF) {
   return MF.getSubtarget<AArch64Subtarget>().getStreamingHazardSize();
 }
 
+static const TargetRegisterClass *
+getAArch64CalleeSavedRegClass(MCRegister Reg) {
+  if (AArch64::GPR64RegClass.contains(Reg))
+    return &AArch64::GPR64RegClass;
+  if (AArch64::FPR64RegClass.contains(Reg))
+    return &AArch64::FPR64RegClass;
+  if (AArch64::FPR128RegClass.contains(Reg))
+    return &AArch64::FPR128RegClass;
+  if (AArch64::ZPRRegClass.contains(Reg))
+    return &AArch64::ZPRRegClass;
+  if (AArch64::PPRRegClass.contains(Reg))
+    return &AArch64::PPRRegClass;
+  if (Reg == AArch64::VG)
+    return &AArch64::FIXED_REGSRegClass;
+  llvm_unreachable("Unsupported register class.");
+}
+
 StackOffset
 AArch64FrameLowering::getZPRStackSize(const MachineFunction &MF) const {
   const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
@@ -2623,8 +2640,7 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF,
   unsigned PPRCSStackSize = 0;
   const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
   for (unsigned Reg : SavedRegs.set_bits()) {
-    auto *RC = TRI->getMinimalPhysRegClass(MCRegister(Reg));
-    assert(RC && "expected register class!");
+    auto *RC = getAArch64CalleeSavedRegClass(MCRegister(Reg));
     auto SpillSize = TRI->getSpillSize(*RC);
     bool IsZPR = AArch64::ZPRRegClass.contains(Reg);
     bool IsPPR = !IsZPR && AArch64::PPRRegClass.contains(Reg);
@@ -2804,7 +2820,7 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots(
   int HazardSlotIndex = std::numeric_limits<int>::max();
   for (auto &CS : CSI) {
     MCRegister Reg = CS.getReg();
-    const TargetRegisterClass *RC = RegInfo->getMinimalPhysRegClass(Reg);
+    const TargetRegisterClass *RC = getAArch64CalleeSavedRegClass(Reg);
 
     // Create a hazard slot as we switch between GPR and FPR CSRs.
     if (AFI->isStackHazardIncludedInCalleeSaveArea() &&

>From 2038fc376d322e7c10dd908d823c95db211098dc Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Mon, 27 Apr 2026 19:18:38 +0000
Subject: [PATCH 2/2] address comments

---
 .../Target/AArch64/AArch64FrameLowering.cpp   | 22 +++----------------
 .../lib/Target/AArch64/AArch64RegisterInfo.td | 15 ++++++++++++-
 2 files changed, 17 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 012857356b033..5a1ff324f948f 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -345,23 +345,6 @@ static unsigned getStackHazardSize(const MachineFunction &MF) {
   return MF.getSubtarget<AArch64Subtarget>().getStreamingHazardSize();
 }
 
-static const TargetRegisterClass *
-getAArch64CalleeSavedRegClass(MCRegister Reg) {
-  if (AArch64::GPR64RegClass.contains(Reg))
-    return &AArch64::GPR64RegClass;
-  if (AArch64::FPR64RegClass.contains(Reg))
-    return &AArch64::FPR64RegClass;
-  if (AArch64::FPR128RegClass.contains(Reg))
-    return &AArch64::FPR128RegClass;
-  if (AArch64::ZPRRegClass.contains(Reg))
-    return &AArch64::ZPRRegClass;
-  if (AArch64::PPRRegClass.contains(Reg))
-    return &AArch64::PPRRegClass;
-  if (Reg == AArch64::VG)
-    return &AArch64::FIXED_REGSRegClass;
-  llvm_unreachable("Unsupported register class.");
-}
-
 StackOffset
 AArch64FrameLowering::getZPRStackSize(const MachineFunction &MF) const {
   const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
@@ -2640,7 +2623,8 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF,
   unsigned PPRCSStackSize = 0;
   const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
   for (unsigned Reg : SavedRegs.set_bits()) {
-    auto *RC = getAArch64CalleeSavedRegClass(MCRegister(Reg));
+    auto *RC = TRI->getPhysRegBaseClass(MCRegister(Reg));
+    assert(RC && "expected register class!");
     auto SpillSize = TRI->getSpillSize(*RC);
     bool IsZPR = AArch64::ZPRRegClass.contains(Reg);
     bool IsPPR = !IsZPR && AArch64::PPRRegClass.contains(Reg);
@@ -2820,7 +2804,7 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots(
   int HazardSlotIndex = std::numeric_limits<int>::max();
   for (auto &CS : CSI) {
     MCRegister Reg = CS.getReg();
-    const TargetRegisterClass *RC = getAArch64CalleeSavedRegClass(Reg);
+    const TargetRegisterClass *RC = RegInfo->getPhysRegBaseClass(Reg);
 
     // Create a hazard slot as we switch between GPR and FPR CSRs.
     if (AFI->isStackHazardIncludedInCalleeSaveArea() &&
diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
index cd94a2590c6d2..d2f03bac88917 100644
--- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td
@@ -202,11 +202,13 @@ def GPR64common : RegisterClass<"AArch64", [i64], 64,
 def GPR32 : RegisterClass<"AArch64", [i32], 32, (add GPR32common, WZR)> {
   let AltOrders = [(rotl GPR32, 8)];
   let AltOrderSelect = [{ return 1; }];
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::GPR32RegClassID, 0, 32>";
 }
 def GPR64 : RegisterClass<"AArch64", [i64], 64, (add GPR64common, XZR)> {
   let AltOrders = [(rotl GPR64, 8)];
   let AltOrderSelect = [{ return 1; }];
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::GPR64RegClassID, 0, 32>";
 }
 
@@ -214,11 +216,13 @@ def GPR64 : RegisterClass<"AArch64", [i64], 64, (add GPR64common, XZR)> {
 def GPR32sp : RegisterClass<"AArch64", [i32], 32, (add GPR32common, WSP)> {
   let AltOrders = [(rotl GPR32sp, 8)];
   let AltOrderSelect = [{ return 1; }];
+  let BaseClassOrder = 1;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::GPR32spRegClassID, 0, 32>";
 }
 def GPR64sp : RegisterClass<"AArch64", [i64], 64, (add GPR64common, SP)> {
   let AltOrders = [(rotl GPR64sp, 8)];
   let AltOrderSelect = [{ return 1; }];
+  let BaseClassOrder = 1;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::GPR64spRegClassID,0, 32>";
 }
 
@@ -500,10 +504,12 @@ def Q31   : AArch64Reg<31, "q31", [D31, D31_HI], ["v31", ""]>, DwarfRegAlias<B31
 
 def FPR8  : RegisterClass<"AArch64", [i8, aarch64mfp8], 8, (sequence "B%u", 0, 31)> {
   let Size = 8;
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::FPR8RegClassID, 0, 32>";
 }
 def FPR16 : RegisterClass<"AArch64", [f16, bf16, i16], 16, (sequence "H%u", 0, 31)> {
   let Size = 16;
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::FPR16RegClassID, 0, 32>";
 }
 
@@ -511,11 +517,13 @@ def FPR16_lo : RegisterClass<"AArch64", [f16], 16, (trunc FPR16, 16)> {
   let Size = 16;
 }
 def FPR32 : RegisterClass<"AArch64", [f32, i32], 32,(sequence "S%u", 0, 31)> {
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::FPR32RegClassID, 0, 32>";
 }
 def FPR64 : RegisterClass<"AArch64", [f64, i64, v2f32, v1f64, v8i8, v4i16, v2i32,
                                       v1i64, v4f16, v4bf16],
                                      64, (sequence "D%u", 0, 31)> {
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::FPR64RegClassID, 0, 32>";
 }
 def FPR64_lo : RegisterClass<"AArch64",
@@ -530,6 +538,7 @@ def FPR128 : RegisterClass<"AArch64",
                            [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64, f128,
                             v8f16, v8bf16],
                            128, (sequence "Q%u", 0, 31)> {
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::FPR128RegClassID, 0, 32>";
 }
 
@@ -987,6 +996,7 @@ class PPRClass<int firstreg, int lastreg, int step = 1> : RegisterClass<"AArch64
 }
 
 def PPR    : PPRClass<0, 15> {
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::PPRRegClassID, 0, 16>";
 }
 def PPR_3b : PPRClass<0, 7> { // Restricted 3 bit SVE predicate register class.
@@ -1190,6 +1200,7 @@ def ZPRMul2_Lo : ZPRClass<0,  14, 2>;
 def ZPRMul2_Hi : ZPRClass<16, 30, 2>;
 
 def ZPR    : ZPRClass<0, 31> {
+  let BaseClassOrder = 0;
   let DecoderMethod = "DecodeSimpleRegisterClass<AArch64::ZPRRegClassID, 0, 32>";
 }
 def ZPR_4b : ZPRClass<0, 15> { // Restricted 4 bit SVE vector register class.
@@ -2010,5 +2021,7 @@ let isAllocatable = 0, GeneratePressureSet = 0 in {
 
 def GeneralPurposeRegisters : RegisterCategory<[GPR64, GPR32]>;
 
-def FIXED_REGS : RegisterClass<"AArch64", [i64], 64, (add FP, SP, VG, FFR)>;
+def FIXED_REGS : RegisterClass<"AArch64", [i64], 64, (add FP, SP, VG, FFR)> {
+  let BaseClassOrder = 2;
+}
 def FixedRegisters : RegisterCategory<[CCR, FIXED_REGS]>;



More information about the llvm-commits mailing list