1//===-- AVRRelaxMemOperations.cpp - Relax out of range loads/stores -------===//
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// This file contains a pass which relaxes out of range memory operations into
10// equivalent operations which handle bigger addresses.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AVR.h"
15#include "AVRInstrInfo.h"
16#include "AVRTargetMachine.h"
17#include "MCTargetDesc/AVRMCTargetDesc.h"
18
19#include "llvm/CodeGen/MachineFunctionPass.h"
20#include "llvm/CodeGen/MachineInstrBuilder.h"
21#include "llvm/CodeGen/MachineRegisterInfo.h"
22#include "llvm/CodeGen/TargetRegisterInfo.h"
23
24using namespace llvm;
25
26#define AVR_RELAX_MEM_OPS_NAME "AVR memory operation relaxation pass"
27
28namespace {
29
30class AVRRelaxMem : public MachineFunctionPass {
31public:
32  static char ID;
33
34  AVRRelaxMem() : MachineFunctionPass(ID) {
35    initializeAVRRelaxMemPass(*PassRegistry::getPassRegistry());
36  }
37
38  bool runOnMachineFunction(MachineFunction &MF) override;
39
40  StringRef getPassName() const override { return AVR_RELAX_MEM_OPS_NAME; }
41
42private:
43  typedef MachineBasicBlock Block;
44  typedef Block::iterator BlockIt;
45
46  const TargetInstrInfo *TII;
47
48  template <unsigned OP> bool relax(Block &MBB, BlockIt MBBI);
49
50  bool runOnBasicBlock(Block &MBB);
51  bool runOnInstruction(Block &MBB, BlockIt MBBI);
52
53  MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) {
54    return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode));
55  }
56};
57
58char AVRRelaxMem::ID = 0;
59
60bool AVRRelaxMem::runOnMachineFunction(MachineFunction &MF) {
61  bool Modified = false;
62
63  const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
64  TII = STI.getInstrInfo();
65
66  for (Block &MBB : MF) {
67    bool BlockModified = runOnBasicBlock(MBB);
68    Modified |= BlockModified;
69  }
70
71  return Modified;
72}
73
74bool AVRRelaxMem::runOnBasicBlock(Block &MBB) {
75  bool Modified = false;
76
77  BlockIt MBBI = MBB.begin(), E = MBB.end();
78  while (MBBI != E) {
79    BlockIt NMBBI = std::next(MBBI);
80    Modified |= runOnInstruction(MBB, MBBI);
81    MBBI = NMBBI;
82  }
83
84  return Modified;
85}
86
87template <>
88bool AVRRelaxMem::relax<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) {
89  MachineInstr &MI = *MBBI;
90
91  MachineOperand &Ptr = MI.getOperand(0);
92  MachineOperand &Src = MI.getOperand(2);
93  int64_t Imm = MI.getOperand(1).getImm();
94
95  // We can definitely optimise this better.
96  if (Imm > 63) {
97    // Push the previous state of the pointer register.
98    // This instruction must preserve the value.
99    buildMI(MBB, MBBI, AVR::PUSHWRr)
100      .addReg(Ptr.getReg());
101
102    // Add the immediate to the pointer register.
103    buildMI(MBB, MBBI, AVR::SBCIWRdK)
104      .addReg(Ptr.getReg(), RegState::Define)
105      .addReg(Ptr.getReg())
106      .addImm(-Imm);
107
108    // Store the value in the source register to the address
109    // pointed to by the pointer register.
110    buildMI(MBB, MBBI, AVR::STWPtrRr)
111      .addReg(Ptr.getReg())
112      .addReg(Src.getReg(), getKillRegState(Src.isKill()));
113
114    // Pop the original state of the pointer register.
115    buildMI(MBB, MBBI, AVR::POPWRd)
116      .addReg(Ptr.getReg(), getKillRegState(Ptr.isKill()));
117
118    MI.removeFromParent();
119  }
120
121  return false;
122}
123
124bool AVRRelaxMem::runOnInstruction(Block &MBB, BlockIt MBBI) {
125  MachineInstr &MI = *MBBI;
126  int Opcode = MBBI->getOpcode();
127
128#define RELAX(Op)                \
129  case Op:                       \
130    return relax<Op>(MBB, MI)
131
132  switch (Opcode) {
133    RELAX(AVR::STDWPtrQRr);
134  }
135#undef RELAX
136  return false;
137}
138
139} // end of anonymous namespace
140
141INITIALIZE_PASS(AVRRelaxMem, "avr-relax-mem",
142                AVR_RELAX_MEM_OPS_NAME, false, false)
143
144namespace llvm {
145
146FunctionPass *createAVRRelaxMemPass() { return new AVRRelaxMem(); }
147
148} // end of namespace llvm
149