[llvm] [RISCV64] liveness analysis (PR #167454)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 16 12:38:17 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/13] [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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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:
>From 7c36246ab8c94a2850108171750dba19f7603bf4 Mon Sep 17 00:00:00 2001
From: AdityaK <hiraditya at msn.com>
Date: Sun, 16 Nov 2025 12:29:34 -0800
Subject: [PATCH 13/13] Comment
---
llvm/lib/Target/RISCV/RISCVLiveVariables.cpp | 36 +++++++++----------
.../CodeGen/RISCV/live-variables-loops.ll | 2 +-
2 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
index 79397836d1bae..6598db336aa12 100644
--- a/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -10,11 +10,9 @@
// 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
+// The analysis performs a backward dataflow analysis to compute
+// liveness information. Also updates the kill flags on register operands.
+// There is also a verification step to ensure consistency with MBB live-ins.
//
//===----------------------------------------------------------------------===//
@@ -50,6 +48,10 @@ static cl::opt<bool> UpdateKills("riscv-liveness-update-kills",
cl::desc("Update kill flags"), cl::init(false),
cl::Hidden);
+static cl::opt<unsigned> MaxVRegs("riscv-liveness-max-vregs",
+ cl::desc("Maximum VRegs to track"),
+ cl::init(1024), cl::Hidden);
+
namespace {
/// LivenessInfo - Stores liveness information for a basic block
@@ -158,21 +160,16 @@ FunctionPass *llvm::createRISCVLiveVariablesPass(bool PreRegAlloc) {
bool RISCVLiveVariables::isTrackableRegister(
Register Reg, const TargetRegisterInfo *TRI,
const MachineRegisterInfo *MRI) const {
- // Track virtual registers
+ // Track all virtual registers but only allocatable physical registers.
+ // 1. General purpose registers (X0-X31)
+ // 2. Floating point registers (F0-F31)
+ // 3. Vector registers if present
+
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;
- }
+ if (Reg.isPhysical())
+ return TRI->isInAllocatableClass(Reg);
return false;
}
@@ -194,8 +191,7 @@ 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
+ // Only add to Use set if not already defined in this block.
if (Info.Gen.find(Reg) == Info.Gen.end()) {
Info.Use.insert(Reg);
@@ -477,7 +473,7 @@ bool RISCVLiveVariables::runOnMachineFunction(MachineFunction &MF) {
// TODO: Update live-in/live-out sets of MBBs
// Step 3: Mark kill flags on operands
- if (UpdateKills)
+ if (UpdateKills && MaxVRegs >= RegCounter)
markKills(MF);
LLVM_DEBUG({
diff --git a/llvm/test/CodeGen/RISCV/live-variables-loops.ll b/llvm/test/CodeGen/RISCV/live-variables-loops.ll
index a689846baccbd..2cc0ff65c74b3 100644
--- a/llvm/test/CodeGen/RISCV/live-variables-loops.ll
+++ b/llvm/test/CodeGen/RISCV/live-variables-loops.ll
@@ -159,4 +159,4 @@ loop:
exit:
ret i64 %sum.next
-}
\ No newline at end of file
+}
More information about the llvm-commits
mailing list