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