From 061d856cd8514d4be23380e34c3138c00a6d25ca Mon Sep 17 00:00:00 2001 From: AdityaK 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 + +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 LiveIn; + + /// Registers that are live out of this block. + /// LiveOut[B] = U LiveIns[∀ Succ(B)]. + std::set LiveOut; + + /// Registers that are defined in this block + std::set Def; + + /// Registers that are used in this block before being defined (if at all). + std::set 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 &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 &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 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 RPOT(&MF); + + for (MachineBasicBlock *MBB : RPOT) { + LivenessInfo &Info = BlockLiveness[MBB]; + std::set OldLiveIn = Info.LiveIn; + std::set 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 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(); + + // 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 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 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 LiveOut; /// Registers that are defined in this block - std::set Def; + std::set Gen; /// Registers that are used in this block before being defined (if at all). std::set 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 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 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 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 @@ -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 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 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(); @@ -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 cl::desc("Enable Machine Pipeliner for RISC-V"), cl::init(false), cl::Hidden); +static cl::opt +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 X(getTheRISCV32Target()); RegisterTargetMachine 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 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 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 cl::desc("Enable Machine Pipeliner for RISC-V"), cl::init(false), cl::Hidden); -static cl::opt -EnableRISCVLiveVariables("riscv-live-variables", - cl::desc("Enable Live Variable Analysis for RISC-V"), - cl::init(false), cl::Hidden); +static cl::opt 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 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 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 #include 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 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 TrackedRegisters; + /// Liveness information for each basic block DenseMap 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 RPOT(&MF); - - for (MachineBasicBlock *MBB : RPOT) { + for (MachineBasicBlock *MBB : post_order(&MF)) { LivenessInfo &Info = BlockLiveness[MBB]; std::set OldLiveIn = Info.LiveIn; std::set 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 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 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 #include +#include 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 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 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 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 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 UpdateKills("riscv-liveness-update-kills", cl::desc("Update kill flags"), cl::init(false), cl::Hidden); +static cl::opt 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 +}