xref: /freebsd/contrib/llvm-project/llvm/lib/Target/M68k/M68kCollapseMOVEMPass.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
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