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