[llvm] [CodeGen] Refactor `determineCalleeSaves`. (PR #166763)

Mikhail Gudim via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 19 13:05:34 PST 2026


https://github.com/mgudim updated https://github.com/llvm/llvm-project/pull/166763

>From 9b39443179cbdb2519ef1ef00bbe33c3397102e2 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 6 Nov 2025 03:49:12 -0800
Subject: [PATCH 01/11] [CodeGen] Refactor `determineCalleeSaves`.

It is possible that target makes sure that callee-saved registers are
preserved by some way other than saving / restoring registers in prolog / epilog.
To account for this possibility we split up `determineCaleeSaves` into:

  `const MCPhysReg *getMustPreserveRegisters(MachineFunction &MF) const`:
        Return the list of registers which must be preserved by the function: the value on exit must be the same as the value on entry. A register from this list does not need to be saved / reloaded if the function did not use it.

  `virtual void determineUncondPrologCalleeSaves(MachineFunction &MF, const MCPhysReg *CSRegs, BitVector &UncondPrologCSRs) const`:
        Determines which of the registers reported by getMustPreserveRegisters() must be saved in prolog and reloaded in epilog regardless of wheather or not they were modified by the function.

  `virtual void determineEarlyCalleeSaves(MachineFunction &MF, BitVector &EarlyCSRs)`:
        Returns the difference between getMustPreserveRegisters and determineUncondPrologCalleeSaves. These registers will be preserved by the code optimizer and do not need to be saved / restored in prolog / epilog.

  `virtual void determinePrologCalleeSaves(MachineFunction &MF,
                                               BitVector &PrologCSRs,
                                               RegScavenger *RS) const`:
        This method returns those registers in the difference of  getMustPreserveRegisters and determineEarlyCalleeSaves that were modified by the function and need to be saved / restored in prolog / epilog.
---
 .../llvm/CodeGen/TargetFrameLowering.h        |  31 ++++-
 .../llvm/CodeGen/TargetSubtargetInfo.h        |   4 +
 llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp  | 106 +++++++++++++-----
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp  |  34 ++++++
 llvm/lib/Target/RISCV/RISCVFrameLowering.h    |   4 +
 llvm/lib/Target/RISCV/RISCVSubtarget.cpp      |   6 +
 llvm/lib/Target/RISCV/RISCVSubtarget.h        |   2 +
 .../CodeGen/RISCV/determine-callee-saves.mir  |  79 +++++++++++++
 8 files changed, 237 insertions(+), 29 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/determine-callee-saves.mir

diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index 99034754e466b..f2a4489ee9f66 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -373,7 +373,7 @@ class LLVM_ABI TargetFrameLowering {
                                   BitVector &SavedRegs) const;
 
   /// This method determines which of the registers reported by
-  /// TargetRegisterInfo::getCalleeSavedRegs() should actually get saved.
+  /// getMustPreserveRegisters() should actually get saved.
   /// The default implementation checks populates the \p SavedRegs bitset with
   /// all registers which are modified in the function, targets may override
   /// this function to save additional registers.
@@ -382,9 +382,38 @@ class LLVM_ABI TargetFrameLowering {
   /// This method should not be called by any passes outside of PEI, because
   /// it may change state passed in by \p MF and \p RS. The preferred
   /// interface outside PEI is getCalleeSaves.
+  LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
+                  "determinePrologCalleeSaves")
   virtual void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
                                     RegScavenger *RS = nullptr) const;
 
+  /// Return the list of registers which must be preserved by the function: the
+  /// value on exit must be the same as the value on entry. A register from this
+  /// list does not need to be saved / reloaded if the function did not use it.
+  const MCPhysReg *getMustPreserveRegisters(MachineFunction &MF) const;
+
+  /// This method determines which of the registers reported by
+  /// getMustPreserveRegisters() must be saved in prolog and reloaded in epilog
+  /// regardless of wheather or not they were modified by the function.
+  virtual void
+  determineUncondPrologCalleeSaves(MachineFunction &MF, const MCPhysReg *CSRegs,
+                                   BitVector &UncondPrologCSRs) const;
+
+  /// If the target has to do all saves / restores of "must preserve" registers
+  /// in prolog / epilog, this method returns empty set. Otherwise, this method
+  /// returns the difference between getMustPreserveRegisters and
+  /// determineUncondPrologCalleeSaves. These registers will be preserved by the
+  /// code optimizer and do not need to be saved / restored in prolog / epilog.
+  virtual void determineEarlyCalleeSaves(MachineFunction &MF,
+                                         BitVector &EarlyCSRs) const;
+
+  /// This method returns those registers in the difference of
+  /// getMustPreserveRegisters and determineEarlyCalleeSaves that were modified
+  /// by the function and need to be saved / restored in prolog / epilog.
+  virtual void determinePrologCalleeSaves(MachineFunction &MF,
+                                          BitVector &PrologCSRs,
+                                          RegScavenger *RS) const;
+
   /// processFunctionBeforeFrameFinalized - This method is called immediately
   /// before the specified function's frame layout (MF.getFrameInfo()) is
   /// finalized.  Once the frame is finalized, MO_FrameIndex operands are
diff --git a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
index 6f95f0fea6441..57978c8585a6a 100644
--- a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
@@ -364,6 +364,10 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
   }
 
   virtual bool isRegisterReservedByUser(Register R) const { return false; }
+
+  // Return true if the target can ensure before PrologEpilogInsertion that
+  // callee-saved registers are preserved.
+  virtual bool savesCSRsEarly() const { return false; }
 };
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index ebf6d1a52448e..95144109ed14a 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -93,36 +93,20 @@ void TargetFrameLowering::getCalleeSaves(const MachineFunction &MF,
     CalleeSaves.set(Info.getReg());
 }
 
+LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
+                "determinePrologCalleeSaves")
 void TargetFrameLowering::determineCalleeSaves(MachineFunction &MF,
                                                BitVector &SavedRegs,
                                                RegScavenger *RS) const {
-  const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
-
-  // Resize before the early returns. Some backends expect that
-  // SavedRegs.size() == TRI.getNumRegs() after this call even if there are no
-  // saved registers.
-  SavedRegs.resize(TRI.getNumRegs());
-
-  // Get the callee saved register list...
-  const MCPhysReg *CSRegs = nullptr;
-
-  // When interprocedural register allocation is enabled, callee saved register
-  // list should be empty, since caller saved registers are preferred over
-  // callee saved registers. Unless it has some risked CSR to be optimized out.
-  if (MF.getTarget().Options.EnableIPRA &&
-      isSafeForNoCSROpt(MF.getFunction()) &&
-      isProfitableForNoCSROpt(MF.getFunction()))
-    CSRegs = TRI.getIPRACSRegs(&MF);
-  else
-    CSRegs = MF.getRegInfo().getCalleeSavedRegs();
-
-  // Early exit if there are no callee saved registers.
-  if (!CSRegs || CSRegs[0] == 0)
-    return;
+  determinePrologCalleeSaves(MF, SavedRegs, RS);
+}
 
+const MCPhysReg *
+TargetFrameLowering::getMustPreserveRegisters(MachineFunction &MF) const {
+  const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
   // In Naked functions we aren't going to save any registers.
   if (MF.getFunction().hasFnAttribute(Attribute::Naked))
-    return;
+    return nullptr;
 
   // Noreturn+nounwind functions never restore CSR, so no saves are needed.
   // Purely noreturn functions may still return through throws, so those must
@@ -135,15 +119,81 @@ void TargetFrameLowering::determineCalleeSaves(MachineFunction &MF,
         MF.getFunction().hasFnAttribute(Attribute::NoUnwind) &&
         !MF.getFunction().hasFnAttribute(Attribute::UWTable) &&
         enableCalleeSaveSkip(MF))
-    return;
+    return nullptr;
+
+  // When interprocedural register allocation is enabled, callee saved register
+  // list should be empty, since caller saved registers are preferred over
+  // callee saved registers. Unless it has some risked CSR to be optimized out.
+  if (MF.getTarget().Options.EnableIPRA &&
+      isSafeForNoCSROpt(MF.getFunction()) &&
+      isProfitableForNoCSROpt(MF.getFunction()))
+    return TRI.getIPRACSRegs(&MF);
+  return MF.getRegInfo().getCalleeSavedRegs();
+}
 
+void TargetFrameLowering::determineUncondPrologCalleeSaves(
+    MachineFunction &MF, const MCPhysReg *CSRegs,
+    BitVector &UncondPrologCSRs) const {
   // Functions which call __builtin_unwind_init get all their registers saved.
-  bool CallsUnwindInit = MF.callsUnwindInit();
+  if (MF.callsUnwindInit()) {
+    for (unsigned i = 0; CSRegs[i]; ++i) {
+      unsigned Reg = CSRegs[i];
+      UncondPrologCSRs.set(Reg);
+    }
+  }
+  return;
+}
+
+void TargetFrameLowering::determineEarlyCalleeSaves(
+    MachineFunction &MF, BitVector &EarlyCSRs) const {
+  const auto &ST = MF.getSubtarget();
+  if (!ST.savesCSRsEarly())
+    return;
+
+  const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+  // Get the callee saved register list...
+  const MCPhysReg *CSRegs = getMustPreserveRegisters(MF);
+  // Early exit if there are no callee saved registers.
+  if (!CSRegs || CSRegs[0] == 0)
+    return;
+
+  BitVector UncondPrologCSRs(TRI.getNumRegs(), false);
+  determineUncondPrologCalleeSaves(MF, CSRegs, UncondPrologCSRs);
+
+  EarlyCSRs.resize(TRI.getNumRegs());
+  for (unsigned i = 0; CSRegs[i]; ++i) {
+    unsigned Reg = CSRegs[i];
+    if (!UncondPrologCSRs[Reg])
+      EarlyCSRs.set(Reg);
+  }
+}
+
+void TargetFrameLowering::determinePrologCalleeSaves(MachineFunction &MF,
+                                                     BitVector &PrologCSRs,
+                                                     RegScavenger *RS) const {
+  const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+
+  // Resize before the early returns. Some backends expect that
+  // SavedRegs.size() == TRI.getNumRegs() after this call even if there are no
+  // saved registers.
+  PrologCSRs.resize(TRI.getNumRegs());
+
+  // Get the callee saved register list...
+  const MCPhysReg *CSRegs = getMustPreserveRegisters(MF);
+  // Early exit if there are no callee saved registers.
+  if (!CSRegs || CSRegs[0] == 0)
+    return;
+
+  determineUncondPrologCalleeSaves(MF, CSRegs, PrologCSRs);
+
+  BitVector EarlyCSRs(TRI.getNumRegs(), false);
+  determineEarlyCalleeSaves(MF, EarlyCSRs);
+
   const MachineRegisterInfo &MRI = MF.getRegInfo();
   for (unsigned i = 0; CSRegs[i]; ++i) {
     unsigned Reg = CSRegs[i];
-    if (CallsUnwindInit || MRI.isPhysRegModified(Reg))
-      SavedRegs.set(Reg);
+    if (MRI.isPhysRegModified(Reg) && !EarlyCSRs[Reg])
+      PrologCSRs.set(Reg);
   }
 }
 
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 92fd7283dc2cd..476117a8acd42 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -32,6 +32,12 @@
 
 using namespace llvm;
 
