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/AVRMCAsmInfo.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/IR/Module.h" |
30 | #include "llvm/MC/MCContext.h" |
31 | #include "llvm/MC/MCInst.h" |
32 | #include "llvm/MC/MCSectionELF.h" |
33 | #include "llvm/MC/MCStreamer.h" |
34 | #include "llvm/MC/MCSymbol.h" |
35 | #include "llvm/MC/TargetRegistry.h" |
36 | #include "llvm/Support/Compiler.h" |
37 | #include "llvm/Support/ErrorHandling.h" |
38 | #include "llvm/Support/raw_ostream.h" |
39 | #include "llvm/Target/TargetLoweringObjectFile.h" |
40 | |
41 | #define DEBUG_TYPE "avr-asm-printer" |
42 | |
43 | using namespace llvm; |
44 | |
45 | namespace { |
46 | |
47 | /// An AVR assembly code printer. |
48 | class AVRAsmPrinter : public AsmPrinter { |
49 | public: |
50 | AVRAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) |
51 | : AsmPrinter(TM, std::move(Streamer), ID), MRI(*TM.getMCRegisterInfo()) {} |
52 | |
53 | StringRef getPassName() const override { return "AVR Assembly Printer" ; } |
54 | |
55 | void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O); |
56 | |
57 | bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, |
58 | const char *, raw_ostream &O) override; |
59 | |
60 | bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, |
61 | const char *, raw_ostream &O) override; |
62 | |
63 | void emitInstruction(const MachineInstr *MI) override; |
64 | |
65 | const MCExpr *lowerConstant(const Constant *CV, const Constant *BaseCV, |
66 | uint64_t Offset) override; |
67 | |
68 | void emitXXStructor(const DataLayout &DL, const Constant *CV) override; |
69 | |
70 | bool doFinalization(Module &M) override; |
71 | |
72 | void emitStartOfAsmFile(Module &M) override; |
73 | |
74 | static char ID; |
75 | |
76 | private: |
77 | const MCRegisterInfo &MRI; |
78 | bool EmittedStructorSymbolAttrs = false; |
79 | }; |
80 | |
81 | } // namespace |
82 | |
83 | void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo, |
84 | raw_ostream &O) { |
85 | const MachineOperand &MO = MI->getOperand(i: OpNo); |
86 | |
87 | switch (MO.getType()) { |
88 | case MachineOperand::MO_Register: |
89 | O << AVRInstPrinter::getPrettyRegisterName(Reg: MO.getReg(), MRI); |
90 | break; |
91 | case MachineOperand::MO_Immediate: |
92 | O << MO.getImm(); |
93 | break; |
94 | case MachineOperand::MO_GlobalAddress: |
95 | O << getSymbol(GV: MO.getGlobal()); |
96 | break; |
97 | case MachineOperand::MO_ExternalSymbol: |
98 | O << *GetExternalSymbolSymbol(Sym: MO.getSymbolName()); |
99 | break; |
100 | case MachineOperand::MO_MachineBasicBlock: |
101 | O << *MO.getMBB()->getSymbol(); |
102 | break; |
103 | default: |
104 | llvm_unreachable("Not implemented yet!" ); |
105 | } |
106 | } |
107 | |
108 | bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, |
109 | const char *, raw_ostream &O) { |
110 | // Default asm printer can only deal with some extra codes, |
111 | // so try it first. |
112 | if (!AsmPrinter::PrintAsmOperand(MI, OpNo: OpNum, ExtraCode, OS&: O)) |
113 | return false; |
114 | |
115 | const MachineOperand &MO = MI->getOperand(i: OpNum); |
116 | |
117 | if (ExtraCode && ExtraCode[0]) { |
118 | // Unknown extra code. |
119 | if (ExtraCode[1] != 0 || ExtraCode[0] < 'A' || ExtraCode[0] > 'Z') |
120 | return true; |
121 | |
122 | // Operand must be a register when using 'A' ~ 'Z' extra code. |
123 | if (!MO.isReg()) |
124 | return true; |
125 | |
126 | Register Reg = MO.getReg(); |
127 | |
128 | unsigned ByteNumber = ExtraCode[0] - 'A'; |
129 | const InlineAsm::Flag OpFlags(MI->getOperand(i: OpNum - 1).getImm()); |
130 | const unsigned NumOpRegs = OpFlags.getNumOperandRegisters(); |
131 | |
132 | const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>(); |
133 | const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); |
134 | |
135 | const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg); |
136 | unsigned BytesPerReg = TRI.getRegSizeInBits(RC: *RC) / 8; |
137 | assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported." ); |
138 | |
139 | unsigned RegIdx = ByteNumber / BytesPerReg; |
140 | if (RegIdx >= NumOpRegs) |
141 | return true; |
142 | Reg = MI->getOperand(i: OpNum + RegIdx).getReg(); |
143 | |
144 | if (BytesPerReg == 2) { |
145 | Reg = TRI.getSubReg(Reg, Idx: (ByteNumber % BytesPerReg) ? AVR::sub_hi |
146 | : AVR::sub_lo); |
147 | } |
148 | |
149 | O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); |
150 | return false; |
151 | } |
152 | |
153 | if (MO.getType() == MachineOperand::MO_GlobalAddress) |
154 | PrintSymbolOperand(MO, OS&: O); // Print global symbols. |
155 | else |
156 | printOperand(MI, OpNo: OpNum, O); // Fallback to ordinary cases. |
157 | |
158 | return false; |
159 | } |
160 | |
161 | bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, |
162 | unsigned OpNum, const char *, |
163 | raw_ostream &O) { |
164 | if (ExtraCode && ExtraCode[0]) |
165 | return true; // Unknown modifier |
166 | |
167 | const MachineOperand &MO = MI->getOperand(i: OpNum); |
168 | (void)MO; |
169 | assert(MO.isReg() && "Unexpected inline asm memory operand" ); |
170 | |
171 | // TODO: We should be able to look up the alternative name for |
172 | // the register if it's given. |
173 | // TableGen doesn't expose a way of getting retrieving names |
174 | // for registers. |
175 | if (MI->getOperand(i: OpNum).getReg() == AVR::R31R30) { |
176 | O << "Z" ; |
177 | } else if (MI->getOperand(i: OpNum).getReg() == AVR::R29R28) { |
178 | O << "Y" ; |
179 | } else if (MI->getOperand(i: OpNum).getReg() == AVR::R27R26) { |
180 | O << "X" ; |
181 | } else { |
182 | assert(false && "Wrong register class for memory operand." ); |
183 | } |
184 | |
185 | // If NumOpRegs == 2, then we assume it is product of a FrameIndex expansion |
186 | // and the second operand is an Imm. |
187 | const InlineAsm::Flag OpFlags(MI->getOperand(i: OpNum - 1).getImm()); |
188 | const unsigned NumOpRegs = OpFlags.getNumOperandRegisters(); |
189 | |
190 | if (NumOpRegs == 2) { |
191 | assert(MI->getOperand(OpNum).getReg() != AVR::R27R26 && |
192 | "Base register X can not have offset/displacement." ); |
193 | O << '+' << MI->getOperand(i: OpNum + 1).getImm(); |
194 | } |
195 | |
196 | return false; |
197 | } |
198 | |
199 | void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) { |
200 | AVR_MC::verifyInstructionPredicates(Opcode: MI->getOpcode(), |
201 | Features: getSubtargetInfo().getFeatureBits()); |
202 | |
203 | AVRMCInstLower MCInstLowering(OutContext, *this); |
204 | |
205 | MCInst I; |
206 | MCInstLowering.lowerInstruction(MI: *MI, OutMI&: I); |
207 | EmitToStreamer(S&: *OutStreamer, Inst: I); |
208 | } |
209 | |
210 | const MCExpr *AVRAsmPrinter::lowerConstant(const Constant *CV, |
211 | const Constant *BaseCV, |
212 | uint64_t Offset) { |
213 | MCContext &Ctx = OutContext; |
214 | |
215 | if (const GlobalValue *GV = dyn_cast<GlobalValue>(Val: CV)) { |
216 | bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory; |
217 | if (IsProgMem) { |
218 | const MCExpr *Expr = MCSymbolRefExpr::create(Symbol: getSymbol(GV), Ctx); |
219 | return AVRMCExpr::create(S: AVR::S_PM, Expr, isNegated: false, Ctx); |
220 | } |
221 | } |
222 | |
223 | return AsmPrinter::lowerConstant(CV, BaseCV, Offset); |
224 | } |
225 | |
226 | void AVRAsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) { |
227 | if (!EmittedStructorSymbolAttrs) { |
228 | OutStreamer->emitRawComment( |
229 | T: " Emitting these undefined symbol references causes us to link the" |
230 | " libgcc code that runs our constructors/destructors" ); |
231 | OutStreamer->emitRawComment(T: " This matches GCC's behavior" ); |
232 | |
233 | MCSymbol * = OutContext.getOrCreateSymbol(Name: "__do_global_ctors" ); |
234 | OutStreamer->emitSymbolAttribute(Symbol: CtorsSym, Attribute: MCSA_Global); |
235 | |
236 | MCSymbol * = OutContext.getOrCreateSymbol(Name: "__do_global_dtors" ); |
237 | OutStreamer->emitSymbolAttribute(Symbol: DtorsSym, Attribute: MCSA_Global); |
238 | |
239 | EmittedStructorSymbolAttrs = true; |
240 | } |
241 | |
242 | AsmPrinter::emitXXStructor(DL, CV); |
243 | } |
244 | |
245 | bool AVRAsmPrinter::doFinalization(Module &M) { |
246 | const TargetLoweringObjectFile &TLOF = getObjFileLowering(); |
247 | const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget(); |
248 | const AVRSubtarget *SubTM = (const AVRSubtarget *)TM.getSubtargetImpl(); |
249 | |
250 | bool NeedsCopyData = false; |
251 | bool NeedsClearBSS = false; |
252 | for (const auto &GO : M.globals()) { |
253 | if (!GO.hasInitializer() || GO.hasAvailableExternallyLinkage()) |
254 | // These globals aren't defined in the current object file. |
255 | continue; |
256 | |
257 | if (GO.hasCommonLinkage()) { |
258 | // COMMON symbols are put in .bss. |
259 | NeedsClearBSS = true; |
260 | continue; |
261 | } |
262 | |
263 | auto *Section = cast<MCSectionELF>(Val: TLOF.SectionForGlobal(GO: &GO, TM)); |
264 | if (Section->getName().starts_with(Prefix: ".data" )) |
265 | NeedsCopyData = true; |
266 | else if (Section->getName().starts_with(Prefix: ".rodata" ) && SubTM->hasLPM()) |
267 | // AVRs that have a separate program memory (that's most AVRs) store |
268 | // .rodata sections in RAM. |
269 | NeedsCopyData = true; |
270 | else if (Section->getName().starts_with(Prefix: ".bss" )) |
271 | NeedsClearBSS = true; |
272 | } |
273 | |
274 | MCSymbol *DoCopyData = OutContext.getOrCreateSymbol(Name: "__do_copy_data" ); |
275 | MCSymbol *DoClearBss = OutContext.getOrCreateSymbol(Name: "__do_clear_bss" ); |
276 | |
277 | if (NeedsCopyData) { |
278 | OutStreamer->emitRawComment( |
279 | T: " Declaring this symbol tells the CRT that it should" ); |
280 | OutStreamer->emitRawComment( |
281 | T: "copy all variables from program memory to RAM on startup" ); |
282 | OutStreamer->emitSymbolAttribute(Symbol: DoCopyData, Attribute: MCSA_Global); |
283 | } |
284 | |
285 | if (NeedsClearBSS) { |
286 | OutStreamer->emitRawComment( |
287 | T: " Declaring this symbol tells the CRT that it should" ); |
288 | OutStreamer->emitRawComment(T: "clear the zeroed data section on startup" ); |
289 | OutStreamer->emitSymbolAttribute(Symbol: DoClearBss, Attribute: MCSA_Global); |
290 | } |
291 | |
292 | return AsmPrinter::doFinalization(M); |
293 | } |
294 | |
295 | void AVRAsmPrinter::emitStartOfAsmFile(Module &M) { |
296 | const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget(); |
297 | const AVRSubtarget *SubTM = (const AVRSubtarget *)TM.getSubtargetImpl(); |
298 | if (!SubTM) |
299 | return; |
300 | |
301 | // Emit __tmp_reg__. |
302 | OutStreamer->emitAssignment( |
303 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__tmp_reg__" )), |
304 | Value: MCConstantExpr::create(Value: SubTM->getRegTmpIndex(), Ctx&: MMI->getContext())); |
305 | // Emit __zero_reg__. |
306 | OutStreamer->emitAssignment( |
307 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__zero_reg__" )), |
308 | Value: MCConstantExpr::create(Value: SubTM->getRegZeroIndex(), Ctx&: MMI->getContext())); |
309 | // Emit __SREG__. |
310 | OutStreamer->emitAssignment( |
311 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SREG__" )), |
312 | Value: MCConstantExpr::create(Value: SubTM->getIORegSREG(), Ctx&: MMI->getContext())); |
313 | // Emit __SP_H__ if available. |
314 | if (!SubTM->hasSmallStack()) |
315 | OutStreamer->emitAssignment( |
316 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SP_H__" )), |
317 | Value: MCConstantExpr::create(Value: SubTM->getIORegSPH(), Ctx&: MMI->getContext())); |
318 | // Emit __SP_L__. |
319 | OutStreamer->emitAssignment( |
320 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SP_L__" )), |
321 | Value: MCConstantExpr::create(Value: SubTM->getIORegSPL(), Ctx&: MMI->getContext())); |
322 | // Emit __EIND__ if available. |
323 | if (SubTM->hasEIJMPCALL()) |
324 | OutStreamer->emitAssignment( |
325 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__EIND__" )), |
326 | Value: MCConstantExpr::create(Value: SubTM->getIORegEIND(), Ctx&: MMI->getContext())); |
327 | // Emit __RAMPZ__ if available. |
328 | if (SubTM->hasELPM()) |
329 | OutStreamer->emitAssignment( |
330 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__RAMPZ__" )), |
331 | Value: MCConstantExpr::create(Value: SubTM->getIORegRAMPZ(), Ctx&: MMI->getContext())); |
332 | } |
333 | |
334 | char AVRAsmPrinter::ID = 0; |
335 | |
336 | INITIALIZE_PASS(AVRAsmPrinter, "avr-asm-printer" , "AVR Assembly Printer" , false, |
337 | false) |
338 | |
339 | extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void |
340 | LLVMInitializeAVRAsmPrinter() { |
341 | llvm::RegisterAsmPrinter<AVRAsmPrinter> X(getTheAVRTarget()); |
342 | } |
343 | |