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