+static cl::opt<std::string> UserDefinedUncondPrologCSRs(
+    "riscv-user-defined-uncond-prolog-csrs",
+    cl::desc("Comma-separated list of registerst that have to be saved / "
+             "restored in prolog / epilog. Used for testing only"),
+    cl::init(""), cl::Hidden);
+
 static Align getABIStackAlignment(RISCVABI::ABI ABI) {
   if (ABI == RISCVABI::ABI_ILP32E)
     return Align(4);
@@ -1580,6 +1586,34 @@ static MCRegister getRVVBaseRegister(const RISCVRegisterInfo &TRI,
   return BaseReg;
 }
 
+#define GET_REGISTER_MATCHER
+#include "RISCVGenAsmMatcher.inc"
+
+void RISCVFrameLowering::determineUncondPrologCalleeSaves(
+    MachineFunction &MF, const MCPhysReg *CSRegs,
+    BitVector &UncondPrologCSRs) const {
+  const RISCVRegisterInfo *TRI = STI.getRegisterInfo();
+
+  StringRef RegString(UserDefinedUncondPrologCSRs);
+  SmallVector<llvm::StringRef, 4> RegNames;
+  llvm::SplitString(RegString, RegNames, ",");
+  for (auto &Name : RegNames) {
+    Register Reg = MatchRegisterName(Name);
+    if (!Reg)
+      Reg = MatchRegisterAltName(Name);
+    if (!Reg) {
+      std::string msg;
+      raw_string_ostream Msg(msg);
+      Msg << "Couldn't parse register: " << Name << "\n";
+      report_fatal_error(Twine(msg));
+    }
+    UncondPrologCSRs.set(Reg.id());
+  }
+
+  TargetFrameLowering::determineUncondPrologCalleeSaves(MF, CSRegs,
+                                                        UncondPrologCSRs);
+}
+
 void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF,
                                               BitVector &SavedRegs,
                                               RegScavenger *RS) const {
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.h b/llvm/lib/Target/RISCV/RISCVFrameLowering.h
index 84e48dbc05d67..5c6b110d690fa 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.h
@@ -34,6 +34,10 @@ class RISCVFrameLowering : public TargetFrameLowering {
   StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
                                      Register &FrameReg) const override;
 
+  void
+  determineUncondPrologCalleeSaves(MachineFunction &MF, const MCPhysReg *CSRegs,
+                                   BitVector &UncondPrologCSRs) const override;
+
   void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
                             RegScavenger *RS) const override;
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index bfc8a86cbfe36..802a0da6f897c 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -76,6 +76,10 @@ static cl::opt<bool> EnablePExtSIMDCodeGen(
              "where only partial codegen is currently supported)"),
     cl::init(false), cl::Hidden);
 
+static cl::opt<bool> SaveCSREarly("riscv-save-csrs-early",
+                                  cl::desc("Save CSRs early"), cl::init(false),
+                                  cl::Hidden);
+
 void RISCVSubtarget::anchor() {}
 
 RISCVSubtarget &
@@ -296,3 +300,5 @@ bool RISCVSubtarget::useMIPSLoadStorePairs() const {
 bool RISCVSubtarget::useMIPSCCMovInsn() const {
   return UseMIPSCCMovInsn && HasVendorXMIPSCMov;
 }
+
+bool RISCVSubtarget::savesCSRsEarly() const { return SaveCSREarly; }
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index fea9aae716fb5..56647c8485ce5 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -442,6 +442,8 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
 
   void overridePostRASchedPolicy(MachineSchedPolicy &Policy,
                                  const SchedRegion &Region) const override;
+
+  bool savesCSRsEarly() const override;
 };
 } // namespace llvm
 
diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
new file mode 100644
index 0000000000000..0027e18f905e7
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
@@ -0,0 +1,79 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=false \
+# RUN: -o - | FileCheck %s -check-prefixes=NOEARLYCSR
+#
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=false \
+# RUN: --riscv-user-defined-uncond-prolog-csrs="x19" \
+# RUN: -o - | FileCheck %s -check-prefixes=NOEARLYCSR_UNCONDX19
+#
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=true \
+# RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR
+#
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=true \
+# RUN: --riscv-user-defined-uncond-prolog-csrs="x19" \
+# RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR_UNCONDX19
+---
+name:            test0
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; NOEARLYCSR-LABEL: name: test0
+    ; NOEARLYCSR: liveins: $x10, $x18
+    ; NOEARLYCSR-NEXT: {{  $}}
+    ; NOEARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; NOEARLYCSR-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.0)
+    ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR-NEXT: $x18 = LD $x10, 0 :: (load (s64))
+    ; NOEARLYCSR-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR-NEXT: PseudoRET
+    ;
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: test0
+    ; NOEARLYCSR_UNCONDX19: liveins: $x10, $x18, $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 0 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x10, 0 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 0 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoRET
+    ;
+    ; EARLYCSR-LABEL: name: test0
+    ; EARLYCSR: liveins: $x10
+    ; EARLYCSR-NEXT: {{  $}}
+    ; EARLYCSR-NEXT: $x18 = LD $x10, 0 :: (load (s64))
+    ; EARLYCSR-NEXT: PseudoRET
+    ;
+    ; EARLYCSR_UNCONDX19-LABEL: name: test0
+    ; EARLYCSR_UNCONDX19: liveins: $x10, $x19
+    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x10, 0 :: (load (s64))
+    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19-NEXT: PseudoRET
+    $x18 = LD $x10, 0 :: (load (s64))
+    PseudoRET
+
+...

>From 4a2191ee4b057c83795fed4f1e524db86c3abffa Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 6 Nov 2025 04:56:01 -0800
Subject: [PATCH 02/11] commnet out "LLVM_DEPRECATED" because otherwise build
 fails.

