1 //===-- AVRAsmPrinter.cpp - AVR LLVM assembly writer ----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file contains a printer that converts from our internal representation
10 // of machine-dependent LLVM code to GAS-format AVR assembly language.
12 //===----------------------------------------------------------------------===//
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"
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/ErrorHandling.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include "llvm/Target/TargetLoweringObjectFile.h"
40 #define DEBUG_TYPE "avr-asm-printer"
44 /// An AVR assembly code printer.
45 class AVRAsmPrinter
: public AsmPrinter
{
47 AVRAsmPrinter(TargetMachine
&TM
, std::unique_ptr
<MCStreamer
> Streamer
)
48 : AsmPrinter(TM
, std::move(Streamer
)), MRI(*TM
.getMCRegisterInfo()) {}
50 StringRef
getPassName() const override
{ return "AVR Assembly Printer"; }
52 void printOperand(const MachineInstr
*MI
, unsigned OpNo
, raw_ostream
&O
);
54 bool PrintAsmOperand(const MachineInstr
*MI
, unsigned OpNum
,
55 const char *ExtraCode
, raw_ostream
&O
) override
;
57 bool PrintAsmMemoryOperand(const MachineInstr
*MI
, unsigned OpNum
,
58 const char *ExtraCode
, raw_ostream
&O
) override
;
60 void emitInstruction(const MachineInstr
*MI
) override
;
62 const MCExpr
*lowerConstant(const Constant
*CV
) override
;
64 void emitXXStructor(const DataLayout
&DL
, const Constant
*CV
) override
;
66 bool doFinalization(Module
&M
) override
;
68 void emitStartOfAsmFile(Module
&M
) override
;
71 const MCRegisterInfo
&MRI
;
72 bool EmittedStructorSymbolAttrs
= false;
75 void AVRAsmPrinter::printOperand(const MachineInstr
*MI
, unsigned OpNo
,
77 const MachineOperand
&MO
= MI
->getOperand(OpNo
);
79 switch (MO
.getType()) {
80 case MachineOperand::MO_Register
:
81 O
<< AVRInstPrinter::getPrettyRegisterName(MO
.getReg(), MRI
);
83 case MachineOperand::MO_Immediate
:
86 case MachineOperand::MO_GlobalAddress
:
87 O
<< getSymbol(MO
.getGlobal());
89 case MachineOperand::MO_ExternalSymbol
:
90 O
<< *GetExternalSymbolSymbol(MO
.getSymbolName());
92 case MachineOperand::MO_MachineBasicBlock
:
93 O
<< *MO
.getMBB()->getSymbol();
96 llvm_unreachable("Not implemented yet!");
100 bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr
*MI
, unsigned OpNum
,
101 const char *ExtraCode
, raw_ostream
&O
) {
102 // Default asm printer can only deal with some extra codes,
104 if (!AsmPrinter::PrintAsmOperand(MI
, OpNum
, ExtraCode
, O
))
107 const MachineOperand
&MO
= MI
->getOperand(OpNum
);
109 if (ExtraCode
&& ExtraCode
[0]) {
110 // Unknown extra code.
111 if (ExtraCode
[1] != 0 || ExtraCode
[0] < 'A' || ExtraCode
[0] > 'Z')
114 // Operand must be a register when using 'A' ~ 'Z' extra code.
118 Register Reg
= MO
.getReg();
120 unsigned ByteNumber
= ExtraCode
[0] - 'A';
121 const InlineAsm::Flag
OpFlags(MI
->getOperand(OpNum
- 1).getImm());
122 const unsigned NumOpRegs
= OpFlags
.getNumOperandRegisters();
124 const AVRSubtarget
&STI
= MF
->getSubtarget
<AVRSubtarget
>();
125 const TargetRegisterInfo
&TRI
= *STI
.getRegisterInfo();
127 const TargetRegisterClass
*RC
= TRI
.getMinimalPhysRegClass(Reg
);
128 unsigned BytesPerReg
= TRI
.getRegSizeInBits(*RC
) / 8;
129 assert(BytesPerReg
<= 2 && "Only 8 and 16 bit regs are supported.");
131 unsigned RegIdx
= ByteNumber
/ BytesPerReg
;
132 if (RegIdx
>= NumOpRegs
)
134 Reg
= MI
->getOperand(OpNum
+ RegIdx
).getReg();
136 if (BytesPerReg
== 2) {
137 Reg
= TRI
.getSubReg(Reg
, (ByteNumber
% BytesPerReg
) ? AVR::sub_hi
141 O
<< AVRInstPrinter::getPrettyRegisterName(Reg
, MRI
);
145 if (MO
.getType() == MachineOperand::MO_GlobalAddress
)
146 PrintSymbolOperand(MO
, O
); // Print global symbols.
148 printOperand(MI
, OpNum
, O
); // Fallback to ordinary cases.
153 bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr
*MI
,
154 unsigned OpNum
, const char *ExtraCode
,
156 if (ExtraCode
&& ExtraCode
[0])
157 return true; // Unknown modifier
159 const MachineOperand
&MO
= MI
->getOperand(OpNum
);
161 assert(MO
.isReg() && "Unexpected inline asm memory operand");
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
167 if (MI
->getOperand(OpNum
).getReg() == AVR::R31R30
) {
169 } else if (MI
->getOperand(OpNum
).getReg() == AVR::R29R28
) {
171 } else if (MI
->getOperand(OpNum
).getReg() == AVR::R27R26
) {
174 assert(false && "Wrong register class for memory operand.");
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(OpNum
- 1).getImm());
180 const unsigned NumOpRegs
= OpFlags
.getNumOperandRegisters();
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(OpNum
+ 1).getImm();
191 void AVRAsmPrinter::emitInstruction(const MachineInstr
*MI
) {
192 AVR_MC::verifyInstructionPredicates(MI
->getOpcode(),
193 getSubtargetInfo().getFeatureBits());
195 AVRMCInstLower
MCInstLowering(OutContext
, *this);
198 MCInstLowering
.lowerInstruction(*MI
, I
);
199 EmitToStreamer(*OutStreamer
, I
);
202 const MCExpr
*AVRAsmPrinter::lowerConstant(const Constant
*CV
) {
203 MCContext
&Ctx
= OutContext
;
205 if (const GlobalValue
*GV
= dyn_cast
<GlobalValue
>(CV
)) {
206 bool IsProgMem
= GV
->getAddressSpace() == AVR::ProgramMemory
;
208 const MCExpr
*Expr
= MCSymbolRefExpr::create(getSymbol(GV
), Ctx
);
209 return AVRMCExpr::create(AVRMCExpr::VK_AVR_PM
, Expr
, false, Ctx
);
213 return AsmPrinter::lowerConstant(CV
);
216 void AVRAsmPrinter::emitXXStructor(const DataLayout
&DL
, const Constant
*CV
) {
217 if (!EmittedStructorSymbolAttrs
) {
218 OutStreamer
->emitRawComment(
219 " Emitting these undefined symbol references causes us to link the"
220 " libgcc code that runs our constructors/destructors");
221 OutStreamer
->emitRawComment(" This matches GCC's behavior");
223 MCSymbol
*CtorsSym
= OutContext
.getOrCreateSymbol("__do_global_ctors");
224 OutStreamer
->emitSymbolAttribute(CtorsSym
, MCSA_Global
);
226 MCSymbol
*DtorsSym
= OutContext
.getOrCreateSymbol("__do_global_dtors");
227 OutStreamer
->emitSymbolAttribute(DtorsSym
, MCSA_Global
);
229 EmittedStructorSymbolAttrs
= true;
232 AsmPrinter::emitXXStructor(DL
, CV
);
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();
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.
247 if (GO
.hasCommonLinkage()) {
248 // COMMON symbols are put in .bss.
249 NeedsClearBSS
= true;
253 auto *Section
= cast
<MCSectionELF
>(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;
264 MCSymbol
*DoCopyData
= OutContext
.getOrCreateSymbol("__do_copy_data");
265 MCSymbol
*DoClearBss
= OutContext
.getOrCreateSymbol("__do_clear_bss");
268 OutStreamer
->emitRawComment(
269 " Declaring this symbol tells the CRT that it should");
270 OutStreamer
->emitRawComment(
271 "copy all variables from program memory to RAM on startup");
272 OutStreamer
->emitSymbolAttribute(DoCopyData
, MCSA_Global
);
276 OutStreamer
->emitRawComment(
277 " Declaring this symbol tells the CRT that it should");
278 OutStreamer
->emitRawComment("clear the zeroed data section on startup");
279 OutStreamer
->emitSymbolAttribute(DoClearBss
, MCSA_Global
);
282 return AsmPrinter::doFinalization(M
);
285 void AVRAsmPrinter::emitStartOfAsmFile(Module
&M
) {
286 const AVRTargetMachine
&TM
= (const AVRTargetMachine
&)MMI
->getTarget();
287 const AVRSubtarget
*SubTM
= (const AVRSubtarget
*)TM
.getSubtargetImpl();
292 OutStreamer
->emitAssignment(
293 MMI
->getContext().getOrCreateSymbol(StringRef("__tmp_reg__")),
294 MCConstantExpr::create(SubTM
->getRegTmpIndex(), MMI
->getContext()));
295 // Emit __zero_reg__.
296 OutStreamer
->emitAssignment(
297 MMI
->getContext().getOrCreateSymbol(StringRef("__zero_reg__")),
298 MCConstantExpr::create(SubTM
->getRegZeroIndex(), MMI
->getContext()));
300 OutStreamer
->emitAssignment(
301 MMI
->getContext().getOrCreateSymbol(StringRef("__SREG__")),
302 MCConstantExpr::create(SubTM
->getIORegSREG(), MMI
->getContext()));
303 // Emit __SP_H__ if available.
304 if (!SubTM
->hasSmallStack())
305 OutStreamer
->emitAssignment(
306 MMI
->getContext().getOrCreateSymbol(StringRef("__SP_H__")),
307 MCConstantExpr::create(SubTM
->getIORegSPH(), MMI
->getContext()));
309 OutStreamer
->emitAssignment(
310 MMI
->getContext().getOrCreateSymbol(StringRef("__SP_L__")),
311 MCConstantExpr::create(SubTM
->getIORegSPL(), MMI
->getContext()));
312 // Emit __EIND__ if available.
313 if (SubTM
->hasEIJMPCALL())
314 OutStreamer
->emitAssignment(
315 MMI
->getContext().getOrCreateSymbol(StringRef("__EIND__")),
316 MCConstantExpr::create(SubTM
->getIORegEIND(), MMI
->getContext()));
317 // Emit __RAMPZ__ if available.
318 if (SubTM
->hasELPM())
319 OutStreamer
->emitAssignment(
320 MMI
->getContext().getOrCreateSymbol(StringRef("__RAMPZ__")),
321 MCConstantExpr::create(SubTM
->getIORegRAMPZ(), MMI
->getContext()));
324 } // end of namespace llvm
326 extern "C" LLVM_EXTERNAL_VISIBILITY
void LLVMInitializeAVRAsmPrinter() {
327 llvm::RegisterAsmPrinter
<llvm::AVRAsmPrinter
> X(llvm::getTheAVRTarget());