1*5f757f3fSDimitry Andric //===-- RISCVMoveMerger.cpp - RISC-V move merge pass ----------------------===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric // 906c3fb27SDimitry Andric // This file contains a pass that performs move related peephole optimizations 1006c3fb27SDimitry Andric // as Zcmp has specified. This pass should be run after register allocation. 1106c3fb27SDimitry Andric // 1206c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1306c3fb27SDimitry Andric 1406c3fb27SDimitry Andric #include "RISCVInstrInfo.h" 1506c3fb27SDimitry Andric #include "RISCVMachineFunctionInfo.h" 1606c3fb27SDimitry Andric 1706c3fb27SDimitry Andric using namespace llvm; 1806c3fb27SDimitry Andric 1906c3fb27SDimitry Andric #define RISCV_MOVE_MERGE_NAME "RISC-V Zcmp move merging pass" 2006c3fb27SDimitry Andric 2106c3fb27SDimitry Andric namespace { 2206c3fb27SDimitry Andric struct RISCVMoveMerge : public MachineFunctionPass { 2306c3fb27SDimitry Andric static char ID; 2406c3fb27SDimitry Andric 25*5f757f3fSDimitry Andric RISCVMoveMerge() : MachineFunctionPass(ID) {} 2606c3fb27SDimitry Andric 2706c3fb27SDimitry Andric const RISCVInstrInfo *TII; 2806c3fb27SDimitry Andric const TargetRegisterInfo *TRI; 2906c3fb27SDimitry Andric 3006c3fb27SDimitry Andric // Track which register units have been modified and used. 3106c3fb27SDimitry Andric LiveRegUnits ModifiedRegUnits, UsedRegUnits; 3206c3fb27SDimitry Andric 3306c3fb27SDimitry Andric bool isCandidateToMergeMVA01S(const DestSourcePair &RegPair); 3406c3fb27SDimitry Andric bool isCandidateToMergeMVSA01(const DestSourcePair &RegPair); 3506c3fb27SDimitry Andric // Merge the two instructions indicated into a single pair instruction. 3606c3fb27SDimitry Andric MachineBasicBlock::iterator 3706c3fb27SDimitry Andric mergePairedInsns(MachineBasicBlock::iterator I, 3806c3fb27SDimitry Andric MachineBasicBlock::iterator Paired, unsigned Opcode); 3906c3fb27SDimitry Andric 4006c3fb27SDimitry Andric // Look for C.MV instruction that can be combined with 4106c3fb27SDimitry Andric // the given instruction into CM.MVA01S or CM.MVSA01. Return the matching 4206c3fb27SDimitry Andric // instruction if one exists. 4306c3fb27SDimitry Andric MachineBasicBlock::iterator 4406c3fb27SDimitry Andric findMatchingInst(MachineBasicBlock::iterator &MBBI, unsigned InstOpcode, 4506c3fb27SDimitry Andric const DestSourcePair &RegPair); 4606c3fb27SDimitry Andric bool mergeMoveSARegPair(MachineBasicBlock &MBB); 4706c3fb27SDimitry Andric bool runOnMachineFunction(MachineFunction &Fn) override; 4806c3fb27SDimitry Andric 4906c3fb27SDimitry Andric StringRef getPassName() const override { return RISCV_MOVE_MERGE_NAME; } 5006c3fb27SDimitry Andric }; 5106c3fb27SDimitry Andric 5206c3fb27SDimitry Andric char RISCVMoveMerge::ID = 0; 5306c3fb27SDimitry Andric 5406c3fb27SDimitry Andric } // end of anonymous namespace 5506c3fb27SDimitry Andric 5606c3fb27SDimitry Andric INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge", RISCV_MOVE_MERGE_NAME, 5706c3fb27SDimitry Andric false, false) 5806c3fb27SDimitry Andric 5906c3fb27SDimitry Andric // Check if registers meet CM.MVA01S constraints. 6006c3fb27SDimitry Andric bool RISCVMoveMerge::isCandidateToMergeMVA01S(const DestSourcePair &RegPair) { 6106c3fb27SDimitry Andric Register Destination = RegPair.Destination->getReg(); 6206c3fb27SDimitry Andric Register Source = RegPair.Source->getReg(); 6306c3fb27SDimitry Andric // If destination is not a0 or a1. 6406c3fb27SDimitry Andric if ((Destination == RISCV::X10 || Destination == RISCV::X11) && 6506c3fb27SDimitry Andric RISCV::SR07RegClass.contains(Source)) 6606c3fb27SDimitry Andric return true; 6706c3fb27SDimitry Andric return false; 6806c3fb27SDimitry Andric } 6906c3fb27SDimitry Andric 7006c3fb27SDimitry Andric // Check if registers meet CM.MVSA01 constraints. 7106c3fb27SDimitry Andric bool RISCVMoveMerge::isCandidateToMergeMVSA01(const DestSourcePair &RegPair) { 7206c3fb27SDimitry Andric Register Destination = RegPair.Destination->getReg(); 7306c3fb27SDimitry Andric Register Source = RegPair.Source->getReg(); 7406c3fb27SDimitry Andric // If Source is s0 - s7. 7506c3fb27SDimitry Andric if ((Source == RISCV::X10 || Source == RISCV::X11) && 7606c3fb27SDimitry Andric RISCV::SR07RegClass.contains(Destination)) 7706c3fb27SDimitry Andric return true; 7806c3fb27SDimitry Andric return false; 7906c3fb27SDimitry Andric } 8006c3fb27SDimitry Andric 8106c3fb27SDimitry Andric MachineBasicBlock::iterator 8206c3fb27SDimitry Andric RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I, 8306c3fb27SDimitry Andric MachineBasicBlock::iterator Paired, 8406c3fb27SDimitry Andric unsigned Opcode) { 8506c3fb27SDimitry Andric const MachineOperand *Sreg1, *Sreg2; 8606c3fb27SDimitry Andric MachineBasicBlock::iterator E = I->getParent()->end(); 8706c3fb27SDimitry Andric MachineBasicBlock::iterator NextI = next_nodbg(I, E); 8806c3fb27SDimitry Andric DestSourcePair FirstPair = TII->isCopyInstrImpl(*I).value(); 8906c3fb27SDimitry Andric DestSourcePair PairedRegs = TII->isCopyInstrImpl(*Paired).value(); 9006c3fb27SDimitry Andric Register ARegInFirstPair = Opcode == RISCV::CM_MVA01S 9106c3fb27SDimitry Andric ? FirstPair.Destination->getReg() 9206c3fb27SDimitry Andric : FirstPair.Source->getReg(); 9306c3fb27SDimitry Andric 9406c3fb27SDimitry Andric if (NextI == Paired) 9506c3fb27SDimitry Andric NextI = next_nodbg(NextI, E); 9606c3fb27SDimitry Andric DebugLoc DL = I->getDebugLoc(); 9706c3fb27SDimitry Andric 9806c3fb27SDimitry Andric // The order of S-reg depends on which instruction holds A0, instead of 9906c3fb27SDimitry Andric // the order of register pair. 10006c3fb27SDimitry Andric // e,g. 10106c3fb27SDimitry Andric // mv a1, s1 10206c3fb27SDimitry Andric // mv a0, s2 => cm.mva01s s2,s1 10306c3fb27SDimitry Andric // 10406c3fb27SDimitry Andric // mv a0, s2 10506c3fb27SDimitry Andric // mv a1, s1 => cm.mva01s s2,s1 10606c3fb27SDimitry Andric bool StartWithX10 = ARegInFirstPair == RISCV::X10; 10706c3fb27SDimitry Andric if (Opcode == RISCV::CM_MVA01S) { 10806c3fb27SDimitry Andric Sreg1 = StartWithX10 ? FirstPair.Source : PairedRegs.Source; 10906c3fb27SDimitry Andric Sreg2 = StartWithX10 ? PairedRegs.Source : FirstPair.Source; 11006c3fb27SDimitry Andric } else { 11106c3fb27SDimitry Andric Sreg1 = StartWithX10 ? FirstPair.Destination : PairedRegs.Destination; 11206c3fb27SDimitry Andric Sreg2 = StartWithX10 ? PairedRegs.Destination : FirstPair.Destination; 11306c3fb27SDimitry Andric } 11406c3fb27SDimitry Andric 11506c3fb27SDimitry Andric BuildMI(*I->getParent(), I, DL, TII->get(Opcode)).add(*Sreg1).add(*Sreg2); 11606c3fb27SDimitry Andric 11706c3fb27SDimitry Andric I->eraseFromParent(); 11806c3fb27SDimitry Andric Paired->eraseFromParent(); 11906c3fb27SDimitry Andric return NextI; 12006c3fb27SDimitry Andric } 12106c3fb27SDimitry Andric 12206c3fb27SDimitry Andric MachineBasicBlock::iterator 12306c3fb27SDimitry Andric RISCVMoveMerge::findMatchingInst(MachineBasicBlock::iterator &MBBI, 12406c3fb27SDimitry Andric unsigned InstOpcode, 12506c3fb27SDimitry Andric const DestSourcePair &RegPair) { 12606c3fb27SDimitry Andric MachineBasicBlock::iterator E = MBBI->getParent()->end(); 12706c3fb27SDimitry Andric 12806c3fb27SDimitry Andric // Track which register units have been modified and used between the first 12906c3fb27SDimitry Andric // insn and the second insn. 13006c3fb27SDimitry Andric ModifiedRegUnits.clear(); 13106c3fb27SDimitry Andric UsedRegUnits.clear(); 13206c3fb27SDimitry Andric 13306c3fb27SDimitry Andric for (MachineBasicBlock::iterator I = next_nodbg(MBBI, E); I != E; 13406c3fb27SDimitry Andric I = next_nodbg(I, E)) { 13506c3fb27SDimitry Andric 13606c3fb27SDimitry Andric MachineInstr &MI = *I; 13706c3fb27SDimitry Andric 13806c3fb27SDimitry Andric if (auto SecondPair = TII->isCopyInstrImpl(MI)) { 13906c3fb27SDimitry Andric Register SourceReg = SecondPair->Source->getReg(); 14006c3fb27SDimitry Andric Register DestReg = SecondPair->Destination->getReg(); 14106c3fb27SDimitry Andric 14206c3fb27SDimitry Andric if (InstOpcode == RISCV::CM_MVA01S && 14306c3fb27SDimitry Andric isCandidateToMergeMVA01S(*SecondPair)) { 14406c3fb27SDimitry Andric // If register pair is valid and destination registers are different. 14506c3fb27SDimitry Andric if ((RegPair.Destination->getReg() == DestReg)) 14606c3fb27SDimitry Andric return E; 14706c3fb27SDimitry Andric 14806c3fb27SDimitry Andric // If paired destination register was modified or used, the source reg 14906c3fb27SDimitry Andric // was modified, there is no possibility of finding matching 15006c3fb27SDimitry Andric // instruction so exit early. 15106c3fb27SDimitry Andric if (!ModifiedRegUnits.available(DestReg) || 15206c3fb27SDimitry Andric !UsedRegUnits.available(DestReg) || 15306c3fb27SDimitry Andric !ModifiedRegUnits.available(SourceReg)) 15406c3fb27SDimitry Andric return E; 15506c3fb27SDimitry Andric 15606c3fb27SDimitry Andric return I; 15706c3fb27SDimitry Andric } else if (InstOpcode == RISCV::CM_MVSA01 && 15806c3fb27SDimitry Andric isCandidateToMergeMVSA01(*SecondPair)) { 15906c3fb27SDimitry Andric if ((RegPair.Source->getReg() == SourceReg) || 16006c3fb27SDimitry Andric (RegPair.Destination->getReg() == DestReg)) 16106c3fb27SDimitry Andric return E; 16206c3fb27SDimitry Andric 16306c3fb27SDimitry Andric if (!ModifiedRegUnits.available(DestReg) || 16406c3fb27SDimitry Andric !UsedRegUnits.available(DestReg) || 16506c3fb27SDimitry Andric !ModifiedRegUnits.available(SourceReg)) 16606c3fb27SDimitry Andric return E; 16706c3fb27SDimitry Andric 16806c3fb27SDimitry Andric return I; 16906c3fb27SDimitry Andric } 17006c3fb27SDimitry Andric } 17106c3fb27SDimitry Andric // Update modified / used register units. 17206c3fb27SDimitry Andric LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI); 17306c3fb27SDimitry Andric } 17406c3fb27SDimitry Andric return E; 17506c3fb27SDimitry Andric } 17606c3fb27SDimitry Andric 17706c3fb27SDimitry Andric // Finds instructions, which could be represented as C.MV instructions and 17806c3fb27SDimitry Andric // merged into CM.MVA01S or CM.MVSA01. 17906c3fb27SDimitry Andric bool RISCVMoveMerge::mergeMoveSARegPair(MachineBasicBlock &MBB) { 18006c3fb27SDimitry Andric bool Modified = false; 18106c3fb27SDimitry Andric 18206c3fb27SDimitry Andric for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); 18306c3fb27SDimitry Andric MBBI != E;) { 18406c3fb27SDimitry Andric // Check if the instruction can be compressed to C.MV instruction. If it 18506c3fb27SDimitry Andric // can, return Dest/Src register pair. 18606c3fb27SDimitry Andric auto RegPair = TII->isCopyInstrImpl(*MBBI); 18706c3fb27SDimitry Andric if (RegPair.has_value()) { 18806c3fb27SDimitry Andric unsigned Opcode = 0; 18906c3fb27SDimitry Andric 19006c3fb27SDimitry Andric if (isCandidateToMergeMVA01S(*RegPair)) 19106c3fb27SDimitry Andric Opcode = RISCV::CM_MVA01S; 19206c3fb27SDimitry Andric else if (isCandidateToMergeMVSA01(*RegPair)) 19306c3fb27SDimitry Andric Opcode = RISCV::CM_MVSA01; 19406c3fb27SDimitry Andric else { 19506c3fb27SDimitry Andric ++MBBI; 19606c3fb27SDimitry Andric continue; 19706c3fb27SDimitry Andric } 19806c3fb27SDimitry Andric 19906c3fb27SDimitry Andric MachineBasicBlock::iterator Paired = 20006c3fb27SDimitry Andric findMatchingInst(MBBI, Opcode, RegPair.value()); 20106c3fb27SDimitry Andric // If matching instruction can be found merge them. 20206c3fb27SDimitry Andric if (Paired != E) { 20306c3fb27SDimitry Andric MBBI = mergePairedInsns(MBBI, Paired, Opcode); 20406c3fb27SDimitry Andric Modified = true; 20506c3fb27SDimitry Andric continue; 20606c3fb27SDimitry Andric } 20706c3fb27SDimitry Andric } 20806c3fb27SDimitry Andric ++MBBI; 20906c3fb27SDimitry Andric } 21006c3fb27SDimitry Andric return Modified; 21106c3fb27SDimitry Andric } 21206c3fb27SDimitry Andric 21306c3fb27SDimitry Andric bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) { 21406c3fb27SDimitry Andric if (skipFunction(Fn.getFunction())) 21506c3fb27SDimitry Andric return false; 21606c3fb27SDimitry Andric 21706c3fb27SDimitry Andric const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>(); 21806c3fb27SDimitry Andric if (!Subtarget->hasStdExtZcmp()) 21906c3fb27SDimitry Andric return false; 22006c3fb27SDimitry Andric 22106c3fb27SDimitry Andric TII = Subtarget->getInstrInfo(); 22206c3fb27SDimitry Andric TRI = Subtarget->getRegisterInfo(); 22306c3fb27SDimitry Andric // Resize the modified and used register unit trackers. We do this once 22406c3fb27SDimitry Andric // per function and then clear the register units each time we optimize a 22506c3fb27SDimitry Andric // move. 22606c3fb27SDimitry Andric ModifiedRegUnits.init(*TRI); 22706c3fb27SDimitry Andric UsedRegUnits.init(*TRI); 22806c3fb27SDimitry Andric bool Modified = false; 22906c3fb27SDimitry Andric for (auto &MBB : Fn) 23006c3fb27SDimitry Andric Modified |= mergeMoveSARegPair(MBB); 23106c3fb27SDimitry Andric return Modified; 23206c3fb27SDimitry Andric } 23306c3fb27SDimitry Andric 23406c3fb27SDimitry Andric /// createRISCVMoveMergePass - returns an instance of the 23506c3fb27SDimitry Andric /// move merge pass. 23606c3fb27SDimitry Andric FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); } 237