[llvm] [SPARC] Preserve liveins after leaf register remapping (PR #213359)

Kirill A. Korinsky via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 1 16:39:57 PDT 2026


https://github.com/catap updated https://github.com/llvm/llvm-project/pull/213359

>From a3a01bce11bb3ee967f55dfdeb6694fa24cc74db Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 2 Aug 2026 01:37:47 +0200
Subject: [PATCH] [SPARC] Preserve liveins after leaf register remapping

Leaf procedure lowering maps I registers to O registers. Named reserved
register reads do not add liveins, so mapping i7 to unreserved o7 leaves
an undefined physical register use.

Seems that exists for a while and cee7b71929312666c5cf48eb79873893811b99a7
just makes it more probably.
---
 .../llvm/CodeGen/MachineRegisterInfo.h        |  4 +
 llvm/lib/CodeGen/MachineRegisterInfo.cpp      | 15 +++
 llvm/lib/Target/Sparc/SparcFrameLowering.cpp  | 99 ++++++++++---------
 llvm/lib/Target/Sparc/SparcFrameLowering.h    |  5 +-
 llvm/lib/Target/Sparc/SparcISelLowering.cpp   |  8 +-
 llvm/lib/Target/Sparc/SparcISelLowering.h     |  1 +
 llvm/lib/Target/Sparc/SparcRegisterInfo.cpp   | 17 ++++
 llvm/lib/Target/Sparc/SparcRegisterInfo.td    | 30 +++++-
 llvm/test/CodeGen/SPARC/2011-01-11-CC.ll      | 24 ++---
 .../CodeGen/SPARC/LeonInsertNOPLoadPassUT.ll  |  4 +-
 llvm/test/CodeGen/SPARC/fp128-select.ll       |  8 +-
 llvm/test/CodeGen/SPARC/leafproc-reg-pair.mir | 27 +++++
 llvm/test/CodeGen/SPARC/leafproc.ll           | 18 ++++
 .../test/CodeGen/SPARC/reserved-regs-named.ll | 58 +++++++++--
 llvm/test/CodeGen/SPARC/reserved-regs.ll      | 44 ++++++---
 .../SPARC/smulo-128-legalisation-lowering.ll  |  4 +-
 llvm/test/CodeGen/SPARC/stackaddress.ll       | 12 +--
 .../SPARC/umulo-128-legalisation-lowering.ll  |  4 +-
 18 files changed, 273 insertions(+), 109 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPARC/leafproc-reg-pair.mir

diff --git a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
index e6fc300d19672..610d4c95f01c0 100644
--- a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
@@ -1021,6 +1021,10 @@ class MachineRegisterInfo {
     LiveIns.push_back(std::make_pair(Reg, vreg));
   }
 
+  /// Replace a physical register in the function livein list. ToReg must not
+  /// already be a livein.
+  LLVM_ABI void replaceLiveInPhysReg(MCRegister FromReg, MCRegister ToReg);
+
   // Iteration support for the live-ins set.  It's kept in sorted order
   // by register number.
   using livein_iterator =
diff --git a/llvm/lib/CodeGen/MachineRegisterInfo.cpp b/llvm/lib/CodeGen/MachineRegisterInfo.cpp
index dbba413548018..35250c1e959f4 100644
--- a/llvm/lib/CodeGen/MachineRegisterInfo.cpp
+++ b/llvm/lib/CodeGen/MachineRegisterInfo.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineFunction.h"
@@ -462,6 +463,20 @@ bool MachineRegisterInfo::isLiveIn(Register Reg) const {
   return false;
 }
 
+void MachineRegisterInfo::replaceLiveInPhysReg(MCRegister FromReg,
+                                               MCRegister ToReg) {
+  assert(FromReg.isPhysical() && ToReg.isPhysical() && FromReg != ToReg);
+  auto From = llvm::find_if(LiveIns, [FromReg](const auto &LiveIn) {
+    return LiveIn.first == FromReg;
+  });
+  if (From == LiveIns.end())
+    return;
+  assert(llvm::none_of(
+      LiveIns, [ToReg](const auto &LiveIn) { return LiveIn.first == ToReg; }));
+  From->first = ToReg;
+  llvm::sort(LiveIns);
+}
+
 /// getLiveInPhysReg - If VReg is a live-in virtual register, return the
 /// corresponding live-in physical register.
 MCRegister MachineRegisterInfo::getLiveInPhysReg(Register VReg) const {
diff --git a/llvm/lib/Target/Sparc/SparcFrameLowering.cpp b/llvm/lib/Target/Sparc/SparcFrameLowering.cpp
index fa08d4474f39e..df374ba8cb6f7 100644
--- a/llvm/lib/Target/Sparc/SparcFrameLowering.cpp
+++ b/llvm/lib/Target/Sparc/SparcFrameLowering.cpp
@@ -14,6 +14,7 @@
 #include "SparcInstrInfo.h"
 #include "SparcMachineFunctionInfo.h"
 #include "SparcSubtarget.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/CodeGen/CFIInstBuilder.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
@@ -137,9 +138,13 @@ void SparcFrameLowering::emitPrologue(MachineFunction &MF,
 
   if (MF.needsFrameMoves()) {
     CFIInstBuilder CFIBuilder(MBB, MBBI, MachineInstr::NoFlags);
-    CFIBuilder.buildDefCFARegister(SP::I6);
-    CFIBuilder.buildWindowSave();
-    CFIBuilder.buildRegister(SP::O7, SP::I7);
+    if (FuncInfo->isLeafProc()) {
+      CFIBuilder.buildDefCFAOffset(NumBytes + Subtarget.getStackPointerBias());
+    } else {
+      CFIBuilder.buildDefCFARegister(SP::I6);
+      CFIBuilder.buildWindowSave();
+      CFIBuilder.buildRegister(SP::O7, SP::I7);
+    }
   }
 }
 
@@ -259,53 +264,57 @@ SparcFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI,
   return true;
 }
 
-bool SparcFrameLowering::isLeafProc(MachineFunction &MF) const
-{
-
-  MachineRegisterInfo &MRI = MF.getRegInfo();
-  MachineFrameInfo    &MFI = MF.getFrameInfo();
+bool SparcFrameLowering::isLeafProc(MachineFunction &MF) const {
+  if (MF.getFrameInfo().hasCalls() || hasFP(MF) || MF.hasInlineAsm())
+    return false;
 
-  return !(MFI.hasCalls()               // has calls
-           || MRI.isPhysRegUsed(SP::L0) // Too many registers needed
-           || MRI.isPhysRegUsed(SP::O6) // %sp is used
-           || hasFP(MF)                 // need %fp
-           || MF.hasInlineAsm());       // has inline assembly
+  const MachineRegisterInfo &MRI = MF.getRegInfo();
+  for (unsigned Reg = SP::L0; Reg <= SP::L7; ++Reg)
+    if (MRI.isPhysRegUsed(Reg))
+      return false;
+  return true;
 }
 
 void SparcFrameLowering::remapRegsForLeafProc(MachineFunction &MF) const {
   MachineRegisterInfo &MRI = MF.getRegInfo();
-  // Remap %i[0-7] to %o[0-7].
-  for (unsigned reg = SP::I0; reg <= SP::I7; ++reg) {
-    if (!MRI.isPhysRegUsed(reg))
-      continue;
-
-    unsigned mapped_reg = reg - SP::I0 + SP::O0;
-
-    // Replace I register with O register.
-    MRI.replaceRegWith(reg, mapped_reg);
+  const SparcRegisterInfo *TRI =
+      MF.getSubtarget<SparcSubtarget>().getRegisterInfo();
+  DenseMap<MCRegister, MCRegister> RegMap;
+  auto RemapReg = [&](MCRegister Reg, MCRegister MappedReg) {
+    auto [It, Inserted] = RegMap.try_emplace(Reg, MappedReg);
+    assert((Inserted || It->second == MappedReg) &&
+           "inconsistent leaf register mapping");
+    if (!Inserted)
+      return;
+    MRI.replaceRegWith(Reg, MappedReg);
+    MRI.replaceLiveInPhysReg(Reg, MappedReg);
+  };
 
-    // Also replace register pair super-registers.
-    if ((reg - SP::I0) % 2 == 0) {
-      unsigned preg = (reg - SP::I0) / 2 + SP::I0_I1;
-      unsigned mapped_preg = preg - SP::I0_I1 + SP::O0_O1;
-      MRI.replaceRegWith(preg, mapped_preg);
+  // Remap %i[0-7] to %o[0-7].
+  for (unsigned Reg = SP::I0; Reg <= SP::I7; ++Reg) {
+    MCRegister MappedReg = Reg - SP::I0 + SP::O0;
+    RemapReg(Reg, MappedReg);
+    for (MCRegAliasIterator AI(Reg, TRI, false); AI.isValid(); ++AI) {
+      unsigned SubRegIndex = TRI->getSubRegIndex(*AI, Reg);
+      if (!SubRegIndex)
+        continue;
+      MCRegister MappedAlias = TRI->getMatchingSuperReg(MappedReg, SubRegIndex,
+                                                        &SP::IntPairRegClass);
+      assert(MappedAlias);
+      RemapReg(*AI, MappedAlias);
     }
   }
 
   // Rewrite MBB's Live-ins.
   for (MachineBasicBlock &MBB : MF) {
-    for (unsigned reg = SP::I0_I1; reg <= SP::I6_I7; ++reg) {
-      if (!MBB.isLiveIn(reg))
-        continue;
-      MBB.removeLiveIn(reg);
-      MBB.addLiveIn(reg - SP::I0_I1 + SP::O0_O1);
-    }
-    for (unsigned reg = SP::I0; reg <= SP::I7; ++reg) {
-      if (!MBB.isLiveIn(reg))
-        continue;
-      MBB.removeLiveIn(reg);
-      MBB.addLiveIn(reg - SP::I0 + SP::O0);
+    std::vector<MachineBasicBlock::RegisterMaskPair> LiveIns;
+    MBB.clearLiveIns(LiveIns);
+    for (const auto &LiveIn : LiveIns) {
+      auto It = RegMap.find(LiveIn.PhysReg);
+      MBB.addLiveIn(It == RegMap.end() ? LiveIn.PhysReg : It->second,
+                    LiveIn.LaneMask);
     }
+    MBB.sortUniqueLiveIns();
   }
 
   assert(verifyLeafProcRegUse(&MRI));
@@ -314,15 +323,9 @@ void SparcFrameLowering::remapRegsForLeafProc(MachineFunction &MF) const {
 #endif
 }
 
-void SparcFrameLowering::determineCalleeSaves(MachineFunction &MF,
-                                              BitVector &SavedRegs,
-                                              RegScavenger *RS) const {
-  TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);
-  if (!DisableLeafProc && isLeafProc(MF)) {
-    SparcMachineFunctionInfo *MFI = MF.getInfo<SparcMachineFunctionInfo>();
-    MFI->setLeafProc(true);
-
+void SparcFrameLowering::finalizeLeafProc(MachineFunction &MF) const {
+  bool IsLeafProc = !DisableLeafProc && isLeafProc(MF);
+  MF.getInfo<SparcMachineFunctionInfo>()->setLeafProc(IsLeafProc);
+  if (IsLeafProc)
     remapRegsForLeafProc(MF);
-  }
-
 }
diff --git a/llvm/lib/Target/Sparc/SparcFrameLowering.h b/llvm/lib/Target/Sparc/SparcFrameLowering.h
index 803856811969b..d1bcd2b85303b 100644
--- a/llvm/lib/Target/Sparc/SparcFrameLowering.h
+++ b/llvm/lib/Target/Sparc/SparcFrameLowering.h
@@ -35,8 +35,8 @@ class SparcFrameLowering : public TargetFrameLowering {
                                 MachineBasicBlock::iterator I) const override;
 
   bool hasReservedCallFrame(const MachineFunction &MF) const override;
-  void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
-                            RegScavenger *RS = nullptr) const override;
+
+  void finalizeLeafProc(MachineFunction &MF) const;
 
   StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
                                      Register &FrameReg) const override;
@@ -56,7 +56,6 @@ class SparcFrameLowering : public TargetFrameLowering {
   // Returns true if MF is a leaf procedure.
   bool isLeafProc(MachineFunction &MF) const;
 
-
   // Emits code for adjusting SP in function prologue/epilogue.
   void emitSPAdjustment(MachineFunction &MF,
                         MachineBasicBlock &MBB,
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.cpp b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
index 488cc2f8e900f..2c4e0033d280e 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.cpp
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
@@ -13,6 +13,7 @@
 
 #include "SparcISelLowering.h"
 #include "MCTargetDesc/SparcMCTargetDesc.h"
+#include "SparcFrameLowering.h"
 #include "SparcMachineFunctionInfo.h"
 #include "SparcRegisterInfo.h"
 #include "SparcSelectionDAGInfo.h"
@@ -39,7 +40,6 @@
 #include "llvm/Support/KnownBits.h"
 using namespace llvm;
 
-
 //===----------------------------------------------------------------------===//
 // Calling Convention Implementation
 //===----------------------------------------------------------------------===//
@@ -2071,6 +2071,12 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM,
   computeRegisterProperties(Subtarget->getRegisterInfo());
 }
 
+void SparcTargetLowering::finalizeLowering(MachineFunction &MF) const {
+  static_cast<const SparcFrameLowering *>(Subtarget->getFrameLowering())
+      ->finalizeLeafProc(MF);
+  TargetLoweringBase::finalizeLowering(MF);
+}
+
 bool SparcTargetLowering::useSoftFloat() const {
   return Subtarget->useSoftFloat();
 }
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.h b/llvm/lib/Target/Sparc/SparcISelLowering.h
index 5098480e192a6..e716eac668b61 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.h
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.h
@@ -25,6 +25,7 @@ namespace llvm {
   public:
     SparcTargetLowering(const TargetMachine &TM, const SparcSubtarget &STI);
     SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
+    void finalizeLowering(MachineFunction &MF) const override;
 
     bool useSoftFloat() const override;
 
diff --git a/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp b/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
index 0a14746f587bb..be7f561386fef 100644
--- a/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp
@@ -12,6 +12,7 @@
 
 #include "SparcRegisterInfo.h"
 #include "Sparc.h"
+#include "SparcMachineFunctionInfo.h"
 #include "SparcSubtarget.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
@@ -103,6 +104,22 @@ BitVector SparcRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
       markSuperRegs(Reserved, *i);
   }
 
+  if (MF.getInfo<SparcMachineFunctionInfo>()->isLeafProc()) {
+    auto ReserveAliases = [&](MCRegister Reg) {
+      for (MCRegAliasIterator AI(Reg, this, true); AI.isValid(); ++AI)
+        Reserved.set(*AI);
+    };
+
+    for (unsigned Reg = SP::I0; Reg <= SP::I7; ++Reg)
+      if (Reserved.test(Reg))
+        ReserveAliases(Reg - SP::I0 + SP::O0);
+
+    for (unsigned Reg = SP::I0; Reg <= SP::I7; ++Reg)
+      ReserveAliases(Reg);
+    for (unsigned Reg = SP::L0; Reg <= SP::L7; ++Reg)
+      ReserveAliases(Reg);
+  }
+
   assert(checkAllSuperRegsMarked(Reserved));
   return Reserved;
 }
diff --git a/llvm/lib/Target/Sparc/SparcRegisterInfo.td b/llvm/lib/Target/Sparc/SparcRegisterInfo.td
index d8319a8d41dda..f607701de5b6b 100644
--- a/llvm/lib/Target/Sparc/SparcRegisterInfo.td
+++ b/llvm/lib/Target/Sparc/SparcRegisterInfo.td
@@ -343,20 +343,44 @@ def IntRegs : RegisterClass<"SP", [i32, i64], 32,
                             (add (sequence "I%u", 0, 7),
                                  (sequence "G%u", 0, 7),
                                  (sequence "L%u", 0, 7),
-                                 (sequence "O%u", 0, 7))>;
+                                 (sequence "O%u", 0, 7))> {
+  let AltOrders = [(add (sequence "O%u", 0, 7),
+                        (sequence "G%u", 0, 7),
+                        (sequence "L%u", 0, 7),
+                        (sequence "I%u", 0, 7))];
+  let AltOrderSelect = [{
+    return MF.getInfo<SparcMachineFunctionInfo>()->isLeafProc();
+  }];
+}
 
 // Should be in the same order as IntRegs.
 def IntPair : RegisterClass<"SP", [v2i32], 64,
     (add I0_I1, I2_I3, I4_I5, I6_I7,
          G0_G1, G2_G3, G4_G5, G6_G7,
          L0_L1, L2_L3, L4_L5, L6_L7,
-         O0_O1, O2_O3, O4_O5, O6_O7)>;
+         O0_O1, O2_O3, O4_O5, O6_O7)> {
+  let AltOrders = [(add O0_O1, O2_O3, O4_O5, O6_O7,
+                        G0_G1, G2_G3, G4_G5, G6_G7,
+                        L0_L1, L2_L3, L4_L5, L6_L7,
+                        I0_I1, I2_I3, I4_I5, I6_I7)];
+  let AltOrderSelect = [{
+    return MF.getInfo<SparcMachineFunctionInfo>()->isLeafProc();
+  }];
+}
 
 // Register class for 64-bit mode, with a 64-bit spill slot size.
 // These are the same as the 32-bit registers, so TableGen will consider this
 // to be a sub-class of IntRegs. That works out because requiring a 64-bit
 // spill slot is a stricter constraint than only requiring a 32-bit spill slot.
-def I64Regs : RegisterClass<"SP", [i64], 64, (add IntRegs)>;
+def I64Regs : RegisterClass<"SP", [i64], 64, (add IntRegs)> {
+  let AltOrders = [(add (sequence "O%u", 0, 7),
+                        (sequence "G%u", 0, 7),
+                        (sequence "L%u", 0, 7),
+                        (sequence "I%u", 0, 7))];
+  let AltOrderSelect = [{
+    return MF.getInfo<SparcMachineFunctionInfo>()->isLeafProc();
+  }];
+}
 
 // Floating point register classes.
 def FPRegs : RegisterClass<"SP", [f32], 32, (sequence "F%u", 0, 31)>;
diff --git a/llvm/test/CodeGen/SPARC/2011-01-11-CC.ll b/llvm/test/CodeGen/SPARC/2011-01-11-CC.ll
index e05c47bfee766..b62e7c30f71f1 100644
--- a/llvm/test/CodeGen/SPARC/2011-01-11-CC.ll
+++ b/llvm/test/CodeGen/SPARC/2011-01-11-CC.ll
@@ -167,14 +167,14 @@ define double @test_select_dfp_icc(i32 %a, double %f1, double %f2) nounwind {
 ; V8-LABEL: test_select_dfp_icc:
 ; V8:       ! %bb.0: ! %entry
 ; V8-NEXT:    add %sp, -112, %sp
-; V8-NEXT:    mov %o4, %o5
-; V8-NEXT:    mov %o2, %g3
-; V8-NEXT:    mov %o3, %o4
-; V8-NEXT:    std %o4, [%sp+96]
+; V8-NEXT:    mov %o4, %g3
+; V8-NEXT:    mov %o2, %o5
+; V8-NEXT:    mov %o3, %g2
+; V8-NEXT:    std %g2, [%sp+96]
 ; V8-NEXT:    cmp %o0, 0
-; V8-NEXT:    mov %o1, %g2
+; V8-NEXT:    mov %o1, %o4
 ; V8-NEXT:    be .LBB3_2
-; V8-NEXT:    std %g2, [%sp+104]
+; V8-NEXT:    std %o4, [%sp+104]
 ; V8-NEXT:  ! %bb.1: ! %entry
 ; V8-NEXT:    ldd [%sp+96], %f0
 ; V8-NEXT:    retl
@@ -187,12 +187,12 @@ define double @test_select_dfp_icc(i32 %a, double %f1, double %f2) nounwind {
 ; V9-LABEL: test_select_dfp_icc:
 ; V9:       ! %bb.0: ! %entry
 ; V9-NEXT:    add %sp, -112, %sp
-; V9-NEXT:    mov %o4, %o5
-; V9-NEXT:    mov %o2, %g3
-; V9-NEXT:    mov %o3, %o4
-; V9-NEXT:    std %o4, [%sp+96]
-; V9-NEXT:    mov %o1, %g2
-; V9-NEXT:    std %g2, [%sp+104]
+; V9-NEXT:    mov %o4, %g3
+; V9-NEXT:    mov %o2, %o5
+; V9-NEXT:    mov %o3, %g2
+; V9-NEXT:    std %g2, [%sp+96]
+; V9-NEXT:    mov %o1, %o4
+; V9-NEXT:    std %o4, [%sp+104]
 ; V9-NEXT:    ldd [%sp+96], %f0
 ; V9-NEXT:    ldd [%sp+104], %f2
 ; V9-NEXT:    cmp %o0, 0
diff --git a/llvm/test/CodeGen/SPARC/LeonInsertNOPLoadPassUT.ll b/llvm/test/CodeGen/SPARC/LeonInsertNOPLoadPassUT.ll
index 7b4db4d3034cc..11d2a72d4ce4b 100644
--- a/llvm/test/CodeGen/SPARC/LeonInsertNOPLoadPassUT.ll
+++ b/llvm/test/CodeGen/SPARC/LeonInsertNOPLoadPassUT.ll
@@ -7,9 +7,7 @@ define float @ld_float_test() #0 {
 ; CHECK:         .cfi_startproc
 ; CHECK-NEXT:  ! %bb.0: ! %entry
 ; CHECK-NEXT:    add %sp, -96, %sp
-; CHECK-NEXT:    .cfi_def_cfa_register %fp
-; CHECK-NEXT:    .cfi_window_save
-; CHECK-NEXT:    .cfi_register %o7, %i7
+; CHECK-NEXT:    .cfi_def_cfa_offset 96
 ; CHECK-NEXT:    sethi 1042305, %o0
 ; CHECK-NEXT:    or %o0, 25, %o0
 ; CHECK-NEXT:    st %o0, [%sp+92]
diff --git a/llvm/test/CodeGen/SPARC/fp128-select.ll b/llvm/test/CodeGen/SPARC/fp128-select.ll
index 2868896dd83b3..cb56f740dd583 100644
--- a/llvm/test/CodeGen/SPARC/fp128-select.ll
+++ b/llvm/test/CodeGen/SPARC/fp128-select.ll
@@ -6,9 +6,7 @@ define fp128 @f128_select_soft(fp128 %a, fp128 %b) #0 {
 ; V9:         .cfi_startproc
 ; V9-NEXT:  ! %bb.0: ! %entry
 ; V9-NEXT:    add %sp, -144, %sp
-; V9-NEXT:    .cfi_def_cfa_register %fp
-; V9-NEXT:    .cfi_window_save
-; V9-NEXT:    .cfi_register %o7, %i7
+; V9-NEXT:    .cfi_def_cfa_offset 2191
 ; V9-NEXT:    add %sp, 2175, %o0
 ; V9-NEXT:    or %o0, 8, %o0
 ; V9-NEXT:    std %f6, [%o0]
@@ -48,9 +46,7 @@ define fp128 @f128_select_hard(fp128 %a, fp128 %b) #1 {
 ; V9:         .cfi_startproc
 ; V9-NEXT:  ! %bb.0: ! %entry
 ; V9-NEXT:    add %sp, -144, %sp
-; V9-NEXT:    .cfi_def_cfa_register %fp
-; V9-NEXT:    .cfi_window_save
-; V9-NEXT:    .cfi_register %o7, %i7
+; V9-NEXT:    .cfi_def_cfa_offset 2191
 ; V9-NEXT:    stq %f4, [%sp+2175]
 ; V9-NEXT:    add %sp, 2175, %o0
 ; V9-NEXT:    or %o0, 8, %o0
diff --git a/llvm/test/CodeGen/SPARC/leafproc-reg-pair.mir b/llvm/test/CodeGen/SPARC/leafproc-reg-pair.mir
new file mode 100644
index 0000000000000..c918f5cf8d813
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/leafproc-reg-pair.mir
@@ -0,0 +1,27 @@
+# RUN: llc -mtriple=sparc -run-pass=finalize-isel -verify-machineinstrs -o - %s | FileCheck %s
+
+--- |
+  define void @pair() { ret void }
+...
+---
+name: pair
+# CHECK-LABEL: name: pair
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: intpair }
+liveins:
+# CHECK: liveins:
+# CHECK-NEXT: - { reg: '$o0_o1', virtual-reg: '%0' }
+# CHECK-NOT: reg: '$i0_i1'
+  - { reg: '$i0_i1', virtual-reg: '%0' }
+# CHECK: bb.0:
+# CHECK-NEXT: liveins: $o0_o1
+# CHECK: %0:intpair = COPY $o0_o1
+# CHECK-NOT: $i0_i1
+body: |
+  bb.0:
+    liveins: $i0_i1
+    %0:intpair = COPY $i0_i1
+    $o0_o1 = COPY %0
+    RETL 8, implicit $o0, implicit $o1
+...
diff --git a/llvm/test/CodeGen/SPARC/leafproc.ll b/llvm/test/CodeGen/SPARC/leafproc.ll
index 3998ef26f7b20..70e0e2d633fab 100644
--- a/llvm/test/CodeGen/SPARC/leafproc.ll
+++ b/llvm/test/CodeGen/SPARC/leafproc.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -mtriple=sparc -disable-sparc-leaf-proc=0 < %s | FileCheck %s
+; RUN: llc -mtriple=sparcv9 -disable-sparc-leaf-proc=0 < %s | FileCheck %s --check-prefix=V9
 
 ; CHECK-LABEL:      func_nobody:
 ; CHECK:      retl
@@ -39,6 +40,16 @@ entry:
   ret i32 %1
 }
 
+; CHECK-LABEL: leaf_proc_with_i64_arg:
+; CHECK:       addcc %o1, 1, %o1
+; CHECK:       retl
+; CHECK-NEXT:  addxcc %o0, 0, %o0
+define i64 @leaf_proc_with_i64_arg(i64 %x) {
+entry:
+  %value = add i64 %x, 1
+  ret i64 %value
+}
+
 ; CHECK-LABEL:     leaf_proc_with_args_in_stack:
 ; CHECK-DAG: ld [%sp+92], {{%[go][0-7]}}
 ; CHECK-DAG: ld [%sp+96], {{%[go][0-7]}}
@@ -58,6 +69,8 @@ entry:
 
 ; CHECK-LABEL:      leaf_proc_with_local_array:
 ; CHECK:      add %sp, -104, %sp
+; CHECK-NEXT: .cfi_def_cfa_offset 104
+; CHECK-NOT:  .cfi_window_save
 ; CHECK:      mov 1, [[R1:%[go][0-7]]]
 ; CHECK:      st [[R1]], [%sp+96]
 ; CHECK:      mov 2, [[R2:%[go][0-7]]]
@@ -67,6 +80,11 @@ entry:
 ; CHECK-NEXT: add %sp, 104, %sp
 
 define i32 @leaf_proc_with_local_array(i32 %a, i32 %b, i32 %c) {
+; V9-LABEL: leaf_proc_with_local_array:
+; V9:       add %sp, -144, %sp
+; V9-NEXT:  .cfi_def_cfa_offset 2191
+; V9-NOT:   .cfi_window_save
+; V9:       retl
 entry:
   %array = alloca [2 x i32], align 4
   %0 = sub nsw i32 %b, %c
diff --git a/llvm/test/CodeGen/SPARC/reserved-regs-named.ll b/llvm/test/CodeGen/SPARC/reserved-regs-named.ll
index 0904d8ed74ef1..6b1a29bf9193f 100644
--- a/llvm/test/CodeGen/SPARC/reserved-regs-named.ll
+++ b/llvm/test/CodeGen/SPARC/reserved-regs-named.ll
@@ -1,5 +1,6 @@
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L0
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -stop-after=finalize-isel -o - %s | FileCheck %s --check-prefix=NAMES
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -verify-machineinstrs -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L0
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -verify-machineinstrs -stop-after=finalize-isel -o - %s | FileCheck %s --check-prefix=NAMES
+; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l0 -verify-machineinstrs -stop-after=prolog-epilog -simplify-mir -o - %s | FileCheck %s --check-prefix=LIVEINS
 
 ;; Ensure explicit register references are catched as well.
 
@@ -38,7 +39,7 @@ entry:
 }
 
 ; NAMES-LABEL: name: {{ *}}read_percent_fp
-; NAMES: {{%[0-9]+}}:i64regs = COPY $i6
+; NAMES: {{%[0-9]+}}:i64regs = COPY $o6
 define i64 @read_percent_fp() {
 entry:
   %value = call i64 @llvm.read_register.i64(metadata !4)
@@ -46,7 +47,7 @@ entry:
 }
 
 ; NAMES-LABEL: name: {{ *}}read_fp
-; NAMES: {{%[0-9]+}}:i64regs = COPY $i6
+; NAMES: {{%[0-9]+}}:i64regs = COPY $o6
 define i64 @read_fp() {
 entry:
   %value = call i64 @llvm.read_register.i64(metadata !5)
@@ -54,7 +55,7 @@ entry:
 }
 
 ; NAMES-LABEL: name: {{ *}}read_r30
-; NAMES: {{%[0-9]+}}:i64regs = COPY $i6
+; NAMES: {{%[0-9]+}}:i64regs = COPY $o6
 define i64 @read_r30() {
 entry:
   %value = call i64 @llvm.read_register.i64(metadata !6)
@@ -79,21 +80,63 @@ entry:
 
 ; NAMES-LABEL: name: {{ *}}read_r16
 ; NAMES: {{%[0-9]+}}:i64regs = COPY $l0
+; CHECK-RESERVED-L0-LABEL: read_r16:
+; CHECK-RESERVED-L0: save %sp
+; CHECK-RESERVED-L0: restore {{.*}}%l0
 define i64 @read_r16() {
 entry:
   %value = call i64 @llvm.read_register.i64(metadata !9)
   ret i64 %value
 }
 
-; NAMES-LABEL: name: {{ *}}read_r31
-; NAMES: {{%[0-9]+}}:i64regs = COPY $i7
+; LIVEINS-LABEL: name: {{ *}}read_r31
+; LIVEINS-NOT: liveins:
+; LIVEINS: bb.0.entry:
+; LIVEINS-NOT: liveins:
+; LIVEINS: $o0 = COPY $o7
 define i64 @read_r31() {
 entry:
   %value = call i64 @llvm.read_register.i64(metadata !10)
   ret i64 %value
 }
 
+; LIVEINS-LABEL: name: {{ *}}read_i7_nonentry
+; LIVEINS: liveins:
+; LIVEINS-NEXT: - { reg: '$o0' }
+; LIVEINS-NEXT: - { reg: '$o1' }
+; LIVEINS-NOT: reg: '$i{{[01]}}'
+; LIVEINS: bb.0.entry:
+; LIVEINS: liveins: $o0, $o1{{$}}
+; LIVEINS: bb.{{[0-9]+}}.read:
+; LIVEINS-NOT: liveins:
+; LIVEINS: $o0 = COPY $o7
+define i64 @read_i7_nonentry(i64 %zero_value, i1 %cond) {
+entry:
+  br i1 %cond, label %read, label %zero
+
+read:
+  %value = call i64 @llvm.read_register.i64(metadata !11)
+  ret i64 %value
+
+zero:
+  ret i64 %zero_value
+}
+
+; NAMES-LABEL: name: {{ *}}read_i7_nonleaf
+; NAMES: {{%[0-9]+}}:i64regs = COPY $i7
+; CHECK-RESERVED-L0-LABEL: read_i7_nonleaf:
+; CHECK-RESERVED-L0: save %sp
+; CHECK-RESERVED-L0: call callee
+; CHECK-RESERVED-L0: ret
+define i64 @read_i7_nonleaf() {
+entry:
+  call void @callee()
+  %value = call i64 @llvm.read_register.i64(metadata !11)
+  ret i64 %value
+}
+
 declare i64 @llvm.read_register.i64(metadata)
+declare void @callee()
 
 !1 = !{!"%sp"}
 !2 = !{!"sp"}
@@ -105,3 +148,4 @@ declare i64 @llvm.read_register.i64(metadata)
 !8 = !{!"r0"}
 !9 = !{!"r16"}
 !10 = !{!"r31"}
+!11 = !{!"i7"}
diff --git a/llvm/test/CodeGen/SPARC/reserved-regs.ll b/llvm/test/CodeGen/SPARC/reserved-regs.ll
index 6bb4e1245589d..1e247ba187658 100644
--- a/llvm/test/CodeGen/SPARC/reserved-regs.ll
+++ b/llvm/test/CodeGen/SPARC/reserved-regs.ll
@@ -1,13 +1,15 @@
 ; RUN: llc -mtriple=sparc -verify-machineinstrs < %s | FileCheck %s
 
 ;; Test reserve-* options.
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-g1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-o1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-O1
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-l1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L1
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-I1
+; RUN: llc -mtriple=sparc64-linux-gnu -disable-sparc-leaf-proc -mattr=+reserve-g1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1
+; RUN: llc -mtriple=sparc64-linux-gnu -disable-sparc-leaf-proc -mattr=+reserve-o1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-O1
+; RUN: llc -mtriple=sparc64-linux-gnu -disable-sparc-leaf-proc -mattr=+reserve-l1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-L1
+; RUN: llc -mtriple=sparc64-linux-gnu -disable-sparc-leaf-proc -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-I1
+; RUN: llc -mtriple=sparc64-linux-gnu -verify-machineinstrs -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=LEAF-RESERVED-I1
+; RUN: llc -mtriple=sparc -verify-machineinstrs -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=LEAF32-RESERVED-I1
 
 ;; Test multiple reserve-* options together.
-; RUN: llc -mtriple=sparc64-linux-gnu -mattr=+reserve-g1 -mattr=+reserve-o1 -mattr=+reserve-l1 -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1,CHECK-RESERVED-O1,CHECK-RESERVED-L1,CHECK-RESERVED-I1
+; RUN: llc -mtriple=sparc64-linux-gnu -disable-sparc-leaf-proc -mattr=+reserve-g1 -mattr=+reserve-o1 -mattr=+reserve-l1 -mattr=+reserve-i1 -o - %s | FileCheck %s --check-prefixes=CHECK-RESERVED-G1,CHECK-RESERVED-O1,CHECK-RESERVED-L1,CHECK-RESERVED-I1
 
 @g = common global [32 x i32] zeroinitializer, align 16
 @h = common global [16 x i64] zeroinitializer, align 16
@@ -15,21 +17,25 @@
 ;; Ensures that we don't use registers which are supposed to be reserved.
 
 ; CHECK-LABEL: use_all_i32_regs:
-; CHECK: save %sp
-; CHECK: .cfi_register %o7, %i7
+; CHECK: add %sp, -{{[0-9]+}}, %sp
+; CHECK-NOT: .cfi_window_save
 ; CHECK-NOT: %g0
 ; CHECK-NOT: %g1
 ; CHECK-NOT: %g5
 ; CHECK-NOT: %g6
 ; CHECK-NOT: %g7
 ; CHECK-NOT: %o6
-; CHECK-NOT: %i6
-; CHECK-NOT: %i7
+; CHECK-NOT: %i{{[0-7]}}
+; CHECK-NOT: %l{{[0-7]}}
+; CHECK-NOT: %fp
 ; CHECK-RESERVED-G1-NOT: %g1
 ; CHECK-RESERVED-O1-NOT: %o1
 ; CHECK-RESERVED-L1-NOT: %l1
 ; CHECK-RESERVED-I1-NOT: %i1
-; CHECK: ret
+; CHECK: retl
+; LEAF-RESERVED-I1-LABEL: use_all_i32_regs:
+; LEAF-RESERVED-I1-NOT: %o1
+; LEAF-RESERVED-I1: retl
 define void @use_all_i32_regs() {
 entry:
   %0 = load volatile i32, ptr @g, align 16
@@ -101,23 +107,29 @@ entry:
 
 
 ; CHECK-LABEL: use_all_i64_regs:
-; CHECK: save %sp
-; CHECK: .cfi_register %o7, %i7
+; CHECK: add %sp, -{{[0-9]+}}, %sp
+; CHECK-NOT: .cfi_window_save
 ; CHECK-NOT: %g0
 ; CHECK-NOT: %g1
-; CHECK-NOT: %g4
 ; CHECK-NOT: %g5
+; CHECK-NOT: ldd {{.*}}, %g4
+; CHECK-NOT: std %g4
 ; CHECK-NOT: %g6
 ; CHECK-NOT: %g7
 ; CHECK-NOT: %o6
 ; CHECK-NOT: %o7
-; CHECK-NOT: %i6
-; CHECK-NOT: %i7
+; CHECK-NOT: %i{{[0-7]}}
+; CHECK-NOT: %l{{[0-7]}}
+; CHECK-NOT: %fp
 ; CHECK-RESERVED-G1-NOT: %g1
 ; CHECK-RESERVED-O1-NOT: %o1
 ; CHECK-RESERVED-L1-NOT: %l1
 ; CHECK-RESERVED-I1-NOT: %i1
-; CHECK: ret
+; CHECK: retl
+; LEAF32-RESERVED-I1-LABEL: use_all_i64_regs:
+; LEAF32-RESERVED-I1-NOT: ldd {{.*}}, %o0
+; LEAF32-RESERVED-I1-NOT: std %o0
+; LEAF32-RESERVED-I1: retl
 define void @use_all_i64_regs() {
 entry:
   %0 = load volatile i64, ptr @h, align 16
diff --git a/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll b/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll
index e0c7032b1d9d4..904625dc1a37d 100644
--- a/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll
+++ b/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll
@@ -1,7 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=sparc-unknown-linux-gnu | FileCheck %s --check-prefixes=SPARC
+; RUN: llc < %s -mtriple=sparc-unknown-linux-gnu -disable-sparc-leaf-proc | FileCheck %s --check-prefixes=SPARC
 ; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu | FileCheck %s --check-prefixes=SPARC64
-; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu -mattr=vis3 | FileCheck %s --check-prefixes=SPARC64-VIS3
+; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu -mattr=vis3 -disable-sparc-leaf-proc | FileCheck %s --check-prefixes=SPARC64-VIS3
 
 define { i128, i8 } @muloti_test(i128 %l, i128 %r) nounwind {
 ; SPARC-LABEL: muloti_test:
diff --git a/llvm/test/CodeGen/SPARC/stackaddress.ll b/llvm/test/CodeGen/SPARC/stackaddress.ll
index fce5f1cba8fb1..0580527c86dd2 100644
--- a/llvm/test/CodeGen/SPARC/stackaddress.ll
+++ b/llvm/test/CodeGen/SPARC/stackaddress.ll
@@ -4,13 +4,13 @@
 declare ptr @llvm.stackaddress.p0()
 
 define ptr @test() {
-; sparc32: save %sp, -96, %sp
-; sparc32: ret
-; sparc32: restore %sp, 68, %o0
+; sparc32-LABEL: test:
+; sparc32:       retl
+; sparc32-NEXT:  add %sp, 68, %o0
 ;
-; sparc64: save %sp, -128, %sp
-; sparc64: ret
-; sparc64: restore %sp, 2175, %o0
+; sparc64-LABEL: test:
+; sparc64:       retl
+; sparc64-NEXT:  add %sp, 2175, %o0
   %sp = call ptr @llvm.stackaddress.p0()
   ret ptr %sp
 }
diff --git a/llvm/test/CodeGen/SPARC/umulo-128-legalisation-lowering.ll b/llvm/test/CodeGen/SPARC/umulo-128-legalisation-lowering.ll
index 40e90d6bdd6af..9295fdb3e1f55 100644
--- a/llvm/test/CodeGen/SPARC/umulo-128-legalisation-lowering.ll
+++ b/llvm/test/CodeGen/SPARC/umulo-128-legalisation-lowering.ll
@@ -1,7 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=sparc-unknown-linux-gnu | FileCheck %s --check-prefixes=SPARC
+; RUN: llc < %s -mtriple=sparc-unknown-linux-gnu -disable-sparc-leaf-proc | FileCheck %s --check-prefixes=SPARC
 ; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu | FileCheck %s --check-prefixes=SPARC64
-; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu -mattr=vis3 | FileCheck %s --check-prefixes=SPARC64-VIS3
+; RUN: llc < %s -mtriple=sparc64-unknown-linux-gnu -mattr=vis3 -disable-sparc-leaf-proc | FileCheck %s --check-prefixes=SPARC64-VIS3
 
 define { i128, i8 } @muloti_test(i128 %l, i128 %r) nounwind {
 ; SPARC-LABEL: muloti_test:



More information about the llvm-commits mailing list