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