//===-- M68kExpandPseudo.cpp - Expand pseudo instructions -------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// /// /// \file /// This file contains a pass that expands pseudo instructions into target /// instructions to allow proper scheduling, if-conversion, other late /// optimizations, or simply the encoding of the instructions. /// //===----------------------------------------------------------------------===// #include "M68k.h" #include "M68kFrameLowering.h" #include "M68kInstrInfo.h" #include "M68kMachineFunction.h" #include "M68kSubtarget.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/Passes.h" // For IDs of passes that are preserved. #include "llvm/IR/GlobalValue.h" using namespace llvm; #define DEBUG_TYPE "M68k-expand-pseudos" namespace { class M68kExpandPseudo : public MachineFunctionPass { public: static char ID; M68kExpandPseudo() : MachineFunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); AU.addPreservedID(MachineLoopInfoID); AU.addPreservedID(MachineDominatorsID); MachineFunctionPass::getAnalysisUsage(AU); } const M68kSubtarget *STI; const M68kInstrInfo *TII; const M68kRegisterInfo *TRI; const M68kMachineFunctionInfo *MFI; const M68kFrameLowering *FL; bool runOnMachineFunction(MachineFunction &Fn) override; MachineFunctionProperties getRequiredProperties() const override { return MachineFunctionProperties().set( MachineFunctionProperties::Property::NoVRegs); } StringRef getPassName() const override { return "M68k pseudo instruction expansion pass"; } private: bool ExpandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI); bool ExpandMBB(MachineBasicBlock &MBB); }; char M68kExpandPseudo::ID = 0; } // End anonymous namespace. /// If \p MBBI is a pseudo instruction, this method expands /// it to the corresponding (sequence of) actual instruction(s). /// \returns true if \p MBBI has been expanded. bool M68kExpandPseudo::ExpandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) { MachineInstr &MI = *MBBI; MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI); unsigned Opcode = MI.getOpcode(); DebugLoc DL = MBBI->getDebugLoc(); /// TODO infer argument size to create less switch cases switch (Opcode) { default: return false; case M68k::MOVXd16d8: return TII->ExpandMOVX_RR(MIB, MVT::i16, MVT::i8); case M68k::MOVXd32d8: return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i8); case M68k::MOVXd32d16: return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i16); case M68k::MOVSXd16d8: return TII->ExpandMOVSZX_RR(MIB, true, MVT::i16, MVT::i8); case M68k::MOVSXd32d8: return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i8); case M68k::MOVSXd32d16: return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i16); case M68k::MOVZXd16d8: return TII->ExpandMOVSZX_RR(MIB, false, MVT::i16, MVT::i8); case M68k::MOVZXd32d8: return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i8); case M68k::MOVZXd32d16: return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i16); case M68k::MOVSXd16j8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dj), MVT::i16, MVT::i8); case M68k::MOVSXd32j8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dj), MVT::i32, MVT::i8); case M68k::MOVSXd32j16: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rj), MVT::i32, MVT::i16); case M68k::MOVZXd16j8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dj), MVT::i16, MVT::i8); case M68k::MOVZXd32j8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dj), MVT::i32, MVT::i8); case M68k::MOVZXd32j16: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rj), MVT::i32, MVT::i16); case M68k::MOVSXd16p8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dp), MVT::i16, MVT::i8); case M68k::MOVSXd32p8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dp), MVT::i32, MVT::i8); case M68k::MOVSXd32p16: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rp), MVT::i32, MVT::i16); case M68k::MOVZXd16p8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dp), MVT::i16, MVT::i8); case M68k::MOVZXd32p8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dp), MVT::i32, MVT::i8); case M68k::MOVZXd32p16: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rp), MVT::i32, MVT::i16); case M68k::MOVSXd16f8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8df), MVT::i16, MVT::i8); case M68k::MOVSXd32f8: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8df), MVT::i32, MVT::i8); case M68k::MOVSXd32f16: return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rf), MVT::i32, MVT::i16); case M68k::MOVZXd16f8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8df), MVT::i16, MVT::i8); case M68k::MOVZXd32f8: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8df), MVT::i32, MVT::i8); case M68k::MOVZXd32f16: return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rf), MVT::i32, MVT::i16); case M68k::MOV8cd: return TII->ExpandCCR(MIB, /*IsToCCR=*/true); case M68k::MOV8dc: return TII->ExpandCCR(MIB, /*IsToCCR=*/false); case M68k::MOVM8jm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false); case M68k::MOVM16jm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false); case M68k::MOVM32jm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false); case M68k::MOVM8pm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false); case M68k::MOVM16pm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false); case M68k::MOVM32pm_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false); case M68k::MOVM8mj_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true); case M68k::MOVM16mj_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true); case M68k::MOVM32mj_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true); case M68k::MOVM8mp_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true); case M68k::MOVM16mp_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true); case M68k::MOVM32mp_P: return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true); case M68k::TCRETURNq: case M68k::TCRETURNj: { MachineOperand &JumpTarget = MI.getOperand(0); MachineOperand &StackAdjust = MI.getOperand(1); assert(StackAdjust.isImm() && "Expecting immediate value."); // Adjust stack pointer. int StackAdj = StackAdjust.getImm(); int MaxTCDelta = MFI->getTCReturnAddrDelta(); int Offset = 0; assert(MaxTCDelta <= 0 && "MaxTCDelta should never be positive"); // Incoporate the retaddr area. Offset = StackAdj - MaxTCDelta; assert(Offset >= 0 && "Offset should never be negative"); if (Offset) { // Check for possible merge with preceding ADD instruction. Offset += FL->mergeSPUpdates(MBB, MBBI, true); FL->emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true); } // Jump to label or value in register. if (Opcode == M68k::TCRETURNq) { MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(M68k::TAILJMPq)); if (JumpTarget.isGlobal()) { MIB.addGlobalAddress(JumpTarget.getGlobal(), JumpTarget.getOffset(), JumpTarget.getTargetFlags()); } else { assert(JumpTarget.isSymbol()); MIB.addExternalSymbol(JumpTarget.getSymbolName(), JumpTarget.getTargetFlags()); } } else { BuildMI(MBB, MBBI, DL, TII->get(M68k::TAILJMPj)) .addReg(JumpTarget.getReg(), RegState::Kill); } MachineInstr &NewMI = *std::prev(MBBI); NewMI.copyImplicitOps(*MBBI->getParent()->getParent(), *MBBI); // Delete the pseudo instruction TCRETURN. MBB.erase(MBBI); return true; } case M68k::RET: { // Adjust stack to erase error code int64_t StackAdj = MBBI->getOperand(0).getImm(); MachineInstrBuilder MIB; if (StackAdj == 0) { MIB = BuildMI(MBB, MBBI, DL, TII->get(M68k::RTS)); } else if (isUInt<16>(StackAdj)) { if (STI->atLeastM68020()) { llvm_unreachable("RTD is not implemented"); } else { // Copy PC from stack to a free address(A0 or A1) register // TODO check if pseudo expand uses free address register BuildMI(MBB, MBBI, DL, TII->get(M68k::MOV32aj), M68k::A1) .addReg(M68k::SP); // Adjust SP FL->emitSPUpdate(MBB, MBBI, StackAdj, /*InEpilogue=*/true); // Put the return address on stack BuildMI(MBB, MBBI, DL, TII->get(M68k::MOV32ja)) .addReg(M68k::SP) .addReg(M68k::A1); // RTS BuildMI(MBB, MBBI, DL, TII->get(M68k::RTS)); } } else { // TODO: RTD can only handle immediates as big as 2**16-1. // If we need to pop off bytes before the return address, we // must do it manually. llvm_unreachable("Stack adjustment size not supported"); } // FIXME: Can rest of the operands be ignored, if there is any? MBB.erase(MBBI); return true; } } llvm_unreachable("Previous switch has a fallthrough?"); } /// Expand all pseudo instructions contained in \p MBB. /// \returns true if any expansion occurred for \p MBB. bool M68kExpandPseudo::ExpandMBB(MachineBasicBlock &MBB) { bool Modified = false; // MBBI may be invalidated by the expansion. MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); while (MBBI != E) { MachineBasicBlock::iterator NMBBI = std::next(MBBI); Modified |= ExpandMI(MBB, MBBI); MBBI = NMBBI; } return Modified; } bool M68kExpandPseudo::runOnMachineFunction(MachineFunction &MF) { STI = &static_cast(MF.getSubtarget()); TII = STI->getInstrInfo(); TRI = STI->getRegisterInfo(); MFI = MF.getInfo(); FL = STI->getFrameLowering(); bool Modified = false; for (MachineBasicBlock &MBB : MF) Modified |= ExpandMBB(MBB); return Modified; } /// Returns an instance of the pseudo instruction expansion pass. FunctionPass *llvm::createM68kExpandPseudoPass() { return new M68kExpandPseudo(); }