//===-- M68kCollapseMOVEMPass.cpp - Expand MOVEM pass -----------*- 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 /// `MOVEM` is an instruction that moves multiple registers a time according to /// the given mask. Thus sometimes it's pretty expensive. /// This file contains a pass that collapses sequential MOVEM instructions into /// a single one. /// //===----------------------------------------------------------------------===// #include "M68k.h" #include "M68kFrameLowering.h" #include "M68kInstrInfo.h" #include "M68kMachineFunction.h" #include "M68kSubtarget.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/IR/EHPersonalities.h" #include "llvm/IR/GlobalValue.h" #include "llvm/Support/MathExtras.h" using namespace llvm; #define DEBUG_TYPE "m68k-collapse-movem" #define PASS_NAME "M68k MOVEM collapser pass" namespace { enum UpdateType { Ascending, Descending, Intermixed }; /// An abtraction of the MOVEM chain currently processing class MOVEMState { MachineBasicBlock::iterator Begin; MachineBasicBlock::iterator End; unsigned Base; int Start; int Stop; unsigned Mask; enum class AccessTy { None, Load, Store }; AccessTy Access; public: MOVEMState() : Begin(nullptr), End(nullptr), Base(0), Start(INT_MIN), Stop(INT_MAX), Mask(0), Access(AccessTy::None) {} void setBegin(MachineBasicBlock::iterator &MI) { assert(Begin == nullptr); Begin = MI; } void setEnd(MachineBasicBlock::iterator &MI) { assert(End == nullptr); End = MI; } bool hasBase() const { return Base != 0; } unsigned getBase() const { assert(Base); return Base; } MachineBasicBlock::iterator begin() { assert(Begin != nullptr); return Begin; } MachineBasicBlock::iterator end() { assert(End != nullptr); return End; } unsigned getMask() const { return Mask; } void setBase(int Value) { assert(!hasBase()); Base = Value; } // You need to call this before Mask update UpdateType classifyUpdateByMask(unsigned NewMask) const { assert(NewMask && "Mask needs to select at least one register"); if (NewMask > Mask) { return Ascending; } else if (NewMask < Mask) { return Descending; } return Intermixed; } bool update(int O, int M) { UpdateType Type = classifyUpdateByMask(M); if (Type == Intermixed) return false; if (Start == INT_MIN) { Start = Stop = O; updateMask(M); return true; } else if (Type == Descending && O == Start - 4) { Start -= 4; updateMask(M); return true; } else if (Type == Ascending && O == Stop + 4) { Stop += 4; updateMask(M); return true; } return false; } int getFinalOffset() const { assert( Start != INT_MIN && "MOVEM in control mode should increment the address in each iteration"); return Start; } bool updateMask(unsigned Value) { assert(isUInt<16>(Value) && "Mask must fit 16 bit"); assert(!(Value & Mask) && "This is weird, there should be no intersections"); Mask |= Value; return true; } void setLoad() { Access = AccessTy::Load; } void setStore() { Access = AccessTy::Store; } bool isLoad() const { return Access == AccessTy::Load; } bool isStore() const { return Access == AccessTy::Store; } }; /// This Pass first walks through all the MOVEM instructions /// that are chained together and record each of the /// instruction's properties like register mask and data /// access type into a `MOVEState` instance. /// Then we perform reduction / collapsing on this `MOVEMState` /// representation before creating a new `MOVEM` instruction /// based on the collapsed result, as well as removing /// redundant `MOVEM` instructions. class M68kCollapseMOVEM : public MachineFunctionPass { public: static char ID; const M68kSubtarget *STI; const M68kInstrInfo *TII; const M68kRegisterInfo *TRI; const M68kMachineFunctionInfo *MFI; const M68kFrameLowering *FL; M68kCollapseMOVEM() : MachineFunctionPass(ID) {} void Finish(MachineBasicBlock &MBB, MOVEMState &State) { auto MI = State.begin(); auto End = State.end(); DebugLoc DL = MI->getDebugLoc(); // No need to delete then add a single instruction if (std::next(MI) == End) { State = MOVEMState(); return; } // Delete all the MOVEM instruction till the end while (MI != End) { auto Next = std::next(MI); MBB.erase(MI); MI = Next; } // Add a unified one if (State.isLoad()) { BuildMI(MBB, End, DL, TII->get(M68k::MOVM32mp)) .addImm(State.getMask()) .addImm(State.getFinalOffset()) .addReg(State.getBase()); } else { BuildMI(MBB, End, DL, TII->get(M68k::MOVM32pm)) .addImm(State.getFinalOffset()) .addReg(State.getBase()) .addImm(State.getMask()); } State = MOVEMState(); } bool ProcessMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, MOVEMState &State, unsigned Mask, int Offset, unsigned Reg, bool IsStore = false) { if (State.hasBase()) { // If current Type, Reg, Offset and Mask is in proper order then // merge in the state MOVEMState Temp = State; if (State.isStore() == IsStore && State.getBase() == Reg && State.update(Offset, Mask)) { return true; // Otherwise we Finish processing of the current MOVEM sequance and // start a new one } else { State = Temp; State.setEnd(MI); Finish(MBB, State); return ProcessMI(MBB, MI, State, Mask, Offset, Reg, IsStore); } // If this is the first instruction is sequance then initialize the State } else if (Reg == TRI->getStackRegister() || Reg == TRI->getBaseRegister() || Reg == TRI->getFrameRegister(*MBB.getParent())) { State.setBegin(MI); State.setBase(Reg); State.update(Offset, Mask); IsStore ? State.setStore() : State.setLoad(); return true; } return false; } bool runOnMachineFunction(MachineFunction &MF) override { STI = &MF.getSubtarget(); TII = STI->getInstrInfo(); TRI = STI->getRegisterInfo(); MFI = MF.getInfo(); FL = STI->getFrameLowering(); bool Modified = false; MOVEMState State; unsigned Mask = 0; unsigned Reg = 0; int Offset = 0; for (auto &MBB : MF) { auto MI = MBB.begin(), E = MBB.end(); while (MI != E) { // Processing might change current instruction, save next first auto NMI = std::next(MI); switch (MI->getOpcode()) { default: if (State.hasBase()) { State.setEnd(MI); Finish(MBB, State); Modified = true; } break; case M68k::MOVM32jm: Mask = MI->getOperand(1).getImm(); Reg = MI->getOperand(0).getReg(); Offset = 0; Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); break; case M68k::MOVM32pm: Mask = MI->getOperand(2).getImm(); Reg = MI->getOperand(1).getReg(); Offset = MI->getOperand(0).getImm(); Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); break; case M68k::MOVM32mj: Mask = MI->getOperand(0).getImm(); Reg = MI->getOperand(1).getReg(); Offset = 0; Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); break; case M68k::MOVM32mp: Mask = MI->getOperand(0).getImm(); Reg = MI->getOperand(2).getReg(); Offset = MI->getOperand(1).getImm(); Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); break; } MI = NMI; } if (State.hasBase()) { State.setEnd(MI); Finish(MBB, State); } } return Modified; } }; char M68kCollapseMOVEM::ID = 0; } // anonymous namespace. INITIALIZE_PASS(M68kCollapseMOVEM, DEBUG_TYPE, PASS_NAME, false, false) /// Returns an instance of the pseudo instruction expansion pass. FunctionPass *llvm::createM68kCollapseMOVEMPass() { return new M68kCollapseMOVEM(); }