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