Skip to content

Commit

Permalink
[RISCV] Add a pass to merge moving parameter registers instructions f…
Browse files Browse the repository at this point in the history
…or Zcmp

This patch adds a pass to generate `cm.mvsa01` & `cm.mva01s`.

RISCVMoveOptimizer.cpp which combines two mv inst into one cm.mva01s or cm.mva01s.

Reviewed By: craig.topper

Differential Revision: https://reviews.llvm.org/D150415
  • Loading branch information
Xinlong-Wu committed Jun 21, 2023
1 parent 42a82b1 commit c9e08fa
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 0 deletions.
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/CMakeLists.txt
Expand Up @@ -36,6 +36,7 @@ add_llvm_target(RISCVCodeGen
RISCVMergeBaseOffset.cpp
RISCVOptWInstrs.cpp
RISCVRedundantCopyElimination.cpp
RISCVMoveMerger.cpp
RISCVRegisterInfo.cpp
RISCVRVVInitUndef.cpp
RISCVSubtarget.cpp
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCV.h
Expand Up @@ -70,6 +70,9 @@ FunctionPass *createRISCVInitUndefPass();
void initializeRISCVInitUndefPass(PassRegistry &);
extern char &RISCVInitUndefID;

FunctionPass *createRISCVMoveMergePass();
void initializeRISCVMoveMergePass(PassRegistry &);

InstructionSelector *createRISCVInstructionSelector(const RISCVTargetMachine &,
RISCVSubtarget &,
RISCVRegisterBankInfo &);
Expand Down
238 changes: 238 additions & 0 deletions llvm/lib/Target/RISCV/RISCVMoveMerger.cpp
@@ -0,0 +1,238 @@
//===---------- RISCVMoveMerge.cpp - RISCV 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.
//
//===----------------------------------------------------------------------===//

#include "RISCVInstrInfo.h"
#include "RISCVMachineFunctionInfo.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) {
initializeRISCVMoveMergePass(*PassRegistry::getPassRegistry());
}

const RISCVInstrInfo *TII;
const TargetRegisterInfo *TRI;

// Track which register units have been modified and used.
LiveRegUnits ModifiedRegUnits, UsedRegUnits;

bool isCandidateToMergeMVA01S(const DestSourcePair &RegPair);
bool isCandidateToMergeMVSA01(const DestSourcePair &RegPair);
// Merge the two instructions indicated into a single pair instruction.
MachineBasicBlock::iterator
mergePairedInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired, unsigned Opcode);

// 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
findMatchingInst(MachineBasicBlock::iterator &MBBI, unsigned InstOpcode,
const DestSourcePair &RegPair);
bool mergeMoveSARegPair(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)

// 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 ((Destination == RISCV::X10 || Destination == RISCV::X11) &&
RISCV::SR07RegClass.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 ((Source == RISCV::X10 || Source == RISCV::X11) &&
RISCV::SR07RegClass.contains(Destination))
return true;
return false;
}

MachineBasicBlock::iterator
RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator Paired,
unsigned Opcode) {
const MachineOperand *Sreg1, *Sreg2;
MachineBasicBlock::iterator E = I->getParent()->end();
MachineBasicBlock::iterator NextI = next_nodbg(I, E);
DestSourcePair FirstPair = TII->isCopyInstrImpl(*I).value();
DestSourcePair PairedRegs = TII->isCopyInstrImpl(*Paired).value();
Register ARegInFirstPair = Opcode == RISCV::CM_MVA01S
? FirstPair.Destination->getReg()
: FirstPair.Source->getReg();

if (NextI == Paired)
NextI = next_nodbg(NextI, E);
DebugLoc DL = I->getDebugLoc();

// 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
bool StartWithX10 = ARegInFirstPair == RISCV::X10;
if (Opcode == RISCV::CM_MVA01S) {
Sreg1 = StartWithX10 ? FirstPair.Source : PairedRegs.Source;
Sreg2 = StartWithX10 ? PairedRegs.Source : FirstPair.Source;
} else {
Sreg1 = StartWithX10 ? FirstPair.Destination : PairedRegs.Destination;
Sreg2 = StartWithX10 ? PairedRegs.Destination : FirstPair.Destination;
}

BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2);

I->eraseFromParent();
Paired->eraseFromParent();
return NextI;
}

MachineBasicBlock::iterator
RISCVMoveMerge::findMatchingInst(MachineBasicBlock::iterator &MBBI,
unsigned InstOpcode,
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();

if (InstOpcode == RISCV::CM_MVA01S &&
isCandidateToMergeMVA01S(*SecondPair)) {
// If register pair is valid and destination registers are different.
if ((RegPair.Destination->getReg() == DestReg))
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;
} else if (InstOpcode == RISCV::CM_MVSA01 &&
isCandidateToMergeMVSA01(*SecondPair)) {
if ((RegPair.Source->getReg() == SourceReg) ||
(RegPair.Destination->getReg() == DestReg))
return E;

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;
}

// Finds instructions, which could be represented as C.MV instructions and
// merged into CM.MVA01S or CM.MVSA01.
bool RISCVMoveMerge::mergeMoveSARegPair(MachineBasicBlock &MBB) {
bool Modified = false;

for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E;) {
// 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()) {
unsigned Opcode = 0;

if (isCandidateToMergeMVA01S(*RegPair))
Opcode = RISCV::CM_MVA01S;
else if (isCandidateToMergeMVSA01(*RegPair))
Opcode = RISCV::CM_MVSA01;
else {
++MBBI;
continue;
}

MachineBasicBlock::iterator Paired =
findMatchingInst(MBBI, Opcode, RegPair.value());
// If matching instruction can be found merge them.
if (Paired != E) {
MBBI = mergePairedInsns(MBBI, Paired, Opcode);
Modified = true;
continue;
}
}
++MBBI;
}
return Modified;
}

bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) {
if (skipFunction(Fn.getFunction()))
return false;

const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>();
if (!Subtarget->hasStdExtZcmp())
return false;

TII = Subtarget->getInstrInfo();
TRI = Subtarget->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 |= mergeMoveSARegPair(MBB);
return Modified;
}

/// createRISCVMoveMergePass - returns an instance of the
/// move merge pass.
FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); }
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
Expand Up @@ -87,6 +87,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
initializeRISCVInsertReadWriteCSRPass(*PR);
initializeRISCVDAGToDAGISelPass(*PR);
initializeRISCVInitUndefPass(*PR);
initializeRISCVMoveMergePass(*PR);
}

static StringRef computeDataLayout(const Triple &TT) {
Expand Down Expand Up @@ -348,6 +349,8 @@ void RISCVPassConfig::addPreEmitPass() {
}

void RISCVPassConfig::addPreEmitPass2() {
if (TM->getOptLevel() != CodeGenOpt::None)
addPass(createRISCVMoveMergePass());
addPass(createRISCVExpandPseudoPass());

// Schedule the expansion of AMOs at the last possible moment, avoiding the
Expand Down
1 change: 1 addition & 0 deletions llvm/test/CodeGen/RISCV/O3-pipeline.ll
Expand Up @@ -177,6 +177,7 @@
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: Stack Frame Layout Analysis
; CHECK-NEXT: RISC-V Zcmp move merging pass
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
Expand Down

0 comments on commit c9e08fa

Please sign in to comment.