1*06c3fb27SDimitry Andric //===------- RISCVPushPopOptimizer.cpp - RISCV Push/Pop opt. pass ---------===// 2*06c3fb27SDimitry Andric // 3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*06c3fb27SDimitry Andric // 7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 8*06c3fb27SDimitry Andric // 9*06c3fb27SDimitry Andric // This file contains a pass that modifies PUSH/POP instructions from Zca 10*06c3fb27SDimitry Andric // standard to use their non prolog/epilog related functionalities 11*06c3fb27SDimitry Andric // and generates POPRET instruction. 12*06c3fb27SDimitry Andric // 13*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 14*06c3fb27SDimitry Andric 15*06c3fb27SDimitry Andric #include "RISCVInstrInfo.h" 16*06c3fb27SDimitry Andric #include "RISCVMachineFunctionInfo.h" 17*06c3fb27SDimitry Andric 18*06c3fb27SDimitry Andric using namespace llvm; 19*06c3fb27SDimitry Andric 20*06c3fb27SDimitry Andric #define RISCV_PUSH_POP_OPT_NAME "RISC-V Zcmp Push/Pop optimization pass" 21*06c3fb27SDimitry Andric 22*06c3fb27SDimitry Andric namespace { 23*06c3fb27SDimitry Andric struct RISCVPushPopOpt : public MachineFunctionPass { 24*06c3fb27SDimitry Andric static char ID; 25*06c3fb27SDimitry Andric 26*06c3fb27SDimitry Andric RISCVPushPopOpt() : MachineFunctionPass(ID) { 27*06c3fb27SDimitry Andric initializeRISCVPushPopOptPass(*PassRegistry::getPassRegistry()); 28*06c3fb27SDimitry Andric } 29*06c3fb27SDimitry Andric 30*06c3fb27SDimitry Andric const RISCVInstrInfo *TII; 31*06c3fb27SDimitry Andric const TargetRegisterInfo *TRI; 32*06c3fb27SDimitry Andric 33*06c3fb27SDimitry Andric // Track which register units have been modified and used. 34*06c3fb27SDimitry Andric LiveRegUnits ModifiedRegUnits, UsedRegUnits; 35*06c3fb27SDimitry Andric 36*06c3fb27SDimitry Andric bool usePopRet(MachineBasicBlock::iterator &MBBI, 37*06c3fb27SDimitry Andric MachineBasicBlock::iterator &NextI, bool IsReturnZero); 38*06c3fb27SDimitry Andric bool adjustRetVal(MachineBasicBlock::iterator &MBBI); 39*06c3fb27SDimitry Andric bool runOnMachineFunction(MachineFunction &Fn) override; 40*06c3fb27SDimitry Andric 41*06c3fb27SDimitry Andric StringRef getPassName() const override { return RISCV_PUSH_POP_OPT_NAME; } 42*06c3fb27SDimitry Andric }; 43*06c3fb27SDimitry Andric 44*06c3fb27SDimitry Andric char RISCVPushPopOpt::ID = 0; 45*06c3fb27SDimitry Andric 46*06c3fb27SDimitry Andric } // end of anonymous namespace 47*06c3fb27SDimitry Andric 48*06c3fb27SDimitry Andric INITIALIZE_PASS(RISCVPushPopOpt, "riscv-push-pop-opt", RISCV_PUSH_POP_OPT_NAME, 49*06c3fb27SDimitry Andric false, false) 50*06c3fb27SDimitry Andric 51*06c3fb27SDimitry Andric // Check if POP instruction was inserted into the MBB and return iterator to it. 52*06c3fb27SDimitry Andric static MachineBasicBlock::iterator containsPop(MachineBasicBlock &MBB) { 53*06c3fb27SDimitry Andric for (MachineBasicBlock::iterator MBBI = MBB.begin(); MBBI != MBB.end(); 54*06c3fb27SDimitry Andric MBBI = next_nodbg(MBBI, MBB.end())) 55*06c3fb27SDimitry Andric if (MBBI->getOpcode() == RISCV::CM_POP) 56*06c3fb27SDimitry Andric return MBBI; 57*06c3fb27SDimitry Andric 58*06c3fb27SDimitry Andric return MBB.end(); 59*06c3fb27SDimitry Andric } 60*06c3fb27SDimitry Andric 61*06c3fb27SDimitry Andric bool RISCVPushPopOpt::usePopRet(MachineBasicBlock::iterator &MBBI, 62*06c3fb27SDimitry Andric MachineBasicBlock::iterator &NextI, 63*06c3fb27SDimitry Andric bool IsReturnZero) { 64*06c3fb27SDimitry Andric // Since Pseudo instruction lowering happen later in the pipeline, 65*06c3fb27SDimitry Andric // this will detect all ret instruction. 66*06c3fb27SDimitry Andric DebugLoc DL = NextI->getDebugLoc(); 67*06c3fb27SDimitry Andric unsigned Opc = IsReturnZero ? RISCV::CM_POPRETZ : RISCV::CM_POPRET; 68*06c3fb27SDimitry Andric BuildMI(*NextI->getParent(), NextI, DL, TII->get(Opc)) 69*06c3fb27SDimitry Andric .add(MBBI->getOperand(0)) 70*06c3fb27SDimitry Andric .add(MBBI->getOperand(1)); 71*06c3fb27SDimitry Andric 72*06c3fb27SDimitry Andric MBBI->eraseFromParent(); 73*06c3fb27SDimitry Andric NextI->eraseFromParent(); 74*06c3fb27SDimitry Andric return true; 75*06c3fb27SDimitry Andric } 76*06c3fb27SDimitry Andric 77*06c3fb27SDimitry Andric // Search for last assignment to a0 and if possible use ret_val slot of POP to 78*06c3fb27SDimitry Andric // store return value. 79*06c3fb27SDimitry Andric bool RISCVPushPopOpt::adjustRetVal(MachineBasicBlock::iterator &MBBI) { 80*06c3fb27SDimitry Andric MachineBasicBlock::reverse_iterator RE = MBBI->getParent()->rend(); 81*06c3fb27SDimitry Andric // Track which register units have been modified and used between the POP 82*06c3fb27SDimitry Andric // insn and the last assignment to register a0. 83*06c3fb27SDimitry Andric ModifiedRegUnits.clear(); 84*06c3fb27SDimitry Andric UsedRegUnits.clear(); 85*06c3fb27SDimitry Andric // Since POP instruction is in Epilogue no normal instructions will follow 86*06c3fb27SDimitry Andric // after it. Therefore search only previous ones to find the return value. 87*06c3fb27SDimitry Andric for (MachineBasicBlock::reverse_iterator I = 88*06c3fb27SDimitry Andric next_nodbg(MBBI.getReverse(), RE); 89*06c3fb27SDimitry Andric I != RE; I = next_nodbg(I, RE)) { 90*06c3fb27SDimitry Andric MachineInstr &MI = *I; 91*06c3fb27SDimitry Andric if (auto OperandPair = TII->isCopyInstrImpl(MI)) { 92*06c3fb27SDimitry Andric Register DestReg = OperandPair->Destination->getReg(); 93*06c3fb27SDimitry Andric Register Source = OperandPair->Source->getReg(); 94*06c3fb27SDimitry Andric if (DestReg == RISCV::X10 && Source == RISCV::X0) { 95*06c3fb27SDimitry Andric MI.removeFromParent(); 96*06c3fb27SDimitry Andric return true; 97*06c3fb27SDimitry Andric } 98*06c3fb27SDimitry Andric } 99*06c3fb27SDimitry Andric // Update modified / used register units. 100*06c3fb27SDimitry Andric LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI); 101*06c3fb27SDimitry Andric // If a0 was modified or used, there is no possibility 102*06c3fb27SDimitry Andric // of using ret_val slot of POP instruction. 103*06c3fb27SDimitry Andric if (!ModifiedRegUnits.available(RISCV::X10) || 104*06c3fb27SDimitry Andric !UsedRegUnits.available(RISCV::X10)) 105*06c3fb27SDimitry Andric return false; 106*06c3fb27SDimitry Andric } 107*06c3fb27SDimitry Andric return false; 108*06c3fb27SDimitry Andric } 109*06c3fb27SDimitry Andric 110*06c3fb27SDimitry Andric bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) { 111*06c3fb27SDimitry Andric if (skipFunction(Fn.getFunction())) 112*06c3fb27SDimitry Andric return false; 113*06c3fb27SDimitry Andric 114*06c3fb27SDimitry Andric // If Zcmp extension is not supported, abort. 115*06c3fb27SDimitry Andric const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>(); 116*06c3fb27SDimitry Andric if (!Subtarget->hasStdExtZcmp()) 117*06c3fb27SDimitry Andric return false; 118*06c3fb27SDimitry Andric 119*06c3fb27SDimitry Andric // If frame pointer elimination has been disabled, abort to avoid breaking the 120*06c3fb27SDimitry Andric // ABI. 121*06c3fb27SDimitry Andric if (Fn.getTarget().Options.DisableFramePointerElim(Fn)) 122*06c3fb27SDimitry Andric return false; 123*06c3fb27SDimitry Andric 124*06c3fb27SDimitry Andric TII = Subtarget->getInstrInfo(); 125*06c3fb27SDimitry Andric TRI = Subtarget->getRegisterInfo(); 126*06c3fb27SDimitry Andric // Resize the modified and used register unit trackers. We do this once 127*06c3fb27SDimitry Andric // per function and then clear the register units each time we determine 128*06c3fb27SDimitry Andric // correct return value for the POP. 129*06c3fb27SDimitry Andric ModifiedRegUnits.init(*TRI); 130*06c3fb27SDimitry Andric UsedRegUnits.init(*TRI); 131*06c3fb27SDimitry Andric bool Modified = false; 132*06c3fb27SDimitry Andric for (auto &MBB : Fn) { 133*06c3fb27SDimitry Andric MachineBasicBlock::iterator MBBI = containsPop(MBB); 134*06c3fb27SDimitry Andric MachineBasicBlock::iterator NextI = next_nodbg(MBBI, MBB.end()); 135*06c3fb27SDimitry Andric if (MBBI != MBB.end() && NextI->getOpcode() == RISCV::PseudoRET) 136*06c3fb27SDimitry Andric Modified |= usePopRet(MBBI, NextI, adjustRetVal(MBBI)); 137*06c3fb27SDimitry Andric } 138*06c3fb27SDimitry Andric return Modified; 139*06c3fb27SDimitry Andric } 140*06c3fb27SDimitry Andric 141*06c3fb27SDimitry Andric /// createRISCVPushPopOptimizationPass - returns an instance of the 142*06c3fb27SDimitry Andric /// Push/Pop optimization pass. 143*06c3fb27SDimitry Andric FunctionPass *llvm::createRISCVPushPopOptimizationPass() { 144*06c3fb27SDimitry Andric return new RISCVPushPopOpt(); 145*06c3fb27SDimitry Andric } 146