LLVM 22.0.0git
AVRAsmPrinter.cpp
Go to the documentation of this file.
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"
21
28#include "llvm/IR/Mangler.h"
29#include "llvm/IR/Module.h"
30#include "llvm/MC/MCContext.h"
31#include "llvm/MC/MCInst.h"
33#include "llvm/MC/MCStreamer.h"
34#include "llvm/MC/MCSymbol.h"
40
41#define DEBUG_TYPE "avr-asm-printer"
42
43using namespace llvm;
44
45namespace {
46
47/// An AVR assembly code printer.
48class AVRAsmPrinter : public AsmPrinter {
49public:
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 *ExtraCode, raw_ostream &O) override;
59
60 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
61 const char *ExtraCode, 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
76private:
77 const MCRegisterInfo &MRI;
78 bool EmittedStructorSymbolAttrs = false;
79};
80
81} // namespace
82
83void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo,
84 raw_ostream &O) {
85 const MachineOperand &MO = MI->getOperand(OpNo);
86
87 switch (MO.getType()) {
90 break;
92 O << MO.getImm();
93 break;
95 O << getSymbol(MO.getGlobal());
96 break;
98 O << *GetExternalSymbolSymbol(MO.getSymbolName());
99 break;
101 O << *MO.getMBB()->getSymbol();
102 break;
103 default:
104 llvm_unreachable("Not implemented yet!");
105 }
106}
107
108bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
109 const char *ExtraCode, raw_ostream &O) {
110 // Default asm printer can only deal with some extra codes,
111 // so try it first.
112 if (!AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O))
113 return false;
114
115 const MachineOperand &MO = MI->getOperand(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(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) / 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(OpNum + RegIdx).getReg();
143
144 if (BytesPerReg == 2) {
145 Reg = TRI.getSubReg(Reg, (ByteNumber % BytesPerReg) ? AVR::sub_hi
146 : AVR::sub_lo);
147 }
148
150 return false;
151 }
152
154 PrintSymbolOperand(MO, O); // Print global symbols.
155 else
156 printOperand(MI, OpNum, O); // Fallback to ordinary cases.
157
158 return false;
159}
160
161bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
162 unsigned OpNum, const char *ExtraCode,
163 raw_ostream &O) {
164 if (ExtraCode && ExtraCode[0])
165 return true; // Unknown modifier
166
167 const MachineOperand &MO = MI->getOperand(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(OpNum).getReg() == AVR::R31R30) {
176 O << "Z";
177 } else if (MI->getOperand(OpNum).getReg() == AVR::R29R28) {
178 O << "Y";
179 } else if (MI->getOperand(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(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(OpNum + 1).getImm();
194 }
195
196 return false;
197}
198
199void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) {
200 AVR_MC::verifyInstructionPredicates(MI->getOpcode(),
201 getSubtargetInfo().getFeatureBits());
202
203 AVRMCInstLower MCInstLowering(OutContext, *this);
204
205 MCInst I;
206 MCInstLowering.lowerInstruction(*MI, I);
207 EmitToStreamer(*OutStreamer, I);
208}
209
210const 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>(CV)) {
216 bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory;
217 if (IsProgMem) {
218 const MCExpr *Expr = MCSymbolRefExpr::create(getSymbol(GV), Ctx);
219 return AVRMCExpr::create(AVR::S_PM, Expr, false, Ctx);
220 }
221 }
222
223 return AsmPrinter::lowerConstant(CV, BaseCV, Offset);
224}
225
226void AVRAsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) {
227 if (!EmittedStructorSymbolAttrs) {
228 OutStreamer->emitRawComment(
229 " Emitting these undefined symbol references causes us to link the"
230 " libgcc code that runs our constructors/destructors");
231 OutStreamer->emitRawComment(" This matches GCC's behavior");
232
233 MCSymbol *CtorsSym = OutContext.getOrCreateSymbol("__do_global_ctors");
234 OutStreamer->emitSymbolAttribute(CtorsSym, MCSA_Global);
235
236 MCSymbol *DtorsSym = OutContext.getOrCreateSymbol("__do_global_dtors");
237 OutStreamer->emitSymbolAttribute(DtorsSym, MCSA_Global);
238
239 EmittedStructorSymbolAttrs = true;
240 }
241
243}
244
245bool AVRAsmPrinter::doFinalization(Module &M) {
246 const TargetLoweringObjectFile &TLOF = getObjFileLowering();
247 const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget();
248 const AVRSubtarget *SubTM = 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 = static_cast<MCSectionELF *>(TLOF.SectionForGlobal(&GO, TM));
264 if (Section->getName().starts_with(".data"))
265 NeedsCopyData = true;
266 else if (Section->getName().starts_with(".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(".bss"))
271 NeedsClearBSS = true;
272 }
273
274 MCSymbol *DoCopyData = OutContext.getOrCreateSymbol("__do_copy_data");
275 MCSymbol *DoClearBss = OutContext.getOrCreateSymbol("__do_clear_bss");
276
277 if (NeedsCopyData) {
278 OutStreamer->emitRawComment(
279 " Declaring this symbol tells the CRT that it should");
280 OutStreamer->emitRawComment(
281 "copy all variables from program memory to RAM on startup");
282 OutStreamer->emitSymbolAttribute(DoCopyData, MCSA_Global);
283 }
284
285 if (NeedsClearBSS) {
286 OutStreamer->emitRawComment(
287 " Declaring this symbol tells the CRT that it should");
288 OutStreamer->emitRawComment("clear the zeroed data section on startup");
289 OutStreamer->emitSymbolAttribute(DoClearBss, MCSA_Global);
290 }
291
293}
294
295void AVRAsmPrinter::emitStartOfAsmFile(Module &M) {
296 const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget();
297 const AVRSubtarget *SubTM = TM.getSubtargetImpl();
298 if (!SubTM)
299 return;
300
301 // Emit __tmp_reg__.
302 OutStreamer->emitAssignment(
303 MMI->getContext().getOrCreateSymbol(StringRef("__tmp_reg__")),
304 MCConstantExpr::create(SubTM->getRegTmpIndex(), MMI->getContext()));
305 // Emit __zero_reg__.
306 OutStreamer->emitAssignment(
307 MMI->getContext().getOrCreateSymbol(StringRef("__zero_reg__")),
308 MCConstantExpr::create(SubTM->getRegZeroIndex(), MMI->getContext()));
309 // Emit __SREG__.
310 OutStreamer->emitAssignment(
311 MMI->getContext().getOrCreateSymbol(StringRef("__SREG__")),
312 MCConstantExpr::create(SubTM->getIORegSREG(), MMI->getContext()));
313 // Emit __SP_H__ if available.
314 if (!SubTM->hasSmallStack())
315 OutStreamer->emitAssignment(
316 MMI->getContext().getOrCreateSymbol(StringRef("__SP_H__")),
317 MCConstantExpr::create(SubTM->getIORegSPH(), MMI->getContext()));
318 // Emit __SP_L__.
319 OutStreamer->emitAssignment(
320 MMI->getContext().getOrCreateSymbol(StringRef("__SP_L__")),
321 MCConstantExpr::create(SubTM->getIORegSPL(), MMI->getContext()));
322 // Emit __EIND__ if available.
323 if (SubTM->hasEIJMPCALL())
324 OutStreamer->emitAssignment(
325 MMI->getContext().getOrCreateSymbol(StringRef("__EIND__")),
326 MCConstantExpr::create(SubTM->getIORegEIND(), MMI->getContext()));
327 // Emit __RAMPZ__ if available.
328 if (SubTM->hasELPM())
329 OutStreamer->emitAssignment(
330 MMI->getContext().getOrCreateSymbol(StringRef("__RAMPZ__")),
331 MCConstantExpr::create(SubTM->getIORegRAMPZ(), MMI->getContext()));
332}
333
334char AVRAsmPrinter::ID = 0;
335
336INITIALIZE_PASS(AVRAsmPrinter, "avr-asm-printer", "AVR Assembly Printer", false,
337 false)
338
340LLVMInitializeAVRAsmPrinter() {
342}
unsigned const MachineRegisterInfo * MRI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define LLVM_ABI
Definition Compiler.h:213
#define LLVM_EXTERNAL_VISIBILITY
Definition Compiler.h:132
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
#define I(x, y, z)
Definition MD5.cpp:58
Machine Check Debug Module
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static SDValue lowerConstant(SDValue Op, SelectionDAG &DAG, const RISCVSubtarget &Subtarget)
static bool printOperand(raw_ostream &OS, const SelectionDAG *G, const SDValue Value)
static TableGen::Emitter::OptClass< SkeletonEmitter > X("gen-skeleton-class", "Generate example skeleton class")
static const char * getPrettyRegisterName(MCRegister Reg, MCRegisterInfo const &MRI)
static const AVRMCExpr * create(Specifier S, const MCExpr *Expr, bool isNegated, MCContext &Ctx)
Specifies the type of an expression.
Definition AVRMCExpr.cpp:17
int getIORegRAMPZ() const
Get I/O register addresses.
int getIORegSPL() const
int getRegTmpIndex() const
Get GPR aliases.
int getIORegSREG() const
int getIORegEIND() const
int getRegZeroIndex() const
int getIORegSPH() const
const AVRRegisterInfo * getRegisterInfo() const override
This class is intended to be used as a driving class for all asm writers.
Definition AsmPrinter.h:90
virtual const MCExpr * lowerConstant(const Constant *CV, const Constant *BaseCV=nullptr, uint64_t Offset=0)
Lower the specified LLVM Constant to an MCExpr.
bool doFinalization(Module &M) override
Shut down the asmprinter.
virtual void emitXXStructor(const DataLayout &DL, const Constant *CV)
Targets can override this to change how global constants that are part of a C++ static/global constru...
Definition AsmPrinter.h:646
virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS)
Print the specified operand of MI, an INLINEASM instruction, using the specified assembler variant.
This is an important base class in LLVM.
Definition Constant.h:43
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:63
static LLVM_ABI const MCConstantExpr * create(int64_t Value, MCContext &Ctx, bool PrintInHex=false, unsigned SizeInBytes=0)
Definition MCExpr.cpp:212
Base class for the full range of assembler expressions which are needed for parsing.
Definition MCExpr.h:34
MCRegisterInfo base class - We assume that the target defines a static array of MCRegisterDesc object...
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:214
LLVM_ABI MCSymbol * getSymbol() const
Return the MCSymbol for this basic block.
Representation of each machine instruction.
const GlobalValue * getGlobal() const
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
MachineBasicBlock * getMBB() const
MachineOperandType getType() const
getType - Returns the MachineOperandType for this operand.
const char * getSymbolName() const
Register getReg() const
getReg - Returns the register number.
@ MO_Immediate
Immediate operand.
@ MO_GlobalAddress
Address of a global value.
@ MO_MachineBasicBlock
MachineBasicBlock reference.
@ MO_Register
Register operand.
@ MO_ExternalSymbol
Name of external global symbol.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
MCSection * SectionForGlobal(const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const
This method computes the appropriate section to emit the specified global variable or function defini...
Primary interface to the complete machine description for the target machine.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ ProgramMemory
Definition AVR.h:45
@ S_PM
Corresponds to pm(), reference to program memory.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:477
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:649
Target & getTheAVRTarget()
@ MCSA_Global
.type _foo, @gnu_unique_object
RegisterAsmPrinter - Helper template for registering a target specific assembly printer,...