blob: f05f50b2489c9205ab5110b875637838f7d6f825 [file]
//===-- RISCVMoveMerger.cpp - RISC-V move merge pass ----------------------===//
//
// 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 contains a pass that performs move related peephole optimizations
// as Zcmp has specified. This pass should be run after register allocation.
//
// This pass also supports Xqccmp, which has identical instructions.
//
//===----------------------------------------------------------------------===//
#include "RISCVInstrInfo.h"
#include "RISCVSubtarget.h"
using namespace llvm;
#define RISCV_MOVE_MERGE_NAME "RISC-V Zcmp move merging pass"
namespace {
struct RISCVMoveMerge : public MachineFunctionPass {
static char ID;
RISCVMoveMerge() : MachineFunctionPass(ID) {}
const RISCVSubtarget *ST;
const RISCVInstrInfo *TII;
const TargetRegisterInfo *TRI;
// Track which register units have been modified and used.
LiveRegUnits ModifiedRegUnits, UsedRegUnits;
bool isGPRPairCopyCandidate(const DestSourcePair &RegPair, bool EvenRegPair);
bool isCandidateToMergeMVA01S(const DestSourcePair &RegPair);
bool isCandidateToMergeMVSA01(const DestSourcePair &RegPair);
bool isPLIPairCandidate(const MachineInstr &MI, bool EvenRegPair);
// Merge the two instructions indicated into a single pair instruction.
MachineBasicBlock::iterator
mergeGPRPairInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired, bool RegPairIsEven);
MachineBasicBlock::iterator
mergePairedInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired, bool MoveFromSToA);
MachineBasicBlock::iterator mergePLIPair(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired,
bool RegPairIsEven);
MachineBasicBlock::iterator
findMatchingGPRPairCopy(MachineBasicBlock::iterator &MBBI, bool EvenRegPair,
const DestSourcePair &RegPair);
// Look for C.MV instruction that can be combined with
// the given instruction into CM.MVA01S or CM.MVSA01. Return the matching
// instruction if one exists.
MachineBasicBlock::iterator
findMatchingSACopy(MachineBasicBlock::iterator &MBBI, bool MoveFromSToA,
const DestSourcePair &RegPair);
MachineBasicBlock::iterator findMatchingPLI(MachineBasicBlock::iterator &MBBI,
bool EvenRegPair);
bool mergeMovePairs(MachineBasicBlock &MBB);
bool runOnMachineFunction(MachineFunction &Fn) override;
StringRef getPassName() const override { return RISCV_MOVE_MERGE_NAME; }
};
char RISCVMoveMerge::ID = 0;
} // end of anonymous namespace
INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge", RISCV_MOVE_MERGE_NAME,
false, false)
static unsigned getCM_MVOpcode(const RISCVSubtarget &ST, bool MoveFromSToA) {
if (ST.hasStdExtZcmp())
return MoveFromSToA ? RISCV::CM_MVA01S : RISCV::CM_MVSA01;
if (ST.hasVendorXqccmp())
return MoveFromSToA ? RISCV::QC_CM_MVA01S : RISCV::QC_CM_MVSA01;
llvm_unreachable("Unhandled subtarget with paired move.");
}
// Returns 0 if Opc has no paired form.
static unsigned getPairedPLIOpcode(unsigned Opc) {
switch (Opc) {
case RISCV::PLI_B:
return RISCV::PLI_DB;
case RISCV::PLI_H:
return RISCV::PLI_DH;
case RISCV::PLUI_H:
return RISCV::PLUI_DH;
default:
return 0;
}
}
bool RISCVMoveMerge::isGPRPairCopyCandidate(const DestSourcePair &RegPair,
bool EvenRegPair) {
Register Destination = RegPair.Destination->getReg();
Register Source = RegPair.Source->getReg();
if (Source == Destination)
return false;
if ((!ST->hasStdExtZdinx() && !ST->hasStdExtP()) || ST->is64Bit())
return false;
unsigned SubIdx = EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
Register SrcPair =
TRI->getMatchingSuperReg(Source, SubIdx, &RISCV::GPRPairRegClass);
Register DestPair =
TRI->getMatchingSuperReg(Destination, SubIdx, &RISCV::GPRPairRegClass);
return SrcPair.isValid() && DestPair.isValid();
}
// Check if registers meet CM.MVA01S constraints.
bool RISCVMoveMerge::isCandidateToMergeMVA01S(const DestSourcePair &RegPair) {
Register Destination = RegPair.Destination->getReg();
Register Source = RegPair.Source->getReg();
// If destination is not a0 or a1.
if ((ST->hasStdExtZcmp() || ST->hasVendorXqccmp()) &&
(Destination == RISCV::X10 || Destination == RISCV::X11) &&
RISCV::GPRS07RegClass.contains(Source))
return true;
return false;
}
// Check if registers meet CM.MVSA01 constraints.
bool RISCVMoveMerge::isCandidateToMergeMVSA01(const DestSourcePair &RegPair) {
Register Destination = RegPair.Destination->getReg();
Register Source = RegPair.Source->getReg();
// If Source is s0 - s7.
if ((ST->hasStdExtZcmp() || ST->hasVendorXqccmp()) &&
(Source == RISCV::X10 || Source == RISCV::X11) &&
RISCV::GPRS07RegClass.contains(Destination))
return true;
return false;
}
// Check if MI is a single-reg pli/plui whose destination is a half of a
// GPRPair.
bool RISCVMoveMerge::isPLIPairCandidate(const MachineInstr &MI,
bool EvenRegPair) {
if (!ST->hasStdExtP() || ST->is64Bit())
return false;
if (!getPairedPLIOpcode(MI.getOpcode()))
return false;
unsigned SubIdx = EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
return TRI
->getMatchingSuperReg(MI.getOperand(0).getReg(), SubIdx,
&RISCV::GPRPairRegClass)
.isValid();
}
MachineBasicBlock::iterator
RISCVMoveMerge::mergeGPRPairInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired,
bool RegPairIsEven) {
MachineBasicBlock::iterator E = I->getParent()->end();
MachineBasicBlock::iterator NextI = next_nodbg(I, E);
DestSourcePair FirstPair = *TII->isCopyInstrImpl(*I);
DestSourcePair SecondPair = *TII->isCopyInstrImpl(*Paired);
if (NextI == Paired)
NextI = next_nodbg(NextI, E);
DebugLoc DL = I->getDebugLoc();
// Make a copy of the second instruction to update the kill
// flag.
MachineOperand PairedSource = *SecondPair.Source;
for (auto It = std::next(I); It != Paired && PairedSource.isKill(); ++It)
if (It->readsRegister(PairedSource.getReg(), TRI))
PairedSource.setIsKill(false);
unsigned GPRPairIdx =
RegPairIsEven ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
MCRegister SrcReg = TRI->getMatchingSuperReg(
FirstPair.Source->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
MCRegister DestReg = TRI->getMatchingSuperReg(
FirstPair.Destination->getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
bool KillSrc = PairedSource.isKill() && FirstPair.Source->isKill();
TII->copyPhysReg(*I->getParent(), I, DL, DestReg, SrcReg, KillSrc);
I->eraseFromParent();
Paired->eraseFromParent();
return NextI;
}
MachineBasicBlock::iterator
RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired,
bool MoveFromSToA) {
const MachineOperand *Sreg1, *Sreg2;
MachineBasicBlock::iterator E = I->getParent()->end();
MachineBasicBlock::iterator NextI = next_nodbg(I, E);
DestSourcePair FirstPair = *TII->isCopyInstrImpl(*I);
DestSourcePair PairedRegs = *TII->isCopyInstrImpl(*Paired);
if (NextI == Paired)
NextI = next_nodbg(NextI, E);
DebugLoc DL = I->getDebugLoc();
// Make a copy so we can update the kill flag in the MoveFromSToA case. The
// copied operand needs to be scoped outside the if since we make a pointer
// to it.
MachineOperand PairedSource = *PairedRegs.Source;
// The order of S-reg depends on which instruction holds A0, instead of
// the order of register pair.
// e,g.
// mv a1, s1
// mv a0, s2 => cm.mva01s s2,s1
//
// mv a0, s2
// mv a1, s1 => cm.mva01s s2,s1
unsigned Opcode = getCM_MVOpcode(*ST, MoveFromSToA);
if (MoveFromSToA) {
// We are moving one of the copies earlier so its kill flag may become
// invalid. Clear the copied kill flag if there are any reads of the
// register between the new location and the old location.
for (auto It = std::next(I); It != Paired && PairedSource.isKill(); ++It)
if (It->readsRegister(PairedSource.getReg(), TRI))
PairedSource.setIsKill(false);
Sreg1 = FirstPair.Source;
Sreg2 = &PairedSource;
if (FirstPair.Destination->getReg() != RISCV::X10)
std::swap(Sreg1, Sreg2);
} else {
Sreg1 = FirstPair.Destination;
Sreg2 = PairedRegs.Destination;
if (FirstPair.Source->getReg() != RISCV::X10)
std::swap(Sreg1, Sreg2);
}
BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2);
I->eraseFromParent();
Paired->eraseFromParent();
return NextI;
}
MachineBasicBlock::iterator
RISCVMoveMerge::mergePLIPair(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired,
bool RegPairIsEven) {
MachineBasicBlock::iterator E = I->getParent()->end();
MachineBasicBlock::iterator NextI = next_nodbg(I, E);
if (NextI == Paired)
NextI = next_nodbg(NextI, E);
DebugLoc DL = I->getDebugLoc();
unsigned Opcode = getPairedPLIOpcode(I->getOpcode());
unsigned GPRPairIdx =
RegPairIsEven ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
Register DestReg = TRI->getMatchingSuperReg(
I->getOperand(0).getReg(), GPRPairIdx, &RISCV::GPRPairRegClass);
BuildMI(*I->getParent(), I, DL, TII->get(Opcode), DestReg)
.addImm(I->getOperand(1).getImm());
I->eraseFromParent();
Paired->eraseFromParent();
return NextI;
}
MachineBasicBlock::iterator
RISCVMoveMerge::findMatchingGPRPairCopy(MachineBasicBlock::iterator &MBBI,
bool EvenRegPair,
const DestSourcePair &RegPair) {
MachineBasicBlock::iterator E = MBBI->getParent()->end();
ModifiedRegUnits.clear();
UsedRegUnits.clear();
unsigned RegPairIdx = EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
unsigned SecondPairIdx =
!EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
// Get the expected source/destination registers of the matching lane.
Register SrcGPRPair = TRI->getMatchingSuperReg(
RegPair.Source->getReg(), RegPairIdx, &RISCV::GPRPairRegClass);
Register DestGPRPair = TRI->getMatchingSuperReg(
RegPair.Destination->getReg(), RegPairIdx, &RISCV::GPRPairRegClass);
Register ExpectedSourceReg = TRI->getSubReg(SrcGPRPair, SecondPairIdx);
Register ExpectedDestReg = TRI->getSubReg(DestGPRPair, SecondPairIdx);
for (MachineBasicBlock::iterator I = next_nodbg(MBBI, E); I != E;
I = next_nodbg(I, E)) {
MachineInstr &MI = *I;
if (auto SecondPair = TII->isCopyInstrImpl(MI)) {
Register SourceReg = SecondPair->Source->getReg();
Register DestReg = SecondPair->Destination->getReg();
if (RegPair.Destination->getReg() == DestReg ||
RegPair.Source->getReg() == SourceReg)
return E;
// Check if the second pair's registers match the other lane of the
// GPRPairs.
if (SourceReg == ExpectedSourceReg && DestReg == ExpectedDestReg)
return I;
}
// Update modified / used register units.
LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
// Once expected lane registers are clobbered/read in-between, we can stop
// scanning since the pair cannot be legally merged anymore.
if (!ModifiedRegUnits.available(ExpectedDestReg) ||
!UsedRegUnits.available(ExpectedDestReg) ||
!ModifiedRegUnits.available(ExpectedSourceReg))
return E;
}
return E;
}
MachineBasicBlock::iterator
RISCVMoveMerge::findMatchingSACopy(MachineBasicBlock::iterator &MBBI,
bool MoveFromSToA,
const DestSourcePair &RegPair) {
MachineBasicBlock::iterator E = MBBI->getParent()->end();
// Track which register units have been modified and used between the first
// insn and the second insn.
ModifiedRegUnits.clear();
UsedRegUnits.clear();
for (MachineBasicBlock::iterator I = next_nodbg(MBBI, E); I != E;
I = next_nodbg(I, E)) {
MachineInstr &MI = *I;
if (auto SecondPair = TII->isCopyInstrImpl(MI)) {
Register SourceReg = SecondPair->Source->getReg();
Register DestReg = SecondPair->Destination->getReg();
bool IsCandidate = MoveFromSToA ? isCandidateToMergeMVA01S(*SecondPair)
: isCandidateToMergeMVSA01(*SecondPair);
if (IsCandidate) {
// Second destination must be different.
if (RegPair.Destination->getReg() == DestReg)
return E;
// For AtoS the source must also be different.
if (!MoveFromSToA && RegPair.Source->getReg() == SourceReg)
return E;
// If paired destination register was modified or used, the source reg
// was modified, there is no possibility of finding matching
// instruction so exit early.
if (!ModifiedRegUnits.available(DestReg) ||
!UsedRegUnits.available(DestReg) ||
!ModifiedRegUnits.available(SourceReg))
return E;
return I;
}
}
// Update modified / used register units.
LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
}
return E;
}
// Look for a same-opcode pli/plui writing the other lane of the same GPRPair
// with the same immediate. Return the matching instruction if one exists.
MachineBasicBlock::iterator
RISCVMoveMerge::findMatchingPLI(MachineBasicBlock::iterator &MBBI,
bool EvenRegPair) {
MachineBasicBlock::iterator E = MBBI->getParent()->end();
ModifiedRegUnits.clear();
UsedRegUnits.clear();
unsigned Opc = MBBI->getOpcode();
Register FirstDestReg = MBBI->getOperand(0).getReg();
int64_t FirstImm = MBBI->getOperand(1).getImm();
unsigned RegPairIdx = EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
unsigned SecondPairIdx =
!EvenRegPair ? RISCV::sub_gpr_even : RISCV::sub_gpr_odd;
// Get the expected destination register of the matching lane.
Register DestGPRPair = TRI->getMatchingSuperReg(FirstDestReg, RegPairIdx,
&RISCV::GPRPairRegClass);
Register ExpectedDestReg = TRI->getSubReg(DestGPRPair, SecondPairIdx);
for (MachineBasicBlock::iterator I = next_nodbg(MBBI, E); I != E;
I = next_nodbg(I, E)) {
MachineInstr &MI = *I;
if (MI.getOpcode() == Opc) {
Register DestReg = MI.getOperand(0).getReg();
int64_t Imm = MI.getOperand(1).getImm();
if (FirstDestReg == DestReg)
return E;
// Check if the second PLI matches the other lane and immediate.
if (DestReg == ExpectedDestReg && Imm == FirstImm)
return I;
}
// Update modified / used register units.
LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
// Once the expected lane register is clobbered/read in-between, we can
// stop scanning since the pair cannot be legally merged anymore.
if (!ModifiedRegUnits.available(ExpectedDestReg) ||
!UsedRegUnits.available(ExpectedDestReg))
return E;
}
return E;
}
// Finds instructions, which could be represented as C.MV instructions and
// merged into CM.MVA01S or CM.MVSA01.
bool RISCVMoveMerge::mergeMovePairs(MachineBasicBlock &MBB) {
bool Modified = false;
for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E;) {
// Try merging a pair of single-reg PLI/PLUI into a paired form.
bool IsPLIEven = isPLIPairCandidate(*MBBI, /*EvenRegPair=*/true);
bool IsPLIOdd = isPLIPairCandidate(*MBBI, /*EvenRegPair=*/false);
if (IsPLIEven != IsPLIOdd) {
MachineBasicBlock::iterator Paired = findMatchingPLI(MBBI, IsPLIEven);
if (Paired != E) {
MBBI = mergePLIPair(MBBI, Paired, IsPLIEven);
Modified = true;
continue;
}
}
// Check if the instruction can be compressed to C.MV instruction. If it
// can, return Dest/Src register pair.
auto RegPair = TII->isCopyInstrImpl(*MBBI);
if (RegPair.has_value()) {
bool MoveFromSToA = isCandidateToMergeMVA01S(*RegPair);
bool MoveFromAToS = isCandidateToMergeMVSA01(*RegPair);
bool IsEven = isGPRPairCopyCandidate(*RegPair, /*EvenRegPair=*/true);
bool IsOdd = isGPRPairCopyCandidate(*RegPair, /*EvenRegPair=*/false);
if (!MoveFromSToA && !MoveFromAToS && !IsEven && !IsOdd) {
++MBBI;
continue;
}
MachineBasicBlock::iterator Paired = E;
if (MoveFromSToA || MoveFromAToS) {
Paired = findMatchingSACopy(MBBI, MoveFromSToA, *RegPair);
if (Paired != E) {
MBBI = mergePairedInsns(MBBI, Paired, MoveFromSToA);
Modified = true;
continue;
}
}
if (IsEven != IsOdd) {
Paired = findMatchingGPRPairCopy(MBBI, IsEven, *RegPair);
if (Paired != E) {
MBBI = mergeGPRPairInsns(MBBI, Paired, IsEven);
Modified = true;
continue;
}
}
}
++MBBI;
}
return Modified;
}
bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) {
if (skipFunction(Fn.getFunction()))
return false;
ST = &Fn.getSubtarget<RISCVSubtarget>();
bool HasGPRPairCopy =
!ST->is64Bit() && (ST->hasStdExtZdinx() || ST->hasStdExtP());
if (!ST->hasStdExtZcmp() && !ST->hasVendorXqccmp() && !HasGPRPairCopy)
return false;
TII = ST->getInstrInfo();
TRI = ST->getRegisterInfo();
// Resize the modified and used register unit trackers. We do this once
// per function and then clear the register units each time we optimize a
// move.
ModifiedRegUnits.init(*TRI);
UsedRegUnits.init(*TRI);
bool Modified = false;
for (auto &MBB : Fn)
Modified |= mergeMovePairs(MBB);
return Modified;
}
/// createRISCVMoveMergePass - returns an instance of the
/// move merge pass.
FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); }