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