| //===-- 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(); } |