Skip to content

[RISCV] Teach RISCVInsertVSETVLI to work without LiveIntervals #94686

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

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 104 additions & 53 deletions llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@ static cl::opt<bool> DisableInsertVSETVLPHIOpt(
namespace {

/// Given a virtual register \p Reg, return the corresponding VNInfo for it.
/// This will return nullptr if the virtual register is an implicit_def.
/// This will return nullptr if the virtual register is an implicit_def or
/// if LiveIntervals is not available.
static VNInfo *getVNInfoFromReg(Register Reg, const MachineInstr &MI,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, should probably update the comment to mention it will return nullptr if LIS is null too

const LiveIntervals *LIS) {
assert(Reg.isVirtual());
if (!LIS)
return nullptr;
auto &LI = LIS->getInterval(Reg);
SlotIndex SI = LIS->getSlotIndexes()->getInstructionIndex(MI);
return LI.getVNInfoBefore(SI);
Expand Down Expand Up @@ -512,7 +515,8 @@ DemandedFields getDemanded(const MachineInstr &MI, const RISCVSubtarget *ST) {
/// values of the VL and VTYPE registers after insertion.
class VSETVLIInfo {
struct AVLDef {
// Every AVLDef should have a VNInfo.
// Every AVLDef should have a VNInfo, unless we're running without
// LiveIntervals in which case this will be nullptr.
const VNInfo *ValNo;
Register DefReg;
};
Expand All @@ -526,7 +530,7 @@ class VSETVLIInfo {
AVLIsReg,
AVLIsImm,
AVLIsVLMAX,
Unknown,
Unknown, // AVL and VTYPE are fully unknown
} State = Uninitialized;

// Fields from VTYPE.
Expand All @@ -552,7 +556,7 @@ class VSETVLIInfo {
bool isUnknown() const { return State == Unknown; }

void setAVLRegDef(const VNInfo *VNInfo, Register AVLReg) {
assert(VNInfo && AVLReg.isVirtual());
assert(AVLReg.isVirtual());
AVLRegDef.ValNo = VNInfo;
AVLRegDef.DefReg = AVLReg;
State = AVLIsReg;
Expand Down Expand Up @@ -582,9 +586,11 @@ class VSETVLIInfo {
}
// Most AVLIsReg infos will have a single defining MachineInstr, unless it was
// a PHI node. In that case getAVLVNInfo()->def will point to the block
// boundary slot.
// boundary slot. If LiveIntervals isn't available, then nullptr is returned.
const MachineInstr *getAVLDefMI(const LiveIntervals *LIS) const {
assert(hasAVLReg());
if (!LIS)
return nullptr;
auto *MI = LIS->getInstructionFromIndex(getAVLVNInfo()->def);
assert(!(getAVLVNInfo()->isPHIDef() && MI));
return MI;
Expand Down Expand Up @@ -628,10 +634,15 @@ class VSETVLIInfo {
return (hasNonZeroAVL(LIS) && Other.hasNonZeroAVL(LIS));
}

bool hasSameAVL(const VSETVLIInfo &Other) const {
if (hasAVLReg() && Other.hasAVLReg())
bool hasSameAVLLatticeValue(const VSETVLIInfo &Other) const {
if (hasAVLReg() && Other.hasAVLReg()) {
assert(!getAVLVNInfo() == !Other.getAVLVNInfo() &&
"we either have intervals or we don't");
if (!getAVLVNInfo())
return getAVLReg() == Other.getAVLReg();
return getAVLVNInfo()->id == Other.getAVLVNInfo()->id &&
getAVLReg() == Other.getAVLReg();
}

if (hasAVLImm() && Other.hasAVLImm())
return getAVLImm() == Other.getAVLImm();
Expand All @@ -642,6 +653,21 @@ class VSETVLIInfo {
return false;
}

// Return true if the two lattice values are guaranteed to have
// the same AVL value at runtime.
bool hasSameAVL(const VSETVLIInfo &Other) const {
// Without LiveIntervals, we don't know which instruction defines a
// register. Since a register may be redefined, this means all AVLIsReg
// states must be treated as possibly distinct.
if (hasAVLReg() && Other.hasAVLReg()) {
assert(!getAVLVNInfo() == !Other.getAVLVNInfo() &&
"we either have intervals or we don't");
if (!getAVLVNInfo())
return false;
}
return hasSameAVLLatticeValue(Other);
}

void setVTYPE(unsigned VType) {
assert(isValid() && !isUnknown() &&
"Can't set VTYPE for uninitialized or unknown");
Expand Down Expand Up @@ -741,7 +767,7 @@ class VSETVLIInfo {
if (Other.isUnknown())
return isUnknown();

if (!hasSameAVL(Other))
if (!hasSameAVLLatticeValue(Other))
return false;

// If the SEWLMULRatioOnly bits are different, then they aren't equal.
Expand Down Expand Up @@ -849,6 +875,7 @@ class RISCVInsertVSETVLI : public MachineFunctionPass {
const RISCVSubtarget *ST;
const TargetInstrInfo *TII;
MachineRegisterInfo *MRI;
// Possibly null!
LiveIntervals *LIS;

std::vector<BlockData> BlockInfo;
Expand All @@ -863,9 +890,9 @@ class RISCVInsertVSETVLI : public MachineFunctionPass {
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();

AU.addRequired<LiveIntervals>();
AU.addUsedIfAvailable<LiveIntervals>();
AU.addPreserved<LiveIntervals>();
AU.addRequired<SlotIndexes>();
AU.addUsedIfAvailable<SlotIndexes>();
AU.addPreserved<SlotIndexes>();
AU.addPreserved<LiveDebugVariables>();
AU.addPreserved<LiveStacks>();
Expand Down Expand Up @@ -1061,7 +1088,8 @@ void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB,
.addReg(RISCV::X0, RegState::Kill)
.addImm(Info.encodeVTYPE())
.addReg(RISCV::VL, RegState::Implicit);
LIS->InsertMachineInstrInMaps(*MI);
if (LIS)
LIS->InsertMachineInstrInMaps(*MI);
return;
}

Expand All @@ -1078,7 +1106,8 @@ void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB,
.addReg(RISCV::X0, RegState::Kill)
.addImm(Info.encodeVTYPE())
.addReg(RISCV::VL, RegState::Implicit);
LIS->InsertMachineInstrInMaps(*MI);
if (LIS)
LIS->InsertMachineInstrInMaps(*MI);
return;
}
}
Expand All @@ -1090,7 +1119,8 @@ void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB,
.addReg(RISCV::X0, RegState::Define | RegState::Dead)
.addImm(Info.getAVLImm())
.addImm(Info.encodeVTYPE());
LIS->InsertMachineInstrInMaps(*MI);
if (LIS)
LIS->InsertMachineInstrInMaps(*MI);
return;
}

Expand All @@ -1100,8 +1130,10 @@ void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB,
.addReg(DestReg, RegState::Define | RegState::Dead)
.addReg(RISCV::X0, RegState::Kill)
.addImm(Info.encodeVTYPE());
LIS->InsertMachineInstrInMaps(*MI);
LIS->createAndComputeVirtRegInterval(DestReg);
if (LIS) {
LIS->InsertMachineInstrInMaps(*MI);
LIS->createAndComputeVirtRegInterval(DestReg);
}
return;
}

Expand All @@ -1111,12 +1143,14 @@ void RISCVInsertVSETVLI::insertVSETVLI(MachineBasicBlock &MBB,
.addReg(RISCV::X0, RegState::Define | RegState::Dead)
.addReg(AVLReg)
.addImm(Info.encodeVTYPE());
LIS->InsertMachineInstrInMaps(*MI);
// Normally the AVL's live range will already extend past the inserted vsetvli
// because the pseudos below will already use the AVL. But this isn't always
// the case, e.g. PseudoVMV_X_S doesn't have an AVL operand.
LIS->getInterval(AVLReg).extendInBlock(
LIS->getMBBStartIdx(&MBB), LIS->getInstructionIndex(*MI).getRegSlot());
if (LIS) {
LIS->InsertMachineInstrInMaps(*MI);
// Normally the AVL's live range will already extend past the inserted
// vsetvli because the pseudos below will already use the AVL. But this
// isn't always the case, e.g. PseudoVMV_X_S doesn't have an AVL operand.
LIS->getInterval(AVLReg).extendInBlock(
LIS->getMBBStartIdx(&MBB), LIS->getInstructionIndex(*MI).getRegSlot());
}
}

/// Return true if a VSETVLI is required to transition from CurInfo to Require
Expand Down Expand Up @@ -1230,10 +1264,14 @@ void RISCVInsertVSETVLI::transferAfter(VSETVLIInfo &Info,
if (RISCV::isFaultFirstLoad(MI)) {
// Update AVL to vl-output of the fault first load.
assert(MI.getOperand(1).getReg().isVirtual());
auto &LI = LIS->getInterval(MI.getOperand(1).getReg());
SlotIndex SI = LIS->getSlotIndexes()->getInstructionIndex(MI).getRegSlot();
VNInfo *VNI = LI.getVNInfoAt(SI);
Info.setAVLRegDef(VNI, MI.getOperand(1).getReg());
if (LIS) {
auto &LI = LIS->getInterval(MI.getOperand(1).getReg());
SlotIndex SI =
LIS->getSlotIndexes()->getInstructionIndex(MI).getRegSlot();
VNInfo *VNI = LI.getVNInfoAt(SI);
Info.setAVLRegDef(VNI, MI.getOperand(1).getReg());
} else
Info.setAVLRegDef(nullptr, MI.getOperand(1).getReg());
return;
}

Expand Down Expand Up @@ -1327,6 +1365,9 @@ bool RISCVInsertVSETVLI::needVSETVLIPHI(const VSETVLIInfo &Require,
if (!Require.hasAVLReg())
return true;

if (!LIS)
return true;

// We need the AVL to have been produced by a PHI node in this basic block.
const VNInfo *Valno = Require.getAVLVNInfo();
if (!Valno->isPHIDef() || LIS->getMBBFromIndex(Valno->def) != &MBB)
Expand Down Expand Up @@ -1402,27 +1443,29 @@ void RISCVInsertVSETVLI::emitVSETVLIs(MachineBasicBlock &MBB) {
MachineOperand &VLOp = MI.getOperand(getVLOpNum(MI));
if (VLOp.isReg()) {
Register Reg = VLOp.getReg();
LiveInterval &LI = LIS->getInterval(Reg);

// Erase the AVL operand from the instruction.
VLOp.setReg(RISCV::NoRegister);
VLOp.setIsKill(false);
SmallVector<MachineInstr *> DeadMIs;
LIS->shrinkToUses(&LI, &DeadMIs);
// We might have separate components that need split due to
// needVSETVLIPHI causing us to skip inserting a new VL def.
SmallVector<LiveInterval *> SplitLIs;
LIS->splitSeparateComponents(LI, SplitLIs);

// If the AVL was an immediate > 31, then it would have been emitted
// as an ADDI. However, the ADDI might not have been used in the
// vsetvli, or a vsetvli might not have been emitted, so it may be
// dead now.
for (MachineInstr *DeadMI : DeadMIs) {
if (!TII->isAddImmediate(*DeadMI, Reg))
continue;
LIS->RemoveMachineInstrFromMaps(*DeadMI);
DeadMI->eraseFromParent();
if (LIS) {
LiveInterval &LI = LIS->getInterval(Reg);
SmallVector<MachineInstr *> DeadMIs;
LIS->shrinkToUses(&LI, &DeadMIs);
// We might have separate components that need split due to
// needVSETVLIPHI causing us to skip inserting a new VL def.
SmallVector<LiveInterval *> SplitLIs;
LIS->splitSeparateComponents(LI, SplitLIs);

// If the AVL was an immediate > 31, then it would have been emitted
// as an ADDI. However, the ADDI might not have been used in the
// vsetvli, or a vsetvli might not have been emitted, so it may be
// dead now.
for (MachineInstr *DeadMI : DeadMIs) {
if (!TII->isAddImmediate(*DeadMI, Reg))
continue;
LIS->RemoveMachineInstrFromMaps(*DeadMI);
DeadMI->eraseFromParent();
}
}
}
MI.addOperand(MachineOperand::CreateReg(RISCV::VL, /*isDef*/ false,
Expand Down Expand Up @@ -1479,6 +1522,9 @@ void RISCVInsertVSETVLI::doPRE(MachineBasicBlock &MBB) {
if (!UnavailablePred || !AvailableInfo.isValid())
return;

if (!LIS)
return;

// If we don't know the exact VTYPE, we can't copy the vsetvli to the exit of
// the unavailable pred.
if (AvailableInfo.hasSEWLMULRatioOnly())
Expand Down Expand Up @@ -1625,7 +1671,7 @@ void RISCVInsertVSETVLI::coalesceVSETVLIs(MachineBasicBlock &MBB) const {

// The def of DefReg moved to MI, so extend the LiveInterval up to
// it.
if (DefReg.isVirtual()) {
if (DefReg.isVirtual() && LIS) {
LiveInterval &DefLI = LIS->getInterval(DefReg);
SlotIndex MISlot = LIS->getInstructionIndex(MI).getRegSlot();
VNInfo *DefVNI = DefLI.getVNInfoAt(DefLI.beginIndex());
Expand Down Expand Up @@ -1654,13 +1700,15 @@ void RISCVInsertVSETVLI::coalesceVSETVLIs(MachineBasicBlock &MBB) const {

if (OldVLReg && OldVLReg.isVirtual()) {
// NextMI no longer uses OldVLReg so shrink its LiveInterval.
LIS->shrinkToUses(&LIS->getInterval(OldVLReg));
if (LIS)
LIS->shrinkToUses(&LIS->getInterval(OldVLReg));

MachineInstr *VLOpDef = MRI->getUniqueVRegDef(OldVLReg);
if (VLOpDef && TII->isAddImmediate(*VLOpDef, OldVLReg) &&
MRI->use_nodbg_empty(OldVLReg)) {
VLOpDef->eraseFromParent();
LIS->removeInterval(OldVLReg);
if (LIS)
LIS->removeInterval(OldVLReg);
}
}
MI.setDesc(NextMI->getDesc());
Expand All @@ -1676,7 +1724,8 @@ void RISCVInsertVSETVLI::coalesceVSETVLIs(MachineBasicBlock &MBB) const {

NumCoalescedVSETVL += ToDelete.size();
for (auto *MI : ToDelete) {
LIS->RemoveMachineInstrFromMaps(*MI);
if (LIS)
LIS->RemoveMachineInstrFromMaps(*MI);
MI->eraseFromParent();
}
}
Expand All @@ -1691,12 +1740,14 @@ void RISCVInsertVSETVLI::insertReadVL(MachineBasicBlock &MBB) {
auto ReadVLMI = BuildMI(MBB, I, MI.getDebugLoc(),
TII->get(RISCV::PseudoReadVL), VLOutput);
// Move the LiveInterval's definition down to PseudoReadVL.
SlotIndex NewDefSI =
LIS->InsertMachineInstrInMaps(*ReadVLMI).getRegSlot();
LiveInterval &DefLI = LIS->getInterval(VLOutput);
VNInfo *DefVNI = DefLI.getVNInfoAt(DefLI.beginIndex());
DefLI.removeSegment(DefLI.beginIndex(), NewDefSI);
DefVNI->def = NewDefSI;
if (LIS) {
SlotIndex NewDefSI =
LIS->InsertMachineInstrInMaps(*ReadVLMI).getRegSlot();
LiveInterval &DefLI = LIS->getInterval(VLOutput);
VNInfo *DefVNI = DefLI.getVNInfoAt(DefLI.beginIndex());
DefLI.removeSegment(DefLI.beginIndex(), NewDefSI);
DefVNI->def = NewDefSI;
}
}
// We don't use the vl output of the VLEFF/VLSEGFF anymore.
MI.getOperand(1).setReg(RISCV::X0);
Expand All @@ -1714,7 +1765,7 @@ bool RISCVInsertVSETVLI::runOnMachineFunction(MachineFunction &MF) {

TII = ST->getInstrInfo();
MRI = &MF.getRegInfo();
LIS = &getAnalysis<LiveIntervals>();
LIS = getAnalysisIfAvailable<LiveIntervals>();

assert(BlockInfo.empty() && "Expect empty block infos");
BlockInfo.resize(MF.getNumBlockIDs());
Expand Down
3 changes: 0 additions & 3 deletions llvm/test/CodeGen/RISCV/O0-pipeline.ll
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@
; CHECK-NEXT: Eliminate PHI nodes for register allocation
; CHECK-NEXT: Two-Address instruction pass
; CHECK-NEXT: Fast Register Allocator
; CHECK-NEXT: MachineDominator Tree Construction
; CHECK-NEXT: Slot index numbering
; CHECK-NEXT: Live Interval Analysis
; CHECK-NEXT: RISC-V Insert VSETVLI pass
; CHECK-NEXT: Fast Register Allocator
; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis
Expand Down
37 changes: 37 additions & 0 deletions llvm/test/CodeGen/RISCV/rvv/pr93587.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc -mtriple=riscv64 -O0 < %s | FileCheck %s

; Make sure we don't run LiveIntervals at O0, otherwise it will crash when
; running on this unreachable block.

define i16 @f() {
; CHECK-LABEL: f:
; CHECK: # %bb.0: # %BB
; CHECK-NEXT: addi sp, sp, -16
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: j .LBB0_1
; CHECK-NEXT: .LBB0_1: # %BB1
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
; CHECK-NEXT: li a0, 0
; CHECK-NEXT: sd a0, 8(sp) # 8-byte Folded Spill
; CHECK-NEXT: j .LBB0_1
; CHECK-NEXT: # %bb.2: # %BB1
; CHECK-NEXT: li a0, 0
; CHECK-NEXT: bnez a0, .LBB0_1
; CHECK-NEXT: j .LBB0_3
; CHECK-NEXT: .LBB0_3: # %BB2
; CHECK-NEXT: ld a0, 8(sp) # 8-byte Folded Reload
; CHECK-NEXT: addi sp, sp, 16
; CHECK-NEXT: ret
BB:
br label %BB1

BB1:
%A = or i16 0, 0
%B = fcmp true float 0.000000e+00, 0.000000e+00
%C = or i1 %B, false
br i1 %C, label %BB1, label %BB2

BB2:
ret i16 %A
}
Loading
Loading