-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[RISCV64] liveness analysis #167454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[RISCV64] liveness analysis #167454
Conversation
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
4bde964 to
e9152c8
Compare
|
@llvm/pr-subscribers-backend-risc-v Author: AdityaK (hiraditya) ChangesPatch is 49.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167454.diff 10 Files Affected:
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..5a9f192ad23ce
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVLiveVariables.cpp
@@ -0,0 +1,444 @@
+//===- 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/PostOrderIterator.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "riscv-live-variables"
+#define RISCV_LIVE_VARIABLES_NAME "RISC-V Live Variable Analysis"
+
+STATISTIC(NumLiveRegsAtEntry, "Number of registers live at function entry");
+STATISTIC(NumLiveRegsTotal, "Total number of live registers across all blocks");
+
+namespace {
+
+/// LivenessInfo - Stores liveness information for a basic block
+/// TODO: Optimize storage using BitVectors for large register sets.
+struct LivenessInfo {
+ /// Registers that are live into this block
+ /// LiveIn[B] = Use[B] U (LiveOut[B] - Def[B])
+ std::set<Register> LiveIn;
+
+ /// Registers that are live out of this block.
+ /// LiveOut[B] = U LiveIns[∀ Succ(B)].
+ std::set<Register> LiveOut;
+
+ /// Registers that are defined in this block
+ std::set<Register> Gen;
+
+ /// Registers that are used in this block before being defined (if at all).
+ std::set<Register> Use;
+};
+
+class RISCVLiveVariables : public MachineFunctionPass {
+public:
+ static char ID;
+
+ RISCVLiveVariables() : MachineFunctionPass(ID) {
+ initializeRISCVLiveVariablesPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+
+ StringRef getPassName() const override { return RISCV_LIVE_VARIABLES_NAME; }
+
+ /// Returns the set of registers live at the entry of the given basic block
+ const std::set<Register> &getLiveInSet(const MachineBasicBlock *MBB) const {
+ auto It = BlockLiveness.find(MBB);
+ assert(It != BlockLiveness.end() && "Block not analyzed");
+ return It->second.LiveIn;
+ }
+
+ /// Returns the set of registers live at the exit of the given basic block
+ const std::set<Register> &getLiveOutSet(const MachineBasicBlock *MBB) const {
+ auto It = BlockLiveness.find(MBB);
+ assert(It != BlockLiveness.end() && "Block not analyzed");
+ return It->second.LiveOut;
+ }
+
+ /// Check if a register is live at a specific instruction
+ bool isLiveAt(Register Reg, const MachineInstr &MI) const;
+
+ /// Print liveness information for debugging
+ void print(raw_ostream &OS, const Module *M = nullptr) const override;
+
+ void verifyLiveness(MachineFunction &MF) const;
+
+private:
+ /// Compute local liveness information (Use and Def sets) for each block
+ void computeLocalLiveness(MachineFunction &MF);
+
+ /// Compute global liveness information (LiveIn and LiveOut sets)
+ void computeGlobalLiveness(MachineFunction &MF);
+
+ /// Process a single instruction to extract def/use information
+ void processInstruction(const MachineInstr &MI, LivenessInfo &Info,
+ const TargetRegisterInfo *TRI);
+
+ /// Check if a register is allocatable (relevant for liveness tracking)
+ bool isTrackableRegister(Register Reg, const TargetRegisterInfo *TRI,
+ const MachineRegisterInfo *MRI) const;
+
+ /// Liveness information for each basic block
+ DenseMap<const MachineBasicBlock *, LivenessInfo> BlockLiveness;
+
+ /// Cached pointer to MachineRegisterInfo
+ const MachineRegisterInfo *MRI;
+
+ /// Cached pointer to TargetRegisterInfo
+ const TargetRegisterInfo *TRI;
+};
+
+} // end anonymous namespace
+
+char RISCVLiveVariables::ID = 0;
+
+INITIALIZE_PASS(RISCVLiveVariables, DEBUG_TYPE, RISCV_LIVE_VARIABLES_NAME,
+ false, true)
+
+FunctionPass *llvm::createRISCVLiveVariablesPass() {
+ return new RISCVLiveVariables();
+}
+
+bool RISCVLiveVariables::isTrackableRegister(
+ Register Reg, const TargetRegisterInfo *TRI,
+ const MachineRegisterInfo *MRI) const {
+ // Track virtual registers
+ if (Reg.isVirtual())
+ return true;
+
+ // For physical registers, only track allocatable ones
+ if (Reg.isPhysical()) {
+ // Check if register is allocatable
+ if (!TRI->isInAllocatableClass(Reg))
+ return false;
+
+ // Track general purpose registers (X0-X31)
+ // Track floating point registers (F0-F31)
+ // Track vector registers for RVV if present
+ return true;
+ }
+
+ return false;
+}
+
+void RISCVLiveVariables::processInstruction(const MachineInstr &MI,
+ LivenessInfo &Info,
+ const TargetRegisterInfo *TRI) {
+ std::vector<Register> GenVec;
+ 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.isUse()) {
+ // 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);
+
+ // Also handle sub-registers for physical registers
+ if (Reg.isPhysical()) {
+ for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+ SubRegs.isValid(); ++SubRegs) {
+ if (Info.Gen.find(*SubRegs) == Info.Gen.end()) {
+ Info.Use.insert(*SubRegs);
+ }
+ }
+ }
+ }
+ }
+
+ // Handle implicit operands (like condition codes, stack pointer updates)
+ if (MO.isImplicit() && MO.isUse() && Reg.isPhysical()) {
+ if (Info.Gen.find(Reg) == Info.Gen.end()) {
+ Info.Use.insert(Reg);
+ }
+ }
+
+ if (MO.isDef()) // Collect defs for later processing.
+ GenVec.push_back(Reg);
+ }
+
+ for (auto Reg : GenVec) {
+ Info.Gen.insert(Reg);
+ if (Reg.isPhysical()) {
+ for (MCSubRegIterator SubRegs(Reg, TRI, /*IncludeSelf=*/false);
+ SubRegs.isValid(); ++SubRegs) {
+ Info.Gen.insert(*SubRegs);
+ }
+ }
+ }
+
+ // Handle RegMasks (from calls) - they kill all non-preserved registers
+ for (const MachineOperand &MO : MI.operands()) {
+ if (MO.isRegMask()) {
+ const uint32_t *RegMask = MO.getRegMask();
+
+ // Iterate through all physical registers
+ for (unsigned PhysReg = 1; PhysReg < TRI->getNumRegs(); ++PhysReg) {
+ // If the register is not preserved by this mask, it's clobbered
+ if (!MachineOperand::clobbersPhysReg(RegMask, PhysReg))
+ continue;
+
+ // Mark as defined (clobbered)
+ if (isTrackableRegister(Register(PhysReg), TRI, MRI)) {
+ Info.Gen.insert(Register(PhysReg));
+ }
+ }
+ }
+ }
+}
+
+void RISCVLiveVariables::computeLocalLiveness(MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "Computing local liveness for " << MF.getName() << "\n");
+
+ // Process each basic block
+ for (MachineBasicBlock &MBB : MF) {
+ LivenessInfo &Info = BlockLiveness[&MBB];
+ Info.Gen.clear();
+ Info.Use.clear();
+
+ // Process instructions in forward order to build Use and Def sets
+ 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.Gen)
+ dbgs() << printReg(Reg, TRI) << " ";
+ dbgs() << "\n";
+ });
+ }
+}
+
+void RISCVLiveVariables::computeGlobalLiveness(MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "Computing global liveness (fixed-point iteration)\n");
+
+ bool Changed = true;
+ [[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
+ // Live-in[B] = Use[B] Union (Live-out[B] - Def[B])
+ while (Changed) {
+ Changed = false;
+ ++Iterations;
+
+ // Process blocks in **post-order** for better convergence
+ ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
+
+ for (MachineBasicBlock *MBB : RPOT) {
+ LivenessInfo &Info = BlockLiveness[MBB];
+ std::set<Register> OldLiveIn = Info.LiveIn;
+ std::set<Register> NewLiveOut;
+
+ // Compute Live-out: Union of Live-in of all successors
+ for (MachineBasicBlock *Succ : MBB->successors()) {
+ LivenessInfo &SuccInfo = BlockLiveness[Succ];
+ NewLiveOut.insert(SuccInfo.LiveIn.begin(), SuccInfo.LiveIn.end());
+ }
+
+ Info.LiveOut = NewLiveOut;
+
+ // Compute Live-in: Use Union (Live-out - Def)
+ std::set<Register> NewLiveIn = Info.Use;
+
+ for (Register Reg : Info.LiveOut) {
+ if (Info.Gen.find(Reg) == Info.Gen.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;
+}
+
+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()) || MF.empty())
+ return false;
+
+ const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
+
+ // Verify we're targeting RV64
+ if (!Subtarget.is64Bit()) {
+ LLVM_DEBUG(dbgs() << "Warning: RISCVLiveVariables optimized for RV64, "
+ << "but running on RV32\n");
+ return false;
+ }
+
+ MRI = &MF.getRegInfo();
+ TRI = Subtarget.getRegisterInfo();
+
+ LLVM_DEBUG(dbgs() << "***** RISC-V Live Variable Analysis *****\n");
+ LLVM_DEBUG(dbgs() << "Function: " << MF.getName() << "\n");
+
+ // 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());
+ });
+
+ verifyLiveness(MF);
+ // 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.Gen) {
+ 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..1adf930fbaa03 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -103,6 +103,11 @@ static cl::opt<bool>
cl::desc("Enable Machine Pipeliner for RISC-V"),
cl::init(false), cl::Hidden);
+static cl::opt<bool> EnableRISCVLiveVariables(
+ "riscv-enable-live-variables",
+ cl::desc("Enable Live Variable Analysis for RISC-V"), cl::init(false),
+ cl::Hidden);
+
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
RegisterTargetMachine<RISCVTargetMachine> X(getTheRISCV32Target());
RegisterTargetMachine<RISCVTargetMachine> Y(getTheRISCV64Target());
@@ -115,6 +120,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
initializeRISCVPostLegalizerCombinerPass(*PR);
initializeKCFIPass(*PR);
initializeRISCVDeadRegisterDefinitionsPass(*PR);
+ initializeRISCVLiveVariablesPass(*PR);
initializeRISCVLateBranchOptPass(*PR);
initializeRISCVMakeCompressibleOptPass(*PR);
initializeRISCVGatherScatterLoweringPass(*PR);
@@ -618,6 +624,9 @@ void RISCVPassConfig::addPostRegAlloc() {
if (TM->getOptLevel() != CodeGenOptLevel::None &&
EnableRedundantCopyElimination)
addPass(createRISCVRedundantCopyEliminationPass());
+
+ 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
new file mode 100644
index 0000000000000..3339b04602953
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/live-variables-basic.mir
@@ -0,0 +1,160 @@
+# 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
+
+# REQUIRES: Assertions
+
+--- |
+ 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
+ }
+
+ 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
+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 b...
[truncated]
|
|
Forgot the issue where there was a discussion on how liveness analysis will help. Please tag the ticket if someone remembers. |
I think it was #166141 but that was PreRA LICM. |
Add an mir test
dcab4e3 to
d0e5b45
Compare
No description provided.