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