| //===-- CSKYAsmPrinter.cpp - CSKY LLVM assembly writer --------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains a printer that converts from our internal representation | |
| // of machine-dependent LLVM code to the CSKY assembly language. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "CSKYAsmPrinter.h" | |
| #include "CSKY.h" | |
| #include "CSKYConstantPoolValue.h" | |
| #include "CSKYTargetMachine.h" | |
| #include "MCTargetDesc/CSKYInstPrinter.h" | |
| #include "MCTargetDesc/CSKYMCExpr.h" | |
| #include "MCTargetDesc/CSKYTargetStreamer.h" | |
| #include "TargetInfo/CSKYTargetInfo.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/MachineConstantPool.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCInstBuilder.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "csky-asm-printer" | |
| STATISTIC(CSKYNumInstrsCompressed, | |
| "Number of C-SKY Compressed instructions emitted"); | |
| CSKYAsmPrinter::CSKYAsmPrinter(llvm::TargetMachine &TM, | |
| std::unique_ptr<llvm::MCStreamer> Streamer) | |
| : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this) {} | |
| bool CSKYAsmPrinter::runOnMachineFunction(MachineFunction &MF) { | |
| MCP = MF.getConstantPool(); | |
| TII = MF.getSubtarget().getInstrInfo(); | |
| // Set the current MCSubtargetInfo to a copy which has the correct | |
| // feature bits for the current MachineFunction | |
| MCSubtargetInfo &NewSTI = | |
| OutStreamer->getContext().getSubtargetCopy(*TM.getMCSubtargetInfo()); | |
| NewSTI.setFeatureBits(MF.getSubtarget().getFeatureBits()); | |
| Subtarget = &NewSTI; | |
| return AsmPrinter::runOnMachineFunction(MF); | |
| } | |
| #define GEN_COMPRESS_INSTR | |
| #include "CSKYGenCompressInstEmitter.inc" | |
| void CSKYAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { | |
| MCInst CInst; | |
| bool Res = compressInst(CInst, Inst, *Subtarget); | |
| if (Res) | |
| ++CSKYNumInstrsCompressed; | |
| AsmPrinter::EmitToStreamer(*OutStreamer, Res ? CInst : Inst); | |
| } | |
| // Simple pseudo-instructions have their lowering (with expansion to real | |
| // instructions) auto-generated. | |
| #include "CSKYGenMCPseudoLowering.inc" | |
| void CSKYAsmPrinter::expandTLSLA(const MachineInstr *MI) { | |
| DebugLoc DL = MI->getDebugLoc(); | |
| MCSymbol *PCLabel = OutContext.getOrCreateSymbol( | |
| Twine(MAI->getPrivateGlobalPrefix()) + "PC" + Twine(getFunctionNumber()) + | |
| "_" + Twine(MI->getOperand(3).getImm())); | |
| OutStreamer->emitLabel(PCLabel); | |
| auto Instr = BuildMI(*MF, DL, TII->get(CSKY::LRW32)) | |
| .add(MI->getOperand(0)) | |
| .add(MI->getOperand(2)); | |
| MCInst LRWInst; | |
| MCInstLowering.Lower(Instr, LRWInst); | |
| EmitToStreamer(*OutStreamer, LRWInst); | |
| Instr = BuildMI(*MF, DL, TII->get(CSKY::GRS32)) | |
| .add(MI->getOperand(1)) | |
| .addSym(PCLabel); | |
| MCInst GRSInst; | |
| MCInstLowering.Lower(Instr, GRSInst); | |
| EmitToStreamer(*OutStreamer, GRSInst); | |
| return; | |
| } | |
| void CSKYAsmPrinter::emitCustomConstantPool(const MachineInstr *MI) { | |
| // This instruction represents a floating constant pool in the function. | |
| // The first operand is the ID# for this instruction, the second is the | |
| // index into the MachineConstantPool that this is, the third is the size | |
| // in bytes of this constant pool entry. | |
| // The required alignment is specified on the basic block holding this MI. | |
| unsigned LabelId = (unsigned)MI->getOperand(0).getImm(); | |
| unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex(); | |
| // If this is the first entry of the pool, mark it. | |
| if (!InConstantPool) { | |
| OutStreamer->emitValueToAlignment(Align(4)); | |
| InConstantPool = true; | |
| } | |
| OutStreamer->emitLabel(GetCPISymbol(LabelId)); | |
| const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx]; | |
| if (MCPE.isMachineConstantPoolEntry()) | |
| emitMachineConstantPoolValue(MCPE.Val.MachineCPVal); | |
| else | |
| emitGlobalConstant(MF->getDataLayout(), MCPE.Val.ConstVal); | |
| return; | |
| } | |
| void CSKYAsmPrinter::emitFunctionBodyEnd() { | |
| // Make sure to terminate any constant pools that were at the end | |
| // of the function. | |
| if (!InConstantPool) | |
| return; | |
| InConstantPool = false; | |
| } | |
| void CSKYAsmPrinter::emitStartOfAsmFile(Module &M) { | |
| if (TM.getTargetTriple().isOSBinFormatELF()) | |
| emitAttributes(); | |
| } | |
| void CSKYAsmPrinter::emitEndOfAsmFile(Module &M) { | |
| CSKYTargetStreamer &CTS = | |
| static_cast<CSKYTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| if (TM.getTargetTriple().isOSBinFormatELF()) | |
| CTS.finishAttributeSection(); | |
| } | |
| void CSKYAsmPrinter::emitInstruction(const MachineInstr *MI) { | |
| CSKY_MC::verifyInstructionPredicates(MI->getOpcode(), | |
| getSubtargetInfo().getFeatureBits()); | |
| // Do any auto-generated pseudo lowerings. | |
| if (emitPseudoExpansionLowering(*OutStreamer, MI)) | |
| return; | |
| // If we just ended a constant pool, mark it as such. | |
| if (InConstantPool && MI->getOpcode() != CSKY::CONSTPOOL_ENTRY) { | |
| InConstantPool = false; | |
| } | |
| if (MI->getOpcode() == CSKY::PseudoTLSLA32) | |
| return expandTLSLA(MI); | |
| if (MI->getOpcode() == CSKY::CONSTPOOL_ENTRY) | |
| return emitCustomConstantPool(MI); | |
| MCInst TmpInst; | |
| MCInstLowering.Lower(MI, TmpInst); | |
| EmitToStreamer(*OutStreamer, TmpInst); | |
| } | |
| // Convert a CSKY-specific constant pool modifier into the associated | |
| // MCSymbolRefExpr variant kind. | |
| static CSKYMCExpr::VariantKind | |
| getModifierVariantKind(CSKYCP::CSKYCPModifier Modifier) { | |
| switch (Modifier) { | |
| case CSKYCP::NO_MOD: | |
| return CSKYMCExpr::VK_CSKY_None; | |
| case CSKYCP::ADDR: | |
| return CSKYMCExpr::VK_CSKY_ADDR; | |
| case CSKYCP::GOT: | |
| return CSKYMCExpr::VK_CSKY_GOT; | |
| case CSKYCP::GOTOFF: | |
| return CSKYMCExpr::VK_CSKY_GOTOFF; | |
| case CSKYCP::PLT: | |
| return CSKYMCExpr::VK_CSKY_PLT; | |
| case CSKYCP::TLSGD: | |
| return CSKYMCExpr::VK_CSKY_TLSGD; | |
| case CSKYCP::TLSLE: | |
| return CSKYMCExpr::VK_CSKY_TLSLE; | |
| case CSKYCP::TLSIE: | |
| return CSKYMCExpr::VK_CSKY_TLSIE; | |
| } | |
| llvm_unreachable("Invalid CSKYCPModifier!"); | |
| } | |
| void CSKYAsmPrinter::emitMachineConstantPoolValue( | |
| MachineConstantPoolValue *MCPV) { | |
| int Size = getDataLayout().getTypeAllocSize(MCPV->getType()); | |
| CSKYConstantPoolValue *CCPV = static_cast<CSKYConstantPoolValue *>(MCPV); | |
| MCSymbol *MCSym; | |
| if (CCPV->isBlockAddress()) { | |
| const BlockAddress *BA = | |
| cast<CSKYConstantPoolConstant>(CCPV)->getBlockAddress(); | |
| MCSym = GetBlockAddressSymbol(BA); | |
| } else if (CCPV->isGlobalValue()) { | |
| const GlobalValue *GV = cast<CSKYConstantPoolConstant>(CCPV)->getGV(); | |
| MCSym = getSymbol(GV); | |
| } else if (CCPV->isMachineBasicBlock()) { | |
| const MachineBasicBlock *MBB = cast<CSKYConstantPoolMBB>(CCPV)->getMBB(); | |
| MCSym = MBB->getSymbol(); | |
| } else if (CCPV->isJT()) { | |
| signed JTI = cast<CSKYConstantPoolJT>(CCPV)->getJTI(); | |
| MCSym = GetJTISymbol(JTI); | |
| } else if (CCPV->isConstPool()) { | |
| const Constant *C = cast<CSKYConstantPoolConstant>(CCPV)->getConstantPool(); | |
| MCSym = GetCPISymbol(MCP->getConstantPoolIndex(C, Align(4))); | |
| } else { | |
| assert(CCPV->isExtSymbol() && "unrecognized constant pool value"); | |
| StringRef Sym = cast<CSKYConstantPoolSymbol>(CCPV)->getSymbol(); | |
| MCSym = GetExternalSymbolSymbol(Sym); | |
| } | |
| // Create an MCSymbol for the reference. | |
| const MCExpr *Expr = | |
| MCSymbolRefExpr::create(MCSym, MCSymbolRefExpr::VK_None, OutContext); | |
| if (CCPV->getPCAdjustment()) { | |
| MCSymbol *PCLabel = OutContext.getOrCreateSymbol( | |
| Twine(MAI->getPrivateGlobalPrefix()) + "PC" + | |
| Twine(getFunctionNumber()) + "_" + Twine(CCPV->getLabelID())); | |
| const MCExpr *PCRelExpr = MCSymbolRefExpr::create(PCLabel, OutContext); | |
| if (CCPV->mustAddCurrentAddress()) { | |
| // We want "(<expr> - .)", but MC doesn't have a concept of the '.' | |
| // label, so just emit a local label end reference that instead. | |
| MCSymbol *DotSym = OutContext.createTempSymbol(); | |
| OutStreamer->emitLabel(DotSym); | |
| const MCExpr *DotExpr = MCSymbolRefExpr::create(DotSym, OutContext); | |
| PCRelExpr = MCBinaryExpr::createSub(PCRelExpr, DotExpr, OutContext); | |
| } | |
| Expr = MCBinaryExpr::createSub(Expr, PCRelExpr, OutContext); | |
| } | |
| // Create an MCSymbol for the reference. | |
| Expr = CSKYMCExpr::create(Expr, getModifierVariantKind(CCPV->getModifier()), | |
| OutContext); | |
| OutStreamer->emitValue(Expr, Size); | |
| } | |
| void CSKYAsmPrinter::emitAttributes() { | |
| CSKYTargetStreamer &CTS = | |
| static_cast<CSKYTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| const Triple &TT = TM.getTargetTriple(); | |
| StringRef CPU = TM.getTargetCPU(); | |
| StringRef FS = TM.getTargetFeatureString(); | |
| const CSKYTargetMachine &CTM = static_cast<const CSKYTargetMachine &>(TM); | |
| /* TuneCPU doesn't impact emission of ELF attributes, ELF attributes only | |
| care about arch related features, so we can set TuneCPU as CPU. */ | |
| const CSKYSubtarget STI(TT, CPU, /*TuneCPU=*/CPU, FS, CTM); | |
| CTS.emitTargetAttributes(STI); | |
| } | |
| bool CSKYAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &OS) { | |
| // First try the generic code, which knows about modifiers like 'c' and 'n'. | |
| if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) | |
| return false; | |
| const MachineOperand &MO = MI->getOperand(OpNo); | |
| if (ExtraCode && ExtraCode[0]) { | |
| if (ExtraCode[1] != 0) | |
| return true; // Unknown modifier. | |
| switch (ExtraCode[0]) { | |
| default: | |
| return true; // Unknown modifier. | |
| case 'R': | |
| if (MO.getType() == MachineOperand::MO_Register) { | |
| OS << CSKYInstPrinter::getRegisterName(MO.getReg() + 1); | |
| return false; | |
| } | |
| } | |
| } | |
| switch (MO.getType()) { | |
| case MachineOperand::MO_Immediate: | |
| OS << MO.getImm(); | |
| return false; | |
| case MachineOperand::MO_Register: | |
| if (MO.getReg() == CSKY::C) | |
| return false; | |
| OS << CSKYInstPrinter::getRegisterName(MO.getReg()); | |
| return false; | |
| case MachineOperand::MO_GlobalAddress: | |
| PrintSymbolOperand(MO, OS); | |
| return false; | |
| case MachineOperand::MO_BlockAddress: { | |
| MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress()); | |
| Sym->print(OS, MAI); | |
| return false; | |
| } | |
| default: | |
| break; | |
| } | |
| return true; | |
| } | |
| bool CSKYAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, | |
| unsigned OpNo, const char *ExtraCode, | |
| raw_ostream &OS) { | |
| if (!ExtraCode) { | |
| const MachineOperand &MO = MI->getOperand(OpNo); | |
| // For now, we only support register memory operands in registers and | |
| // assume there is no addend | |
| if (!MO.isReg()) | |
| return true; | |
| OS << "(" << CSKYInstPrinter::getRegisterName(MO.getReg()) << ", 0)"; | |
| return false; | |
| } | |
| return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); | |
| } | |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeCSKYAsmPrinter() { | |
| RegisterAsmPrinter<CSKYAsmPrinter> X(getTheCSKYTarget()); | |
| } |