[llvm] [RISCV64] liveness analysis (PR #167454)

via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 16 12:25:43 PST 2025


https://github.com/hiraditya updated https://github.com/llvm/llvm-project/pull/167454

>From 061d856cd8514d4be23380e34c3138c00a6d25ca Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Mon, 10 Nov 2025 20:49:02 -0800
Subject: [PATCH 01/12] [RISCV64] Add live variable analysis pass

---
 llvm/lib/Target/RISCV/CMakeLists.txt         |   1 +
 llvm/lib/Target/RISCV/RISCV.h                |   3 +
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp | 428 +++++++++++++++++++
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp |   3 +
 4 files changed, 435 insertions(+)
 create mode 100644 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp

diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt
index e9088a4d9275c..a6bf75cfe9e60 100644
--- a/llvm/lib/Target/RISCV/CMakeLists.txt
+++ b/llvm/lib/Target/RISCV/CMakeLists.txt
@@ -51,6 +51,7 @@ add_llvm_target(RISCVCodeGen
   RISCVISelLowering.cpp
   RISCVLandingPadSetup.cpp
   RISCVLateBranchOpt.cpp
+  RISCVLiveVariables.cpp
   RISCVLoadStoreOptimizer.cpp
   RISCVMachineFunctionInfo.cpp
   RISCVMakeCompressible.cpp
diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h
index 51e8e8574ed15..ec90e9ff02d32 100644
--- a/llvm/lib/Target/RISCV/RISCV.h
+++ b/llvm/lib/Target/RISCV/RISCV.h
@@ -97,6 +97,9 @@ void initializeRISCVLoadStoreOptPass(PassRegistry &);
 FunctionPass *createRISCVZacasABIFixPass();
 void initializeRISCVZacasABIFixPass(PassRegistry &);
 
+FunctionPass *createRISCVLiveVariablesPass();
+void initializeRISCVLiveVariablesPass(PassRegistry &);
+
 InstructionSelector *
 createRISCVInstructionSelector(const RISCVTargetMachine &,
                                const RISCVSubtarget &,
diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
new file mode 100644
index 0000000000000..b8eb6614470b9
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -0,0 +1,428 @@
+//===- RISCVLiveVariables.cpp - Live Variable Analysis for RISC-V --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a live variable analysis pass for the RISC-V backend.
+// The pass computes liveness information for virtual and physical registers
+// in RISC-V machine functions, optimized for RV64 (64-bit RISC-V architecture).
+//
+// The analysis performs a backward dataflow analysis to compute:
+// - Live-in sets: Registers that are live at the entry of a basic block
+// - Live-out sets: Registers that are live at the exit of a basic block
+// - Kill points: Instructions where a register's last use occurs
+// - Def points: Instructions where a register is defined
+//
+//===----------------------------------------------------------------------===//
+
+#include "RISCV.h"
+#include "RISCVInstrInfo.h"
+#include "RISCVSubtarget.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "riscv-live-variables"
+#define RISCV_LIVE_VARIABLES_NAME "RISC-V Live Variable Analysis"
+
+STATISTIC(NumLiveRegsAtEntry, "Number of registers live at function entry");
+STATISTIC(NumLiveRegsTotal, "Total number of live registers across all blocks");
+
+namespace {
+
+/// LivenessInfo - Stores liveness information for a basic block
+/// TODO: Optimize storage using BitVectors for large register sets.
+struct LivenessInfo {
+  /// Registers that are live into this block
+  /// LiveIn[B] = Use[B] U (LiveOut[B] - Def[B])
+  std::set<Register> LiveIn;
+
+  /// Registers that are live out of this block.
+  /// LiveOut[B] = U LiveIns[∀ Succ(B)].
+  std::set<Register> LiveOut;
+
+  /// Registers that are defined in this block
+  std::set<Register> Def;
+
+  /// Registers that are used in this block before being defined (if at all).
+  std::set<Register> Use;
+};
+
+class RISCVLiveVariables : public MachineFunctionPass {
+public:
+  static char ID;
+
+  RISCVLiveVariables() : MachineFunctionPass(ID) {
+    initializeRISCVLiveVariablesPass(*PassRegistry::getPassRegistry());
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesAll();
+    MachineFunctionPass::getAnalysisUsage(AU);
+  }
+
+  StringRef getPassName() const override { return RISCV_LIVE_VARIABLES_NAME; }
+
+  /// Returns the set of registers live at the entry of the given basic block
+  const std::set<Register> &getLiveInSet(const MachineBasicBlock *MBB) const {
+    auto It = BlockLiveness.find(MBB);
+    assert(It != BlockLiveness.end() && "Block not analyzed");
+    return It->second.LiveIn;
+  }
+
+  /// Returns the set of registers live at the exit of the given basic block
+  const std::set<Register> &getLiveOutSet(const MachineBasicBlock *MBB) const {
+    auto It = BlockLiveness.find(MBB);
+    assert(It != BlockLiveness.end() && "Block not analyzed");
+    return It->second.LiveOut;
+  }
+
+  /// Check if a register is live at a specific instruction
+  bool isLiveAt(Register Reg, const MachineInstr &MI) const;
+
+  /// Print liveness information for debugging
+  void print(raw_ostream &OS, const Module *M = nullptr) const override;
+
+private:
+  /// Compute local liveness information (Use and Def sets) for each block
+  void computeLocalLiveness(MachineFunction &MF);
+
+  /// Compute global liveness information (LiveIn and LiveOut sets)
+  void computeGlobalLiveness(MachineFunction &MF);
+
+  /// Process a single instruction to extract def/use information
+  void processInstruction(const MachineInstr &MI, LivenessInfo &Info,
+                          const TargetRegisterInfo *TRI);
+
+  /// Check if a register is allocatable (relevant for liveness tracking)
+  bool isTrackableRegister(Register Reg, const TargetRegisterInfo *TRI,
+                           const MachineRegisterInfo *MRI) const;
+
+  /// Liveness information for each basic block
+  DenseMap<const MachineBasicBlock *, LivenessInfo> BlockLiveness;
+
+  /// Cached pointer to MachineRegisterInfo
+  const MachineRegisterInfo *MRI;
+
+  /// Cached pointer to TargetRegisterInfo
+  const TargetRegisterInfo *TRI;
+};
+
+} // end anonymous namespace
+
+char RISCVLiveVariables::ID = 0;
+
+INITIALIZE_PASS(RISCVLiveVariables, DEBUG_TYPE, RISCV_LIVE_VARIABLES_NAME,
+                false, true)
+
+FunctionPass *llvm::createRISCVLiveVariablesPass() {
+  return new RISCVLiveVariables();
+}
+
+bool RISCVLiveVariables::isTrackableRegister(
+    Register Reg, const TargetRegisterInfo *TRI,
+    const MachineRegisterInfo *MRI) const {
+  // Track virtual registers
+  if (Reg.isVirtual())
+    return true;
+
+  // For physical registers, only track allocatable ones
+  if (Reg.isPhysical()) {
+    // Check if register is allocatable
+    if (!TRI->isInAllocatableClass(Reg))
+      return false;
+
+    // Track general purpose registers (X0-X31)
+    // Track floating point registers (F0-F31)
+    // Track vector registers for RVV if present
+    return true;
+  }
+
+  return false;
+}
+
+void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
+                                             LivenessInfo &Info,
+                                             const TargetRegisterInfo *TRI) {
+  // Process all operands
+  for (const MachineOperand &MO : MI.operands()) {
+    if (!MO.isReg() || !MO.getReg())
+      continue;
+
+    Register Reg = MO.getReg();
+
+    // Skip non-trackable registers
+    if (!isTrackableRegister(Reg, TRI, MRI))
+      continue;
+
+    if (MO.isDef()) {
+      // This is a definition
+      Info.Def.insert(Reg);
+
+      // Also handle sub-registers for physical registers
+      if (Reg.isPhysical()) {
+        for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+             SubRegs.isValid(); ++SubRegs) {
+          Info.Def.insert(*SubRegs);
+        }
+      }
+    }
+
+    if (MO.isUse()) {
+      // This is a use - only add to Use set if not already defined in this block
+      if (Info.Def.find(Reg) == Info.Def.end()) {
+        Info.Use.insert(Reg);
+
+        // Also handle sub-registers for physical registers
+        if (Reg.isPhysical()) {
+          for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+               SubRegs.isValid(); ++SubRegs) {
+            if (Info.Def.find(*SubRegs) == Info.Def.end()) {
+              Info.Use.insert(*SubRegs);
+            }
+          }
+        }
+      }
+    }
+
+    // Handle implicit operands (like condition codes, stack pointer updates)
+    if (MO.isImplicit() && MO.isUse() && Reg.isPhysical()) {
+      if (Info.Def.find(Reg) == Info.Def.end()) {
+        Info.Use.insert(Reg);
+      }
+    }
+  }
+
+  // Handle RegMasks (from calls) - they kill all non-preserved registers
+  for (const MachineOperand &MO : MI.operands()) {
+    if (MO.isRegMask()) {
+      const uint32_t *RegMask = MO.getRegMask();
+
+      // Iterate through all physical registers
+      for (unsigned PhysReg = 1; PhysReg < TRI->getNumRegs(); ++PhysReg) {
+        // If the register is not preserved by this mask, it's clobbered
+        if (!MachineOperand::clobbersPhysReg(RegMask, PhysReg))
+          continue;
+
+        // Mark as defined (clobbered)
+        if (isTrackableRegister(Register(PhysReg), TRI, MRI)) {
+          Info.Def.insert(Register(PhysReg));
+        }
+      }
+    }
+  }
+}
+
+void RISCVLiveVariables::computeLocalLiveness(MachineFunction &MF) {
+  LLVM_DEBUG(dbgs() << "Computing local liveness for " << MF.getName() << "\n");
+
+  // Process each basic block
+  for (MachineBasicBlock &MBB : MF) {
+    LivenessInfo &Info = BlockLiveness[&MBB];
+    Info.Def.clear();
+    Info.Use.clear();
+
+    // Process instructions in forward order to build Use and Def sets
+    for (const MachineInstr &MI : MBB) {
+      // Skip debug instructions and meta instructions
+      if (MI.isDebugInstr() || MI.isMetaInstruction())
+        continue;
+
+      processInstruction(MI, Info, TRI);
+    }
+
+    LLVM_DEBUG({
+      dbgs() << "Block " << MBB.getName() << ":\n";
+      dbgs() << "  Use: ";
+      for (Register Reg : Info.Use)
+        dbgs() << printReg(Reg, TRI) << " ";
+      dbgs() << "\n  Def: ";
+      for (Register Reg : Info.Def)
+        dbgs() << printReg(Reg, TRI) << " ";
+      dbgs() << "\n";
+    });
+  }
+}
+
+void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
+  LLVM_DEBUG(dbgs() << "Computing global liveness (fixed-point iteration)\n");
+
+  bool Changed = true;
+  unsigned Iterations = 0;
+
+  // Iterate until we reach a fixed point
+  // Live-out[B] = Union of Live-in[S] for all successors S of B
+  // Live-in[B] = Use[B] Union (Live-out[B] - Def[B])
+  while (Changed) {
+    Changed = false;
+    ++Iterations;
+
+    // Process blocks in reverse post-order for better convergence
+    ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
+
+    for (MachineBasicBlock *MBB : RPOT) {
+      LivenessInfo &Info = BlockLiveness[MBB];
+      std::set<Register> OldLiveIn = Info.LiveIn;
+      std::set<Register> NewLiveOut;
+
+      // Compute Live-out: Union of Live-in of all successors
+      for (MachineBasicBlock *Succ : MBB->successors()) {
+        LivenessInfo &SuccInfo = BlockLiveness[Succ];
+        NewLiveOut.insert(SuccInfo.LiveIn.begin(), SuccInfo.LiveIn.end());
+      }
+
+      Info.LiveOut = NewLiveOut;
+
+      // Compute Live-in: Use Union (Live-out - Def)
+      std::set<Register> NewLiveIn = Info.Use;
+
+      for (Register Reg : Info.LiveOut) {
+        if (Info.Def.find(Reg) == Info.Def.end()) {
+          NewLiveIn.insert(Reg);
+        }
+      }
+
+      Info.LiveIn = NewLiveIn;
+
+      // Check if anything changed
+      if (Info.LiveIn != OldLiveIn) {
+        Changed = true;
+      }
+    }
+  }
+
+  LLVM_DEBUG(dbgs() << "Global liveness converged in " << Iterations
+                    << " iterations\n");
+
+  // Update statistics
+  for (auto &Entry : BlockLiveness) {
+    NumLiveRegsTotal += Entry.second.LiveIn.size();
+  }
+
+  // Count live registers at function entry
+  if (!MF.empty()) {
+    const MachineBasicBlock &EntryBB = MF.front();
+    NumLiveRegsAtEntry += BlockLiveness[&EntryBB].LiveIn.size();
+  }
+}
+
+bool RISCVLiveVariables::isLiveAt(Register Reg,
+                                   const MachineInstr &MI) const {
+  const MachineBasicBlock *MBB = MI.getParent();
+  auto It = BlockLiveness.find(MBB);
+
+  if (It == BlockLiveness.end())
+    return false;
+
+  const LivenessInfo &Info = It->second;
+
+  // A register is live at an instruction if:
+  // 1. It's in the live-in set of the block, OR
+  // 2. It's defined before this instruction and used after (not yet killed)
+
+  // Check if it's live-in to the block
+  if (Info.LiveIn.count(Reg))
+    return true;
+
+  // For a more precise answer, we'd need to track instruction-level liveness
+  // For now, conservatively return true if it's in live-out and not killed yet
+  if (Info.LiveOut.count(Reg))
+    return true;
+
+  return false;
+}
+
+bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
+  if (skipFunction(MF.getFunction()))
+    return false;
+
+  const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
+
+  // Verify we're targeting RV64
+  if (!Subtarget.is64Bit()) {
+    LLVM_DEBUG(dbgs() << "Warning: RISCVLiveVariables optimized for RV64, "
+                      << "but running on RV32\n");
+    return false;
+  }
+
+  MRI = &MF.getRegInfo();
+  TRI = Subtarget.getRegisterInfo();
+
+  LLVM_DEBUG(dbgs() << "***** RISC-V Live Variable Analysis *****\n");
+  LLVM_DEBUG(dbgs() << "Function: " << MF.getName() << "\n");
+  LLVM_DEBUG(dbgs() << "Target: RV" << (Subtarget.is64Bit() ? "64" : "32")
+                    << "\n");
+
+  // Clear any previous analysis
+  BlockLiveness.clear();
+
+  // Step 1: Compute local liveness (Use and Def sets)
+  computeLocalLiveness(MF);
+
+  // Step 2: Compute global liveness (LiveIn and LiveOut sets)
+  computeGlobalLiveness(MF);
+
+  LLVM_DEBUG({
+    dbgs() << "\n***** Final Liveness Information *****\n";
+    print(dbgs());
+  });
+
+  // This is an analysis pass, it doesn't modify the function
+  return false;
+}
+
+void RISCVLiveVariables::print(raw_ostream &OS, const Module *M) const {
+  OS << "RISC-V Live Variable Analysis Results:\n";
+  OS << "======================================\n\n";
+
+  for (const auto &Entry : BlockLiveness) {
+    const MachineBasicBlock *MBB = Entry.first;
+    const LivenessInfo &Info = Entry.second;
+
+    OS << "Block: " << MBB->getName() << " (Number: " << MBB->getNumber()
+       << ")\n";
+
+    OS << "  Live-In:  { ";
+    for (Register Reg : Info.LiveIn) {
+      OS << printReg(Reg, TRI) << " ";
+    }
+    OS << "}\n";
+
+    OS << "  Live-Out: { ";
+    for (Register Reg : Info.LiveOut) {
+      OS << printReg(Reg, TRI) << " ";
+    }
+    OS << "}\n";
+
+    OS << "  Use:      { ";
+    for (Register Reg : Info.Use) {
+      OS << printReg(Reg, TRI) << " ";
+    }
+    OS << "}\n";
+
+    OS << "  Def:      { ";
+    for (Register Reg : Info.Def) {
+      OS << printReg(Reg, TRI) << " ";
+    }
+    OS << "}\n\n";
+  }
+}
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 16ef67da83128..788c117283da0 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -115,6 +115,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
   initializeRISCVPostLegalizerCombinerPass(*PR);
   initializeKCFIPass(*PR);
   initializeRISCVDeadRegisterDefinitionsPass(*PR);
