1 //===-- AVRAsmPrinter.cpp - AVR LLVM assembly writer ----------------------===//
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 printer that converts from our internal representation
10 // of machine-dependent LLVM code to GAS-format AVR assembly language.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "AVR.h"
15 #include "AVRMCInstLower.h"
16 #include "AVRSubtarget.h"
17 #include "AVRTargetMachine.h"
18 #include "MCTargetDesc/AVRInstPrinter.h"
19 #include "MCTargetDesc/AVRMCExpr.h"
20 #include "TargetInfo/AVRTargetInfo.h"
21
22 #include "llvm/CodeGen/AsmPrinter.h"
23 #include "llvm/CodeGen/MachineFunction.h"
24 #include "llvm/CodeGen/MachineInstr.h"
25 #include "llvm/CodeGen/MachineModuleInfo.h"
26 #include "llvm/CodeGen/TargetRegisterInfo.h"
27 #include "llvm/CodeGen/TargetSubtargetInfo.h"
28 #include "llvm/IR/Mangler.h"
29 #include "llvm/MC/MCContext.h"
30 #include "llvm/MC/MCInst.h"
31 #include "llvm/MC/MCStreamer.h"
32 #include "llvm/MC/MCSymbol.h"
33 #include "llvm/MC/TargetRegistry.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include "llvm/Support/raw_ostream.h"
36
37 #define DEBUG_TYPE "avr-asm-printer"
38
39 namespace llvm {
40
41 /// An AVR assembly code printer.
42 class AVRAsmPrinter : public AsmPrinter {
43 public:
AVRAsmPrinter(TargetMachine & TM,std::unique_ptr<MCStreamer> Streamer)44 AVRAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
45 : AsmPrinter(TM, std::move(Streamer)), MRI(*TM.getMCRegisterInfo()) {}
46
getPassName() const47 StringRef getPassName() const override { return "AVR Assembly Printer"; }
48
49 void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O);
50
51 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
52 const char *ExtraCode, raw_ostream &O) override;
53
54 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
55 const char *ExtraCode, raw_ostream &O) override;
56
57 void emitInstruction(const MachineInstr *MI) override;
58
59 const MCExpr *lowerConstant(const Constant *CV) override;
60
61 void emitXXStructor(const DataLayout &DL, const Constant *CV) override;
62
63 bool doFinalization(Module &M) override;
64
65 void emitStartOfAsmFile(Module &M) override;
66
67 private:
68 const MCRegisterInfo &MRI;
69 bool EmittedStructorSymbolAttrs = false;
70 };
71
printOperand(const MachineInstr * MI,unsigned OpNo,raw_ostream & O)72 void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo,
73 raw_ostream &O) {
74 const MachineOperand &MO = MI->getOperand(OpNo);
75
76 switch (MO.getType()) {
77 case MachineOperand::MO_Register:
78 O << AVRInstPrinter::getPrettyRegisterName(MO.getReg(), MRI);
79 break;
80 case MachineOperand::MO_Immediate:
81 O << MO.getImm();
82 break;
83 case MachineOperand::MO_GlobalAddress:
84 O << getSymbol(MO.getGlobal());
85 break;
86 case MachineOperand::MO_ExternalSymbol:
87 O << *GetExternalSymbolSymbol(MO.getSymbolName());
88 break;
89 case MachineOperand::MO_MachineBasicBlock:
90 O << *MO.getMBB()->getSymbol();
91 break;
92 default:
93 llvm_unreachable("Not implemented yet!");
94 }
95 }
96
PrintAsmOperand(const MachineInstr * MI,unsigned OpNum,const char * ExtraCode,raw_ostream & O)97 bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
98 const char *ExtraCode, raw_ostream &O) {
99 // Default asm printer can only deal with some extra codes,
100 // so try it first.
101 bool Error = AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O);
102
103 if (Error && ExtraCode && ExtraCode[0]) {
104 if (ExtraCode[1] != 0)
105 return true; // Unknown modifier.
106
107 if (ExtraCode[0] >= 'A' && ExtraCode[0] <= 'Z') {
108 const MachineOperand &RegOp = MI->getOperand(OpNum);
109
110 assert(RegOp.isReg() && "Operand must be a register when you're"
111 "using 'A'..'Z' operand extracodes.");
112 Register Reg = RegOp.getReg();
113
114 unsigned ByteNumber = ExtraCode[0] - 'A';
115
116 unsigned OpFlags = MI->getOperand(OpNum - 1).getImm();
117 unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags);
118 (void)NumOpRegs;
119
120 const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>();
121 const TargetRegisterInfo &TRI = *STI.getRegisterInfo();
122
123 const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);
124 unsigned BytesPerReg = TRI.getRegSizeInBits(*RC) / 8;
125 assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported.");
126
127 unsigned RegIdx = ByteNumber / BytesPerReg;
128 assert(RegIdx < NumOpRegs && "Multibyte index out of range.");
129
130 Reg = MI->getOperand(OpNum + RegIdx).getReg();
131
132 if (BytesPerReg == 2) {
133 Reg = TRI.getSubReg(Reg, ByteNumber % BytesPerReg ? AVR::sub_hi
134 : AVR::sub_lo);
135 }
136
137 O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI);
138 return false;
139 }
140 }
141
142 if (Error)
143 printOperand(MI, OpNum, O);
144
145 return false;
146 }
147
PrintAsmMemoryOperand(const MachineInstr * MI,unsigned OpNum,const char * ExtraCode,raw_ostream & O)148 bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
149 unsigned OpNum, const char *ExtraCode,
150 raw_ostream &O) {
151 if (ExtraCode && ExtraCode[0])
152 return true; // Unknown modifier
153
154 const MachineOperand &MO = MI->getOperand(OpNum);
155 (void)MO;
156 assert(MO.isReg() && "Unexpected inline asm memory operand");
157
158 // TODO: We should be able to look up the alternative name for
159 // the register if it's given.
160 // TableGen doesn't expose a way of getting retrieving names
161 // for registers.
162 if (MI->getOperand(OpNum).getReg() == AVR::R31R30) {
163 O << "Z";
164 } else {
165 assert(MI->getOperand(OpNum).getReg() == AVR::R29R28 &&
166 "Wrong register class for memory operand.");
167 O << "Y";
168 }
169
170 // If NumOpRegs == 2, then we assume it is product of a FrameIndex expansion
171 // and the second operand is an Imm.
172 unsigned OpFlags = MI->getOperand(OpNum - 1).getImm();
173 unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags);
174
175 if (NumOpRegs == 2) {
176 O << '+' << MI->getOperand(OpNum + 1).getImm();
177 }
178
179 return false;
180 }
181
emitInstruction(const MachineInstr * MI)182 void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) {
183 // FIXME: Enable feature predicate checks once all the test pass.
184 // AVR_MC::verifyInstructionPredicates(MI->getOpcode(),
185 // getSubtargetInfo().getFeatureBits());
186
187 AVRMCInstLower MCInstLowering(OutContext, *this);
188
189 MCInst I;
190 MCInstLowering.lowerInstruction(*MI, I);
191 EmitToStreamer(*OutStreamer, I);
192 }
193
lowerConstant(const Constant * CV)194 const MCExpr *AVRAsmPrinter::lowerConstant(const Constant *CV) {
195 MCContext &Ctx = OutContext;
196
197 if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) {
198 bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory;
199 if (IsProgMem) {
200 const MCExpr *Expr = MCSymbolRefExpr::create(getSymbol(GV), Ctx);
201 return AVRMCExpr::create(AVRMCExpr::VK_AVR_PM, Expr, false, Ctx);
202 }
203 }
204
205 return AsmPrinter::lowerConstant(CV);
206 }
207
emitXXStructor(const DataLayout & DL,const Constant * CV)208 void AVRAsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) {
209 if (!EmittedStructorSymbolAttrs) {
210 OutStreamer->emitRawComment(
211 " Emitting these undefined symbol references causes us to link the"
212 " libgcc code that runs our constructors/destructors");
213 OutStreamer->emitRawComment(" This matches GCC's behavior");
214
215 MCSymbol *CtorsSym = OutContext.getOrCreateSymbol("__do_global_ctors");
216 OutStreamer->emitSymbolAttribute(CtorsSym, MCSA_Global);
217
218 MCSymbol *DtorsSym = OutContext.getOrCreateSymbol("__do_global_dtors");
219 OutStreamer->emitSymbolAttribute(DtorsSym, MCSA_Global);
220
221 EmittedStructorSymbolAttrs = true;
222 }
223
224 AsmPrinter::emitXXStructor(DL, CV);
225 }
226
doFinalization(Module & M)227 bool AVRAsmPrinter::doFinalization(Module &M) {
228 MCSymbol *DoCopyData = OutContext.getOrCreateSymbol("__do_copy_data");
229 MCSymbol *DoClearBss = OutContext.getOrCreateSymbol("__do_clear_bss");
230
231 // FIXME: We can disable __do_copy_data if there are no static RAM variables.
232
233 OutStreamer->emitRawComment(
234 " Declaring this symbol tells the CRT that it should");
235 OutStreamer->emitRawComment(
236 "copy all variables from program memory to RAM on startup");
237 OutStreamer->emitSymbolAttribute(DoCopyData, MCSA_Global);
238
239 OutStreamer->emitRawComment(
240 " Declaring this symbol tells the CRT that it should");
241 OutStreamer->emitRawComment("clear the zeroed data section on startup");
242 OutStreamer->emitSymbolAttribute(DoClearBss, MCSA_Global);
243
244 return AsmPrinter::doFinalization(M);
245 }
246
emitStartOfAsmFile(Module & M)247 void AVRAsmPrinter::emitStartOfAsmFile(Module &M) {
248 const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget();
249 const AVRSubtarget *SubTM = (const AVRSubtarget *)TM.getSubtargetImpl();
250 if (!SubTM)
251 return;
252
253 // Emit __tmp_reg__.
254 OutStreamer->emitAssignment(
255 MMI->getContext().getOrCreateSymbol(StringRef("__tmp_reg__")),
256 MCConstantExpr::create(SubTM->getRegTmpIndex(), MMI->getContext()));
257 // Emit __zero_reg__.
258 OutStreamer->emitAssignment(
259 MMI->getContext().getOrCreateSymbol(StringRef("__zero_reg__")),
260 MCConstantExpr::create(SubTM->getRegZeroIndex(), MMI->getContext()));
261 // Emit __SREG__.
262 OutStreamer->emitAssignment(
263 MMI->getContext().getOrCreateSymbol(StringRef("__SREG__")),
264 MCConstantExpr::create(SubTM->getIORegSREG(), MMI->getContext()));
265 // Emit __SP_H__ if available.
266 if (!SubTM->hasSmallStack())
267 OutStreamer->emitAssignment(
268 MMI->getContext().getOrCreateSymbol(StringRef("__SP_H__")),
269 MCConstantExpr::create(SubTM->getIORegSPH(), MMI->getContext()));
270 // Emit __SP_L__.
271 OutStreamer->emitAssignment(
272 MMI->getContext().getOrCreateSymbol(StringRef("__SP_L__")),
273 MCConstantExpr::create(SubTM->getIORegSPL(), MMI->getContext()));
274 // Emit __EIND__ if available.
275 if (SubTM->hasEIJMPCALL())
276 OutStreamer->emitAssignment(
277 MMI->getContext().getOrCreateSymbol(StringRef("__EIND__")),
278 MCConstantExpr::create(SubTM->getIORegEIND(), MMI->getContext()));
279 // Emit __RAMPZ__ if available.
280 if (SubTM->hasELPM())
281 OutStreamer->emitAssignment(
282 MMI->getContext().getOrCreateSymbol(StringRef("__RAMPZ__")),
283 MCConstantExpr::create(SubTM->getIORegRAMPZ(), MMI->getContext()));
284 }
285
286 } // end of namespace llvm
287
LLVMInitializeAVRAsmPrinter()288 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAVRAsmPrinter() {
289 llvm::RegisterAsmPrinter<llvm::AVRAsmPrinter> X(llvm::getTheAVRTarget());
290 }
291