---
 llvm/include/llvm/CodeGen/TargetFrameLowering.h | 4 ++--
 llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp    | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index f2a4489ee9f66..bbd285dfcf227 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -382,8 +382,8 @@ class LLVM_ABI TargetFrameLowering {
   /// This method should not be called by any passes outside of PEI, because
   /// it may change state passed in by \p MF and \p RS. The preferred
   /// interface outside PEI is getCalleeSaves.
-  LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
-                  "determinePrologCalleeSaves")
+  // LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
+  //                 "determinePrologCalleeSaves")
   virtual void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
                                     RegScavenger *RS = nullptr) const;
 
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index 95144109ed14a..b56794f4c88d7 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -93,8 +93,8 @@ void TargetFrameLowering::getCalleeSaves(const MachineFunction &MF,
     CalleeSaves.set(Info.getReg());
 }
 
-LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
-                "determinePrologCalleeSaves")
+// LLVM_DEPRECATED("Use determinePrologCalleeSaves instead",
+//                 "determinePrologCalleeSaves")
 void TargetFrameLowering::determineCalleeSaves(MachineFunction &MF,
                                                BitVector &SavedRegs,
                                                RegScavenger *RS) const {

>From b88a57b16df5301482b9c17eec383a96c62351f8 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 6 Nov 2025 05:12:24 -0800
Subject: [PATCH 03/11] fixed a warning

---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 476117a8acd42..125e7bd59c46d 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1592,7 +1592,6 @@ static MCRegister getRVVBaseRegister(const RISCVRegisterInfo &TRI,
 void RISCVFrameLowering::determineUncondPrologCalleeSaves(
     MachineFunction &MF, const MCPhysReg *CSRegs,
     BitVector &UncondPrologCSRs) const {
-  const RISCVRegisterInfo *TRI = STI.getRegisterInfo();
 
   StringRef RegString(UserDefinedUncondPrologCSRs);
   SmallVector<llvm::StringRef, 4> RegNames;

>From a0e17b004c2f02b88e04b2182b72947040e1b2d4 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 6 Nov 2025 06:07:19 -0800
Subject: [PATCH 04/11] updated test

---
 .../CodeGen/RISCV/determine-callee-saves.mir  | 46 ++++++++++++-------
 1 file changed, 30 insertions(+), 16 deletions(-)

diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
index 0027e18f905e7..ada45b172ce9a 100644
--- a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
@@ -19,6 +19,10 @@
 ---
 name:            test0
 tracksRegLiveness: true
+stack:
+  - { id: 0, name: '', type: default, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
 body:             |
   bb.0.entry:
     liveins: $x10
@@ -27,10 +31,10 @@ body:             |
     ; NOEARLYCSR-NEXT: {{  $}}
     ; NOEARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; NOEARLYCSR-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.0)
+    ; NOEARLYCSR-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.1)
     ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
-    ; NOEARLYCSR-NEXT: $x18 = LD $x10, 0 :: (load (s64))
-    ; NOEARLYCSR-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; NOEARLYCSR-NEXT: $x18 = LD $x2, 4 :: (load (s64))
+    ; NOEARLYCSR-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
     ; NOEARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
@@ -39,25 +43,32 @@ body:             |
     ; NOEARLYCSR_UNCONDX19-LABEL: name: test0
     ; NOEARLYCSR_UNCONDX19: liveins: $x10, $x18, $x19
     ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.0)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 0 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x10, 0 :: (load (s64))
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 0 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 12 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; NOEARLYCSR_UNCONDX19-NEXT: PseudoRET
     ;
     ; EARLYCSR-LABEL: name: test0
     ; EARLYCSR: liveins: $x10
     ; EARLYCSR-NEXT: {{  $}}
-    ; EARLYCSR-NEXT: $x18 = LD $x10, 0 :: (load (s64))
+    ; EARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR-NEXT: BUNDLE implicit-def $x18 {
+    ; EARLYCSR-NEXT:   $x18 = LD %stack.0, 0 :: (load (s64))
+    ; EARLYCSR-NEXT:   CFI_INSTRUCTION register $x18, $x18
+    ; EARLYCSR-NEXT: }
+    ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR-NEXT: PseudoRET
     ;
     ; EARLYCSR_UNCONDX19-LABEL: name: test0
@@ -65,15 +76,18 @@ body:             |
     ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
     ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x10, 0 :: (load (s64))
-    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: BUNDLE implicit-def $x18 {
+    ; EARLYCSR_UNCONDX19-NEXT:   $x18 = LD %stack.0, 0 :: (load (s64))
+    ; EARLYCSR_UNCONDX19-NEXT:   CFI_INSTRUCTION register $x18, $x18
+    ; EARLYCSR_UNCONDX19-NEXT: }
+    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
     ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR_UNCONDX19-NEXT: PseudoRET
-    $x18 = LD $x10, 0 :: (load (s64))
+    $x18 = LD %stack.0, 0 :: (load (s64))
     PseudoRET
 
 ...

>From c4e025e40aa87d7bd2462ac101c57e7c2d2481e6 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Fri, 7 Nov 2025 06:16:38 -0800
Subject: [PATCH 05/11] addressed review comments and fixed a test.

---
 llvm/include/llvm/CodeGen/TargetFrameLowering.h    |  2 +-
 llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp       |  6 +++---
 llvm/test/CodeGen/RISCV/determine-callee-saves.mir | 10 ++--------
 3 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index bbd285dfcf227..a85c1903cde68 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -390,7 +390,7 @@ class LLVM_ABI TargetFrameLowering {
   /// Return the list of registers which must be preserved by the function: the
   /// value on exit must be the same as the value on entry. A register from this
   /// list does not need to be saved / reloaded if the function did not use it.
-  const MCPhysReg *getMustPreserveRegisters(MachineFunction &MF) const;
+  const MCPhysReg *getMustPreserveRegisters(const MachineFunction &MF) const;
 
   /// This method determines which of the registers reported by
   /// getMustPreserveRegisters() must be saved in prolog and reloaded in epilog
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index b56794f4c88d7..8765837c76161 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -102,7 +102,7 @@ void TargetFrameLowering::determineCalleeSaves(MachineFunction &MF,
 }
 
 const MCPhysReg *
-TargetFrameLowering::getMustPreserveRegisters(MachineFunction &MF) const {
+TargetFrameLowering::getMustPreserveRegisters(const MachineFunction &MF) const {
   const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
   // In Naked functions we aren't going to save any registers.
   if (MF.getFunction().hasFnAttribute(Attribute::Naked))
@@ -146,11 +146,11 @@ void TargetFrameLowering::determineUncondPrologCalleeSaves(
 
 void TargetFrameLowering::determineEarlyCalleeSaves(
     MachineFunction &MF, BitVector &EarlyCSRs) const {
-  const auto &ST = MF.getSubtarget();
+  const TargetSubtargetInfo &ST = MF.getSubtarget();
   if (!ST.savesCSRsEarly())
     return;
 
-  const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
+  const TargetRegisterInfo &TRI = *ST.getRegisterInfo();
   // Get the callee saved register list...
   const MCPhysReg *CSRegs = getMustPreserveRegisters(MF);
   // Early exit if there are no callee saved registers.
diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
index ada45b172ce9a..6d4e238f4fbef 100644
--- a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
@@ -63,10 +63,7 @@ body:             |
     ; EARLYCSR-NEXT: {{  $}}
     ; EARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; EARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR-NEXT: BUNDLE implicit-def $x18 {
-    ; EARLYCSR-NEXT:   $x18 = LD %stack.0, 0 :: (load (s64))
-    ; EARLYCSR-NEXT:   CFI_INSTRUCTION register $x18, $x18
-    ; EARLYCSR-NEXT: }
+    ; EARLYCSR-NEXT: $x18 = LD $x2, 12 :: (load (s64))
     ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR-NEXT: PseudoRET
@@ -78,10 +75,7 @@ body:             |
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: BUNDLE implicit-def $x18 {
-    ; EARLYCSR_UNCONDX19-NEXT:   $x18 = LD %stack.0, 0 :: (load (s64))
-    ; EARLYCSR_UNCONDX19-NEXT:   CFI_INSTRUCTION register $x18, $x18
-    ; EARLYCSR_UNCONDX19-NEXT: }
+    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 4 :: (load (s64))
     ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
     ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16

>From 0c07d9382b05636778dcd57e561d6a3bb0310627 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at gmail.com>
Date: Mon, 15 Dec 2025 17:07:14 -0500
Subject: [PATCH 06/11] addressed review comments.

---
 .../CodeGen/RISCV/determine-callee-saves.mir  | 144 ++++++++++++++++--
 1 file changed, 134 insertions(+), 10 deletions(-)

diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
index 6d4e238f4fbef..dca4a235b823e 100644
--- a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
@@ -16,31 +16,51 @@
 # RUN: --riscv-save-csrs-early=true \
 # RUN: --riscv-user-defined-uncond-prolog-csrs="x19" \
 # RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR_UNCONDX19
+--- |
+  define void @use_callee_saved() {
+  entry:
+    ret void
+  }
+
+  define void @make_a_call() {
+  entry:
+    tail call void @bar()
+    ret void
+  }
+
+  define void @make_a_call_and_use_callee_saved() {
+  entry:
+    tail call void @bar()
+    ret void
+  }
+
+  declare void @bar(...)
+...
 ---
-name:            test0
+name: use_callee_saved
 tracksRegLiveness: true
 stack:
-  - { id: 0, name: '', type: default, offset: 0, size: 4, alignment: 4,
+  - { id: 0, name: '', type: default, offset: 0, size: 8, alignment: 8,
       stack-id: default, callee-saved-register: '', callee-saved-restored: true,
       debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
 body:             |
   bb.0.entry:
     liveins: $x10
-    ; NOEARLYCSR-LABEL: name: test0
+    ; NOEARLYCSR-LABEL: name: use_callee_saved
     ; NOEARLYCSR: liveins: $x10, $x18
     ; NOEARLYCSR-NEXT: {{  $}}
     ; NOEARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
     ; NOEARLYCSR-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.1)
     ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
-    ; NOEARLYCSR-NEXT: $x18 = LD $x2, 4 :: (load (s64))
+    ; NOEARLYCSR-NEXT: $x18 = LD $x2, 0 :: (load (s64))
     ; NOEARLYCSR-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
     ; NOEARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; NOEARLYCSR-NEXT: PseudoRET
     ;
-    ; NOEARLYCSR_UNCONDX19-LABEL: name: test0
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: use_callee_saved
     ; NOEARLYCSR_UNCONDX19: liveins: $x10, $x18, $x19
     ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
     ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
@@ -49,7 +69,7 @@ body:             |
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 12 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
     ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
     ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
@@ -58,24 +78,24 @@ body:             |
     ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; NOEARLYCSR_UNCONDX19-NEXT: PseudoRET
     ;
-    ; EARLYCSR-LABEL: name: test0
+    ; EARLYCSR-LABEL: name: use_callee_saved
     ; EARLYCSR: liveins: $x10
     ; EARLYCSR-NEXT: {{  $}}
     ; EARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; EARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR-NEXT: $x18 = LD $x2, 12 :: (load (s64))
+    ; EARLYCSR-NEXT: $x18 = LD $x2, 8 :: (load (s64))
     ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR-NEXT: PseudoRET
     ;
-    ; EARLYCSR_UNCONDX19-LABEL: name: test0
+    ; EARLYCSR_UNCONDX19-LABEL: name: use_callee_saved
     ; EARLYCSR_UNCONDX19: liveins: $x10, $x19
     ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
     ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 4 :: (load (s64))
+    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 0 :: (load (s64))
     ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
     ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
     ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
@@ -85,3 +105,107 @@ body:             |
     PseudoRET
 
 ...
+---
+name: make_a_call
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    ; NOEARLYCSR-LABEL: name: make_a_call
+    ; NOEARLYCSR: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call
+    ; NOEARLYCSR_UNCONDX19: liveins: $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR-LABEL: name: make_a_call
+    ; EARLYCSR: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR_UNCONDX19-LABEL: name: make_a_call
+    ; EARLYCSR_UNCONDX19: liveins: $x19
+    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+
+...
+---
+name: make_a_call_and_use_callee_saved
+tracksRegLiveness: true
+stack:
+  - { id: 0, name: '', type: default, offset: 0, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:             |
+  bb.0.entry:
+    ; NOEARLYCSR-LABEL: name: make_a_call_and_use_callee_saved
+    ; NOEARLYCSR: liveins: $x18
+    ; NOEARLYCSR-NEXT: {{  $}}
+    ; NOEARLYCSR-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; NOEARLYCSR-NEXT: frame-setup SD killed $x18, $x2, 8 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR-NEXT: $x18 = LD $x2, 0 :: (load (s64))
+    ; NOEARLYCSR-NEXT: $x18 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call_and_use_callee_saved
+    ; NOEARLYCSR_UNCONDX19: liveins: $x18, $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR-LABEL: name: make_a_call_and_use_callee_saved
+    ; EARLYCSR: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR-NEXT: $x18 = LD $x2, 8 :: (load (s64))
+    ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR_UNCONDX19-LABEL: name: make_a_call_and_use_callee_saved
+    ; EARLYCSR_UNCONDX19: liveins: $x19
+    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 0 :: (load (s64))
+    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    $x18 = LD %stack.0, 0 :: (load (s64))
+    PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+...

>From 296d15e40097559c97564be6d79209839b34c12b Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at gmail.com>
Date: Tue, 16 Dec 2025 10:09:07 -0500
Subject: [PATCH 07/11] addressed review comments.

---
 llvm/include/llvm/CodeGen/TargetFrameLowering.h | 2 +-
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index a85c1903cde68..b04d9a10e3caf 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -394,7 +394,7 @@ class LLVM_ABI TargetFrameLowering {
 
   /// This method determines which of the registers reported by
   /// getMustPreserveRegisters() must be saved in prolog and reloaded in epilog
-  /// regardless of wheather or not they were modified by the function.
+  /// regardless of whether or not they were modified by the function.
   virtual void
   determineUncondPrologCalleeSaves(MachineFunction &MF, const MCPhysReg *CSRegs,
                                    BitVector &UncondPrologCSRs) const;
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 125e7bd59c46d..e8b4c608f158c 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1604,7 +1604,7 @@ void RISCVFrameLowering::determineUncondPrologCalleeSaves(
       std::string msg;
       raw_string_ostream Msg(msg);
       Msg << "Couldn't parse register: " << Name << "\n";
-      report_fatal_error(Twine(msg));
+      reportFatalUsageError(Twine(msg));
     }
     UncondPrologCSRs.set(Reg.id());
   }

>From 07410fc6b279479fc26fda4ad27e7087362c53c3 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at gmail.com>
Date: Tue, 16 Dec 2025 11:08:53 -0500
Subject: [PATCH 08/11] addressed review commits.

---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index e8b4c608f158c..1aee650d6d0b3 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -34,9 +34,9 @@ using namespace llvm;
 
 static cl::opt<std::string> UserDefinedUncondPrologCSRs(
     "riscv-user-defined-uncond-prolog-csrs",
-    cl::desc("Comma-separated list of registerst that have to be saved / "
+    cl::desc("Comma-separated list of registers that have to be saved / "
              "restored in prolog / epilog. Used for testing only"),
-    cl::init(""), cl::Hidden);
+    cl::init(""), cl::ReallyHidden);
 
 static Align getABIStackAlignment(RISCVABI::ABI ABI) {
   if (ABI == RISCVABI::ABI_ILP32E)

>From 0ffd8aed91c7e6d7e8d80fe622335e55a3b500f7 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at gmail.com>
Date: Tue, 16 Dec 2025 15:21:33 -0500
Subject: [PATCH 09/11] addressed review comments.

---
 llvm/include/llvm/CodeGen/TargetSubtargetInfo.h | 6 ++++--
 llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp    | 4 ++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
index 57978c8585a6a..bc3682fcb1be9 100644
--- a/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetSubtargetInfo.h
@@ -365,8 +365,10 @@ class LLVM_ABI TargetSubtargetInfo : public MCSubtargetInfo {
 
   virtual bool isRegisterReservedByUser(Register R) const { return false; }
 
-  // Return true if the target can ensure before PrologEpilogInsertion that
-  // callee-saved registers are preserved.
+  /// Returns `true` is the target must ensure that the registers returned by
+  /// TargetFrameLowering::determineEarlyCalleeSaves are preserved before
+  /// `PrologEpilogInserter`. Also see comments for
+  /// `TargetFrameLowering::determinePrologCalleeSaves`.
   virtual bool savesCSRsEarly() const { return false; }
 };
 } // end namespace llvm
diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
index 8765837c76161..2cde68c0d3a94 100644
--- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
+++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
@@ -137,7 +137,7 @@ void TargetFrameLowering::determineUncondPrologCalleeSaves(
   // Functions which call __builtin_unwind_init get all their registers saved.
   if (MF.callsUnwindInit()) {
     for (unsigned i = 0; CSRegs[i]; ++i) {
-      unsigned Reg = CSRegs[i];
+      MCRegister Reg = CSRegs[i];
       UncondPrologCSRs.set(Reg);
     }
   }
@@ -162,7 +162,7 @@ void TargetFrameLowering::determineEarlyCalleeSaves(
 
   EarlyCSRs.resize(TRI.getNumRegs());
   for (unsigned i = 0; CSRegs[i]; ++i) {
-    unsigned Reg = CSRegs[i];
+    MCRegister Reg = CSRegs[i];
     if (!UncondPrologCSRs[Reg])
       EarlyCSRs.set(Reg);
   }

>From e659d8306d529f8f0bedd3002599e99215d6c214 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at gmail.com>
Date: Tue, 16 Dec 2025 15:29:38 -0500
Subject: [PATCH 10/11] use twine to report error.

---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 1aee650d6d0b3..0e8d935eefd04 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1601,10 +1601,7 @@ void RISCVFrameLowering::determineUncondPrologCalleeSaves(
     if (!Reg)
       Reg = MatchRegisterAltName(Name);
     if (!Reg) {
-      std::string msg;
-      raw_string_ostream Msg(msg);
-      Msg << "Couldn't parse register: " << Name << "\n";
-      reportFatalUsageError(Twine(msg));
+      reportFatalUsageError(Twine("Couldn't parse register: ") + Name + "\n");
     }
     UncondPrologCSRs.set(Reg.id());
   }

>From 9a4a2d7b88c58286f0904869bcf273e5746577e6 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at qti.qualcomm.com>
Date: Wed, 7 Jan 2026 14:21:35 -0800
Subject: [PATCH 11/11] use function attribute instead of cli option

---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp  |  13 +-
 .../RISCV/determine-callee-saves-uncond.mir   | 152 ++++++++++++++++++
 .../CodeGen/RISCV/determine-callee-saves.mir  | 106 +-----------
 3 files changed, 162 insertions(+), 109 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/determine-callee-saves-uncond.mir

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 0e8d935eefd04..39b8c784de52e 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -32,12 +32,6 @@
 
 using namespace llvm;
 
-static cl::opt<std::string> UserDefinedUncondPrologCSRs(
-    "riscv-user-defined-uncond-prolog-csrs",
-    cl::desc("Comma-separated list of registers that have to be saved / "
-             "restored in prolog / epilog. Used for testing only"),
-    cl::init(""), cl::ReallyHidden);
-
 static Align getABIStackAlignment(RISCVABI::ABI ABI) {
   if (ABI == RISCVABI::ABI_ILP32E)
     return Align(4);
@@ -1593,7 +1587,12 @@ void RISCVFrameLowering::determineUncondPrologCalleeSaves(
     MachineFunction &MF, const MCPhysReg *CSRegs,
     BitVector &UncondPrologCSRs) const {
 
-  StringRef RegString(UserDefinedUncondPrologCSRs);
+  const Function &F = MF.getFunction();
+  if (!F.hasFnAttribute("riscv-user-defined-uncond-prolog-csrs"))
+    return;
+  const AttributeList &Attrs = F.getAttributes();
+  StringRef RegString = Attrs.getFnAttr("riscv-user-defined-uncond-prolog-csrs")
+                            .getValueAsString();
   SmallVector<llvm::StringRef, 4> RegNames;
   llvm::SplitString(RegString, RegNames, ",");
   for (auto &Name : RegNames) {
diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves-uncond.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves-uncond.mir
new file mode 100644
index 0000000000000..a1ab469ddc4c0
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves-uncond.mir
@@ -0,0 +1,152 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=false \
+# RUN: -o - | FileCheck %s  -check-prefixes=NOEARLYCSR_UNCONDX19
+
+# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
+# RUN: --riscv-save-csrs-early=true \
+# RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR_UNCONDX19-
+
+--- |
+  define void @use_callee_saved() #0 {
+  entry:
+    ret void
+  }
+
+  define void @make_a_call() #0 {
+  entry:
+    tail call void @bar()
+    ret void
+  }
+
+  define void @make_a_call_and_use_callee_saved() #0 {
+  entry:
+    tail call void @bar()
+    ret void
+  }
+
+  attributes #0 = {"riscv-user-defined-uncond-prolog-csrs"="x19"}
+  declare void @bar(...)
+...
+---
+name: use_callee_saved
+tracksRegLiveness: true
+stack:
+  - { id: 0, name: '', type: default, offset: 0, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: use_callee_saved
+    ; NOEARLYCSR_UNCONDX19: liveins: $x10, $x18, $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoRET
+    ;
+    ; EARLYCSR_UNCONDX19--LABEL: name: use_callee_saved
+    ; EARLYCSR_UNCONDX19-: liveins: $x10, $x19
+    ; EARLYCSR_UNCONDX19--NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19--NEXT: $x18 = LD $x2, 0 :: (load (s64))
+    ; EARLYCSR_UNCONDX19--NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19--NEXT: PseudoRET
+    $x18 = LD %stack.0, 0 :: (load (s64))
+    PseudoRET
+
+...
+---
+name: make_a_call
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call
+    ; NOEARLYCSR_UNCONDX19: liveins: $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR_UNCONDX19--LABEL: name: make_a_call
+    ; EARLYCSR_UNCONDX19-: liveins: $x19
+    ; EARLYCSR_UNCONDX19--NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19--NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19--NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+
+...
+---
+name: make_a_call_and_use_callee_saved
+tracksRegLiveness: true
+stack:
+  - { id: 0, name: '', type: default, offset: 0, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:             |
+  bb.0.entry:
+    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call_and_use_callee_saved
+    ; NOEARLYCSR_UNCONDX19: liveins: $x18, $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
+    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    ;
+    ; EARLYCSR_UNCONDX19--LABEL: name: make_a_call_and_use_callee_saved
+    ; EARLYCSR_UNCONDX19-: liveins: $x19
+    ; EARLYCSR_UNCONDX19--NEXT: {{  $}}
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-setup ADDI $x2, -16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
+    ; EARLYCSR_UNCONDX19--NEXT: $x18 = LD $x2, 0 :: (load (s64))
+    ; EARLYCSR_UNCONDX19--NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; EARLYCSR_UNCONDX19--NEXT: $x2 = frame-destroy ADDI $x2, 16
+    ; EARLYCSR_UNCONDX19--NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; EARLYCSR_UNCONDX19--NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+    $x18 = LD %stack.0, 0 :: (load (s64))
+    PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
+...
diff --git a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
index dca4a235b823e..68372d7342ad0 100644
--- a/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
+++ b/llvm/test/CodeGen/RISCV/determine-callee-saves.mir
@@ -1,21 +1,13 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+
 # RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
 # RUN: --riscv-save-csrs-early=false \
-# RUN: -o - | FileCheck %s -check-prefixes=NOEARLYCSR
-#
-# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
-# RUN: --riscv-save-csrs-early=false \
-# RUN: --riscv-user-defined-uncond-prolog-csrs="x19" \
-# RUN: -o - | FileCheck %s -check-prefixes=NOEARLYCSR_UNCONDX19
-#
+# RUN: -o - | FileCheck %s  -check-prefixes=NOEARLYCSR
+
 # RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
 # RUN: --riscv-save-csrs-early=true \
 # RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR
-#
-# RUN: llc %s -mtriple=riscv64 -run-pass=prologepilog \
-# RUN: --riscv-save-csrs-early=true \
-# RUN: --riscv-user-defined-uncond-prolog-csrs="x19" \
-# RUN: -o - | FileCheck %s -check-prefixes=EARLYCSR_UNCONDX19
+
 --- |
   define void @use_callee_saved() {
   entry:
@@ -60,24 +52,6 @@ body:             |
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; NOEARLYCSR-NEXT: PseudoRET
     ;
-    ; NOEARLYCSR_UNCONDX19-LABEL: name: use_callee_saved
-    ; NOEARLYCSR_UNCONDX19: liveins: $x10, $x18, $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoRET
-    ;
     ; EARLYCSR-LABEL: name: use_callee_saved
     ; EARLYCSR: liveins: $x10
     ; EARLYCSR-NEXT: {{  $}}
@@ -87,20 +61,6 @@ body:             |
     ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR-NEXT: PseudoRET
-    ;
-    ; EARLYCSR_UNCONDX19-LABEL: name: use_callee_saved
-    ; EARLYCSR_UNCONDX19: liveins: $x10, $x19
-    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 0 :: (load (s64))
-    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; EARLYCSR_UNCONDX19-NEXT: PseudoRET
     $x18 = LD %stack.0, 0 :: (load (s64))
     PseudoRET
 
@@ -113,34 +73,8 @@ body:             |
     ; NOEARLYCSR-LABEL: name: make_a_call
     ; NOEARLYCSR: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
     ;
-    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call
-    ; NOEARLYCSR_UNCONDX19: liveins: $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
-    ;
     ; EARLYCSR-LABEL: name: make_a_call
     ; EARLYCSR: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
-    ;
-    ; EARLYCSR_UNCONDX19-LABEL: name: make_a_call
-    ; EARLYCSR_UNCONDX19: liveins: $x19
-    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.0)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.0)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; EARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
     PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
 
 ...
@@ -167,24 +101,6 @@ body:             |
     ; NOEARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; NOEARLYCSR-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
     ;
-    ; NOEARLYCSR_UNCONDX19-LABEL: name: make_a_call_and_use_callee_saved
-    ; NOEARLYCSR_UNCONDX19: liveins: $x18, $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x18, $x2, 24 :: (store (s64) into %stack.1)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 16 :: (store (s64) into %stack.2)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -8
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -16
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 8 :: (load (s64))
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x18 = frame-destroy LD $x2, 24 :: (load (s64) from %stack.1)
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 16 :: (load (s64) from %stack.2)
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; NOEARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 32
-    ; NOEARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; NOEARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
-    ;
     ; EARLYCSR-LABEL: name: make_a_call_and_use_callee_saved
     ; EARLYCSR: $x2 = frame-setup ADDI $x2, -16
     ; EARLYCSR-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
@@ -192,20 +108,6 @@ body:             |
     ; EARLYCSR-NEXT: $x2 = frame-destroy ADDI $x2, 16
     ; EARLYCSR-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
     ; EARLYCSR-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
-    ;
-    ; EARLYCSR_UNCONDX19-LABEL: name: make_a_call_and_use_callee_saved
-    ; EARLYCSR_UNCONDX19: liveins: $x19
-    ; EARLYCSR_UNCONDX19-NEXT: {{  $}}
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-setup ADDI $x2, -16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup SD killed $x19, $x2, 8 :: (store (s64) into %stack.1)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -8
-    ; EARLYCSR_UNCONDX19-NEXT: $x18 = LD $x2, 0 :: (load (s64))
-    ; EARLYCSR_UNCONDX19-NEXT: $x19 = frame-destroy LD $x2, 8 :: (load (s64) from %stack.1)
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; EARLYCSR_UNCONDX19-NEXT: $x2 = frame-destroy ADDI $x2, 16
-    ; EARLYCSR_UNCONDX19-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; EARLYCSR_UNCONDX19-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
     $x18 = LD %stack.0, 0 :: (load (s64))
     PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32d_lp64d, implicit $x2
 ...



More information about the llvm-commits mailing list