[llvm] [RISCV64] liveness analysis (PR #167454)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 24 10:38:46 PST 2025
================
@@ -0,0 +1,598 @@
+//===- 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
+// liveness information. Also updates the kill flags on register operands.
+// There is also a verification step to ensure consistency with MBB live-ins.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#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/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <set>
+#include <unordered_map>
+
+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");
+
+static cl::opt<bool> UpdateKills("riscv-liveness-update-kills",
+ cl::desc("Update kill flags"), cl::init(false),
+ cl::Hidden);
+
+static cl::opt<bool> UpdateLiveIns("riscv-liveness-update-mbb-liveins",
+ cl::desc("Update MBB live-in sets"),
+ 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);
+
+static cl::opt<unsigned> VerifyLiveness("riscv-liveness-verify",
+ cl::desc("Verify liveness information"),
+ cl::init(true), cl::Hidden);
+
+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> Gen;
+
+ /// 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(bool PreRegAlloc)
+ : MachineFunctionPass(ID), PreRegAlloc(PreRegAlloc) {
+ initializeRISCVLiveVariablesPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ 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;
+
+ /// Verify the computed liveness information against MBB live-ins.
+ /// TODO: Extend verification to live-outs and instruction-level liveness.
+ void verifyLiveness(MachineFunction &MF) const;
+
+ /// Mark operands that kill a register
+ /// TODO: Add and remove kill flags as necessary.
+ bool markKills(MachineFunction &MF);
+
+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);
+
+ /// Update MBB live-in sets based on computed liveness information
+ bool updateMBBLiveIns(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;
+
+ 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;
+
+ /// 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(bool PreRegAlloc) {
+ return new RISCVLiveVariables(PreRegAlloc);
+}
+
+bool RISCVLiveVariables::isTrackableRegister(
+ Register Reg, const TargetRegisterInfo *TRI,
+ const MachineRegisterInfo *MRI) const {
+ // 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;
+
+ if (Reg.isPhysical())
+ return TRI->isInAllocatableClass(Reg);
+
+ return false;
+}
+
+void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
+ LivenessInfo &Info,
+ const TargetRegisterInfo *TRI) {
+ std::vector<Register> GenVec;
+ for (const MachineOperand &MO : MI.operands()) {
+ if (!MO.isReg() || !MO.getReg())
+ continue;
+
+ Register Reg = MO.getReg();
+
+ TrackedRegisters.insert(std::pair(Reg, RegCounter++));
+
+ // Skip non-trackable registers
+ if (!isTrackableRegister(Reg, TRI, MRI))
+ continue;
+
+ if (MO.isUse()) {
+ // Only add to Use set if not already defined in this block.
+ 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.Gen.find(*SubRegs) == Info.Gen.end()) {
+ Info.Use.insert(*SubRegs);
+ }
+ }
+ }
+ }
+ }
+
+ // Handle implicit operands (like condition codes, stack pointer updates)
+ if (MO.isImplicit() && MO.isUse() && Reg.isPhysical()) {
+ if (Info.Gen.find(Reg) == Info.Gen.end()) {
+ Info.Use.insert(Reg);
+ }
+ }
+
+ if (MO.isDef()) // Collect defs for later processing.
+ GenVec.push_back(Reg);
+ }
+
+ 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);
+ }
+ }
+ }
+
+ // 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.Gen.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.Gen.clear();
+ Info.Use.clear();
+
+ // Process instructions in forward order to build Use and Def sets
----------------
arsenm wrote:
Liveness should be done in reverse
https://github.com/llvm/llvm-project/pull/167454
More information about the llvm-commits
mailing list