15f757f3fSDimitry Andric //===------- RISCVPushPopOptimizer.cpp - RISC-V Push/Pop opt. 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 // 95f757f3fSDimitry Andric // This file contains a pass that replaces Zcmp POP instructions with 105f757f3fSDimitry Andric // POPRET[Z] where possible. 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_PUSH_POP_OPT_NAME "RISC-V Zcmp Push/Pop optimization pass" 2006c3fb27SDimitry Andric 2106c3fb27SDimitry Andric namespace { 2206c3fb27SDimitry Andric struct RISCVPushPopOpt : public MachineFunctionPass { 2306c3fb27SDimitry Andric static char ID; 2406c3fb27SDimitry Andric 255f757f3fSDimitry Andric RISCVPushPopOpt() : 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 usePopRet(MachineBasicBlock::iterator &MBBI, 3406c3fb27SDimitry Andric MachineBasicBlock::iterator &NextI, bool IsReturnZero); 3506c3fb27SDimitry Andric bool adjustRetVal(MachineBasicBlock::iterator &MBBI); 3606c3fb27SDimitry Andric bool runOnMachineFunction(MachineFunction &Fn) override; 3706c3fb27SDimitry Andric 3806c3fb27SDimitry Andric StringRef getPassName() const override { return RISCV_PUSH_POP_OPT_NAME; } 3906c3fb27SDimitry Andric }; 4006c3fb27SDimitry Andric 4106c3fb27SDimitry Andric char RISCVPushPopOpt::ID = 0; 4206c3fb27SDimitry Andric 4306c3fb27SDimitry Andric } // end of anonymous namespace 4406c3fb27SDimitry Andric 4506c3fb27SDimitry Andric INITIALIZE_PASS(RISCVPushPopOpt, "riscv-push-pop-opt", RISCV_PUSH_POP_OPT_NAME, 4606c3fb27SDimitry Andric false, false) 4706c3fb27SDimitry Andric 4806c3fb27SDimitry Andric // Check if POP instruction was inserted into the MBB and return iterator to it. 4906c3fb27SDimitry Andric static MachineBasicBlock::iterator containsPop(MachineBasicBlock &MBB) { 5006c3fb27SDimitry Andric for (MachineBasicBlock::iterator MBBI = MBB.begin(); MBBI != MBB.end(); 5106c3fb27SDimitry Andric MBBI = next_nodbg(MBBI, MBB.end())) 5206c3fb27SDimitry Andric if (MBBI->getOpcode() == RISCV::CM_POP) 5306c3fb27SDimitry Andric return MBBI; 5406c3fb27SDimitry Andric 5506c3fb27SDimitry Andric return MBB.end(); 5606c3fb27SDimitry Andric } 5706c3fb27SDimitry Andric 5806c3fb27SDimitry Andric bool RISCVPushPopOpt::usePopRet(MachineBasicBlock::iterator &MBBI, 5906c3fb27SDimitry Andric MachineBasicBlock::iterator &NextI, 6006c3fb27SDimitry Andric bool IsReturnZero) { 6106c3fb27SDimitry Andric // Since Pseudo instruction lowering happen later in the pipeline, 6206c3fb27SDimitry Andric // this will detect all ret instruction. 6306c3fb27SDimitry Andric DebugLoc DL = NextI->getDebugLoc(); 6406c3fb27SDimitry Andric unsigned Opc = IsReturnZero ? RISCV::CM_POPRETZ : RISCV::CM_POPRET; 65*0fca6ea1SDimitry Andric MachineInstrBuilder PopRetBuilder = 6606c3fb27SDimitry Andric BuildMI(*NextI->getParent(), NextI, DL, TII->get(Opc)) 6706c3fb27SDimitry Andric .add(MBBI->getOperand(0)) 6806c3fb27SDimitry Andric .add(MBBI->getOperand(1)); 6906c3fb27SDimitry Andric 70*0fca6ea1SDimitry Andric // Copy over the variable implicit uses and defs from the CM_POP. They depend 71*0fca6ea1SDimitry Andric // on what register list has been picked during frame lowering. 72*0fca6ea1SDimitry Andric const MCInstrDesc &PopDesc = MBBI->getDesc(); 73*0fca6ea1SDimitry Andric unsigned FirstNonDeclaredOp = PopDesc.getNumOperands() + 74*0fca6ea1SDimitry Andric PopDesc.NumImplicitUses + 75*0fca6ea1SDimitry Andric PopDesc.NumImplicitDefs; 76*0fca6ea1SDimitry Andric for (unsigned i = FirstNonDeclaredOp; i < MBBI->getNumOperands(); ++i) 77*0fca6ea1SDimitry Andric PopRetBuilder.add(MBBI->getOperand(i)); 78*0fca6ea1SDimitry Andric 7906c3fb27SDimitry Andric MBBI->eraseFromParent(); 8006c3fb27SDimitry Andric NextI->eraseFromParent(); 8106c3fb27SDimitry Andric return true; 8206c3fb27SDimitry Andric } 8306c3fb27SDimitry Andric 8406c3fb27SDimitry Andric // Search for last assignment to a0 and if possible use ret_val slot of POP to 8506c3fb27SDimitry Andric // store return value. 8606c3fb27SDimitry Andric bool RISCVPushPopOpt::adjustRetVal(MachineBasicBlock::iterator &MBBI) { 8706c3fb27SDimitry Andric MachineBasicBlock::reverse_iterator RE = MBBI->getParent()->rend(); 8806c3fb27SDimitry Andric // Track which register units have been modified and used between the POP 8906c3fb27SDimitry Andric // insn and the last assignment to register a0. 9006c3fb27SDimitry Andric ModifiedRegUnits.clear(); 9106c3fb27SDimitry Andric UsedRegUnits.clear(); 9206c3fb27SDimitry Andric // Since POP instruction is in Epilogue no normal instructions will follow 9306c3fb27SDimitry Andric // after it. Therefore search only previous ones to find the return value. 9406c3fb27SDimitry Andric for (MachineBasicBlock::reverse_iterator I = 9506c3fb27SDimitry Andric next_nodbg(MBBI.getReverse(), RE); 9606c3fb27SDimitry Andric I != RE; I = next_nodbg(I, RE)) { 9706c3fb27SDimitry Andric MachineInstr &MI = *I; 9806c3fb27SDimitry Andric if (auto OperandPair = TII->isCopyInstrImpl(MI)) { 9906c3fb27SDimitry Andric Register DestReg = OperandPair->Destination->getReg(); 10006c3fb27SDimitry Andric Register Source = OperandPair->Source->getReg(); 10106c3fb27SDimitry Andric if (DestReg == RISCV::X10 && Source == RISCV::X0) { 10206c3fb27SDimitry Andric MI.removeFromParent(); 10306c3fb27SDimitry Andric return true; 10406c3fb27SDimitry Andric } 10506c3fb27SDimitry Andric } 10606c3fb27SDimitry Andric // Update modified / used register units. 10706c3fb27SDimitry Andric LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI); 10806c3fb27SDimitry Andric // If a0 was modified or used, there is no possibility 10906c3fb27SDimitry Andric // of using ret_val slot of POP instruction. 11006c3fb27SDimitry Andric if (!ModifiedRegUnits.available(RISCV::X10) || 11106c3fb27SDimitry Andric !UsedRegUnits.available(RISCV::X10)) 11206c3fb27SDimitry Andric return false; 11306c3fb27SDimitry Andric } 11406c3fb27SDimitry Andric return false; 11506c3fb27SDimitry Andric } 11606c3fb27SDimitry Andric 11706c3fb27SDimitry Andric bool RISCVPushPopOpt::runOnMachineFunction(MachineFunction &Fn) { 11806c3fb27SDimitry Andric if (skipFunction(Fn.getFunction())) 11906c3fb27SDimitry Andric return false; 12006c3fb27SDimitry Andric 12106c3fb27SDimitry Andric // If Zcmp extension is not supported, abort. 12206c3fb27SDimitry Andric const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>(); 12306c3fb27SDimitry Andric if (!Subtarget->hasStdExtZcmp()) 12406c3fb27SDimitry Andric return false; 12506c3fb27SDimitry Andric 12606c3fb27SDimitry Andric // If frame pointer elimination has been disabled, abort to avoid breaking the 12706c3fb27SDimitry Andric // ABI. 12806c3fb27SDimitry Andric if (Fn.getTarget().Options.DisableFramePointerElim(Fn)) 12906c3fb27SDimitry Andric return false; 13006c3fb27SDimitry Andric 13106c3fb27SDimitry Andric TII = Subtarget->getInstrInfo(); 13206c3fb27SDimitry Andric TRI = Subtarget->getRegisterInfo(); 13306c3fb27SDimitry Andric // Resize the modified and used register unit trackers. We do this once 13406c3fb27SDimitry Andric // per function and then clear the register units each time we determine 13506c3fb27SDimitry Andric // correct return value for the POP. 13606c3fb27SDimitry Andric ModifiedRegUnits.init(*TRI); 13706c3fb27SDimitry Andric UsedRegUnits.init(*TRI); 13806c3fb27SDimitry Andric bool Modified = false; 13906c3fb27SDimitry Andric for (auto &MBB : Fn) { 14006c3fb27SDimitry Andric MachineBasicBlock::iterator MBBI = containsPop(MBB); 14106c3fb27SDimitry Andric MachineBasicBlock::iterator NextI = next_nodbg(MBBI, MBB.end()); 1428a4dda33SDimitry Andric if (MBBI != MBB.end() && NextI != MBB.end() && 1438a4dda33SDimitry Andric NextI->getOpcode() == RISCV::PseudoRET) 14406c3fb27SDimitry Andric Modified |= usePopRet(MBBI, NextI, adjustRetVal(MBBI)); 14506c3fb27SDimitry Andric } 14606c3fb27SDimitry Andric return Modified; 14706c3fb27SDimitry Andric } 14806c3fb27SDimitry Andric 14906c3fb27SDimitry Andric /// createRISCVPushPopOptimizationPass - returns an instance of the 15006c3fb27SDimitry Andric /// Push/Pop optimization pass. 15106c3fb27SDimitry Andric FunctionPass *llvm::createRISCVPushPopOptimizationPass() { 15206c3fb27SDimitry Andric return new RISCVPushPopOpt(); 15306c3fb27SDimitry Andric } 154