14d82df32SDylan McKay //===-- AVRAsmPrinter.cpp - AVR LLVM assembly writer ----------------------===// 24d82df32SDylan McKay // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 64d82df32SDylan McKay // 74d82df32SDylan McKay //===----------------------------------------------------------------------===// 84d82df32SDylan McKay // 94d82df32SDylan McKay // This file contains a printer that converts from our internal representation 104d82df32SDylan McKay // of machine-dependent LLVM code to GAS-format AVR assembly language. 114d82df32SDylan McKay // 124d82df32SDylan McKay //===----------------------------------------------------------------------===// 134d82df32SDylan McKay 144d82df32SDylan McKay #include "AVR.h" 154d82df32SDylan McKay #include "AVRMCInstLower.h" 164d82df32SDylan McKay #include "AVRSubtarget.h" 17bf9e67b5SRichard Trieu #include "MCTargetDesc/AVRInstPrinter.h" 182ccb9417SDylan McKay #include "MCTargetDesc/AVRMCExpr.h" 19e982b420SRichard Trieu #include "TargetInfo/AVRTargetInfo.h" 204d82df32SDylan McKay 214d82df32SDylan McKay #include "llvm/CodeGen/AsmPrinter.h" 224d82df32SDylan McKay #include "llvm/CodeGen/MachineFunction.h" 236bda14b3SChandler Carruth #include "llvm/CodeGen/MachineInstr.h" 24b3bde2eaSDavid Blaikie #include "llvm/CodeGen/TargetRegisterInfo.h" 25b3bde2eaSDavid Blaikie #include "llvm/CodeGen/TargetSubtargetInfo.h" 264d82df32SDylan McKay #include "llvm/IR/Mangler.h" 27*75abeb64SMatt Jacobson #include "llvm/MC/MCContext.h" 284d82df32SDylan McKay #include "llvm/MC/MCInst.h" 294d82df32SDylan McKay #include "llvm/MC/MCStreamer.h" 304d82df32SDylan McKay #include "llvm/MC/MCSymbol.h" 314d82df32SDylan McKay #include "llvm/Support/ErrorHandling.h" 324d82df32SDylan McKay #include "llvm/Support/TargetRegistry.h" 334d82df32SDylan McKay #include "llvm/Support/raw_ostream.h" 344d82df32SDylan McKay 354d82df32SDylan McKay #define DEBUG_TYPE "avr-asm-printer" 364d82df32SDylan McKay 374d82df32SDylan McKay namespace llvm { 384d82df32SDylan McKay 394d82df32SDylan McKay /// An AVR assembly code printer. 404d82df32SDylan McKay class AVRAsmPrinter : public AsmPrinter { 414d82df32SDylan McKay public: 42*75abeb64SMatt Jacobson AVRAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 434d82df32SDylan McKay : AsmPrinter(TM, std::move(Streamer)), MRI(*TM.getMCRegisterInfo()) {} 444d82df32SDylan McKay 454d82df32SDylan McKay StringRef getPassName() const override { return "AVR Assembly Printer"; } 464d82df32SDylan McKay 477ab164c4SNick Desaulniers void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O); 484d82df32SDylan McKay 494d82df32SDylan McKay bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, 505277b3ffSNick Desaulniers const char *ExtraCode, raw_ostream &O) override; 514d82df32SDylan McKay 524d82df32SDylan McKay bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, 535277b3ffSNick Desaulniers const char *ExtraCode, raw_ostream &O) override; 544d82df32SDylan McKay 55bcd24b2dSFangrui Song void emitInstruction(const MachineInstr *MI) override; 564d82df32SDylan McKay 572ccb9417SDylan McKay const MCExpr *lowerConstant(const Constant *CV) override; 582ccb9417SDylan McKay 59*75abeb64SMatt Jacobson void emitXXStructor(const DataLayout &DL, const Constant *CV) override; 60*75abeb64SMatt Jacobson 61*75abeb64SMatt Jacobson bool doFinalization(Module &M) override; 62*75abeb64SMatt Jacobson 634d82df32SDylan McKay private: 644d82df32SDylan McKay const MCRegisterInfo &MRI; 65*75abeb64SMatt Jacobson bool EmittedStructorSymbolAttrs = false; 664d82df32SDylan McKay }; 674d82df32SDylan McKay 684d82df32SDylan McKay void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo, 697ab164c4SNick Desaulniers raw_ostream &O) { 704d82df32SDylan McKay const MachineOperand &MO = MI->getOperand(OpNo); 714d82df32SDylan McKay 724d82df32SDylan McKay switch (MO.getType()) { 734d82df32SDylan McKay case MachineOperand::MO_Register: 744d82df32SDylan McKay O << AVRInstPrinter::getPrettyRegisterName(MO.getReg(), MRI); 754d82df32SDylan McKay break; 764d82df32SDylan McKay case MachineOperand::MO_Immediate: 774d82df32SDylan McKay O << MO.getImm(); 784d82df32SDylan McKay break; 794d82df32SDylan McKay case MachineOperand::MO_GlobalAddress: 804d82df32SDylan McKay O << getSymbol(MO.getGlobal()); 814d82df32SDylan McKay break; 824d82df32SDylan McKay case MachineOperand::MO_ExternalSymbol: 834d82df32SDylan McKay O << *GetExternalSymbolSymbol(MO.getSymbolName()); 844d82df32SDylan McKay break; 854d82df32SDylan McKay case MachineOperand::MO_MachineBasicBlock: 864d82df32SDylan McKay O << *MO.getMBB()->getSymbol(); 874d82df32SDylan McKay break; 884d82df32SDylan McKay default: 894d82df32SDylan McKay llvm_unreachable("Not implemented yet!"); 904d82df32SDylan McKay } 914d82df32SDylan McKay } 924d82df32SDylan McKay 934d82df32SDylan McKay bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, 945277b3ffSNick Desaulniers const char *ExtraCode, raw_ostream &O) { 954d82df32SDylan McKay // Default asm printer can only deal with some extra codes, 964d82df32SDylan McKay // so try it first. 975277b3ffSNick Desaulniers bool Error = AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O); 984d82df32SDylan McKay 994d82df32SDylan McKay if (Error && ExtraCode && ExtraCode[0]) { 1004d82df32SDylan McKay if (ExtraCode[1] != 0) 1014d82df32SDylan McKay return true; // Unknown modifier. 1024d82df32SDylan McKay 1034d82df32SDylan McKay if (ExtraCode[0] >= 'A' && ExtraCode[0] <= 'Z') { 1044d82df32SDylan McKay const MachineOperand &RegOp = MI->getOperand(OpNum); 1054d82df32SDylan McKay 1064d82df32SDylan McKay assert(RegOp.isReg() && "Operand must be a register when you're" 1074d82df32SDylan McKay "using 'A'..'Z' operand extracodes."); 1080c476111SDaniel Sanders Register Reg = RegOp.getReg(); 1094d82df32SDylan McKay 1104d82df32SDylan McKay unsigned ByteNumber = ExtraCode[0] - 'A'; 1114d82df32SDylan McKay 1124d82df32SDylan McKay unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); 1134d82df32SDylan McKay unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); 1141a55f201SDylan McKay (void)NumOpRegs; 1154d82df32SDylan McKay 1164d82df32SDylan McKay const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>(); 1174d82df32SDylan McKay const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); 1184d82df32SDylan McKay 11944e25f37SKrzysztof Parzyszek const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg); 12044e25f37SKrzysztof Parzyszek unsigned BytesPerReg = TRI.getRegSizeInBits(*RC) / 8; 1214d82df32SDylan McKay assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported."); 1224d82df32SDylan McKay 1234d82df32SDylan McKay unsigned RegIdx = ByteNumber / BytesPerReg; 1244d82df32SDylan McKay assert(RegIdx < NumOpRegs && "Multibyte index out of range."); 1254d82df32SDylan McKay 1264d82df32SDylan McKay Reg = MI->getOperand(OpNum + RegIdx).getReg(); 1274d82df32SDylan McKay 1284d82df32SDylan McKay if (BytesPerReg == 2) { 1294d82df32SDylan McKay Reg = TRI.getSubReg(Reg, ByteNumber % BytesPerReg ? AVR::sub_hi 1304d82df32SDylan McKay : AVR::sub_lo); 1314d82df32SDylan McKay } 1324d82df32SDylan McKay 1334d82df32SDylan McKay O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); 1344d82df32SDylan McKay return false; 1354d82df32SDylan McKay } 1364d82df32SDylan McKay } 1374d82df32SDylan McKay 138b78f3665SDylan McKay if (Error) 1394d82df32SDylan McKay printOperand(MI, OpNum, O); 1404d82df32SDylan McKay 1414d82df32SDylan McKay return false; 1424d82df32SDylan McKay } 1434d82df32SDylan McKay 1444d82df32SDylan McKay bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 1455277b3ffSNick Desaulniers unsigned OpNum, const char *ExtraCode, 1464d82df32SDylan McKay raw_ostream &O) { 1474d82df32SDylan McKay if (ExtraCode && ExtraCode[0]) { 1484d82df32SDylan McKay llvm_unreachable("This branch is not implemented yet"); 1494d82df32SDylan McKay } 1504d82df32SDylan McKay 1514d82df32SDylan McKay const MachineOperand &MO = MI->getOperand(OpNum); 1521a55f201SDylan McKay (void)MO; 1534d82df32SDylan McKay assert(MO.isReg() && "Unexpected inline asm memory operand"); 1544d82df32SDylan McKay 155946ab551SDylan McKay // TODO: We should be able to look up the alternative name for 156946ab551SDylan McKay // the register if it's given. 157946ab551SDylan McKay // TableGen doesn't expose a way of getting retrieving names 158946ab551SDylan McKay // for registers. 1594d82df32SDylan McKay if (MI->getOperand(OpNum).getReg() == AVR::R31R30) { 1604d82df32SDylan McKay O << "Z"; 1614d82df32SDylan McKay } else { 1624d82df32SDylan McKay assert(MI->getOperand(OpNum).getReg() == AVR::R29R28 && 1634d82df32SDylan McKay "Wrong register class for memory operand."); 1644d82df32SDylan McKay O << "Y"; 1654d82df32SDylan McKay } 1664d82df32SDylan McKay 1674d82df32SDylan McKay // If NumOpRegs == 2, then we assume it is product of a FrameIndex expansion 1684d82df32SDylan McKay // and the second operand is an Imm. 1694d82df32SDylan McKay unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); 1704d82df32SDylan McKay unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); 1714d82df32SDylan McKay 1724d82df32SDylan McKay if (NumOpRegs == 2) { 1734d82df32SDylan McKay O << '+' << MI->getOperand(OpNum + 1).getImm(); 1744d82df32SDylan McKay } 1754d82df32SDylan McKay 1764d82df32SDylan McKay return false; 1774d82df32SDylan McKay } 1784d82df32SDylan McKay 179bcd24b2dSFangrui Song void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) { 1804d82df32SDylan McKay AVRMCInstLower MCInstLowering(OutContext, *this); 1814d82df32SDylan McKay 1824d82df32SDylan McKay MCInst I; 1834d82df32SDylan McKay MCInstLowering.lowerInstruction(*MI, I); 1844d82df32SDylan McKay EmitToStreamer(*OutStreamer, I); 1854d82df32SDylan McKay } 1864d82df32SDylan McKay 1872ccb9417SDylan McKay const MCExpr *AVRAsmPrinter::lowerConstant(const Constant *CV) { 1882ccb9417SDylan McKay MCContext &Ctx = OutContext; 1892ccb9417SDylan McKay 1902ccb9417SDylan McKay if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) { 1912ccb9417SDylan McKay bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory; 1922ccb9417SDylan McKay if (IsProgMem) { 1932ccb9417SDylan McKay const MCExpr *Expr = MCSymbolRefExpr::create(getSymbol(GV), Ctx); 1942ccb9417SDylan McKay return AVRMCExpr::create(AVRMCExpr::VK_AVR_PM, Expr, false, Ctx); 1952ccb9417SDylan McKay } 1962ccb9417SDylan McKay } 1972ccb9417SDylan McKay 1982ccb9417SDylan McKay return AsmPrinter::lowerConstant(CV); 1992ccb9417SDylan McKay } 2002ccb9417SDylan McKay 201*75abeb64SMatt Jacobson void AVRAsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) { 202*75abeb64SMatt Jacobson if (!EmittedStructorSymbolAttrs) { 203*75abeb64SMatt Jacobson OutStreamer->emitRawComment( 204*75abeb64SMatt Jacobson " Emitting these undefined symbol references causes us to link the" 205*75abeb64SMatt Jacobson " libgcc code that runs our constructors/destructors"); 206*75abeb64SMatt Jacobson OutStreamer->emitRawComment(" This matches GCC's behavior"); 207*75abeb64SMatt Jacobson 208*75abeb64SMatt Jacobson MCSymbol *CtorsSym = OutContext.getOrCreateSymbol("__do_global_ctors"); 209*75abeb64SMatt Jacobson OutStreamer->emitSymbolAttribute(CtorsSym, MCSA_Global); 210*75abeb64SMatt Jacobson 211*75abeb64SMatt Jacobson MCSymbol *DtorsSym = OutContext.getOrCreateSymbol("__do_global_dtors"); 212*75abeb64SMatt Jacobson OutStreamer->emitSymbolAttribute(DtorsSym, MCSA_Global); 213*75abeb64SMatt Jacobson 214*75abeb64SMatt Jacobson EmittedStructorSymbolAttrs = true; 215*75abeb64SMatt Jacobson } 216*75abeb64SMatt Jacobson 217*75abeb64SMatt Jacobson AsmPrinter::emitXXStructor(DL, CV); 218*75abeb64SMatt Jacobson } 219*75abeb64SMatt Jacobson 220*75abeb64SMatt Jacobson bool AVRAsmPrinter::doFinalization(Module &M) { 221*75abeb64SMatt Jacobson MCSymbol *DoCopyData = OutContext.getOrCreateSymbol("__do_copy_data"); 222*75abeb64SMatt Jacobson MCSymbol *DoClearBss = OutContext.getOrCreateSymbol("__do_clear_bss"); 223*75abeb64SMatt Jacobson 224*75abeb64SMatt Jacobson // FIXME: We can disable __do_copy_data if there are no static RAM variables. 225*75abeb64SMatt Jacobson 226*75abeb64SMatt Jacobson OutStreamer->emitRawComment( 227*75abeb64SMatt Jacobson " Declaring this symbol tells the CRT that it should"); 228*75abeb64SMatt Jacobson OutStreamer->emitRawComment( 229*75abeb64SMatt Jacobson "copy all variables from program memory to RAM on startup"); 230*75abeb64SMatt Jacobson OutStreamer->emitSymbolAttribute(DoCopyData, MCSA_Global); 231*75abeb64SMatt Jacobson 232*75abeb64SMatt Jacobson OutStreamer->emitRawComment( 233*75abeb64SMatt Jacobson " Declaring this symbol tells the CRT that it should"); 234*75abeb64SMatt Jacobson OutStreamer->emitRawComment("clear the zeroed data section on startup"); 235*75abeb64SMatt Jacobson OutStreamer->emitSymbolAttribute(DoClearBss, MCSA_Global); 236*75abeb64SMatt Jacobson 237*75abeb64SMatt Jacobson return AsmPrinter::doFinalization(M); 238*75abeb64SMatt Jacobson } 239*75abeb64SMatt Jacobson 2404d82df32SDylan McKay } // end of namespace llvm 2414d82df32SDylan McKay 2420dbcb363STom Stellard extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAVRAsmPrinter() { 243f42454b9SMehdi Amini llvm::RegisterAsmPrinter<llvm::AVRAsmPrinter> X(llvm::getTheAVRTarget()); 2444d82df32SDylan McKay } 245