| //===-- 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 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(const RISCVSubtarget &STI, 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 bool isMoveFromAToS(unsigned Opcode) { |
| switch (Opcode) { |
| case RISCV::CM_MVA01S: |
| case RISCV::QC_CM_MVA01S: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static unsigned getMoveFromAToSOpcode(const RISCVSubtarget &STI) { |
| if (STI.hasStdExtZcmp()) |
| return RISCV::CM_MVA01S; |
| |
| if (STI.hasVendorXqccmp()) |
| return RISCV::QC_CM_MVA01S; |
| |
| llvm_unreachable("Unhandled subtarget with paired A to S move."); |
| } |
| |
| static bool isMoveFromSToA(unsigned Opcode) { |
| switch (Opcode) { |
| case RISCV::CM_MVSA01: |
| case RISCV::QC_CM_MVSA01: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static unsigned getMoveFromSToAOpcode(const RISCVSubtarget &STI) { |
| if (STI.hasStdExtZcmp()) |
| return RISCV::CM_MVSA01; |
| |
| if (STI.hasVendorXqccmp()) |
| return RISCV::QC_CM_MVSA01; |
| |
| llvm_unreachable("Unhandled subtarget with paired S to A move"); |
| } |
| |
| // 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 = isMoveFromAToS(Opcode) |
| ? 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 (isMoveFromAToS(Opcode)) { |
| 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 (isMoveFromAToS(InstOpcode) && 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 (isMoveFromSToA(InstOpcode) && |
| 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(const RISCVSubtarget &STI, |
| 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 = getMoveFromAToSOpcode(STI); |
| else if (isCandidateToMergeMVSA01(*RegPair)) |
| Opcode = getMoveFromSToAOpcode(STI); |
| 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() || Subtarget->hasVendorXqccmp())) |
| 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(*Subtarget, MBB); |
| return Modified; |
| } |
| |
| /// createRISCVMoveMergePass - returns an instance of the |
| /// move merge pass. |
| FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); } |