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
RISCVPushPopOpt__anond36ac2750111::RISCVPushPopOpt255f757f3fSDimitry 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
getPassName__anond36ac2750111::RISCVPushPopOpt3806c3fb27SDimitry 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.
containsPop(MachineBasicBlock & MBB)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
usePopRet(MachineBasicBlock::iterator & MBBI,MachineBasicBlock::iterator & NextI,bool IsReturnZero)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.
adjustRetVal(MachineBasicBlock::iterator & MBBI)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
runOnMachineFunction(MachineFunction & Fn)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.
createRISCVPushPopOptimizationPass()15106c3fb27SDimitry Andric FunctionPass *llvm::createRISCVPushPopOptimizationPass() {
15206c3fb27SDimitry Andric return new RISCVPushPopOpt();
15306c3fb27SDimitry Andric }
154