+  initializeRISCVLiveVariablesPass(*PR);
   initializeRISCVLateBranchOptPass(*PR);
   initializeRISCVMakeCompressibleOptPass(*PR);
   initializeRISCVGatherScatterLoweringPass(*PR);
@@ -558,6 +559,7 @@ void RISCVPassConfig::addPreEmitPass() {
   addPass(createRISCVIndirectBranchTrackingPass());
   addPass(&BranchRelaxationPassID);
   addPass(createRISCVMakeCompressibleOptPass());
+  addPass(createRISCVLiveVariablesPass());
 }
 
 void RISCVPassConfig::addPreEmitPass2() {
@@ -587,6 +589,7 @@ void RISCVPassConfig::addMachineSSAOptimization() {
   TargetPassConfig::addMachineSSAOptimization();
 
   if (TM->getTargetTriple().isRISCV64()) {
+    addPass(createRISCVLiveVariablesPass());
     addPass(createRISCVOptWInstrsPass());
   }
 }

>From 46816773e0605626fd1076aa8665f74b6125edf8 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Mon, 10 Nov 2025 20:49:02 -0800
Subject: [PATCH 02/12] Add tests

---
 .../CodeGen/RISCV/live-variables-basic.mir    | 148 +++++++++
 .../CodeGen/RISCV/live-variables-calls.mir    | 214 +++++++++++++
 .../RISCV/live-variables-edge-cases.mir       | 285 ++++++++++++++++++
 .../CodeGen/RISCV/live-variables-loops.mir    | 238 +++++++++++++++
 .../CodeGen/RISCV/live-variables-rv64.mir     | 188 ++++++++++++
 5 files changed, 1073 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-basic.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-calls.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-loops.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-rv64.mir

diff --git a/llvm/test/CodeGen/RISCV/live-variables-basic.mir b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
new file mode 100644
index 0000000000000..741a8eafa1c27
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
@@ -0,0 +1,148 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+#
+# Test basic live variable analysis with simple control flow and basic blocks
+
+--- |
+  define i64 @test_simple_add(i64 %a, i64 %b) {
+  entry:
+    %sum = add i64 %a, %b
+    ret i64 %sum
+  }
+
+  define i64 @test_if_then_else(i64 %a, i64 %b) {
+  entry:
+    %cmp = icmp sgt i64 %a, %b
+    br i1 %cmp, label %then, label %else
+
+  then:
+    %mul = mul i64 %a, %b
+    br label %end
+
+  else:
+    %sub = sub i64 %a, %b
+    br label %end
+
+  end:
+    %result = phi i64 [ %mul, %then ], [ %sub, %else ]
+    ret i64 %result
+  }
+
+  define i64 @test_multiple_uses(i64 %a, i64 %b, i64 %c) {
+  entry:
+    %t1 = add i64 %a, %b
+    %t2 = mul i64 %t1, %c
+    %t3 = sub i64 %t2, %a
+    %t4 = add i64 %t3, %b
+    ret i64 %t4
+  }
+...
+---
+name:            test_simple_add
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_simple_add
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+    ; Test that %0 and %1 are live-in, used once, and %2 is defined
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = ADD %0, %1
+    $x10 = COPY %2
+    PseudoRET implicit $x10
+...
+---
+name:            test_if_then_else
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_if_then_else
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: successors
+    ; CHECK-NEXT: liveins: $x10, $x11
+    ; Test that %0 and %1 are live across multiple blocks
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = SLT %1, %0
+    BEQ %2, $x0, %bb.2
+
+  bb.1.then:
+    ; CHECK: bb.1.then:
+    ; %0 and %1 should be live-in here
+    %3:gpr = MUL %0, %1
+    PseudoBR %bb.3
+
+  bb.2.else:
+    ; CHECK: bb.2.else:
+    ; %0 and %1 should be live-in here
+    %4:gpr = SUB %0, %1
+    PseudoBR %bb.3
+
+  bb.3.end:
+    ; CHECK: bb.3.end:
+    ; Either %3 or %4 should be live-in (phi sources)
+    %5:gpr = PHI %3, %bb.1, %4, %bb.2
+    $x10 = COPY %5
+    PseudoRET implicit $x10
+...
+---
+name:            test_multiple_uses
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+  - { reg: '$x12', virtual-reg: '%2' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11, $x12
+    ; CHECK-LABEL: name: test_multiple_uses
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11, $x12
+    ; Test that variables with multiple uses have correct liveness ranges
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = COPY $x12
+    ; %0 used here and later
+    %3:gpr = ADD %0, %1
+    ; %3 used in next instruction
+    %4:gpr = MUL %3, %2
+    ; %0 used again here (should still be live)
+    %5:gpr = SUB %4, %0
+    ; %1 used again here
+    %6:gpr = ADD %5, %1
+    $x10 = COPY %6
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-calls.mir b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
new file mode 100644
index 0000000000000..6f80317dbf293
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
@@ -0,0 +1,214 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+#
+# Test live variable analysis with function calls and register clobbering
+# Function calls clobber caller-saved registers, which affects liveness
+
+--- |
+  declare i64 @external_func(i64, i64)
+  declare void @void_func(i64)
+
+  define i64 @test_call_simple(i64 %a, i64 %b) {
+  entry:
+    %result = call i64 @external_func(i64 %a, i64 %b)
+    ret i64 %result
+  }
+
+  define i64 @test_call_with_live_values(i64 %a, i64 %b, i64 %c) {
+  entry:
+    %sum1 = add i64 %a, %b
+    %result = call i64 @external_func(i64 %sum1, i64 %c)
+    ; %b is live across the call
+    %final = add i64 %result, %b
+    ret i64 %final
+  }
+
+  define i64 @test_multiple_calls(i64 %a, i64 %b) {
+  entry:
+    %r1 = call i64 @external_func(i64 %a, i64 %b)
+    call void @void_func(i64 %r1)
+    %r2 = call i64 @external_func(i64 %r1, i64 %b)
+    ret i64 %r2
+  }
+
+  define i64 @test_call_with_spill(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i64 %g, i64 %h) {
+  entry:
+    %sum = add i64 %a, %b
+    %result = call i64 @external_func(i64 %sum, i64 %c)
+    ; Many values live across call - may require spilling
+    %t1 = add i64 %result, %d
+    %t2 = add i64 %t1, %e
+    %t3 = add i64 %t2, %f
+    %t4 = add i64 %t3, %g
+    %t5 = add i64 %t4, %h
+    ret i64 %t5
+  }
+...
+---
+name:            test_call_simple
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_call_simple
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    $x10 = COPY %0
+    $x11 = COPY %1
+    ; Call clobbers many registers per calling convention
+    PseudoCALL target-flags(riscv-call) @external_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    %2:gpr = COPY $x10
+    $x10 = COPY %2
+    PseudoRET implicit $x10
+...
+---
+name:            test_call_with_live_values
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+  - { reg: '$x12', virtual-reg: '%2' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11, $x12
+    ; CHECK-LABEL: name: test_call_with_live_values
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11, $x12
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = COPY $x12
+    %3:gpr = ADD %0, %1
+    ; %1 must remain live across this call
+    $x10 = COPY %3
+    $x11 = COPY %2
+    PseudoCALL target-flags(riscv-call) @external_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    %4:gpr = COPY $x10
+    ; %1 is used again here after the call
+    %5:gpr = ADD %4, %1
+    $x10 = COPY %5
+    PseudoRET implicit $x10
+...
+---
+name:            test_multiple_calls
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_multiple_calls
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+
+    ; First call
+    $x10 = COPY %0
+    $x11 = COPY %1
+    PseudoCALL target-flags(riscv-call) @external_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    %2:gpr = COPY $x10
+
+    ; Second call (void)
+    $x10 = COPY %2
+    PseudoCALL target-flags(riscv-call) @void_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10
+
+    ; Third call - %2 and %1 are both live here
+    $x10 = COPY %2
+    $x11 = COPY %1
+    PseudoCALL target-flags(riscv-call) @external_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    %3:gpr = COPY $x10
+
+    $x10 = COPY %3
+    PseudoRET implicit $x10
+...
+---
+name:            test_call_with_spill
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+  - { id: 8, class: gpr }
+  - { id: 9, class: gpr }
+  - { id: 10, class: gpr }
+  - { id: 11, class: gpr }
+  - { id: 12, class: gpr }
+  - { id: 13, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+  - { reg: '$x12', virtual-reg: '%2' }
+  - { reg: '$x13', virtual-reg: '%3' }
+  - { reg: '$x14', virtual-reg: '%4' }
+  - { reg: '$x15', virtual-reg: '%5' }
+  - { reg: '$x16', virtual-reg: '%6' }
+  - { reg: '$x17', virtual-reg: '%7' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17
+    ; CHECK-LABEL: name: test_call_with_spill
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17
+    ; Many registers live across call - tests register pressure
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = COPY $x12
+    %3:gpr = COPY $x13
+    %4:gpr = COPY $x14
+    %5:gpr = COPY $x15
+    %6:gpr = COPY $x16
+    %7:gpr = COPY $x17
+
+    %8:gpr = ADD %0, %1
+
+    ; Call with many live values - %3, %4, %5, %6, %7 all live across
+    $x10 = COPY %8
+    $x11 = COPY %2
+    PseudoCALL target-flags(riscv-call) @external_func, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    %9:gpr = COPY $x10
+
+    ; All these values should have been kept live
+    %10:gpr = ADD %9, %3
+    %11:gpr = ADD %10, %4
+    %12:gpr = ADD %11, %5
+    %13:gpr = ADD %12, %6
+    %14:gpr = ADD %13, %7
+
+    $x10 = COPY %14
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
new file mode 100644
index 0000000000000..4e04de8a310a4
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
@@ -0,0 +1,285 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+#
+# Test live variable analysis edge cases and special scenarios
+# Including: dead code, unreachable blocks, critical edges, and complex phi nodes
+
+--- |
+  define i64 @test_dead_code(i64 %a, i64 %b) {
+  entry:
+    %dead = add i64 %a, %b
+    ret i64 %a
+  }
+
+  define i64 @test_critical_edge(i64 %a, i64 %b, i64 %c) {
+  entry:
+    %cmp1 = icmp sgt i64 %a, 0
+    br i1 %cmp1, label %then, label %check2
+
+  check2:
+    %cmp2 = icmp sgt i64 %b, 0
+    br i1 %cmp2, label %then, label %else
+
+  then:
+    %mul = mul i64 %a, %b
+    br label %end
+
+  else:
+    %sub = sub i64 %a, %c
+    br label %end
+
+  end:
+    %result = phi i64 [ %mul, %then ], [ %sub, %else ]
+    ret i64 %result
+  }
+
+  define i64 @test_complex_phi(i64 %a, i64 %b, i64 %c, i64 %d) {
+  entry:
+    %cmp1 = icmp sgt i64 %a, 0
+    br i1 %cmp1, label %path1, label %path2
+
+  path1:
+    %v1 = add i64 %a, %b
+    br label %merge
+
+  path2:
+    %cmp2 = icmp sgt i64 %c, 0
+    br i1 %cmp2, label %path2a, label %path2b
+
+  path2a:
+    %v2a = mul i64 %c, %d
+    br label %merge
+
+  path2b:
+    %v2b = sub i64 %c, %d
+    br label %merge
+
+  merge:
+    %result = phi i64 [ %v1, %path1 ], [ %v2a, %path2a ], [ %v2b, %path2b ]
+    ret i64 %result
+  }
+
+  define i64 @test_use_after_def(i64 %a) {
+  entry:
+    %v1 = add i64 %a, 1
+    %v2 = add i64 %v1, 2
+    %v3 = add i64 %v2, 3
+    %v4 = add i64 %v1, %v3
+    ret i64 %v4
+  }
+
+  define i64 @test_implicit_defs(i64 %a, i64 %b) {
+  entry:
+    %div = sdiv i64 %a, %b
+    %rem = srem i64 %a, %b
+    %sum = add i64 %div, %rem
+    ret i64 %sum
+  }
+...
+---
+name:            test_dead_code
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_dead_code
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+    ; %2 is dead - never used, should not affect liveness of inputs
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    ; Dead instruction - result never used
+    %2:gpr = ADD %0, %1
+    ; Only %0 should be live here
+    $x10 = COPY %0
+    PseudoRET implicit $x10
+...
+---
+name:            test_critical_edge
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+  - { reg: '$x12', virtual-reg: '%2' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11, $x12
+    ; CHECK-LABEL: name: test_critical_edge
+    ; CHECK: bb.0.entry:
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = COPY $x12
+    %3:gpr = SLTI %0, 1
+    BEQ %3, $x0, %bb.2
+
+  bb.1.check2:
+    ; CHECK: bb.1.check2:
+    ; %0, %1, %2 should all be live-in
+
+    %4:gpr = SLTI %1, 1
+    BEQ %4, $x0, %bb.3
+
+  bb.2.then:
+    ; CHECK: bb.2.then:
+    ; %0, %1 should be live-in (used in mul)
+
+    %5:gpr = MUL %0, %1
+    PseudoBR %bb.4
+
+  bb.3.else:
+    ; CHECK: bb.3.else:
+    ; %0, %2 should be live-in (used in sub)
+
+    %6:gpr = SUB %0, %2
+    PseudoBR %bb.4
+
+  bb.4.end:
+    ; CHECK: bb.4.end:
+    ; Either %5 or %6 should be live-in
+
+    %7:gpr = PHI %5, %bb.2, %6, %bb.3
+    $x10 = COPY %7
+    PseudoRET implicit $x10
+...
+---
+name:            test_complex_phi
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+  - { id: 8, class: gpr }
+  - { id: 9, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+  - { reg: '$x12', virtual-reg: '%2' }
+  - { reg: '$x13', virtual-reg: '%3' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11, $x12, $x13
+    ; CHECK-LABEL: name: test_complex_phi
+    ; CHECK: bb.0.entry:
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = COPY $x12
+    %3:gpr = COPY $x13
+    %4:gpr = SLTI %0, 1
+    BEQ %4, $x0, %bb.2
+
+  bb.1.path1:
+    ; CHECK: bb.1.path1:
+
+    %5:gpr = ADD %0, %1
+    PseudoBR %bb.5
+
+  bb.2.path2:
+    ; CHECK: bb.2.path2:
+    ; %2, %3 should be live-in
+
+    %6:gpr = SLTI %2, 1
+    BEQ %6, $x0, %bb.4
+
+  bb.3.path2a:
+    ; CHECK: bb.3.path2a:
+
+    %7:gpr = MUL %2, %3
+    PseudoBR %bb.5
+
+  bb.4.path2b:
+    ; CHECK: bb.4.path2b:
+
+    %8:gpr = SUB %2, %3
+    PseudoBR %bb.5
+
+  bb.5.merge:
+    ; CHECK: bb.5.merge:
+    ; One of %5, %7, %8 should be live-in
+
+    %9:gpr = PHI %5, %bb.1, %7, %bb.3, %8, %bb.4
+    $x10 = COPY %9
+    PseudoRET implicit $x10
+...
+---
+name:            test_use_after_def
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: test_use_after_def
+    ; CHECK: bb.0.entry:
+    ; Test that %1 remains live even after being used in %2
+
+    %0:gpr = COPY $x10
+    %1:gpr = ADDI %0, 1
+    %2:gpr = ADDI %1, 2
+    %3:gpr = ADDI %2, 3
+    ; %1 used again here - should have been kept live
+    %4:gpr = ADD %1, %3
+    $x10 = COPY %4
+    PseudoRET implicit $x10
+...
+---
+name:            test_implicit_defs
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_implicit_defs
+    ; CHECK: bb.0.entry:
+    ; Test handling of division which may have implicit defs/uses
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = DIV %0, %1
+    %3:gpr = REM %0, %1
+    %4:gpr = ADD %2, %3
+    $x10 = COPY %4
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-loops.mir b/llvm/test/CodeGen/RISCV/live-variables-loops.mir
new file mode 100644
index 0000000000000..05e24b33a1131
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-loops.mir
@@ -0,0 +1,238 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+#
+# Test live variable analysis with loops and backward edges
+# Loops create interesting liveness patterns with phi nodes and variables
+# that are live across backedges
+
+--- |
+  define i64 @test_simple_loop(i64 %n) {
+  entry:
+    br label %loop
+
+  loop:
+    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+    %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
+    %sum.next = add i64 %sum, %i
+    %i.next = add i64 %i, 1
+    %cmp = icmp slt i64 %i.next, %n
+    br i1 %cmp, label %loop, label %exit
+
+  exit:
+    ret i64 %sum.next
+  }
+
+  define i64 @test_nested_loop(i64 %n, i64 %m) {
+  entry:
+    br label %outer.loop
+
+  outer.loop:
+    %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+    %outer.sum = phi i64 [ 0, %entry ], [ %new.outer.sum, %outer.latch ]
+    br label %inner.loop
+
+  inner.loop:
+    %j = phi i64 [ 0, %outer.loop ], [ %j.next, %inner.loop ]
+    %inner.sum = phi i64 [ 0, %outer.loop ], [ %inner.sum.next, %inner.loop ]
+    %prod = mul i64 %i, %j
+    %inner.sum.next = add i64 %inner.sum, %prod
+    %j.next = add i64 %j, 1
+    %inner.cmp = icmp slt i64 %j.next, %m
+    br i1 %inner.cmp, label %inner.loop, label %outer.latch
+
+  outer.latch:
+    %new.outer.sum = add i64 %outer.sum, %inner.sum.next
+    %i.next = add i64 %i, 1
+    %outer.cmp = icmp slt i64 %i.next, %n
+    br i1 %outer.cmp, label %outer.loop, label %exit
+
+  exit:
+    ret i64 %new.outer.sum
+  }
+
+  define i64 @test_loop_with_invariant(i64 %n, i64 %k) {
+  entry:
+    %double_k = mul i64 %k, 2
+    br label %loop
+
+  loop:
+    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+    %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
+    ; double_k is loop-invariant and should be live throughout the loop
+    %scaled = mul i64 %i, %double_k
+    %sum.next = add i64 %sum, %scaled
+    %i.next = add i64 %i, 1
+    %cmp = icmp slt i64 %i.next, %n
+    br i1 %cmp, label %loop, label %exit
+
+  exit:
+    ret i64 %sum.next
+  }
+...
+---
+name:            test_simple_loop
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: test_simple_loop
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: successors
+    ; CHECK-NEXT: liveins: $x10
+
+    %0:gpr = COPY $x10
+    %1:gpr = ADDI $x0, 0
+    %2:gpr = ADDI $x0, 0
+    PseudoBR %bb.1
+
+  bb.1.loop:
+    ; CHECK: bb.1.loop:
+    ; %0 should be live-in (used in comparison)
+    ; %1 and %2 should be live-in from backedge or entry
+
+    %3:gpr = PHI %1, %bb.0, %5, %bb.1
+    %4:gpr = PHI %2, %bb.0, %6, %bb.1
+    %6:gpr = ADD %4, %3
+    %5:gpr = ADDI %3, 1
+    %7:gpr = SLT %5, %0
+    BNE %7, $x0, %bb.1
+
+  bb.2.exit:
+    ; CHECK: bb.2.exit:
+    ; %6 should be live-in
+
+    $x10 = COPY %6
+    PseudoRET implicit $x10
+...
+---
+name:            test_nested_loop
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+  - { id: 8, class: gpr }
+  - { id: 9, class: gpr }
+  - { id: 10, class: gpr }
+  - { id: 11, class: gpr }
+  - { id: 12, class: gpr }
+  - { id: 13, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_nested_loop
+    ; CHECK: bb.0.entry:
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = ADDI $x0, 0
+    %3:gpr = ADDI $x0, 0
+    PseudoBR %bb.1
+
+  bb.1.outer.loop:
+    ; CHECK: bb.1.outer.loop:
+    ; %0 and %1 should be live-in (loop bounds)
+
+    %4:gpr = PHI %2, %bb.0, %12, %bb.3
+    %5:gpr = PHI %3, %bb.0, %11, %bb.3
+    %6:gpr = ADDI $x0, 0
+    %7:gpr = ADDI $x0, 0
+    PseudoBR %bb.2
+
+  bb.2.inner.loop:
+    ; CHECK: bb.2.inner.loop:
+    ; %1, %4, %5 should be live-in
+
+    %8:gpr = PHI %6, %bb.1, %13, %bb.2
+    %9:gpr = PHI %7, %bb.1, %10, %bb.2
+    %10:gpr = MUL %4, %8
+    %10:gpr = ADD %9, %10
+    %13:gpr = ADDI %8, 1
+    %14:gpr = SLT %13, %1
+    BNE %14, $x0, %bb.2
+
+  bb.3.outer.latch:
+    ; CHECK: bb.3.outer.latch:
+    ; %0, %5, %10 should be live-in
+
+    %11:gpr = ADD %5, %10
+    %12:gpr = ADDI %4, 1
+    %15:gpr = SLT %12, %0
+    BNE %15, $x0, %bb.1
+
+  bb.4.exit:
+    ; CHECK: bb.4.exit:
+
+    $x10 = COPY %11
+    PseudoRET implicit $x10
+...
+---
+name:            test_loop_with_invariant
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+  - { id: 5, class: gpr }
+  - { id: 6, class: gpr }
+  - { id: 7, class: gpr }
+  - { id: 8, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_loop_with_invariant
+    ; CHECK: bb.0.entry:
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    %2:gpr = SLLI %1, 1
+    %3:gpr = ADDI $x0, 0
+    %4:gpr = ADDI $x0, 0
+    PseudoBR %bb.1
+
+  bb.1.loop:
+    ; CHECK: bb.1.loop:
+    ; %0 and %2 should be live-in (loop bound and invariant)
+    ; %2 is computed before loop but used in every iteration
+
+    %5:gpr = PHI %3, %bb.0, %7, %bb.1
+    %6:gpr = PHI %4, %bb.0, %8, %bb.1
+    %9:gpr = MUL %5, %2
+    %8:gpr = ADD %6, %9
+    %7:gpr = ADDI %5, 1
+    %10:gpr = SLT %7, %0
+    BNE %10, $x0, %bb.1
+
+  bb.2.exit:
+    ; CHECK: bb.2.exit:
+
+    $x10 = COPY %8
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-rv64.mir b/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
new file mode 100644
index 0000000000000..5b396373a60f4
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
@@ -0,0 +1,188 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+#
+# Test live variable analysis for RV64-specific scenarios
+# This includes 64-bit operations, wide registers, and RV64-specific instructions
+
+--- |
+  define i64 @test_64bit_ops(i64 %a, i64 %b) {
+  entry:
+    %shl = shl i64 %a, 32
+    %or = or i64 %shl, %b
+    ret i64 %or
+  }
+
+  define i64 @test_word_ops(i64 %a, i64 %b) {
+  entry:
+    %trunc_a = trunc i64 %a to i32
+    %trunc_b = trunc i64 %b to i32
+    %add = add i32 %trunc_a, %trunc_b
+    %ext = sext i32 %add to i64
+    ret i64 %ext
+  }
+
+  define i64 @test_mixed_width(i64 %a, i32 %b) {
+  entry:
+    %ext_b = sext i32 %b to i64
+    %mul = mul i64 %a, %ext_b
+    %trunc = trunc i64 %mul to i32
+    %final = sext i32 %trunc to i64
+    ret i64 %final
+  }
+
+  define double @test_float_64(double %a, double %b, i64 %selector) {
+  entry:
+    %cmp = icmp eq i64 %selector, 0
+    br i1 %cmp, label %then, label %else
+
+  then:
+    %add = fadd double %a, %b
+    br label %end
+
+  else:
+    %mul = fmul double %a, %b
+    br label %end
+
+  end:
+    %result = phi double [ %add, %then ], [ %mul, %else ]
+    ret double %result
+  }
+...
+---
+name:            test_64bit_ops
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_64bit_ops
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+    ; Test 64-bit shift and or operations
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    ; SLLI for 64-bit shift (RV64-specific immediate range)
+    %2:gpr = SLLI %0, 32
+    %3:gpr = OR %2, %1
+    $x10 = COPY %3
+    PseudoRET implicit $x10
+...
+---
+name:            test_word_ops
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11
+    ; CHECK-LABEL: name: test_word_ops
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11
+    ; Test RV64 W-suffix instructions (32-bit ops on 64-bit regs)
+
+    %0:gpr = COPY $x10
+    %1:gpr = COPY $x11
+    ; ADDW is RV64-specific: 32-bit add with sign-extension
+    %2:gpr = ADDW %0, %1
+    $x10 = COPY %2
+    PseudoRET implicit $x10
+...
+---
+name:            test_mixed_width
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gpr }
+  - { id: 1, class: gpr }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: gpr }
+liveins:
+  - { reg: '$x10', virtual-reg: '%0' }
+  - { reg: '$x11_w', virtual-reg: '%1' }
+body:             |
+  bb.0.entry:
+    liveins: $x10, $x11_w
+    ; CHECK-LABEL: name: test_mixed_width
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: liveins: $x10, $x11_w
+    ; Test mixed 32/64-bit operations
+
+    %0:gpr = COPY $x10
+    ; Sign-extend 32-bit value to 64-bit
+    %1:gpr = COPY $x11_w
+    %2:gpr = ADDIW %1, 0
+    %3:gpr = MUL %0, %2
+    ; Extract lower 32 bits and sign-extend
+    %4:gpr = ADDIW %3, 0
+    $x10 = COPY %4
+    PseudoRET implicit $x10
+...
+---
+name:            test_float_64
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: fpr64 }
+  - { id: 1, class: fpr64 }
+  - { id: 2, class: gpr }
+  - { id: 3, class: gpr }
+  - { id: 4, class: fpr64 }
+  - { id: 5, class: fpr64 }
+  - { id: 6, class: fpr64 }
+liveins:
+  - { reg: '$f10_d', virtual-reg: '%0' }
+  - { reg: '$f11_d', virtual-reg: '%1' }
+  - { reg: '$x10', virtual-reg: '%2' }
+body:             |
+  bb.0.entry:
+    liveins: $f10_d, $f11_d, $x10
+    ; CHECK-LABEL: name: test_float_64
+    ; CHECK: bb.0.entry:
+    ; CHECK-NEXT: successors
+    ; CHECK-NEXT: liveins: $f10_d, $f11_d, $x10
+    ; Test 64-bit floating point register liveness
+
+    %0:fpr64 = COPY $f10_d
+    %1:fpr64 = COPY $f11_d
+    %2:gpr = COPY $x10
+    %3:gpr = ADDI $x0, 0
+    BNE %2, %3, %bb.2
+
+  bb.1.then:
+    ; CHECK: bb.1.then:
+    ; %0 and %1 should be live-in (FP registers)
+
+    %4:fpr64 = FADD_D %0, %1, 7
+    PseudoBR %bb.3
+
+  bb.2.else:
+    ; CHECK: bb.2.else:
+    ; %0 and %1 should be live-in
+
+    %5:fpr64 = FMUL_D %0, %1, 7
+    PseudoBR %bb.3
+
+  bb.3.end:
+    ; CHECK: bb.3.end:
+    ; Either %4 or %5 should be live-in
+
+    %6:fpr64 = PHI %4, %bb.1, %5, %bb.2
+    $f10_d = COPY %6
+    PseudoRET implicit $f10_d
+...

>From 0a6ff00b36ae78f6e3cfef1d029ea41637f1c918 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Mon, 10 Nov 2025 20:49:02 -0800
Subject: [PATCH 03/12] WIP genset calculation

---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp  | 47 +++++++++----------
 .../CodeGen/RISCV/live-variables-basic.mir    | 10 ++++
 2 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index b8eb6614470b9..e044da47491fc 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -22,10 +22,7 @@
 #include "RISCVInstrInfo.h"
 #include "RISCVSubtarget.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DepthFirstIterator.h"
 #include "llvm/ADT/PostOrderIterator.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
@@ -59,7 +56,7 @@ struct LivenessInfo {
   std::set<Register> LiveOut;
 
   /// Registers that are defined in this block
-  std::set<Register> Def;
+  std::set<Register> Gen;
 
   /// Registers that are used in this block before being defined (if at all).
   std::set<Register> Use;
@@ -174,29 +171,16 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
     if (!isTrackableRegister(Reg, TRI, MRI))
       continue;
 
-    if (MO.isDef()) {
-      // This is a definition
-      Info.Def.insert(Reg);
-
-      // Also handle sub-registers for physical registers
-      if (Reg.isPhysical()) {
-        for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
-             SubRegs.isValid(); ++SubRegs) {
-          Info.Def.insert(*SubRegs);
-        }
-      }
-    }
-
     if (MO.isUse()) {
       // This is a use - only add to Use set if not already defined in this block
-      if (Info.Def.find(Reg) == Info.Def.end()) {
+      if (Info.Gen.find(Reg) == Info.Gen.end()) {
         Info.Use.insert(Reg);
 
         // Also handle sub-registers for physical registers
         if (Reg.isPhysical()) {
           for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
                SubRegs.isValid(); ++SubRegs) {
-            if (Info.Def.find(*SubRegs) == Info.Def.end()) {
+            if (Info.Gen.find(*SubRegs) == Info.Gen.end()) {
               Info.Use.insert(*SubRegs);
             }
           }
@@ -206,10 +190,23 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
 
     // Handle implicit operands (like condition codes, stack pointer updates)
     if (MO.isImplicit() && MO.isUse() && Reg.isPhysical()) {
-      if (Info.Def.find(Reg) == Info.Def.end()) {
+      if (Info.Gen.find(Reg) == Info.Gen.end()) {
         Info.Use.insert(Reg);
       }
     }
+
+    if (MO.isDef()) {
+      // This is a definition
+      Info.Gen.insert(Reg);
+
+      // Also handle sub-registers for physical registers
+      if (Reg.isPhysical()) {
+        for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+             SubRegs.isValid(); ++SubRegs) {
+          Info.Gen.insert(*SubRegs);
+        }
+      }
+    }
   }
 
   // Handle RegMasks (from calls) - they kill all non-preserved registers
@@ -225,7 +222,7 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
 
         // Mark as defined (clobbered)
         if (isTrackableRegister(Register(PhysReg), TRI, MRI)) {
-          Info.Def.insert(Register(PhysReg));
+          Info.Gen.insert(Register(PhysReg));
         }
       }
     }
@@ -238,7 +235,7 @@ void RISCVLiveVariables::computeLocalLiveness(MachineFunction &MF) {
   // Process each basic block
   for (MachineBasicBlock &MBB : MF) {
     LivenessInfo &Info = BlockLiveness[&MBB];
-    Info.Def.clear();
+    Info.Gen.clear();
     Info.Use.clear();
 
     // Process instructions in forward order to build Use and Def sets
@@ -267,7 +264,7 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
   LLVM_DEBUG(dbgs() << "Computing global liveness (fixed-point iteration)\n");
 
   bool Changed = true;
-  unsigned Iterations = 0;
+  [[maybe_unused]] unsigned Iterations = 0;
 
   // Iterate until we reach a fixed point
   // Live-out[B] = Union of Live-in[S] for all successors S of B
@@ -296,7 +293,7 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
       std::set<Register> NewLiveIn = Info.Use;
 
       for (Register Reg : Info.LiveOut) {
-        if (Info.Def.find(Reg) == Info.Def.end()) {
+        if (Info.Gen.find(Reg) == Info.Gen.end()) {
           NewLiveIn.insert(Reg);
         }
       }
@@ -420,7 +417,7 @@ void RISCVLiveVariables::print(raw_ostream &OS, const Module *M) const {
     OS << "}\n";
 
     OS << "  Def:      { ";
-    for (Register Reg : Info.Def) {
+    for (Register Reg : Info.Gen) {
       OS << printReg(Reg, TRI) << " ";
     }
     OS << "}\n\n";
diff --git a/llvm/test/CodeGen/RISCV/live-variables-basic.mir b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
index 741a8eafa1c27..2695f0bc15c02 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-basic.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
@@ -36,6 +36,16 @@
     %t4 = add i64 %t3, %b
     ret i64 %t4
   }
+
+  define i64 @test_gen_kill(i64 %a, i64 %b, i64 %c) {
+  entry:
+    %t1 = add i64 %a, %b
+    %t2 = mul i64 %t1, %c
+    %t3 = sub i64 %t2, %a
+    %t4 = add i64 %t3, %b
+    ret i64 %t4
+  }
+
 ...
 ---
 name:            test_simple_add

>From 8c4b19d700e31a8ba711ed137601ee60894766e9 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Fri, 14 Nov 2025 20:03:46 -0800
Subject: [PATCH 04/12] Verify liveness is working

---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp | 7 +++++++
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 4 ++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index e044da47491fc..3c78afaa9b2db 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -320,6 +320,13 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
     const MachineBasicBlock &EntryBB = MF.front();
     NumLiveRegsAtEntry += BlockLiveness[&EntryBB].LiveIn.size();
   }
+
+  for (auto &BB : MF) {
+    auto &computedLivein = BlockLiveness[&BB].LiveIn;
+    for (auto &LI : BB.getLiveIns()) {
+      assert(0 && computedLivein.count(LI.PhysReg));
+    }
+  }
 }
 
 bool RISCVLiveVariables::isLiveAt(Register Reg,
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 788c117283da0..3fae6e81a0ff7 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -559,7 +559,6 @@ void RISCVPassConfig::addPreEmitPass() {
   addPass(createRISCVIndirectBranchTrackingPass());
   addPass(&BranchRelaxationPassID);
   addPass(createRISCVMakeCompressibleOptPass());
-  addPass(createRISCVLiveVariablesPass());
 }
 
 void RISCVPassConfig::addPreEmitPass2() {
@@ -589,7 +588,6 @@ void RISCVPassConfig::addMachineSSAOptimization() {
   TargetPassConfig::addMachineSSAOptimization();
 
   if (TM->getTargetTriple().isRISCV64()) {
-    addPass(createRISCVLiveVariablesPass());
     addPass(createRISCVOptWInstrsPass());
   }
 }
@@ -621,6 +619,8 @@ void RISCVPassConfig::addPostRegAlloc() {
   if (TM->getOptLevel() != CodeGenOptLevel::None &&
       EnableRedundantCopyElimination)
     addPass(createRISCVRedundantCopyEliminationPass());
+
+  addPass(createRISCVLiveVariablesPass());
 }
 
 bool RISCVPassConfig::addILPOpts() {

>From 508a05108d50e466ecd3ce06cb8333c23419e031 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sat, 15 Nov 2025 04:45:49 -0800
Subject: [PATCH 05/12] Add flag for live variable analysis

Add an mir test
---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp  | 55 +++++++------
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp  |  8 +-
 .../CodeGen/RISCV/live-variables-basic.mir    |  2 +
 .../CodeGen/RISCV/live-variables-calls.mir    |  2 +
 .../CodeGen/RISCV/machine-live-variables.mir  | 79 +++++++++++++++++++
 5 files changed, 123 insertions(+), 23 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/machine-live-variables.mir

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index 3c78afaa9b2db..f7927b748f4a4 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -31,6 +31,7 @@
 #include "llvm/CodeGen/TargetRegisterInfo.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 #include <set>
 
@@ -99,6 +100,7 @@ class RISCVLiveVariables : public MachineFunctionPass {
   /// Print liveness information for debugging
   void print(raw_ostream &OS, const Module *M = nullptr) const override;
 
+  void verifyLiveness(MachineFunction &MF) const;
 private:
   /// Compute local liveness information (Use and Def sets) for each block
   void computeLocalLiveness(MachineFunction &MF);
@@ -160,7 +162,7 @@ bool RISCVLiveVariables::isTrackableRegister(
 void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
                                              LivenessInfo &Info,
                                              const TargetRegisterInfo *TRI) {
-  // Process all operands
+  std::vector<Register> GenVec;
   for (const MachineOperand &MO : MI.operands()) {
     if (!MO.isReg() || !MO.getReg())
       continue;
@@ -195,16 +197,16 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
       }
     }
 
-    if (MO.isDef()) {
-      // This is a definition
-      Info.Gen.insert(Reg);
+    if (MO.isDef()) // Collect defs for later processing.
+      GenVec.push_back(Reg);
+  }
 
-      // Also handle sub-registers for physical registers
-      if (Reg.isPhysical()) {
-        for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
-             SubRegs.isValid(); ++SubRegs) {
-          Info.Gen.insert(*SubRegs);
-        }
+  for (auto Reg : GenVec) {
+    Info.Gen.insert(Reg);
+    if (Reg.isPhysical()) {
+      for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+                SubRegs.isValid(); ++SubRegs) {
+            Info.Gen.insert(*SubRegs);
       }
     }
   }
@@ -253,7 +255,7 @@ void RISCVLiveVariables::computeLocalLiveness(MachineFunction &MF) {
       for (Register Reg : Info.Use)
         dbgs() << printReg(Reg, TRI) << " ";
       dbgs() << "\n  Def: ";
-      for (Register Reg : Info.Def)
+      for (Register Reg : Info.Gen)
         dbgs() << printReg(Reg, TRI) << " ";
       dbgs() << "\n";
     });
@@ -273,7 +275,7 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
     Changed = false;
     ++Iterations;
 
-    // Process blocks in reverse post-order for better convergence
+    // Process blocks in **post-order** for better convergence
     ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
 
     for (MachineBasicBlock *MBB : RPOT) {
@@ -320,13 +322,6 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
     const MachineBasicBlock &EntryBB = MF.front();
     NumLiveRegsAtEntry += BlockLiveness[&EntryBB].LiveIn.size();
   }
-
-  for (auto &BB : MF) {
-    auto &computedLivein = BlockLiveness[&BB].LiveIn;
-    for (auto &LI : BB.getLiveIns()) {
-      assert(0 && computedLivein.count(LI.PhysReg));
-    }
-  }
 }
 
 bool RISCVLiveVariables::isLiveAt(Register Reg,
@@ -355,8 +350,25 @@ bool RISCVLiveVariables::isLiveAt(Register Reg,
   return false;
 }
 
+void RISCVLiveVariables::verifyLiveness(MachineFunction &MF) const {
+  for (auto &BB : MF) {
+    auto BBLiveness = BlockLiveness.find(&BB);
+    assert(BBLiveness != BlockLiveness.end() && "Missing Liveness");
+    auto &ComputedLivein = BBLiveness->second.LiveIn;
+    for (auto &LI : BB.getLiveIns()) {
+      if (!ComputedLivein.count(LI.PhysReg)) {
+        LLVM_DEBUG(dbgs() << "Warning: Live-in register "
+                          << printReg(LI.PhysReg, TRI)
+                          << " missing from computed live-in set of block "
+                          << BB.getName() << "\n");
+        llvm_unreachable("Computed live-in set is inconsistent with MBB.");
+      }
+    }
+  }
+}
+
 bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
-  if (skipFunction(MF.getFunction()))
+  if (skipFunction(MF.getFunction()) || MF.empty())
     return false;
 
   const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
@@ -373,8 +385,6 @@ bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
 
   LLVM_DEBUG(dbgs() << "***** RISC-V Live Variable Analysis *****\n");
   LLVM_DEBUG(dbgs() << "Function: " << MF.getName() << "\n");
-  LLVM_DEBUG(dbgs() << "Target: RV" << (Subtarget.is64Bit() ? "64" : "32")
-                    << "\n");
 
   // Clear any previous analysis
   BlockLiveness.clear();
@@ -390,6 +400,7 @@ bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
     print(dbgs());
   });
 
+  verifyLiveness(MF);
   // This is an analysis pass, it doesn't modify the function
   return false;
 }
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 3fae6e81a0ff7..2201bd6a02696 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -103,6 +103,11 @@ static cl::opt<bool>
                            cl::desc("Enable Machine Pipeliner for RISC-V"),
                            cl::init(false), cl::Hidden);
 
+static cl::opt<bool>
+EnableRISCVLiveVariables("riscv-live-variables",
+                         cl::desc("Enable Live Variable Analysis for RISC-V"),
+                         cl::init(false), cl::Hidden);
+
 extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
   RegisterTargetMachine<RISCVTargetMachine> X(getTheRISCV32Target());
   RegisterTargetMachine<RISCVTargetMachine> Y(getTheRISCV64Target());
@@ -620,7 +625,8 @@ void RISCVPassConfig::addPostRegAlloc() {
       EnableRedundantCopyElimination)
     addPass(createRISCVRedundantCopyEliminationPass());
 
-  addPass(createRISCVLiveVariablesPass());
+  if (EnableRISCVLiveVariables)
+    addPass(createRISCVLiveVariablesPass());
 }
 
 bool RISCVPassConfig::addILPOpts() {
diff --git a/llvm/test/CodeGen/RISCV/live-variables-basic.mir b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
index 2695f0bc15c02..3339b04602953 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-basic.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
@@ -3,6 +3,8 @@
 #
 # Test basic live variable analysis with simple control flow and basic blocks
 
+# REQUIRES: Assertions
+
 --- |
   define i64 @test_simple_add(i64 %a, i64 %b) {
   entry:
diff --git a/llvm/test/CodeGen/RISCV/live-variables-calls.mir b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
index 6f80317dbf293..2a2738eaa3bad 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-calls.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
@@ -4,6 +4,8 @@
 # Test live variable analysis with function calls and register clobbering
 # Function calls clobber caller-saved registers, which affects liveness
 
+# REQUIRES: Assertions
+
 --- |
   declare i64 @external_func(i64, i64)
   declare void @void_func(i64)
diff --git a/llvm/test/CodeGen/RISCV/machine-live-variables.mir b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
new file mode 100644
index 0000000000000..734184964f50c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
@@ -0,0 +1,79 @@
+# RUN: llc -mtriple=riscv64 -verify-machineinstrs -run-pass=riscv-live-variables -debug < %s | FileCheck %s
+
+# REQUIRES: asserts
+
+;CHECK: Block:  (Number: 1)
+;CHECK:   Live-In:  { }
+;CHECK:   Live-Out: { }
+;CHECK:   Use:      { }
+;CHECK:   Def:      { $x10 $x11 $x12 $x13 $x14 $x15 $x16 $x10_h $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x10_w $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w }
+;CHECK: 
+;CHECK: Block:  (Number: 0)
+;CHECK:   Live-In:  { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
+;CHECK:   Live-Out: { }
+;CHECK:   Use:      { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
+;CHECK:   Def:      { $x10 $x11 $x12 $x10_h $x11_h $x12_h $x10_w $x11_w $x12_w }
+
+--- |
+
+  source_filename = "liveness-varargs.ll"
+  target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+  target triple = "riscv64"
+
+  declare void @notdead(ptr)
+
+  define i32 @va1(ptr %fmt, ...) {
+    %va = alloca ptr, align 8
+    call void @llvm.va_start.p0(ptr %va)
+    %argp.cur = load ptr, ptr %va, align 4
+    %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4
+    store ptr %argp.next, ptr %va, align 4
+    %1 = load i32, ptr %argp.cur, align 4
+    call void @llvm.va_end.p0(ptr %va)
+    ret i32 %1
+  }
+
+  declare void @llvm.va_start.p0(ptr) #1
+
+  declare void @llvm.va_end.p0(ptr) #1
+
+  declare void @llvm.va_copy.p0(ptr, ptr) #1
+
+  attributes #0 = { nounwind }
+  attributes #1 = { nocallback nofree nosync nounwind willreturn }
+...
+
+---
+name: va1
+tracksRegLiveness: true
+noVRegs: false
+fixedStack:
+  - { id: 0, offset: 8, size: 8, alignment: 8, isImmutable: false }
+  - { id: 1, offset: 56, size: 56, alignment: 8, isImmutable: true }
+  - { id: 2, offset: 64, size: 8, alignment: 16, isImmutable: true }
+stack:
+  - { id: 0, name: va, size: 2, alignment: 2, stack-id: default }
+
+body: |
+  bb.0:
+    liveins: $x11, $x12, $x13, $x14, $x15, $x16, $x17
+    SD killed renamable $x11, %fixed-stack.1, 0 :: (store (s64) into %fixed-stack.1)
+    SD killed renamable $x12, %fixed-stack.1, 8 :: (store (s64) into %fixed-stack.1 + 8)
+    SD killed renamable $x13, %fixed-stack.1, 16 :: (store (s64) into %fixed-stack.1 + 16)
+    SD killed renamable $x14, %fixed-stack.1, 24 :: (store (s64) into %fixed-stack.1 + 24)
+    renamable $x10 = ADDI %stack.0.va, 0
+    renamable $x11 = ADDI %fixed-stack.1, 0
+    SD killed renamable $x11, %stack.0.va, 0 :: (store (s64) into %ir.va)
+    renamable $x10 = LW killed renamable $x10, 4 :: (dereferenceable load (s32) from %ir.va + 4)
+    renamable $x11 = LWU %stack.0.va, 0 :: (dereferenceable load (s32) from %ir.va)
+    SD killed renamable $x15, %fixed-stack.1, 32 :: (store (s64) into %fixed-stack.1 + 32)
+    SD killed renamable $x16, %fixed-stack.1, 40 :: (store (s64) into %fixed-stack.1 + 40)
+    SD killed renamable $x17, %fixed-stack.1, 48 :: (store (s64) into %fixed-stack.1 + 48)
+    renamable $x10 = SLLI killed renamable $x10, 32
+    renamable $x10 = OR killed renamable $x10, killed renamable $x11
+    renamable $x11 = nuw nusw inbounds ADDI renamable $x10, 4
+    renamable $x12 = SRLI renamable $x11, 32
+    SW killed renamable $x11, %stack.0.va, 0 :: (store (s32) into %ir.va)
+    SW killed renamable $x12, %stack.0.va, 4 :: (store (s32) into %ir.va + 4)
+    renamable $x10 = LW killed renamable $x10, 0 :: (load (s32) from %ir.argp.cur)
+    PseudoRET implicit $x10

>From c844ebbfd2521665507c60219961a21063487e36 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sat, 15 Nov 2025 04:46:45 -0800
Subject: [PATCH 06/12] clang format

---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp  | 15 ++++++-------
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp  |  8 +++----
 .../CodeGen/RISCV/machine-live-variables.mir  | 21 ++++++++-----------
 3 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index f7927b748f4a4..5a9f192ad23ce 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -101,6 +101,7 @@ class RISCVLiveVariables : public MachineFunctionPass {
   void print(raw_ostream &OS, const Module *M = nullptr) const override;
 
   void verifyLiveness(MachineFunction &MF) const;
+
 private:
   /// Compute local liveness information (Use and Def sets) for each block
   void computeLocalLiveness(MachineFunction &MF);
@@ -160,8 +161,8 @@ bool RISCVLiveVariables::isTrackableRegister(
 }
 
 void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
-                                             LivenessInfo &Info,
-                                             const TargetRegisterInfo *TRI) {
+                                            LivenessInfo &Info,
+                                            const TargetRegisterInfo *TRI) {
   std::vector<Register> GenVec;
   for (const MachineOperand &MO : MI.operands()) {
     if (!MO.isReg() || !MO.getReg())
@@ -174,7 +175,8 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
       continue;
 
     if (MO.isUse()) {
-      // This is a use - only add to Use set if not already defined in this block
+      // This is a use - only add to Use set if not already defined in this
+      // block
       if (Info.Gen.find(Reg) == Info.Gen.end()) {
         Info.Use.insert(Reg);
 
@@ -205,8 +207,8 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
     Info.Gen.insert(Reg);
     if (Reg.isPhysical()) {
       for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
-                SubRegs.isValid(); ++SubRegs) {
-            Info.Gen.insert(*SubRegs);
+           SubRegs.isValid(); ++SubRegs) {
+        Info.Gen.insert(*SubRegs);
       }
     }
   }
@@ -324,8 +326,7 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
   }
 }
 
-bool RISCVLiveVariables::isLiveAt(Register Reg,
-                                   const MachineInstr &MI) const {
+bool RISCVLiveVariables::isLiveAt(Register Reg, const MachineInstr &MI) const {
   const MachineBasicBlock *MBB = MI.getParent();
   auto It = BlockLiveness.find(MBB);
 
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 2201bd6a02696..1adf930fbaa03 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -103,10 +103,10 @@ static cl::opt<bool>
                            cl::desc("Enable Machine Pipeliner for RISC-V"),
                            cl::init(false), cl::Hidden);
 
-static cl::opt<bool>
-EnableRISCVLiveVariables("riscv-live-variables",
-                         cl::desc("Enable Live Variable Analysis for RISC-V"),
-                         cl::init(false), cl::Hidden);
+static cl::opt<bool> EnableRISCVLiveVariables(
+    "riscv-enable-live-variables",
+    cl::desc("Enable Live Variable Analysis for RISC-V"), cl::init(false),
+    cl::Hidden);
 
 extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
   RegisterTargetMachine<RISCVTargetMachine> X(getTheRISCV32Target());
diff --git a/llvm/test/CodeGen/RISCV/machine-live-variables.mir b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
index 734184964f50c..51033658f0cfb 100644
--- a/llvm/test/CodeGen/RISCV/machine-live-variables.mir
+++ b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
@@ -1,18 +1,13 @@
-# RUN: llc -mtriple=riscv64 -verify-machineinstrs -run-pass=riscv-live-variables -debug < %s | FileCheck %s
+# RUN: llc -x mir -mtriple=riscv64 -verify-machineinstrs -run-pass=riscv-live-variables -debug < %s 2>&1 \
+# RUN: | FileCheck %s
 
 # REQUIRES: asserts
 
-;CHECK: Block:  (Number: 1)
-;CHECK:   Live-In:  { }
-;CHECK:   Live-Out: { }
-;CHECK:   Use:      { }
-;CHECK:   Def:      { $x10 $x11 $x12 $x13 $x14 $x15 $x16 $x10_h $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x10_w $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w }
-;CHECK: 
-;CHECK: Block:  (Number: 0)
-;CHECK:   Live-In:  { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
-;CHECK:   Live-Out: { }
-;CHECK:   Use:      { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
-;CHECK:   Def:      { $x10 $x11 $x12 $x10_h $x11_h $x12_h $x10_w $x11_w $x12_w }
+# CHECK: Block: (Number: 0)
+# CHECK:   Live-In:  { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
+# CHECK:   Live-Out: { }
+# CHECK:   Use:      { $x11 $x12 $x13 $x14 $x15 $x16 $x17 $x11_h $x12_h $x13_h $x14_h $x15_h $x16_h $x17_h $x11_w $x12_w $x13_w $x14_w $x15_w $x16_w $x17_w }
+# CHECK:   Def:      { $x10 $x11 $x12 $x10_h $x11_h $x12_h $x10_w $x11_w $x12_w }
 
 --- |
 
@@ -77,3 +72,5 @@ body: |
     SW killed renamable $x12, %stack.0.va, 4 :: (store (s32) into %ir.va + 4)
     renamable $x10 = LW killed renamable $x10, 0 :: (load (s32) from %ir.argp.cur)
     PseudoRET implicit $x10
+
+...

>From d06f78e7f6e8d9bbe9ffe96a3df271fb81121ffd Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sat, 15 Nov 2025 23:36:23 -0800
Subject: [PATCH 07/12] Update kill of individual instructions

---
 llvm/lib/Target/RISCV/RISCV.h                |  2 +-
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp | 94 ++++++++++++++++++--
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp |  4 +-
 3 files changed, 90 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h
index ec90e9ff02d32..77973adf22852 100644
--- a/llvm/lib/Target/RISCV/RISCV.h
+++ b/llvm/lib/Target/RISCV/RISCV.h
@@ -97,7 +97,7 @@ void initializeRISCVLoadStoreOptPass(PassRegistry &);
 FunctionPass *createRISCVZacasABIFixPass();
 void initializeRISCVZacasABIFixPass(PassRegistry &);
 
-FunctionPass *createRISCVLiveVariablesPass();
+FunctionPass *createRISCVLiveVariablesPass(bool PreRegAlloc);
 void initializeRISCVLiveVariablesPass(PassRegistry &);
 
 InstructionSelector *
diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index 5a9f192ad23ce..193070613a436 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -21,6 +21,7 @@
 #include "RISCV.h"
 #include "RISCVInstrInfo.h"
 #include "RISCVSubtarget.h"
+#include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/Statistic.h"
@@ -33,6 +34,8 @@
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
+
+#include <unordered_map>
 #include <set>
 
 using namespace llvm;
@@ -43,6 +46,10 @@ using namespace llvm;
 STATISTIC(NumLiveRegsAtEntry, "Number of registers live at function entry");
 STATISTIC(NumLiveRegsTotal, "Total number of live registers across all blocks");
 
+static cl::opt<bool> UpdateKills("riscv-liveness-update-kills",
+                                 cl::desc("Update kill flags"), cl::init(false),
+                                 cl::Hidden);
+
 namespace {
 
 /// LivenessInfo - Stores liveness information for a basic block
@@ -67,14 +74,14 @@ class RISCVLiveVariables : public MachineFunctionPass {
 public:
   static char ID;
 
-  RISCVLiveVariables() : MachineFunctionPass(ID) {
+  RISCVLiveVariables(bool PreRegAlloc)
+      : MachineFunctionPass(ID), PreRegAlloc(PreRegAlloc) {
     initializeRISCVLiveVariablesPass(*PassRegistry::getPassRegistry());
   }
 
   bool runOnMachineFunction(MachineFunction &MF) override;
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
-    AU.setPreservesAll();
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
@@ -102,6 +109,9 @@ class RISCVLiveVariables : public MachineFunctionPass {
 
   void verifyLiveness(MachineFunction &MF) const;
 
+  /// Mark operands that kill a register
+  void markKills(MachineFunction &MF);
+
 private:
   /// Compute local liveness information (Use and Def sets) for each block
   void computeLocalLiveness(MachineFunction &MF);
@@ -117,6 +127,13 @@ class RISCVLiveVariables : public MachineFunctionPass {
   bool isTrackableRegister(Register Reg, const TargetRegisterInfo *TRI,
                            const MachineRegisterInfo *MRI) const;
 
+  bool PreRegAlloc;
+  unsigned RegCounter = 0;
+
+  // PreRA can have large number of registers and basic block
+  // level liveness may be expensive without a bitvector representation.
+  std::unordered_map<unsigned, unsigned> TrackedRegisters;
+
   /// Liveness information for each basic block
   DenseMap<const MachineBasicBlock *, LivenessInfo> BlockLiveness;
 
@@ -134,8 +151,8 @@ char RISCVLiveVariables::ID = 0;
 INITIALIZE_PASS(RISCVLiveVariables, DEBUG_TYPE, RISCV_LIVE_VARIABLES_NAME,
                 false, true)
 
-FunctionPass *llvm::createRISCVLiveVariablesPass() {
-  return new RISCVLiveVariables();
+FunctionPass *llvm::createRISCVLiveVariablesPass(bool PreRegAlloc) {
+  return new RISCVLiveVariables(PreRegAlloc);
 }
 
 bool RISCVLiveVariables::isTrackableRegister(
@@ -170,6 +187,8 @@ void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
 
     Register Reg = MO.getReg();
 
+    TrackedRegisters.insert(std::pair(Reg, RegCounter++));
+
     // Skip non-trackable registers
     if (!isTrackableRegister(Reg, TRI, MRI))
       continue;
@@ -277,10 +296,7 @@ void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
     Changed = false;
     ++Iterations;
 
-    // Process blocks in **post-order** for better convergence
-    ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
-
-    for (MachineBasicBlock *MBB : RPOT) {
+    for (MachineBasicBlock *MBB : post_order(&MF)) {
       LivenessInfo &Info = BlockLiveness[MBB];
       std::set<Register> OldLiveIn = Info.LiveIn;
       std::set<Register> NewLiveOut;
@@ -368,6 +384,62 @@ void RISCVLiveVariables::verifyLiveness(MachineFunction &MF) const {
   }
 }
 
+void RISCVLiveVariables::markKills(MachineFunction &MF) {
+    auto KillSetSize = PreRegAlloc ? RegCounter : TRI->getNumRegs();
+    for (MachineBasicBlock *MBB : post_order(&MF)) {
+    // Set all the registers that are not live-out of the block.
+    // Since the global liveness is available (even though a bit conservative),
+    // this initialization is safe.
+    llvm::BitVector KillSet(KillSetSize, true);
+    LivenessInfo &Info = BlockLiveness[MBB];
+
+    for (Register Reg : Info.LiveOut) {
+      auto RegIdx = PreRegAlloc ? TrackedRegisters[Reg] : Reg.asMCReg().id();
+      KillSet.reset(RegIdx);
+    }
+
+    for (MachineInstr &MI : reverse(*MBB)) {
+      for (MachineOperand &MO : MI.operands()) {
+        if (!MO.isReg())
+          continue;
+
+        Register Reg = MO.getReg();
+        // Does not track physical registers pre-regalloc.
+        if ((PreRegAlloc && Reg.isPhysical()) || !isTrackableRegister(Reg, TRI, MRI))
+          continue;
+
+        assert(TrackedRegisters.find(Reg) != TrackedRegisters.end() &&
+               "Register not tracked");
+        auto RegIdx = PreRegAlloc ? TrackedRegisters[Reg] : Reg.asMCReg().id();
+
+        if (MO.isDef()) {
+          KillSet.set(RegIdx);
+
+          // Also handle sub-registers for physical registers
+          if (!PreRegAlloc && Reg.isPhysical()) {
+            for (MCRegAliasIterator RA(Reg, TRI, true); RA.isValid(); ++RA)
+              KillSet.set(*RA);
+          }
+          continue;
+        }
+
+        // Use.
+        if (KillSet[RegIdx]) {
+          if (!MO.isKill() && !MI.isPHI() && !MI.isCall())
+            MO.setIsKill(true);
+          LLVM_DEBUG(dbgs() << "Marking kill of " << printReg(Reg, TRI)
+                            << " at instruction: " << MI);
+          KillSet.reset(RegIdx);
+          if (Reg.isPhysical()) {
+            for (MCRegAliasIterator RA(Reg, TRI, true); RA.isValid(); ++RA)
+              KillSet.reset(*RA);
+          }
+        }
+      }
+    }
+  }
+}
+
 bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
   if (skipFunction(MF.getFunction()) || MF.empty())
     return false;
@@ -396,6 +468,12 @@ bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
   // Step 2: Compute global liveness (LiveIn and LiveOut sets)
   computeGlobalLiveness(MF);
 
+  // TODO: Update live-in/live-out sets of MBBs
+
+  // Step 3: Mark kill flags on operands
+  if (UpdateKills)
+    markKills(MF);
+
   LLVM_DEBUG({
     dbgs() << "\n***** Final Liveness Information *****\n";
     print(dbgs());
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 1adf930fbaa03..632054d6dbfb9 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -589,6 +589,8 @@ void RISCVPassConfig::addPreEmitPass2() {
 void RISCVPassConfig::addMachineSSAOptimization() {
   addPass(createRISCVVectorPeepholePass());
   addPass(createRISCVFoldMemOffsetPass());
+  if (EnableRISCVLiveVariables)
+    addPass(createRISCVLiveVariablesPass(true));
 
   TargetPassConfig::addMachineSSAOptimization();
 
@@ -626,7 +628,7 @@ void RISCVPassConfig::addPostRegAlloc() {
     addPass(createRISCVRedundantCopyEliminationPass());
 
   if (EnableRISCVLiveVariables)
-    addPass(createRISCVLiveVariablesPass());
+    addPass(createRISCVLiveVariablesPass(false));
 }
 
 bool RISCVPassConfig::addILPOpts() {

>From a1d0c577d196a39235adbebc11e40768d392e677 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sat, 15 Nov 2025 23:40:55 -0800
Subject: [PATCH 08/12] Add test from  #166141

---
 .../RISCV/live-variables-pre-regalloc.ll      | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll

diff --git a/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll b/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll
new file mode 100644
index 0000000000000..c3465b07dd65c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll
@@ -0,0 +1,65 @@
+; RUN: llc -mtriple riscv64 -mattr=+d -riscv-enable-live-variables --stop-after=riscv-live-variables -riscv-liveness-update-kills < %s | FileCheck %s
+
+; Issue: #166141 Pessimistic MachineLICM due to missing liveness info.
+
+; CHECK: %42:fpr64 = nofpexcept FMUL_D killed %2, killed %41, 7, implicit $frm
+
+define void @f(ptr %p) {
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i64 [0, %entry], [%iv.next, %latch]
+
+  %gep = getelementptr double, ptr %p, i64 %iv
+
+  %x = load double, ptr %gep
+  %y0 = fmul double %x, %x
+  %y1 = fmul double %y0, %y0
+  %y2 = fmul double %y1, %y1
+  %y3 = fmul double %y2, %y2
+  %y4 = fmul double %y3, %y3
+  %y5 = fmul double %y4, %y4
+  %y6 = fmul double %y5, %y5
+  %y7 = fmul double %y6, %y6
+  %y8 = fmul double %y7, %y7
+  %y9 = fmul double %y8, %y8
+  %y10 = fmul double %y9, %y9
+  %y11 = fmul double %y10, %y10
+  %y12 = fmul double %y11, %y11
+  %y13 = fmul double %y12, %y12
+  %y14 = fmul double %y13, %y13
+  %y15 = fmul double %y14, %y14
+  %y16 = fmul double %y15, %y15
+  %y17 = fmul double %y16, %y16
+  %y18 = fmul double %y17, %y17
+  %y19 = fmul double %y18, %y18
+  %y20 = fmul double %y19, %y19
+  %y21 = fmul double %y20, %y20
+  %y22 = fmul double %y21, %y21
+  %y23 = fmul double %y22, %y22
+  %y24 = fmul double %y23, %y23
+  %y25 = fmul double %y24, %y24
+  %y26 = fmul double %y25, %y25
+  %y27 = fmul double %y26, %y26
+  %y28 = fmul double %y27, %y27
+  %y29 = fmul double %y28, %y28
+  %y30 = fmul double %y29, %y29
+  %y31 = fmul double %y30, %y30
+
+  %c = fcmp une double %y31, 0.0
+  br i1 %c, label %if, label %latch
+
+if:
+  %z = fmul double %y31, 3.14159274101257324218750
+  store double %z, ptr %gep
+  br label %latch
+
+latch:
+  %iv.next = add i64 %iv, 1
+  %ec = icmp eq i64 %iv.next, 1024
+  br i1 %ec, label %exit, label %loop
+
+exit:
+  ret void
+}

>From b1a83c130c55a2edd47c6da93390e8a4810c57a4 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sun, 16 Nov 2025 00:43:40 -0800
Subject: [PATCH 09/12] clang-format and add correct flags to the tests

---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp          | 9 +++++----
 llvm/test/CodeGen/RISCV/live-variables-basic.mir      | 2 +-
 llvm/test/CodeGen/RISCV/live-variables-calls.mir      | 2 +-
 llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir | 2 +-
 llvm/test/CodeGen/RISCV/live-variables-loops.mir      | 2 +-
 llvm/test/CodeGen/RISCV/live-variables-rv64.mir       | 5 ++---
 llvm/test/CodeGen/RISCV/machine-live-variables.mir    | 2 +-
 7 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index 193070613a436..2e63bc8aeb207 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -35,8 +35,8 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 
-#include <unordered_map>
 #include <set>
+#include <unordered_map>
 
 using namespace llvm;
 
@@ -385,8 +385,8 @@ void RISCVLiveVariables::verifyLiveness(MachineFunction &MF) const {
 }
 
 void RISCVLiveVariables::markKills(MachineFunction &MF) {
-    auto KillSetSize = PreRegAlloc ? RegCounter : TRI->getNumRegs();
-    for (MachineBasicBlock *MBB : post_order(&MF)) {
+  auto KillSetSize = PreRegAlloc ? RegCounter : TRI->getNumRegs();
+  for (MachineBasicBlock *MBB : post_order(&MF)) {
     // Set all the registers that are not live-out of the block.
     // Since the global liveness is available (even though a bit conservative),
     // this initialization is safe.
@@ -405,7 +405,8 @@ void RISCVLiveVariables::markKills(MachineFunction &MF) {
 
         Register Reg = MO.getReg();
         // Does not track physical registers pre-regalloc.
-        if ((PreRegAlloc && Reg.isPhysical()) || !isTrackableRegister(Reg, TRI, MRI))
+        if ((PreRegAlloc && Reg.isPhysical()) ||
+            !isTrackableRegister(Reg, TRI, MRI))
           continue;
 
         assert(TrackedRegisters.find(Reg) != TrackedRegisters.end() &&
diff --git a/llvm/test/CodeGen/RISCV/live-variables-basic.mir b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
index 3339b04602953..bc3cee44b3878 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-basic.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
 #
 # Test basic live variable analysis with simple control flow and basic blocks
 
diff --git a/llvm/test/CodeGen/RISCV/live-variables-calls.mir b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
index 2a2738eaa3bad..16095ad5a1715 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-calls.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-calls.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
 #
 # Test live variable analysis with function calls and register clobbering
 # Function calls clobber caller-saved registers, which affects liveness
diff --git a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
index 4e04de8a310a4..fe53094ddac8d 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
 #
 # Test live variable analysis edge cases and special scenarios
 # Including: dead code, unreachable blocks, critical edges, and complex phi nodes
diff --git a/llvm/test/CodeGen/RISCV/live-variables-loops.mir b/llvm/test/CodeGen/RISCV/live-variables-loops.mir
index 05e24b33a1131..1588c65ed0ebf 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-loops.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-loops.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
 #
 # Test live variable analysis with loops and backward edges
 # Loops create interesting liveness patterns with phi nodes and variables
diff --git a/llvm/test/CodeGen/RISCV/live-variables-rv64.mir b/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
index 5b396373a60f4..c3b8ec003a5b6 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
+++ b/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -run-pass=riscv-live-variables -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
 #
 # Test live variable analysis for RV64-specific scenarios
 # This includes 64-bit operations, wide registers, and RV64-specific instructions
@@ -70,10 +70,9 @@ body:             |
 
     %0:gpr = COPY $x10
     %1:gpr = COPY $x11
-    ; SLLI for 64-bit shift (RV64-specific immediate range)
     %2:gpr = SLLI %0, 32
     %3:gpr = OR %2, %1
-    $x10 = COPY %3
+    %4:gpr = COPY %3
     PseudoRET implicit $x10
 ...
 ---
diff --git a/llvm/test/CodeGen/RISCV/machine-live-variables.mir b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
index 51033658f0cfb..964324323bf06 100644
--- a/llvm/test/CodeGen/RISCV/machine-live-variables.mir
+++ b/llvm/test/CodeGen/RISCV/machine-live-variables.mir
@@ -1,4 +1,4 @@
-# RUN: llc -x mir -mtriple=riscv64 -verify-machineinstrs -run-pass=riscv-live-variables -debug < %s 2>&1 \
+# RUN: llc -x mir -mtriple=riscv64 -verify-machineinstrs -riscv-enable-live-variables -debug < %s 2>&1 \
 # RUN: | FileCheck %s
 
 # REQUIRES: asserts

>From 928a235c79fd8296f0205388f3e492fc1dc7ce77 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sun, 16 Nov 2025 12:14:02 -0800
Subject: [PATCH 10/12] Use uses and defs iterator to make sure defs are
 processed before uses

---
 llvm/lib/Target/RISCV/RISCVLiveVariables.cpp | 33 +++++++++++---------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index 2e63bc8aeb207..79397836d1bae 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -399,10 +399,7 @@ void RISCVLiveVariables::markKills(MachineFunction &MF) {
     }
 
     for (MachineInstr &MI : reverse(*MBB)) {
-      for (MachineOperand &MO : MI.operands()) {
-        if (!MO.isReg())
-          continue;
-
+      for (MachineOperand &MO : MI.all_defs()) {
         Register Reg = MO.getReg();
         // Does not track physical registers pre-regalloc.
         if ((PreRegAlloc && Reg.isPhysical()) ||
@@ -413,20 +410,28 @@ void RISCVLiveVariables::markKills(MachineFunction &MF) {
                "Register not tracked");
         auto RegIdx = PreRegAlloc ? TrackedRegisters[Reg] : Reg.asMCReg().id();
 
-        if (MO.isDef()) {
-          KillSet.set(RegIdx);
+        KillSet.set(RegIdx);
 
-          // Also handle sub-registers for physical registers
-          if (!PreRegAlloc && Reg.isPhysical()) {
-            for (MCRegAliasIterator RA(Reg, TRI, true); RA.isValid(); ++RA)
-              KillSet.set(*RA);
-          }
-          continue;
+        // Also handle sub-registers for physical registers
+        if (!PreRegAlloc && Reg.isPhysical()) {
+          for (MCRegAliasIterator RA(Reg, TRI, true); RA.isValid(); ++RA)
+            KillSet.set(*RA);
         }
+      }
+
+      for (MachineOperand &MO : MI.all_uses()) {
+        Register Reg = MO.getReg();
+        // Does not track physical registers pre-regalloc.
+        if ((PreRegAlloc && Reg.isPhysical()) ||
+            !isTrackableRegister(Reg, TRI, MRI))
+          continue;
+
+        assert(TrackedRegisters.find(Reg) != TrackedRegisters.end() &&
+               "Register not tracked");
+        auto RegIdx = PreRegAlloc ? TrackedRegisters[Reg] : Reg.asMCReg().id();
 
-        // Use.
         if (KillSet[RegIdx]) {
-          if (!MO.isKill() && !MI.isPHI() && !MI.isCall())
+          if (!MO.isKill() && !MI.isPHI())
             MO.setIsKill(true);
           LLVM_DEBUG(dbgs() << "Marking kill of " << printReg(Reg, TRI)
                             << " at instruction: " << MI);

>From 120034cefcb82947afd89bc1102f20b06ec0cf8e Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sun, 16 Nov 2025 12:14:29 -0800
Subject: [PATCH 11/12] Update test cases to stop after live variables

---
 .../RISCV/live-variables-edge-cases.ll        | 206 +++++++++++++
 .../RISCV/live-variables-edge-cases.mir       | 285 ------------------
 .../CodeGen/RISCV/live-variables-loops.ll     | 162 ++++++++++
 .../CodeGen/RISCV/live-variables-loops.mir    | 238 ---------------
 .../test/CodeGen/RISCV/live-variables-rv64.ll | 127 ++++++++
 .../CodeGen/RISCV/live-variables-rv64.mir     | 187 ------------
 6 files changed, 495 insertions(+), 710 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-edge-cases.ll
 delete mode 100644 llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-loops.ll
 delete mode 100644 llvm/test/CodeGen/RISCV/live-variables-loops.mir
 create mode 100644 llvm/test/CodeGen/RISCV/live-variables-rv64.ll
 delete mode 100644 llvm/test/CodeGen/RISCV/live-variables-rv64.mir

diff --git a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.ll b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.ll
new file mode 100644
index 0000000000000..6cac878940969
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.ll
@@ -0,0 +1,206 @@
+; RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs \
+; RUN: -riscv-enable-live-variables -riscv-liveness-update-kills -stop-after=riscv-live-variables \
+; RUN: -o - %s | FileCheck %s
+
+; Test live variable analysis edge cases and special scenarios
+; Including: dead code, unreachable blocks, critical edges, and complex phi nodes
+
+; CHECK: test_dead_code
+; CHECK:   bb.0.entry:
+; CHECK:     liveins: $x10
+; CHECK:     %0:gpr = COPY $x10
+; CHECK:     $x10 = COPY killed %0
+; CHECK:     PseudoRET implicit $x10
+
+define i64 @test_dead_code(i64 %a, i64 %b) {
+entry:
+  %dead = add i64 %a, %b
+  ret i64 %a
+}
+
+; CHECK: test_critical_edge
+; CHECK:  bb.0.entry:
+; CHECK:    successors: %bb.2(0x50000000), %bb.1(0x30000000)
+; CHECK:    liveins: $x10, $x11, $x12
+;
+; CHECK:    %5:gpr = COPY $x12
+; CHECK:    %4:gpr = COPY $x11
+; CHECK:    %3:gpr = COPY $x10
+; CHECK:    %6:gpr = COPY $x0
+; CHECK:    BLT killed %6, %3, %bb.2
+; CHECK:    PseudoBR %bb.1
+;
+; CHECK:  bb.1.check2:
+; CHECK:    successors: %bb.2(0x50000000), %bb.3(0x30000000)
+;
+; CHECK:    %7:gpr = COPY $x0
+; CHECK:    BGE killed %7, %4, %bb.3
+; CHECK:    PseudoBR %bb.2
+;
+; CHECK:  bb.2.then:
+; CHECK:    successors: %bb.4(0x80000000)
+;
+; CHECK:    ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    $x10 = COPY killed %3
+; CHECK:    $x11 = COPY killed %4
+; CHECK:    PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:    ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    %8:gpr = COPY $x10
+; CHECK:    %0:gpr = COPY killed %8
+; CHECK:    PseudoBR %bb.4
+;
+; CHECK:  bb.3.else:
+; CHECK:    successors: %bb.4(0x80000000)
+;
+; CHECK:    %1:gpr = SUB killed %3, killed %5
+;
+; CHECK:  bb.4.end:
+; CHECK:    %2:gpr = PHI %1, %bb.3, %0, %bb.2
+; CHECK:    $x10 = COPY killed %2
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_critical_edge(i64 %a, i64 %b, i64 %c) {
+entry:
+  %cmp1 = icmp sgt i64 %a, 0
+  br i1 %cmp1, label %then, label %check2
+
+check2:
+  %cmp2 = icmp sgt i64 %b, 0
+  br i1 %cmp2, label %then, label %else
+
+then:
+  %mul = mul i64 %a, %b
+  br label %end
+
+else:
+  %sub = sub i64 %a, %c
+  br label %end
+
+end:
+  %result = phi i64 [ %mul, %then ], [ %sub, %else ]
+  ret i64 %result
+}
+
+; CHECK: test_complex_phi
+; CHECK:   bb.0.entry:
+; CHECK:     successors: %bb.1(0x50000000), %bb.2(0x30000000)
+; CHECK:     liveins: $x10, $x11, $x12, $x13
+;
+; CHECK:     %7:gpr = COPY $x13
+; CHECK:     %6:gpr = COPY $x12
+; CHECK:     %5:gpr = COPY $x11
+; CHECK:     %4:gpr = COPY $x10
+; CHECK:     %8:gpr = COPY $x0
+; CHECK:     BGE killed %8, %4, %bb.2
+; CHECK:     PseudoBR %bb.1
+;
+; CHECK:   bb.1.path1:
+; CHECK:     successors: %bb.5(0x80000000)
+;
+; CHECK:     %0:gpr = ADD killed %4, killed %5
+; CHECK:     PseudoBR %bb.5
+;
+; CHECK:   bb.2.path2:
+; CHECK:     successors: %bb.3(0x50000000), %bb.4(0x30000000)
+;
+; CHECK:     %9:gpr = COPY $x0
+; CHECK:     BGE killed %9, %6, %bb.4
+; CHECK:     PseudoBR %bb.3
+;
+; CHECK:   bb.3.path2a:
+; CHECK:     successors: %bb.5(0x80000000)
+;
+; CHECK:     ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     $x10 = COPY killed %6
+; CHECK:     $x11 = COPY killed %7
+; CHECK:     PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:     ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     %10:gpr = COPY $x10
+; CHECK:     %1:gpr = COPY killed %10
+; CHECK:     PseudoBR %bb.5
+;
+; CHECK:   bb.4.path2b:
+; CHECK:     successors: %bb.5(0x80000000)
+;
+; CHECK:     %2:gpr = SUB killed %6, killed %7
+;
+; CHECK:   bb.5.merge:
+; CHECK:     %3:gpr = PHI %2, %bb.4, %1, %bb.3, %0, %bb.1
+; CHECK:     $x10 = COPY killed %3
+; CHECK:     PseudoRET implicit $x10
+
+define i64 @test_complex_phi(i64 %a, i64 %b, i64 %c, i64 %d) {
+entry:
+  %cmp1 = icmp sgt i64 %a, 0
+  br i1 %cmp1, label %path1, label %path2
+
+path1:
+  %v1 = add i64 %a, %b
+  br label %merge
+
+path2:
+  %cmp2 = icmp sgt i64 %c, 0
+  br i1 %cmp2, label %path2a, label %path2b
+
+path2a:
+  %v2a = mul i64 %c, %d
+  br label %merge
+
+path2b:
+  %v2b = sub i64 %c, %d
+  br label %merge
+
+merge:
+  %result = phi i64 [ %v1, %path1 ], [ %v2a, %path2a ], [ %v2b, %path2b ]
+  ret i64 %result
+}
+
+; CHECK: test_use_after_def
+; CHECK:   bb.0.entry:
+; CHECK:     liveins: $x10
+;
+; CHECK:     %0:gpr = COPY $x10
+; CHECK:     %1:gpr = ADDI killed %0, 1
+; CHECK:     %2:gpr = ADD killed %1, %1
+; CHECK:     %3:gpr = ADDI killed %2, 5
+; CHECK:     $x10 = COPY killed %3
+; CHECK:     PseudoRET implicit $x10
+
+define i64 @test_use_after_def(i64 %a) {
+entry:
+  %v1 = add i64 %a, 1
+  %v2 = add i64 %v1, 2
+  %v3 = add i64 %v2, 3
+  %v4 = add i64 %v1, %v3
+  ret i64 %v4
+}
+
+; CHECK: test_implicit_defs
+; CHECK:   bb.0.entry:
+; CHECK:     liveins: $x10, $x11
+;
+; CHECK:     %1:gpr = COPY $x11
+; CHECK:     %0:gpr = COPY $x10
+; CHECK:     ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     $x10 = COPY %0
+; CHECK:     $x11 = COPY %1
+; CHECK:     PseudoCALL target-flags(riscv-call) &__divdi3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:     ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     %2:gpr = COPY $x10
+; CHECK:     ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     $x10 = COPY killed %0
+; CHECK:     $x11 = COPY killed %1
+; CHECK:     PseudoCALL target-flags(riscv-call) &__moddi3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:     ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:     %3:gpr = COPY $x10
+; CHECK:     %4:gpr = ADD killed %2, killed %3
+; CHECK:     $x10 = COPY killed %4
+; CHECK:     PseudoRET implicit $x10
+
+define i64 @test_implicit_defs(i64 %a, i64 %b) {
+entry:
+  %div = sdiv i64 %a, %b
+  %rem = srem i64 %a, %b
+  %sum = add i64 %div, %rem
+  ret i64 %sum
+}
diff --git a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir b/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
deleted file mode 100644
index fe53094ddac8d..0000000000000
--- a/llvm/test/CodeGen/RISCV/live-variables-edge-cases.mir
+++ /dev/null
@@ -1,285 +0,0 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
-#
-# Test live variable analysis edge cases and special scenarios
-# Including: dead code, unreachable blocks, critical edges, and complex phi nodes
-
---- |
-  define i64 @test_dead_code(i64 %a, i64 %b) {
-  entry:
-    %dead = add i64 %a, %b
-    ret i64 %a
-  }
-
-  define i64 @test_critical_edge(i64 %a, i64 %b, i64 %c) {
-  entry:
-    %cmp1 = icmp sgt i64 %a, 0
-    br i1 %cmp1, label %then, label %check2
-
-  check2:
-    %cmp2 = icmp sgt i64 %b, 0
-    br i1 %cmp2, label %then, label %else
-
-  then:
-    %mul = mul i64 %a, %b
-    br label %end
-
-  else:
-    %sub = sub i64 %a, %c
-    br label %end
-
-  end:
-    %result = phi i64 [ %mul, %then ], [ %sub, %else ]
-    ret i64 %result
-  }
-
-  define i64 @test_complex_phi(i64 %a, i64 %b, i64 %c, i64 %d) {
-  entry:
-    %cmp1 = icmp sgt i64 %a, 0
-    br i1 %cmp1, label %path1, label %path2
-
-  path1:
-    %v1 = add i64 %a, %b
-    br label %merge
-
-  path2:
-    %cmp2 = icmp sgt i64 %c, 0
-    br i1 %cmp2, label %path2a, label %path2b
-
-  path2a:
-    %v2a = mul i64 %c, %d
-    br label %merge
-
-  path2b:
-    %v2b = sub i64 %c, %d
-    br label %merge
-
-  merge:
-    %result = phi i64 [ %v1, %path1 ], [ %v2a, %path2a ], [ %v2b, %path2b ]
-    ret i64 %result
-  }
-
-  define i64 @test_use_after_def(i64 %a) {
-  entry:
-    %v1 = add i64 %a, 1
-    %v2 = add i64 %v1, 2
-    %v3 = add i64 %v2, 3
-    %v4 = add i64 %v1, %v3
-    ret i64 %v4
-  }
-
-  define i64 @test_implicit_defs(i64 %a, i64 %b) {
-  entry:
-    %div = sdiv i64 %a, %b
-    %rem = srem i64 %a, %b
-    %sum = add i64 %div, %rem
-    ret i64 %sum
-  }
-...
----
-name:            test_dead_code
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_dead_code
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: liveins: $x10, $x11
-    ; %2 is dead - never used, should not affect liveness of inputs
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    ; Dead instruction - result never used
-    %2:gpr = ADD %0, %1
-    ; Only %0 should be live here
-    $x10 = COPY %0
-    PseudoRET implicit $x10
-...
----
-name:            test_critical_edge
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-  - { id: 5, class: gpr }
-  - { id: 6, class: gpr }
-  - { id: 7, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-  - { reg: '$x12', virtual-reg: '%2' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11, $x12
-    ; CHECK-LABEL: name: test_critical_edge
-    ; CHECK: bb.0.entry:
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = COPY $x12
-    %3:gpr = SLTI %0, 1
-    BEQ %3, $x0, %bb.2
-
-  bb.1.check2:
-    ; CHECK: bb.1.check2:
-    ; %0, %1, %2 should all be live-in
-
-    %4:gpr = SLTI %1, 1
-    BEQ %4, $x0, %bb.3
-
-  bb.2.then:
-    ; CHECK: bb.2.then:
-    ; %0, %1 should be live-in (used in mul)
-
-    %5:gpr = MUL %0, %1
-    PseudoBR %bb.4
-
-  bb.3.else:
-    ; CHECK: bb.3.else:
-    ; %0, %2 should be live-in (used in sub)
-
-    %6:gpr = SUB %0, %2
-    PseudoBR %bb.4
-
-  bb.4.end:
-    ; CHECK: bb.4.end:
-    ; Either %5 or %6 should be live-in
-
-    %7:gpr = PHI %5, %bb.2, %6, %bb.3
-    $x10 = COPY %7
-    PseudoRET implicit $x10
-...
----
-name:            test_complex_phi
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-  - { id: 5, class: gpr }
-  - { id: 6, class: gpr }
-  - { id: 7, class: gpr }
-  - { id: 8, class: gpr }
-  - { id: 9, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-  - { reg: '$x12', virtual-reg: '%2' }
-  - { reg: '$x13', virtual-reg: '%3' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11, $x12, $x13
-    ; CHECK-LABEL: name: test_complex_phi
-    ; CHECK: bb.0.entry:
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = COPY $x12
-    %3:gpr = COPY $x13
-    %4:gpr = SLTI %0, 1
-    BEQ %4, $x0, %bb.2
-
-  bb.1.path1:
-    ; CHECK: bb.1.path1:
-
-    %5:gpr = ADD %0, %1
-    PseudoBR %bb.5
-
-  bb.2.path2:
-    ; CHECK: bb.2.path2:
-    ; %2, %3 should be live-in
-
-    %6:gpr = SLTI %2, 1
-    BEQ %6, $x0, %bb.4
-
-  bb.3.path2a:
-    ; CHECK: bb.3.path2a:
-
-    %7:gpr = MUL %2, %3
-    PseudoBR %bb.5
-
-  bb.4.path2b:
-    ; CHECK: bb.4.path2b:
-
-    %8:gpr = SUB %2, %3
-    PseudoBR %bb.5
-
-  bb.5.merge:
-    ; CHECK: bb.5.merge:
-    ; One of %5, %7, %8 should be live-in
-
-    %9:gpr = PHI %5, %bb.1, %7, %bb.3, %8, %bb.4
-    $x10 = COPY %9
-    PseudoRET implicit $x10
-...
----
-name:            test_use_after_def
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-body:             |
-  bb.0.entry:
-    liveins: $x10
-    ; CHECK-LABEL: name: test_use_after_def
-    ; CHECK: bb.0.entry:
-    ; Test that %1 remains live even after being used in %2
-
-    %0:gpr = COPY $x10
-    %1:gpr = ADDI %0, 1
-    %2:gpr = ADDI %1, 2
-    %3:gpr = ADDI %2, 3
-    ; %1 used again here - should have been kept live
-    %4:gpr = ADD %1, %3
-    $x10 = COPY %4
-    PseudoRET implicit $x10
-...
----
-name:            test_implicit_defs
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_implicit_defs
-    ; CHECK: bb.0.entry:
-    ; Test handling of division which may have implicit defs/uses
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = DIV %0, %1
-    %3:gpr = REM %0, %1
-    %4:gpr = ADD %2, %3
-    $x10 = COPY %4
-    PseudoRET implicit $x10
-...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-loops.ll b/llvm/test/CodeGen/RISCV/live-variables-loops.ll
new file mode 100644
index 0000000000000..a689846baccbd
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-loops.ll
@@ -0,0 +1,162 @@
+; RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs \
+; RUN: -riscv-enable-live-variables -riscv-liveness-update-kills -stop-after=riscv-live-variables \
+; RUN: -o - %s | FileCheck %s
+
+; Test live variable analysis with loops and backward edges
+; Loops create interesting liveness patterns with phi nodes and variables
+; that are live across backedges
+
+; CHECK:  test_simple_loop
+; CHECK:  bb.0.entry:
+; CHECK:    successors: %bb.1(0x80000000)
+; CHECK:    liveins: $x10
+;
+; CHECK:    %4:gpr = COPY $x10
+; CHECK:    %6:gpr = COPY $x0
+; CHECK:    %5:gpr = COPY killed %6
+;
+; CHECK:  bb.1.loop:
+; CHECK:    successors: %bb.1(0x7c000000), %bb.2(0x04000000)
+;
+; CHECK:    %0:gpr = PHI %5, %bb.0, %3, %bb.1
+; CHECK:    %1:gpr = PHI %5, %bb.0, %2, %bb.1
+; CHECK:    %2:gpr = ADD killed %1, %0
+; CHECK:    %3:gpr = ADDI killed %0, 1
+; CHECK:    BLT %3, %4, %bb.1
+; CHECK:    PseudoBR %bb.2
+; 
+; CHECK:  bb.2.exit:
+; CHECK:    $x10 = COPY killed %2
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_simple_loop(i64 %n) {
+entry:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
+  %sum.next = add i64 %sum, %i
+  %i.next = add i64 %i, 1
+  %cmp = icmp slt i64 %i.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i64 %sum.next
+}
+
+; CHECK:  test_nested_loop
+; CHECK:  bb.0.entry:
+; CHECK:    successors: %bb.1(0x80000000)
+; CHECK:    liveins: $x10, $x11
+;
+; CHECK:    %11:gpr = COPY $x11
+; CHECK:    %10:gpr = COPY $x10
+; CHECK:    %13:gpr = COPY $x0
+; CHECK:    %12:gpr = COPY killed %13
+;
+; CHECK:  bb.1.outer.loop:
+; CHECK:    successors: %bb.2(0x80000000)
+;
+; CHECK:    %0:gpr = PHI %12, %bb.0, %9, %bb.3
+; CHECK:    %1:gpr = PHI %12, %bb.0, %8, %bb.3
+; CHECK:    %15:gpr = COPY $x0
+; CHECK:    %14:gpr = COPY killed %15
+;
+; CHECK:  bb.2.inner.loop:
+; CHECK:    successors: %bb.2(0x7c000000), %bb.3(0x04000000)
+;
+; CHECK:    %2:gpr = PHI %14, %bb.1, %7, %bb.2
+; CHECK:    %3:gpr = PHI %14, %bb.1, %6, %bb.2
+; CHECK:    %4:gpr = PHI %14, %bb.1, %5, %bb.2
+; CHECK:    %5:gpr = ADD %4, %2
+; CHECK:    %6:gpr = ADDI killed %3, 1
+; CHECK:    %7:gpr = ADD killed %2, %0
+; CHECK:    BLT %6, %11, %bb.2
+; CHECK:    PseudoBR %bb.3
+;
+; CHECK:  bb.3.outer.latch:
+; CHECK:    successors: %bb.1(0x7c000000), %bb.4(0x04000000)
+;
+; CHECK:    %8:gpr = ADD killed %1, %5
+; CHECK:    %9:gpr = ADDI killed %0, 1
+; CHECK:    BLT %9, %10, %bb.1
+; CHECK:    PseudoBR %bb.4
+;
+; CHECK:  bb.4.exit:
+; CHECK:    $x10 = COPY killed %8
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_nested_loop(i64 %n, i64 %m) {
+entry:
+  br label %outer.loop
+
+outer.loop:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+  %outer.sum = phi i64 [ 0, %entry ], [ %new.outer.sum, %outer.latch ]
+  br label %inner.loop
+
+inner.loop:
+  %j = phi i64 [ 0, %outer.loop ], [ %j.next, %inner.loop ]
+  %inner.sum = phi i64 [ 0, %outer.loop ], [ %inner.sum.next, %inner.loop ]
+  %prod = mul i64 %i, %j
+  %inner.sum.next = add i64 %inner.sum, %prod
+  %j.next = add i64 %j, 1
+  %inner.cmp = icmp slt i64 %j.next, %m
+  br i1 %inner.cmp, label %inner.loop, label %outer.latch
+
+outer.latch:
+  %new.outer.sum = add i64 %outer.sum, %inner.sum.next
+  %i.next = add i64 %i, 1
+  %outer.cmp = icmp slt i64 %i.next, %n
+  br i1 %outer.cmp, label %outer.loop, label %exit
+
+exit:
+  ret i64 %new.outer.sum
+}
+
+; CHECK:  test_loop_with_invariant
+; CHECK:  bb.0.entry:
+; CHECK:    successors: %bb.1(0x80000000)
+; CHECK:    liveins: $x10, $x11
+;
+; CHECK:    %8:gpr = COPY $x11
+; CHECK:    %7:gpr = COPY $x10
+; CHECK:    %0:gpr = SLLI killed %8, 1
+; CHECK:    %10:gpr = COPY $x0
+; CHECK:    %9:gpr = COPY killed %10
+;
+; CHECK:  bb.1.loop:
+; CHECK:    successors: %bb.1(0x7c000000), %bb.2(0x04000000)
+;
+; CHECK:    %1:gpr = PHI %9, %bb.0, %6, %bb.1
+; CHECK:    %2:gpr = PHI %9, %bb.0, %5, %bb.1
+; CHECK:    %3:gpr = PHI %9, %bb.0, %4, %bb.1
+; CHECK:    %4:gpr = ADD killed %3, %1
+; CHECK:    %5:gpr = ADDI killed %2, 1
+; CHECK:    %6:gpr = ADD killed %1, %0
+; CHECK:    BLT %5, %7, %bb.1
+; CHECK:    PseudoBR %bb.2
+;
+; CHECK:  bb.2.exit:
+; CHECK:    $x10 = COPY killed %4
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_loop_with_invariant(i64 %n, i64 %k) {
+entry:
+  %double_k = mul i64 %k, 2
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+  %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
+  ; double_k is loop-invariant and should be live throughout the loop
+  %scaled = mul i64 %i, %double_k
+  %sum.next = add i64 %sum, %scaled
+  %i.next = add i64 %i, 1
+  %cmp = icmp slt i64 %i.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i64 %sum.next
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/RISCV/live-variables-loops.mir b/llvm/test/CodeGen/RISCV/live-variables-loops.mir
deleted file mode 100644
index 1588c65ed0ebf..0000000000000
--- a/llvm/test/CodeGen/RISCV/live-variables-loops.mir
+++ /dev/null
@@ -1,238 +0,0 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
-#
-# Test live variable analysis with loops and backward edges
-# Loops create interesting liveness patterns with phi nodes and variables
-# that are live across backedges
-
---- |
-  define i64 @test_simple_loop(i64 %n) {
-  entry:
-    br label %loop
-
-  loop:
-    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-    %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
-    %sum.next = add i64 %sum, %i
-    %i.next = add i64 %i, 1
-    %cmp = icmp slt i64 %i.next, %n
-    br i1 %cmp, label %loop, label %exit
-
-  exit:
-    ret i64 %sum.next
-  }
-
-  define i64 @test_nested_loop(i64 %n, i64 %m) {
-  entry:
-    br label %outer.loop
-
-  outer.loop:
-    %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
-    %outer.sum = phi i64 [ 0, %entry ], [ %new.outer.sum, %outer.latch ]
-    br label %inner.loop
-
-  inner.loop:
-    %j = phi i64 [ 0, %outer.loop ], [ %j.next, %inner.loop ]
-    %inner.sum = phi i64 [ 0, %outer.loop ], [ %inner.sum.next, %inner.loop ]
-    %prod = mul i64 %i, %j
-    %inner.sum.next = add i64 %inner.sum, %prod
-    %j.next = add i64 %j, 1
-    %inner.cmp = icmp slt i64 %j.next, %m
-    br i1 %inner.cmp, label %inner.loop, label %outer.latch
-
-  outer.latch:
-    %new.outer.sum = add i64 %outer.sum, %inner.sum.next
-    %i.next = add i64 %i, 1
-    %outer.cmp = icmp slt i64 %i.next, %n
-    br i1 %outer.cmp, label %outer.loop, label %exit
-
-  exit:
-    ret i64 %new.outer.sum
-  }
-
-  define i64 @test_loop_with_invariant(i64 %n, i64 %k) {
-  entry:
-    %double_k = mul i64 %k, 2
-    br label %loop
-
-  loop:
-    %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-    %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
-    ; double_k is loop-invariant and should be live throughout the loop
-    %scaled = mul i64 %i, %double_k
-    %sum.next = add i64 %sum, %scaled
-    %i.next = add i64 %i, 1
-    %cmp = icmp slt i64 %i.next, %n
-    br i1 %cmp, label %loop, label %exit
-
-  exit:
-    ret i64 %sum.next
-  }
-...
----
-name:            test_simple_loop
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-  - { id: 5, class: gpr }
-  - { id: 6, class: gpr }
-  - { id: 7, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-body:             |
-  bb.0.entry:
-    liveins: $x10
-    ; CHECK-LABEL: name: test_simple_loop
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: successors
-    ; CHECK-NEXT: liveins: $x10
-
-    %0:gpr = COPY $x10
-    %1:gpr = ADDI $x0, 0
-    %2:gpr = ADDI $x0, 0
-    PseudoBR %bb.1
-
-  bb.1.loop:
-    ; CHECK: bb.1.loop:
-    ; %0 should be live-in (used in comparison)
-    ; %1 and %2 should be live-in from backedge or entry
-
-    %3:gpr = PHI %1, %bb.0, %5, %bb.1
-    %4:gpr = PHI %2, %bb.0, %6, %bb.1
-    %6:gpr = ADD %4, %3
-    %5:gpr = ADDI %3, 1
-    %7:gpr = SLT %5, %0
-    BNE %7, $x0, %bb.1
-
-  bb.2.exit:
-    ; CHECK: bb.2.exit:
-    ; %6 should be live-in
-
-    $x10 = COPY %6
-    PseudoRET implicit $x10
-...
----
-name:            test_nested_loop
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-  - { id: 5, class: gpr }
-  - { id: 6, class: gpr }
-  - { id: 7, class: gpr }
-  - { id: 8, class: gpr }
-  - { id: 9, class: gpr }
-  - { id: 10, class: gpr }
-  - { id: 11, class: gpr }
-  - { id: 12, class: gpr }
-  - { id: 13, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_nested_loop
-    ; CHECK: bb.0.entry:
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = ADDI $x0, 0
-    %3:gpr = ADDI $x0, 0
-    PseudoBR %bb.1
-
-  bb.1.outer.loop:
-    ; CHECK: bb.1.outer.loop:
-    ; %0 and %1 should be live-in (loop bounds)
-
-    %4:gpr = PHI %2, %bb.0, %12, %bb.3
-    %5:gpr = PHI %3, %bb.0, %11, %bb.3
-    %6:gpr = ADDI $x0, 0
-    %7:gpr = ADDI $x0, 0
-    PseudoBR %bb.2
-
-  bb.2.inner.loop:
-    ; CHECK: bb.2.inner.loop:
-    ; %1, %4, %5 should be live-in
-
-    %8:gpr = PHI %6, %bb.1, %13, %bb.2
-    %9:gpr = PHI %7, %bb.1, %10, %bb.2
-    %10:gpr = MUL %4, %8
-    %10:gpr = ADD %9, %10
-    %13:gpr = ADDI %8, 1
-    %14:gpr = SLT %13, %1
-    BNE %14, $x0, %bb.2
-
-  bb.3.outer.latch:
-    ; CHECK: bb.3.outer.latch:
-    ; %0, %5, %10 should be live-in
-
-    %11:gpr = ADD %5, %10
-    %12:gpr = ADDI %4, 1
-    %15:gpr = SLT %12, %0
-    BNE %15, $x0, %bb.1
-
-  bb.4.exit:
-    ; CHECK: bb.4.exit:
-
-    $x10 = COPY %11
-    PseudoRET implicit $x10
-...
----
-name:            test_loop_with_invariant
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-  - { id: 5, class: gpr }
-  - { id: 6, class: gpr }
-  - { id: 7, class: gpr }
-  - { id: 8, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_loop_with_invariant
-    ; CHECK: bb.0.entry:
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = SLLI %1, 1
-    %3:gpr = ADDI $x0, 0
-    %4:gpr = ADDI $x0, 0
-    PseudoBR %bb.1
-
-  bb.1.loop:
-    ; CHECK: bb.1.loop:
-    ; %0 and %2 should be live-in (loop bound and invariant)
-    ; %2 is computed before loop but used in every iteration
-
-    %5:gpr = PHI %3, %bb.0, %7, %bb.1
-    %6:gpr = PHI %4, %bb.0, %8, %bb.1
-    %9:gpr = MUL %5, %2
-    %8:gpr = ADD %6, %9
-    %7:gpr = ADDI %5, 1
-    %10:gpr = SLT %7, %0
-    BNE %10, $x0, %bb.1
-
-  bb.2.exit:
-    ; CHECK: bb.2.exit:
-
-    $x10 = COPY %8
-    PseudoRET implicit $x10
-...
diff --git a/llvm/test/CodeGen/RISCV/live-variables-rv64.ll b/llvm/test/CodeGen/RISCV/live-variables-rv64.ll
new file mode 100644
index 0000000000000..440337df37f29
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-rv64.ll
@@ -0,0 +1,127 @@
+; RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs \
+; RUN: -riscv-enable-live-variables -riscv-liveness-update-kills -stop-after=riscv-live-variables \
+; RUN: -o - %s | FileCheck %s
+
+; Test live variable analysis for RV64-specific scenarios
+; This includes 64-bit operations, wide registers, and RV64-specific instructions
+
+; CHECK:  test_64bit_ops
+; CHECK:  bb.0.entry:
+; CHECK:    liveins: $x10, $x11
+;
+; CHECK:    %1:gpr = COPY $x11
+; CHECK:    %0:gpr = COPY $x10
+; CHECK:    %2:gpr = SLLI killed %0, 32
+; CHECK:    %3:gpr = OR killed %2, killed %1
+; CHECK:    $x10 = COPY killed %3
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_64bit_ops(i64 %a, i64 %b) {
+entry:
+  %shl = shl i64 %a, 32
+  %or = or i64 %shl, %b
+  ret i64 %or
+}
+
+; CHECK:  test_word_ops
+; CHECK:  bb.0.entry:
+; CHECK:    liveins: $x10, $x11
+;
+; CHECK:    %1:gpr = COPY $x11
+; CHECK:    %0:gpr = COPY $x10
+; CHECK:    %2:gpr = ADDW killed %0, killed %1
+; CHECK:    $x10 = COPY killed %2
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_word_ops(i64 %a, i64 %b) {
+entry:
+  %trunc_a = trunc i64 %a to i32
+  %trunc_b = trunc i64 %b to i32
+  %add = add i32 %trunc_a, %trunc_b
+  %ext = sext i32 %add to i64
+  ret i64 %ext
+}
+
+; CHECK:  test_mixed_width
+; CHECK:  bb.0.entry:
+; CHECK:    liveins: $x10, $x11
+;
+; CHECK:    %1:gpr = COPY $x11
+; CHECK:    %0:gpr = COPY $x10
+; CHECK:    ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    $x10 = COPY killed %0
+; CHECK:    $x11 = COPY killed %1
+; CHECK:    PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:    ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    %2:gpr = COPY $x10
+; CHECK:    %3:gpr = ADDIW killed %2, 0
+; CHECK:    $x10 = COPY killed %3
+; CHECK:    PseudoRET implicit $x10
+
+define i64 @test_mixed_width(i64 %a, i32 %b) {
+entry:
+  %ext_b = sext i32 %b to i64
+  %mul = mul i64 %a, %ext_b
+  %trunc = trunc i64 %mul to i32
+  %final = sext i32 %trunc to i64
+  ret i64 %final
+}
+
+; CHECK:  test_float_64
+; CHECK:  bb.0.entry:
+; CHECK:    successors: %bb.1(0x30000000), %bb.2(0x50000000)
+; CHECK:    liveins: $x10, $x11, $x12
+;
+; CHECK:    %5:gpr = COPY $x12
+; CHECK:    %4:gpr = COPY $x11
+; CHECK:    %3:gpr = COPY $x10
+; CHECK:    %7:gpr = COPY killed %4
+; CHECK:    %6:gpr = COPY killed %3
+; CHECK:    BNE killed %5, $x0, %bb.2
+; CHECK:    PseudoBR %bb.1
+;
+; CHECK:  bb.1.then:
+; CHECK:    successors: %bb.3(0x80000000)
+;
+; CHECK:    ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    $x10 = COPY killed %6
+; CHECK:    $x11 = COPY killed %7
+; CHECK:    PseudoCALL target-flags(riscv-call) &__adddf3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:    ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    %9:gpr = COPY $x10
+; CHECK:    %0:gpr = COPY killed %9
+; CHECK:    PseudoBR %bb.3
+;
+; CHECK:  bb.2.else:
+; CHECK:    successors: %bb.3(0x80000000)
+;
+; CHECK:    ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    $x10 = COPY killed %6
+; CHECK:    $x11 = COPY killed %7
+; CHECK:    PseudoCALL target-flags(riscv-call) &__muldf3, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit-def $x2, implicit-def $x10
+; CHECK:    ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+; CHECK:    %8:gpr = COPY $x10
+; CHECK:    %1:gpr = COPY killed %8
+;
+; CHECK:  bb.3.end:
+; CHECK:    %2:gpr = PHI %1, %bb.2, %0, %bb.1
+; CHECK:    $x10 = COPY killed %2
+; CHECK:    PseudoRET implicit $x10
+
+define double @test_float_64(double %a, double %b, i64 %selector) {
+entry:
+  %cmp = icmp eq i64 %selector, 0
+  br i1 %cmp, label %then, label %else
+
+then:
+  %add = fadd double %a, %b
+  br label %end
+
+else:
+  %mul = fmul double %a, %b
+  br label %end
+
+end:
+  %result = phi double [ %add, %then ], [ %mul, %else ]
+  ret double %result
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/RISCV/live-variables-rv64.mir b/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
deleted file mode 100644
index c3b8ec003a5b6..0000000000000
--- a/llvm/test/CodeGen/RISCV/live-variables-rv64.mir
+++ /dev/null
@@ -1,187 +0,0 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv64 -riscv-enable-live-variables -verify-machineinstrs -o - %s | FileCheck %s
-#
-# Test live variable analysis for RV64-specific scenarios
-# This includes 64-bit operations, wide registers, and RV64-specific instructions
-
---- |
-  define i64 @test_64bit_ops(i64 %a, i64 %b) {
-  entry:
-    %shl = shl i64 %a, 32
-    %or = or i64 %shl, %b
-    ret i64 %or
-  }
-
-  define i64 @test_word_ops(i64 %a, i64 %b) {
-  entry:
-    %trunc_a = trunc i64 %a to i32
-    %trunc_b = trunc i64 %b to i32
-    %add = add i32 %trunc_a, %trunc_b
-    %ext = sext i32 %add to i64
-    ret i64 %ext
-  }
-
-  define i64 @test_mixed_width(i64 %a, i32 %b) {
-  entry:
-    %ext_b = sext i32 %b to i64
-    %mul = mul i64 %a, %ext_b
-    %trunc = trunc i64 %mul to i32
-    %final = sext i32 %trunc to i64
-    ret i64 %final
-  }
-
-  define double @test_float_64(double %a, double %b, i64 %selector) {
-  entry:
-    %cmp = icmp eq i64 %selector, 0
-    br i1 %cmp, label %then, label %else
-
-  then:
-    %add = fadd double %a, %b
-    br label %end
-
-  else:
-    %mul = fmul double %a, %b
-    br label %end
-
-  end:
-    %result = phi double [ %add, %then ], [ %mul, %else ]
-    ret double %result
-  }
-...
----
-name:            test_64bit_ops
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_64bit_ops
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: liveins: $x10, $x11
-    ; Test 64-bit shift and or operations
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    %2:gpr = SLLI %0, 32
-    %3:gpr = OR %2, %1
-    %4:gpr = COPY %3
-    PseudoRET implicit $x10
-...
----
-name:            test_word_ops
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11
-    ; CHECK-LABEL: name: test_word_ops
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: liveins: $x10, $x11
-    ; Test RV64 W-suffix instructions (32-bit ops on 64-bit regs)
-
-    %0:gpr = COPY $x10
-    %1:gpr = COPY $x11
-    ; ADDW is RV64-specific: 32-bit add with sign-extension
-    %2:gpr = ADDW %0, %1
-    $x10 = COPY %2
-    PseudoRET implicit $x10
-...
----
-name:            test_mixed_width
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: gpr }
-  - { id: 1, class: gpr }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: gpr }
-liveins:
-  - { reg: '$x10', virtual-reg: '%0' }
-  - { reg: '$x11_w', virtual-reg: '%1' }
-body:             |
-  bb.0.entry:
-    liveins: $x10, $x11_w
-    ; CHECK-LABEL: name: test_mixed_width
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: liveins: $x10, $x11_w
-    ; Test mixed 32/64-bit operations
-
-    %0:gpr = COPY $x10
-    ; Sign-extend 32-bit value to 64-bit
-    %1:gpr = COPY $x11_w
-    %2:gpr = ADDIW %1, 0
-    %3:gpr = MUL %0, %2
-    ; Extract lower 32 bits and sign-extend
-    %4:gpr = ADDIW %3, 0
-    $x10 = COPY %4
-    PseudoRET implicit $x10
-...
----
-name:            test_float_64
-alignment:       4
-tracksRegLiveness: true
-registers:
-  - { id: 0, class: fpr64 }
-  - { id: 1, class: fpr64 }
-  - { id: 2, class: gpr }
-  - { id: 3, class: gpr }
-  - { id: 4, class: fpr64 }
-  - { id: 5, class: fpr64 }
-  - { id: 6, class: fpr64 }
-liveins:
-  - { reg: '$f10_d', virtual-reg: '%0' }
-  - { reg: '$f11_d', virtual-reg: '%1' }
-  - { reg: '$x10', virtual-reg: '%2' }
-body:             |
-  bb.0.entry:
-    liveins: $f10_d, $f11_d, $x10
-    ; CHECK-LABEL: name: test_float_64
-    ; CHECK: bb.0.entry:
-    ; CHECK-NEXT: successors
-    ; CHECK-NEXT: liveins: $f10_d, $f11_d, $x10
-    ; Test 64-bit floating point register liveness
-
-    %0:fpr64 = COPY $f10_d
-    %1:fpr64 = COPY $f11_d
-    %2:gpr = COPY $x10
-    %3:gpr = ADDI $x0, 0
-    BNE %2, %3, %bb.2
-
-  bb.1.then:
-    ; CHECK: bb.1.then:
-    ; %0 and %1 should be live-in (FP registers)
-
-    %4:fpr64 = FADD_D %0, %1, 7
-    PseudoBR %bb.3
-
-  bb.2.else:
-    ; CHECK: bb.2.else:
-    ; %0 and %1 should be live-in
-
-    %5:fpr64 = FMUL_D %0, %1, 7
-    PseudoBR %bb.3
-
-  bb.3.end:
-    ; CHECK: bb.3.end:
-    ; Either %4 or %5 should be live-in
-
-    %6:fpr64 = PHI %4, %bb.1, %5, %bb.2
-    $f10_d = COPY %6
-    PseudoRET implicit $f10_d
-...

>From 1a8f7122113e1224196fa98674545bd5c0ab2e23 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sun, 16 Nov 2025 12:25:26 -0800
Subject: [PATCH 12/12] Update test to check loop invariant motion

---
 .../RISCV/live-variables-pre-regalloc.ll      | 25 +++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll b/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll
index c3465b07dd65c..a79b15ad56ab0 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll
+++ b/llvm/test/CodeGen/RISCV/live-variables-pre-regalloc.ll
@@ -1,8 +1,29 @@
-; RUN: llc -mtriple riscv64 -mattr=+d -riscv-enable-live-variables --stop-after=riscv-live-variables -riscv-liveness-update-kills < %s | FileCheck %s
+; RUN: llc -mtriple riscv64 -mattr=+d -riscv-enable-live-variables \
+; RUN: --stop-after=riscv-live-variables -riscv-liveness-update-kills < %s | FileCheck %s
+
+; RUN: llc -mtriple riscv64 -mattr=+d -riscv-enable-live-variables \
+; RUN: -riscv-liveness-update-kills < %s | FileCheck --check-prefix: CHECK-LICM %s
 
 ; Issue: #166141 Pessimistic MachineLICM due to missing liveness info.
 
-; CHECK: %42:fpr64 = nofpexcept FMUL_D killed %2, killed %41, 7, implicit $frm
+; Check that live variable analysis correctly marks %41 as kill
+; CHECK:  bb.2.if:
+; CHECK:    successors: %bb.3(0x80000000)
+;
+; CHECK:    %40:gpr = LUI target-flags(riscv-hi) %const.0
+; CHECK:    %41:fpr64 = FLD killed %40, target-flags(riscv-lo) %const.0 :: (load (s64) from constant-pool)
+; CHECK:    %42:fpr64 = nofpexcept FMUL_D killed %2, killed %41, 7, implicit $frm
+; CHECK:    FSD killed %42, %1, 0 :: (store (s64) into %ir.lsr.iv1)
+
+; Check that the loop invariant `fld` is hoisted out of the loop.
+; CHECK-LICM: # %bb.0:
+; CHECK-LICM:        lui     a1, %hi(.LCPI0_0)
+; CHECK-LICM:        fld     fa5, %lo(.LCPI0_0)(a1)
+; CHECK-LICM:        lui     a1, 2
+; CHECK-LICM:        add     a1, a0, a1
+; CHECK-LICM:        fmv.d.x fa4, zero
+; CHECK-LICM:        j       .LBB0_2
+; CHECK-LICM: .LBB0_1:
 
 define void @f(ptr %p) {
 entry:



More information about the llvm-commits mailing list