1*fe6060f1SDimitry Andric //===----- M68kCollapseMOVEMPass.cpp - Expand MOVEM pass --------*- C++ -*-===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric /// 9*fe6060f1SDimitry Andric /// \file 10*fe6060f1SDimitry Andric /// `MOVEM` is an instruction that moves multiple registers a time according to 11*fe6060f1SDimitry Andric /// the given mask. Thus sometimes it's pretty expensive. 12*fe6060f1SDimitry Andric /// This file contains a pass that collapses sequential MOVEM instructions into 13*fe6060f1SDimitry Andric /// a single one. 14*fe6060f1SDimitry Andric /// 15*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 16*fe6060f1SDimitry Andric 17*fe6060f1SDimitry Andric #include "M68k.h" 18*fe6060f1SDimitry Andric #include "M68kFrameLowering.h" 19*fe6060f1SDimitry Andric #include "M68kInstrInfo.h" 20*fe6060f1SDimitry Andric #include "M68kMachineFunction.h" 21*fe6060f1SDimitry Andric #include "M68kSubtarget.h" 22*fe6060f1SDimitry Andric 23*fe6060f1SDimitry Andric #include "llvm/Analysis/EHPersonalities.h" 24*fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 25*fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 26*fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 27*fe6060f1SDimitry Andric #include "llvm/IR/GlobalValue.h" 28*fe6060f1SDimitry Andric #include "llvm/Support/MathExtras.h" 29*fe6060f1SDimitry Andric 30*fe6060f1SDimitry Andric using namespace llvm; 31*fe6060f1SDimitry Andric 32*fe6060f1SDimitry Andric #define DEBUG_TYPE "M68k-collapse-movem" 33*fe6060f1SDimitry Andric 34*fe6060f1SDimitry Andric namespace { 35*fe6060f1SDimitry Andric 36*fe6060f1SDimitry Andric enum UpdateType { Ascending, Descending, Intermixed }; 37*fe6060f1SDimitry Andric 38*fe6060f1SDimitry Andric /// An abtraction of the MOVEM chain currently processing 39*fe6060f1SDimitry Andric class MOVEMState { 40*fe6060f1SDimitry Andric MachineBasicBlock::iterator Begin; 41*fe6060f1SDimitry Andric MachineBasicBlock::iterator End; 42*fe6060f1SDimitry Andric 43*fe6060f1SDimitry Andric unsigned Base; 44*fe6060f1SDimitry Andric 45*fe6060f1SDimitry Andric int Start; 46*fe6060f1SDimitry Andric int Stop; 47*fe6060f1SDimitry Andric 48*fe6060f1SDimitry Andric unsigned Mask; 49*fe6060f1SDimitry Andric 50*fe6060f1SDimitry Andric enum class AccessTy { None, Load, Store }; 51*fe6060f1SDimitry Andric AccessTy Access; 52*fe6060f1SDimitry Andric 53*fe6060f1SDimitry Andric public: 54*fe6060f1SDimitry Andric MOVEMState() 55*fe6060f1SDimitry Andric : Begin(nullptr), End(nullptr), Base(0), Start(INT_MIN), Stop(INT_MAX), 56*fe6060f1SDimitry Andric Mask(0), Access(AccessTy::None) {} 57*fe6060f1SDimitry Andric 58*fe6060f1SDimitry Andric void setBegin(MachineBasicBlock::iterator &MI) { 59*fe6060f1SDimitry Andric assert(Begin == nullptr); 60*fe6060f1SDimitry Andric Begin = MI; 61*fe6060f1SDimitry Andric } 62*fe6060f1SDimitry Andric 63*fe6060f1SDimitry Andric void setEnd(MachineBasicBlock::iterator &MI) { 64*fe6060f1SDimitry Andric assert(End == nullptr); 65*fe6060f1SDimitry Andric End = MI; 66*fe6060f1SDimitry Andric } 67*fe6060f1SDimitry Andric 68*fe6060f1SDimitry Andric bool hasBase() const { return Base != 0; } 69*fe6060f1SDimitry Andric 70*fe6060f1SDimitry Andric unsigned getBase() const { 71*fe6060f1SDimitry Andric assert(Base); 72*fe6060f1SDimitry Andric return Base; 73*fe6060f1SDimitry Andric } 74*fe6060f1SDimitry Andric 75*fe6060f1SDimitry Andric MachineBasicBlock::iterator begin() { 76*fe6060f1SDimitry Andric assert(Begin != nullptr); 77*fe6060f1SDimitry Andric return Begin; 78*fe6060f1SDimitry Andric } 79*fe6060f1SDimitry Andric 80*fe6060f1SDimitry Andric MachineBasicBlock::iterator end() { 81*fe6060f1SDimitry Andric assert(End != nullptr); 82*fe6060f1SDimitry Andric return End; 83*fe6060f1SDimitry Andric } 84*fe6060f1SDimitry Andric 85*fe6060f1SDimitry Andric unsigned getMask() const { return Mask; } 86*fe6060f1SDimitry Andric 87*fe6060f1SDimitry Andric void setBase(int Value) { 88*fe6060f1SDimitry Andric assert(!hasBase()); 89*fe6060f1SDimitry Andric Base = Value; 90*fe6060f1SDimitry Andric } 91*fe6060f1SDimitry Andric 92*fe6060f1SDimitry Andric // You need to call this before Mask update 93*fe6060f1SDimitry Andric UpdateType classifyUpdateByMask(unsigned NewMask) const { 94*fe6060f1SDimitry Andric assert(NewMask && "Mask needs to select at least one register"); 95*fe6060f1SDimitry Andric 96*fe6060f1SDimitry Andric if (NewMask > Mask) { 97*fe6060f1SDimitry Andric return Ascending; 98*fe6060f1SDimitry Andric } else if (NewMask < Mask) { 99*fe6060f1SDimitry Andric return Descending; 100*fe6060f1SDimitry Andric } 101*fe6060f1SDimitry Andric 102*fe6060f1SDimitry Andric return Intermixed; 103*fe6060f1SDimitry Andric } 104*fe6060f1SDimitry Andric 105*fe6060f1SDimitry Andric bool update(int O, int M) { 106*fe6060f1SDimitry Andric UpdateType Type = classifyUpdateByMask(M); 107*fe6060f1SDimitry Andric if (Type == Intermixed) 108*fe6060f1SDimitry Andric return false; 109*fe6060f1SDimitry Andric if (Start == INT_MIN) { 110*fe6060f1SDimitry Andric Start = Stop = O; 111*fe6060f1SDimitry Andric updateMask(M); 112*fe6060f1SDimitry Andric return true; 113*fe6060f1SDimitry Andric } else if (Type == Descending && O == Start - 4) { 114*fe6060f1SDimitry Andric Start -= 4; 115*fe6060f1SDimitry Andric updateMask(M); 116*fe6060f1SDimitry Andric return true; 117*fe6060f1SDimitry Andric } else if (Type == Ascending && O == Stop + 4) { 118*fe6060f1SDimitry Andric Stop += 4; 119*fe6060f1SDimitry Andric updateMask(M); 120*fe6060f1SDimitry Andric return true; 121*fe6060f1SDimitry Andric } 122*fe6060f1SDimitry Andric 123*fe6060f1SDimitry Andric return false; 124*fe6060f1SDimitry Andric } 125*fe6060f1SDimitry Andric 126*fe6060f1SDimitry Andric int getFinalOffset() const { 127*fe6060f1SDimitry Andric assert( 128*fe6060f1SDimitry Andric Start != INT_MIN && 129*fe6060f1SDimitry Andric "MOVEM in control mode should increment the address in each iteration"); 130*fe6060f1SDimitry Andric return Start; 131*fe6060f1SDimitry Andric } 132*fe6060f1SDimitry Andric 133*fe6060f1SDimitry Andric bool updateMask(unsigned Value) { 134*fe6060f1SDimitry Andric assert(isUInt<16>(Value) && "Mask must fit 16 bit"); 135*fe6060f1SDimitry Andric assert(!(Value & Mask) && 136*fe6060f1SDimitry Andric "This is weird, there should be no intersections"); 137*fe6060f1SDimitry Andric Mask |= Value; 138*fe6060f1SDimitry Andric return true; 139*fe6060f1SDimitry Andric } 140*fe6060f1SDimitry Andric 141*fe6060f1SDimitry Andric void setLoad() { Access = AccessTy::Load; } 142*fe6060f1SDimitry Andric void setStore() { Access = AccessTy::Store; } 143*fe6060f1SDimitry Andric 144*fe6060f1SDimitry Andric bool isLoad() const { return Access == AccessTy::Load; } 145*fe6060f1SDimitry Andric bool isStore() const { return Access == AccessTy::Store; } 146*fe6060f1SDimitry Andric }; 147*fe6060f1SDimitry Andric 148*fe6060f1SDimitry Andric /// This Pass first walks through all the MOVEM instructions 149*fe6060f1SDimitry Andric /// that are chained together and record each of the 150*fe6060f1SDimitry Andric /// instruction's properties like register mask and data 151*fe6060f1SDimitry Andric /// access type into a `MOVEState` instance. 152*fe6060f1SDimitry Andric /// Then we perform reduction / collapsing on this `MOVEMState` 153*fe6060f1SDimitry Andric /// representation before creating a new `MOVEM` instruction 154*fe6060f1SDimitry Andric /// based on the collapsed result, as well as removing 155*fe6060f1SDimitry Andric /// redundant `MOVEM` instructions. 156*fe6060f1SDimitry Andric class M68kCollapseMOVEM : public MachineFunctionPass { 157*fe6060f1SDimitry Andric public: 158*fe6060f1SDimitry Andric static char ID; 159*fe6060f1SDimitry Andric 160*fe6060f1SDimitry Andric const M68kSubtarget *STI; 161*fe6060f1SDimitry Andric const M68kInstrInfo *TII; 162*fe6060f1SDimitry Andric const M68kRegisterInfo *TRI; 163*fe6060f1SDimitry Andric const M68kMachineFunctionInfo *MFI; 164*fe6060f1SDimitry Andric const M68kFrameLowering *FL; 165*fe6060f1SDimitry Andric 166*fe6060f1SDimitry Andric M68kCollapseMOVEM() : MachineFunctionPass(ID) {} 167*fe6060f1SDimitry Andric 168*fe6060f1SDimitry Andric void Finish(MachineBasicBlock &MBB, MOVEMState &State) { 169*fe6060f1SDimitry Andric auto MI = State.begin(); 170*fe6060f1SDimitry Andric auto End = State.end(); 171*fe6060f1SDimitry Andric DebugLoc DL = MI->getDebugLoc(); 172*fe6060f1SDimitry Andric 173*fe6060f1SDimitry Andric // No need to delete then add a single instruction 174*fe6060f1SDimitry Andric if (std::next(MI) == End) { 175*fe6060f1SDimitry Andric State = MOVEMState(); 176*fe6060f1SDimitry Andric return; 177*fe6060f1SDimitry Andric } 178*fe6060f1SDimitry Andric 179*fe6060f1SDimitry Andric // Delete all the MOVEM instruction till the end 180*fe6060f1SDimitry Andric while (MI != End) { 181*fe6060f1SDimitry Andric auto Next = std::next(MI); 182*fe6060f1SDimitry Andric MBB.erase(MI); 183*fe6060f1SDimitry Andric MI = Next; 184*fe6060f1SDimitry Andric } 185*fe6060f1SDimitry Andric 186*fe6060f1SDimitry Andric // Add a unified one 187*fe6060f1SDimitry Andric if (State.isLoad()) { 188*fe6060f1SDimitry Andric BuildMI(MBB, End, DL, TII->get(M68k::MOVM32mp)) 189*fe6060f1SDimitry Andric .addImm(State.getMask()) 190*fe6060f1SDimitry Andric .addImm(State.getFinalOffset()) 191*fe6060f1SDimitry Andric .addReg(State.getBase()); 192*fe6060f1SDimitry Andric } else { 193*fe6060f1SDimitry Andric BuildMI(MBB, End, DL, TII->get(M68k::MOVM32pm)) 194*fe6060f1SDimitry Andric .addImm(State.getFinalOffset()) 195*fe6060f1SDimitry Andric .addReg(State.getBase()) 196*fe6060f1SDimitry Andric .addImm(State.getMask()); 197*fe6060f1SDimitry Andric } 198*fe6060f1SDimitry Andric 199*fe6060f1SDimitry Andric State = MOVEMState(); 200*fe6060f1SDimitry Andric } 201*fe6060f1SDimitry Andric 202*fe6060f1SDimitry Andric bool ProcessMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, 203*fe6060f1SDimitry Andric MOVEMState &State, unsigned Mask, int Offset, unsigned Reg, 204*fe6060f1SDimitry Andric bool IsStore = false) { 205*fe6060f1SDimitry Andric if (State.hasBase()) { 206*fe6060f1SDimitry Andric // If current Type, Reg, Offset and Mask is in proper order then 207*fe6060f1SDimitry Andric // merge in the state 208*fe6060f1SDimitry Andric MOVEMState Temp = State; 209*fe6060f1SDimitry Andric if (State.isStore() == IsStore && State.getBase() == Reg && 210*fe6060f1SDimitry Andric State.update(Offset, Mask)) { 211*fe6060f1SDimitry Andric return true; 212*fe6060f1SDimitry Andric // Otherwise we Finish processing of the current MOVEM sequance and 213*fe6060f1SDimitry Andric // start a new one 214*fe6060f1SDimitry Andric } else { 215*fe6060f1SDimitry Andric State = Temp; 216*fe6060f1SDimitry Andric State.setEnd(MI); 217*fe6060f1SDimitry Andric Finish(MBB, State); 218*fe6060f1SDimitry Andric return ProcessMI(MBB, MI, State, Mask, Offset, Reg, IsStore); 219*fe6060f1SDimitry Andric } 220*fe6060f1SDimitry Andric // If this is the first instruction is sequance then initialize the State 221*fe6060f1SDimitry Andric } else if (Reg == TRI->getStackRegister() || 222*fe6060f1SDimitry Andric Reg == TRI->getBaseRegister() || 223*fe6060f1SDimitry Andric Reg == TRI->getFrameRegister(*MBB.getParent())) { 224*fe6060f1SDimitry Andric State.setBegin(MI); 225*fe6060f1SDimitry Andric State.setBase(Reg); 226*fe6060f1SDimitry Andric State.update(Offset, Mask); 227*fe6060f1SDimitry Andric IsStore ? State.setStore() : State.setLoad(); 228*fe6060f1SDimitry Andric return true; 229*fe6060f1SDimitry Andric } 230*fe6060f1SDimitry Andric return false; 231*fe6060f1SDimitry Andric } 232*fe6060f1SDimitry Andric 233*fe6060f1SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 234*fe6060f1SDimitry Andric STI = &static_cast<const M68kSubtarget &>(MF.getSubtarget()); 235*fe6060f1SDimitry Andric TII = STI->getInstrInfo(); 236*fe6060f1SDimitry Andric TRI = STI->getRegisterInfo(); 237*fe6060f1SDimitry Andric MFI = MF.getInfo<M68kMachineFunctionInfo>(); 238*fe6060f1SDimitry Andric FL = STI->getFrameLowering(); 239*fe6060f1SDimitry Andric 240*fe6060f1SDimitry Andric bool Modified = false; 241*fe6060f1SDimitry Andric 242*fe6060f1SDimitry Andric MOVEMState State; 243*fe6060f1SDimitry Andric 244*fe6060f1SDimitry Andric unsigned Mask = 0; 245*fe6060f1SDimitry Andric unsigned Reg = 0; 246*fe6060f1SDimitry Andric int Offset = 0; 247*fe6060f1SDimitry Andric 248*fe6060f1SDimitry Andric for (auto &MBB : MF) { 249*fe6060f1SDimitry Andric auto MI = MBB.begin(), E = MBB.end(); 250*fe6060f1SDimitry Andric while (MI != E) { 251*fe6060f1SDimitry Andric // Processing might change current instruction, save next first 252*fe6060f1SDimitry Andric auto NMI = std::next(MI); 253*fe6060f1SDimitry Andric switch (MI->getOpcode()) { 254*fe6060f1SDimitry Andric default: 255*fe6060f1SDimitry Andric if (State.hasBase()) { 256*fe6060f1SDimitry Andric State.setEnd(MI); 257*fe6060f1SDimitry Andric Finish(MBB, State); 258*fe6060f1SDimitry Andric Modified = true; 259*fe6060f1SDimitry Andric } 260*fe6060f1SDimitry Andric break; 261*fe6060f1SDimitry Andric case M68k::MOVM32jm: 262*fe6060f1SDimitry Andric Mask = MI->getOperand(1).getImm(); 263*fe6060f1SDimitry Andric Reg = MI->getOperand(0).getReg(); 264*fe6060f1SDimitry Andric Offset = 0; 265*fe6060f1SDimitry Andric Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); 266*fe6060f1SDimitry Andric break; 267*fe6060f1SDimitry Andric case M68k::MOVM32pm: 268*fe6060f1SDimitry Andric Mask = MI->getOperand(2).getImm(); 269*fe6060f1SDimitry Andric Reg = MI->getOperand(1).getReg(); 270*fe6060f1SDimitry Andric Offset = MI->getOperand(0).getImm(); 271*fe6060f1SDimitry Andric Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); 272*fe6060f1SDimitry Andric break; 273*fe6060f1SDimitry Andric case M68k::MOVM32mj: 274*fe6060f1SDimitry Andric Mask = MI->getOperand(0).getImm(); 275*fe6060f1SDimitry Andric Reg = MI->getOperand(1).getReg(); 276*fe6060f1SDimitry Andric Offset = 0; 277*fe6060f1SDimitry Andric Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); 278*fe6060f1SDimitry Andric break; 279*fe6060f1SDimitry Andric case M68k::MOVM32mp: 280*fe6060f1SDimitry Andric Mask = MI->getOperand(0).getImm(); 281*fe6060f1SDimitry Andric Reg = MI->getOperand(2).getReg(); 282*fe6060f1SDimitry Andric Offset = MI->getOperand(1).getImm(); 283*fe6060f1SDimitry Andric Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); 284*fe6060f1SDimitry Andric break; 285*fe6060f1SDimitry Andric } 286*fe6060f1SDimitry Andric MI = NMI; 287*fe6060f1SDimitry Andric } 288*fe6060f1SDimitry Andric 289*fe6060f1SDimitry Andric if (State.hasBase()) { 290*fe6060f1SDimitry Andric State.setEnd(MI); 291*fe6060f1SDimitry Andric Finish(MBB, State); 292*fe6060f1SDimitry Andric } 293*fe6060f1SDimitry Andric } 294*fe6060f1SDimitry Andric 295*fe6060f1SDimitry Andric return Modified; 296*fe6060f1SDimitry Andric } 297*fe6060f1SDimitry Andric 298*fe6060f1SDimitry Andric StringRef getPassName() const override { return "M68k MOVEM collapser pass"; } 299*fe6060f1SDimitry Andric }; 300*fe6060f1SDimitry Andric 301*fe6060f1SDimitry Andric char M68kCollapseMOVEM::ID = 0; 302*fe6060f1SDimitry Andric } // anonymous namespace. 303*fe6060f1SDimitry Andric 304*fe6060f1SDimitry Andric /// Returns an instance of the pseudo instruction expansion pass. 305*fe6060f1SDimitry Andric FunctionPass *llvm::createM68kCollapseMOVEMPass() { 306*fe6060f1SDimitry Andric return new M68kCollapseMOVEM(); 307*fe6060f1SDimitry Andric } 308