Skip to content
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
122 changes: 112 additions & 10 deletions llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ struct RISCVLoadStoreOpt : public MachineFunctionPass {
// Convert load/store pairs to single instructions.
bool tryConvertToLdStPair(MachineBasicBlock::iterator First,
MachineBasicBlock::iterator Second);
bool tryConvertToXqcilsmLdStPair(MachineFunction *MF,
MachineBasicBlock::iterator First,
MachineBasicBlock::iterator Second);
bool tryConvertToMIPSLdStPair(MachineFunction *MF,
MachineBasicBlock::iterator First,
MachineBasicBlock::iterator Second);

// Scan the instructions looking for a load/store that can be combined
// with the current instruction into a load/store pair.
Expand Down Expand Up @@ -114,7 +120,7 @@ bool RISCVLoadStoreOpt::runOnMachineFunction(MachineFunction &Fn) {
ModifiedRegUnits.init(*TRI);
UsedRegUnits.init(*TRI);

if (Subtarget.useMIPSLoadStorePairs()) {
if (Subtarget.useMIPSLoadStorePairs() || Subtarget.hasVendorXqcilsm()) {
for (MachineBasicBlock &MBB : Fn) {
LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");

Expand Down Expand Up @@ -168,14 +174,93 @@ bool RISCVLoadStoreOpt::tryToPairLdStInst(MachineBasicBlock::iterator &MBBI) {
return false;
}

// Merge two adjacent load/store instructions into a paired instruction
// (LDP/SDP/SWP/LWP) if the effective address is 8-byte aligned in case of
// SWP/LWP 16-byte aligned in case of LDP/SDP. This function selects the
// appropriate paired opcode, verifies that the memory operand is properly
// aligned, and checks that the offset is valid. If all conditions are met, it
// builds and inserts the paired instruction.
bool RISCVLoadStoreOpt::tryConvertToLdStPair(
MachineBasicBlock::iterator First, MachineBasicBlock::iterator Second) {
bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
MachineFunction *MF, MachineBasicBlock::iterator First,
MachineBasicBlock::iterator Second) {
unsigned Opc = First->getOpcode();
if ((Opc != RISCV::LW && Opc != RISCV::SW) || Second->getOpcode() != Opc)
return false;

const auto &FirstOp1 = First->getOperand(1);
const auto &SecondOp1 = Second->getOperand(1);
const auto &FirstOp2 = First->getOperand(2);
const auto &SecondOp2 = Second->getOperand(2);

// Require simple reg+imm addressing for both.
if (!FirstOp1.isReg() || !SecondOp1.isReg() || !FirstOp2.isImm() ||
!SecondOp2.isImm())
return false;

Register Base1 = FirstOp1.getReg();
Register Base2 = SecondOp1.getReg();

if (Base1 != Base2)
return false;

const MachineMemOperand *MMO = *First->memoperands_begin();
Align MMOAlign = MMO->getAlign();

if (MMOAlign < Align(4))
return false;

auto &FirstOp0 = First->getOperand(0);
auto &SecondOp0 = Second->getOperand(0);

int64_t Off1 = FirstOp2.getImm();
int64_t Off2 = SecondOp2.getImm();

if (Off2 < Off1) {
std::swap(FirstOp0, SecondOp0);
std::swap(Off1, Off2);
}

Register StartReg = FirstOp0.getReg();
Register NextReg = SecondOp0.getReg();

if (StartReg == RISCV::X0 || NextReg == RISCV::X0)
return false;

// If the base reg gets overwritten by one of the loads then bail out.
if (Opc == RISCV::LW && (StartReg == Base1 || NextReg == Base1))
return false;

if (!isShiftedUInt<5, 2>(Off1) || (Off2 - Off1 != 4))
return false;

if (NextReg != StartReg + 1)
return false;

unsigned XqciOpc = (Opc == RISCV::LW) ? RISCV::QC_LWMI : RISCV::QC_SWMI;

auto StartRegState = (Opc == RISCV::LW) ? RegState::Define
: getKillRegState(FirstOp0.isKill());
auto NextRegState =
(Opc == RISCV::LW)
? RegState::ImplicitDefine
: (RegState::Implicit | getKillRegState(SecondOp0.isKill()));

DebugLoc DL =
First->getDebugLoc() ? First->getDebugLoc() : Second->getDebugLoc();
MachineInstrBuilder MIB = BuildMI(*MF, DL, TII->get(XqciOpc));
MIB.addReg(StartReg, StartRegState)
.addReg(Base1, getKillRegState(FirstOp1.isKill() || SecondOp1.isKill()))
.addImm(2)
.addImm(Off1)
.cloneMergedMemRefs({&*First, &*Second})
.addReg(NextReg, NextRegState);

First->getParent()->insert(First, MIB);
First->removeFromParent();
Second->removeFromParent();

return true;
}

bool RISCVLoadStoreOpt::tryConvertToMIPSLdStPair(
MachineFunction *MF, MachineBasicBlock::iterator First,
MachineBasicBlock::iterator Second) {
// Try converting to SWP/LWP/LDP/SDP.
// SWP/LWP requires 8-byte alignment whereas LDP/SDP needs 16-byte alignment.
unsigned PairOpc;
Align RequiredAlignment;
switch (First->getOpcode()) {
Expand All @@ -199,7 +284,6 @@ bool RISCVLoadStoreOpt::tryConvertToLdStPair(
break;
}

MachineFunction *MF = First->getMF();
const MachineMemOperand *MMO = *First->memoperands_begin();
Align MMOAlign = MMO->getAlign();

Expand Down Expand Up @@ -227,6 +311,24 @@ bool RISCVLoadStoreOpt::tryConvertToLdStPair(
return true;
}

// Merge two adjacent load/store instructions into a paired instruction.
// This function calls the vendor specific implementation that seelects the
// appropriate paired opcode, verifies that the memory operand is properly
// aligned, and checks that the offset is valid. If all conditions are met, it
// builds and inserts the paired instruction.
bool RISCVLoadStoreOpt::tryConvertToLdStPair(
MachineBasicBlock::iterator First, MachineBasicBlock::iterator Second) {
MachineFunction *MF = First->getMF();
const RISCVSubtarget &STI = MF->getSubtarget<RISCVSubtarget>();

// Try converting to QC_LWMI/QC_SWMI if the XQCILSM extension is enabled.
if (!STI.is64Bit() && STI.hasVendorXqcilsm())
return tryConvertToXqcilsmLdStPair(MF, First, Second);

// Else try to convert them into MIPS Paired Loads/Stores.
return tryConvertToMIPSLdStPair(MF, First, Second);
}

static bool mayAlias(MachineInstr &MIa,
SmallVectorImpl<MachineInstr *> &MemInsns,
AliasAnalysis *AA) {
Expand Down
Loading