| 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/AVRMCAsmInfo.h" |
| 20 | #include "TargetInfo/AVRTargetInfo.h" |
| 21 | |
| 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/Compiler.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 | using namespace llvm; |
| 44 | |
| 45 | namespace { |
| 46 | |
| 47 | /// An AVR assembly code printer. |
| 48 | class AVRAsmPrinter : public AsmPrinter { |
| 49 | public: |
| 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 *, raw_ostream &O) override; |
| 59 | |
| 60 | bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, |
| 61 | const char *, 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 | |
| 76 | private: |
| 77 | const MCRegisterInfo &MRI; |
| 78 | bool EmittedStructorSymbolAttrs = false; |
| 79 | }; |
| 80 | |
| 81 | } // namespace |
| 82 | |
| 83 | void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo, |
| 84 | raw_ostream &O) { |
| 85 | const MachineOperand &MO = MI->getOperand(i: OpNo); |
| 86 | |
| 87 | switch (MO.getType()) { |
| 88 | case MachineOperand::MO_Register: |
| 89 | O << AVRInstPrinter::getPrettyRegisterName(Reg: MO.getReg(), MRI); |
| 90 | break; |
| 91 | case MachineOperand::MO_Immediate: |
| 92 | O << MO.getImm(); |
| 93 | break; |
| 94 | case MachineOperand::MO_GlobalAddress: |
| 95 | O << getSymbol(GV: MO.getGlobal()); |
| 96 | break; |
| 97 | case MachineOperand::MO_ExternalSymbol: |
| 98 | O << *GetExternalSymbolSymbol(Sym: MO.getSymbolName()); |
| 99 | break; |
| 100 | case MachineOperand::MO_MachineBasicBlock: |
| 101 | O << *MO.getMBB()->getSymbol(); |
| 102 | break; |
| 103 | default: |
| 104 | llvm_unreachable("Not implemented yet!" ); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, |
| 109 | const char *, raw_ostream &O) { |
| 110 | // Default asm printer can only deal with some extra codes, |
| 111 | // so try it first. |
| 112 | if (!AsmPrinter::PrintAsmOperand(MI, OpNo: OpNum, ExtraCode, OS&: O)) |
| 113 | return false; |
| 114 | |
| 115 | const MachineOperand &MO = MI->getOperand(i: 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(i: 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: *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(i: OpNum + RegIdx).getReg(); |
| 143 | |
| 144 | if (BytesPerReg == 2) { |
| 145 | Reg = TRI.getSubReg(Reg, Idx: (ByteNumber % BytesPerReg) ? AVR::sub_hi |
| 146 | : AVR::sub_lo); |
| 147 | } |
| 148 | |
| 149 | O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); |
| 150 | return false; |
| 151 | } |
| 152 | |
| 153 | if (MO.getType() == MachineOperand::MO_GlobalAddress) |
| 154 | PrintSymbolOperand(MO, OS&: O); // Print global symbols. |
| 155 | else |
| 156 | printOperand(MI, OpNo: OpNum, O); // Fallback to ordinary cases. |
| 157 | |
| 158 | return false; |
| 159 | } |
| 160 | |
| 161 | bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, |
| 162 | unsigned OpNum, const char *, |
| 163 | raw_ostream &O) { |
| 164 | if (ExtraCode && ExtraCode[0]) |
| 165 | return true; // Unknown modifier |
| 166 | |
| 167 | const MachineOperand &MO = MI->getOperand(i: 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(i: OpNum).getReg() == AVR::R31R30) { |
| 176 | O << "Z" ; |
| 177 | } else if (MI->getOperand(i: OpNum).getReg() == AVR::R29R28) { |
| 178 | O << "Y" ; |
| 179 | } else if (MI->getOperand(i: 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(i: 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(i: OpNum + 1).getImm(); |
| 194 | } |
| 195 | |
| 196 | return false; |
| 197 | } |
| 198 | |
| 199 | void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) { |
| 200 | AVR_MC::verifyInstructionPredicates(Opcode: MI->getOpcode(), |
| 201 | Features: getSubtargetInfo().getFeatureBits()); |
| 202 | |
| 203 | AVRMCInstLower MCInstLowering(OutContext, *this); |
| 204 | |
| 205 | MCInst I; |
| 206 | MCInstLowering.lowerInstruction(MI: *MI, OutMI&: I); |
| 207 | EmitToStreamer(S&: *OutStreamer, Inst: I); |
| 208 | } |
| 209 | |
| 210 | const 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>(Val: CV)) { |
| 216 | bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory; |
| 217 | if (IsProgMem) { |
| 218 | const MCExpr *Expr = MCSymbolRefExpr::create(Symbol: getSymbol(GV), Ctx); |
| 219 | return AVRMCExpr::create(S: AVR::S_PM, Expr, isNegated: false, Ctx); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | return AsmPrinter::lowerConstant(CV, BaseCV, Offset); |
| 224 | } |
| 225 | |
| 226 | void AVRAsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) { |
| 227 | if (!EmittedStructorSymbolAttrs) { |
| 228 | OutStreamer->emitRawComment( |
| 229 | T: " Emitting these undefined symbol references causes us to link the" |
| 230 | " libgcc code that runs our constructors/destructors" ); |
| 231 | OutStreamer->emitRawComment(T: " This matches GCC's behavior" ); |
| 232 | |
| 233 | MCSymbol * = OutContext.getOrCreateSymbol(Name: "__do_global_ctors" ); |
| 234 | OutStreamer->emitSymbolAttribute(Symbol: CtorsSym, Attribute: MCSA_Global); |
| 235 | |
| 236 | MCSymbol * = OutContext.getOrCreateSymbol(Name: "__do_global_dtors" ); |
| 237 | OutStreamer->emitSymbolAttribute(Symbol: DtorsSym, Attribute: MCSA_Global); |
| 238 | |
| 239 | EmittedStructorSymbolAttrs = true; |
| 240 | } |
| 241 | |
| 242 | AsmPrinter::emitXXStructor(DL, CV); |
| 243 | } |
| 244 | |
| 245 | bool AVRAsmPrinter::doFinalization(Module &M) { |
| 246 | const TargetLoweringObjectFile &TLOF = getObjFileLowering(); |
| 247 | const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget(); |
| 248 | const AVRSubtarget *SubTM = (const AVRSubtarget *)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 = cast<MCSectionELF>(Val: TLOF.SectionForGlobal(GO: &GO, TM)); |
| 264 | if (Section->getName().starts_with(Prefix: ".data" )) |
| 265 | NeedsCopyData = true; |
| 266 | else if (Section->getName().starts_with(Prefix: ".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(Prefix: ".bss" )) |
| 271 | NeedsClearBSS = true; |
| 272 | } |
| 273 | |
| 274 | MCSymbol *DoCopyData = OutContext.getOrCreateSymbol(Name: "__do_copy_data" ); |
| 275 | MCSymbol *DoClearBss = OutContext.getOrCreateSymbol(Name: "__do_clear_bss" ); |
| 276 | |
| 277 | if (NeedsCopyData) { |
| 278 | OutStreamer->emitRawComment( |
| 279 | T: " Declaring this symbol tells the CRT that it should" ); |
| 280 | OutStreamer->emitRawComment( |
| 281 | T: "copy all variables from program memory to RAM on startup" ); |
| 282 | OutStreamer->emitSymbolAttribute(Symbol: DoCopyData, Attribute: MCSA_Global); |
| 283 | } |
| 284 | |
| 285 | if (NeedsClearBSS) { |
| 286 | OutStreamer->emitRawComment( |
| 287 | T: " Declaring this symbol tells the CRT that it should" ); |
| 288 | OutStreamer->emitRawComment(T: "clear the zeroed data section on startup" ); |
| 289 | OutStreamer->emitSymbolAttribute(Symbol: DoClearBss, Attribute: MCSA_Global); |
| 290 | } |
| 291 | |
| 292 | return AsmPrinter::doFinalization(M); |
| 293 | } |
| 294 | |
| 295 | void AVRAsmPrinter::emitStartOfAsmFile(Module &M) { |
| 296 | const AVRTargetMachine &TM = (const AVRTargetMachine &)MMI->getTarget(); |
| 297 | const AVRSubtarget *SubTM = (const AVRSubtarget *)TM.getSubtargetImpl(); |
| 298 | if (!SubTM) |
| 299 | return; |
| 300 | |
| 301 | // Emit __tmp_reg__. |
| 302 | OutStreamer->emitAssignment( |
| 303 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__tmp_reg__" )), |
| 304 | Value: MCConstantExpr::create(Value: SubTM->getRegTmpIndex(), Ctx&: MMI->getContext())); |
| 305 | // Emit __zero_reg__. |
| 306 | OutStreamer->emitAssignment( |
| 307 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__zero_reg__" )), |
| 308 | Value: MCConstantExpr::create(Value: SubTM->getRegZeroIndex(), Ctx&: MMI->getContext())); |
| 309 | // Emit __SREG__. |
| 310 | OutStreamer->emitAssignment( |
| 311 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SREG__" )), |
| 312 | Value: MCConstantExpr::create(Value: SubTM->getIORegSREG(), Ctx&: MMI->getContext())); |
| 313 | // Emit __SP_H__ if available. |
| 314 | if (!SubTM->hasSmallStack()) |
| 315 | OutStreamer->emitAssignment( |
| 316 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SP_H__" )), |
| 317 | Value: MCConstantExpr::create(Value: SubTM->getIORegSPH(), Ctx&: MMI->getContext())); |
| 318 | // Emit __SP_L__. |
| 319 | OutStreamer->emitAssignment( |
| 320 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__SP_L__" )), |
| 321 | Value: MCConstantExpr::create(Value: SubTM->getIORegSPL(), Ctx&: MMI->getContext())); |
| 322 | // Emit __EIND__ if available. |
| 323 | if (SubTM->hasEIJMPCALL()) |
| 324 | OutStreamer->emitAssignment( |
| 325 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__EIND__" )), |
| 326 | Value: MCConstantExpr::create(Value: SubTM->getIORegEIND(), Ctx&: MMI->getContext())); |
| 327 | // Emit __RAMPZ__ if available. |
| 328 | if (SubTM->hasELPM()) |
| 329 | OutStreamer->emitAssignment( |
| 330 | Symbol: MMI->getContext().getOrCreateSymbol(Name: StringRef("__RAMPZ__" )), |
| 331 | Value: MCConstantExpr::create(Value: SubTM->getIORegRAMPZ(), Ctx&: MMI->getContext())); |
| 332 | } |
| 333 | |
| 334 | char AVRAsmPrinter::ID = 0; |
| 335 | |
| 336 | INITIALIZE_PASS(AVRAsmPrinter, "avr-asm-printer" , "AVR Assembly Printer" , false, |
| 337 | false) |
| 338 | |
| 339 | extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void |
| 340 | LLVMInitializeAVRAsmPrinter() { |
| 341 | llvm::RegisterAsmPrinter<AVRAsmPrinter> X(getTheAVRTarget()); |
| 342 | } |
| 343 | |