| //===-- VEAsmPrinter.cpp - VE 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 GAS-format VE assembly language. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/VEInstPrinter.h" | |
| #include "MCTargetDesc/VEMCExpr.h" | |
| #include "MCTargetDesc/VETargetStreamer.h" | |
| #include "TargetInfo/VETargetInfo.h" | |
| #include "VE.h" | |
| #include "VEInstrInfo.h" | |
| #include "VETargetMachine.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/MachineInstr.h" | |
| #include "llvm/CodeGen/MachineModuleInfoImpls.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstBuilder.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "ve-asmprinter" | |
| namespace { | |
| class VEAsmPrinter : public AsmPrinter { | |
| VETargetStreamer &getTargetStreamer() { | |
| return static_cast<VETargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| } | |
| public: | |
| explicit VEAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) | |
| : AsmPrinter(TM, std::move(Streamer)) {} | |
| StringRef getPassName() const override { return "VE Assembly Printer"; } | |
| void lowerGETGOTAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI); | |
| void lowerGETFunPLTAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI); | |
| void lowerGETTLSAddrAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI); | |
| void emitInstruction(const MachineInstr *MI) override; | |
| static const char *getRegisterName(MCRegister Reg) { | |
| return VEInstPrinter::getRegisterName(Reg); | |
| } | |
| void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS); | |
| bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &O) override; | |
| bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &O) override; | |
| }; | |
| } // end of anonymous namespace | |
| static MCOperand createVEMCOperand(VEMCExpr::VariantKind Kind, MCSymbol *Sym, | |
| MCContext &OutContext) { | |
| const MCSymbolRefExpr *MCSym = MCSymbolRefExpr::create(Sym, OutContext); | |
| const VEMCExpr *expr = VEMCExpr::create(Kind, MCSym, OutContext); | |
| return MCOperand::createExpr(expr); | |
| } | |
| static MCOperand createGOTRelExprOp(VEMCExpr::VariantKind Kind, | |
| MCSymbol *GOTLabel, MCContext &OutContext) { | |
| const MCSymbolRefExpr *GOT = MCSymbolRefExpr::create(GOTLabel, OutContext); | |
| const VEMCExpr *expr = VEMCExpr::create(Kind, GOT, OutContext); | |
| return MCOperand::createExpr(expr); | |
| } | |
| static void emitSIC(MCStreamer &OutStreamer, MCOperand &RD, | |
| const MCSubtargetInfo &STI) { | |
| MCInst SICInst; | |
| SICInst.setOpcode(VE::SIC); | |
| SICInst.addOperand(RD); | |
| OutStreamer.emitInstruction(SICInst, STI); | |
| } | |
| static void emitBSIC(MCStreamer &OutStreamer, MCOperand &R1, MCOperand &R2, | |
| const MCSubtargetInfo &STI) { | |
| MCInst BSICInst; | |
| BSICInst.setOpcode(VE::BSICrii); | |
| BSICInst.addOperand(R1); | |
| BSICInst.addOperand(R2); | |
| MCOperand czero = MCOperand::createImm(0); | |
| BSICInst.addOperand(czero); | |
| BSICInst.addOperand(czero); | |
| OutStreamer.emitInstruction(BSICInst, STI); | |
| } | |
| static void emitLEAzzi(MCStreamer &OutStreamer, MCOperand &Imm, MCOperand &RD, | |
| const MCSubtargetInfo &STI) { | |
| MCInst LEAInst; | |
| LEAInst.setOpcode(VE::LEAzii); | |
| LEAInst.addOperand(RD); | |
| MCOperand CZero = MCOperand::createImm(0); | |
| LEAInst.addOperand(CZero); | |
| LEAInst.addOperand(CZero); | |
| LEAInst.addOperand(Imm); | |
| OutStreamer.emitInstruction(LEAInst, STI); | |
| } | |
| static void emitLEASLzzi(MCStreamer &OutStreamer, MCOperand &Imm, MCOperand &RD, | |
| const MCSubtargetInfo &STI) { | |
| MCInst LEASLInst; | |
| LEASLInst.setOpcode(VE::LEASLzii); | |
| LEASLInst.addOperand(RD); | |
| MCOperand CZero = MCOperand::createImm(0); | |
| LEASLInst.addOperand(CZero); | |
| LEASLInst.addOperand(CZero); | |
| LEASLInst.addOperand(Imm); | |
| OutStreamer.emitInstruction(LEASLInst, STI); | |
| } | |
| static void emitLEAzii(MCStreamer &OutStreamer, MCOperand &RS1, MCOperand &Imm, | |
| MCOperand &RD, const MCSubtargetInfo &STI) { | |
| MCInst LEAInst; | |
| LEAInst.setOpcode(VE::LEAzii); | |
| LEAInst.addOperand(RD); | |
| MCOperand CZero = MCOperand::createImm(0); | |
| LEAInst.addOperand(CZero); | |
| LEAInst.addOperand(RS1); | |
| LEAInst.addOperand(Imm); | |
| OutStreamer.emitInstruction(LEAInst, STI); | |
| } | |
| static void emitLEASLrri(MCStreamer &OutStreamer, MCOperand &RS1, | |
| MCOperand &RS2, MCOperand &Imm, MCOperand &RD, | |
| const MCSubtargetInfo &STI) { | |
| MCInst LEASLInst; | |
| LEASLInst.setOpcode(VE::LEASLrri); | |
| LEASLInst.addOperand(RD); | |
| LEASLInst.addOperand(RS1); | |
| LEASLInst.addOperand(RS2); | |
| LEASLInst.addOperand(Imm); | |
| OutStreamer.emitInstruction(LEASLInst, STI); | |
| } | |
| static void emitBinary(MCStreamer &OutStreamer, unsigned Opcode, MCOperand &RS1, | |
| MCOperand &Src2, MCOperand &RD, | |
| const MCSubtargetInfo &STI) { | |
| MCInst Inst; | |
| Inst.setOpcode(Opcode); | |
| Inst.addOperand(RD); | |
| Inst.addOperand(RS1); | |
| Inst.addOperand(Src2); | |
| OutStreamer.emitInstruction(Inst, STI); | |
| } | |
| static void emitANDrm(MCStreamer &OutStreamer, MCOperand &RS1, MCOperand &Imm, | |
| MCOperand &RD, const MCSubtargetInfo &STI) { | |
| emitBinary(OutStreamer, VE::ANDrm, RS1, Imm, RD, STI); | |
| } | |
| static void emitHiLo(MCStreamer &OutStreamer, MCSymbol *GOTSym, | |
| VEMCExpr::VariantKind HiKind, VEMCExpr::VariantKind LoKind, | |
| MCOperand &RD, MCContext &OutContext, | |
| const MCSubtargetInfo &STI) { | |
| MCOperand hi = createVEMCOperand(HiKind, GOTSym, OutContext); | |
| MCOperand lo = createVEMCOperand(LoKind, GOTSym, OutContext); | |
| emitLEAzzi(OutStreamer, lo, RD, STI); | |
| MCOperand M032 = MCOperand::createImm(M0(32)); | |
| emitANDrm(OutStreamer, RD, M032, RD, STI); | |
| emitLEASLzzi(OutStreamer, hi, RD, STI); | |
| } | |
| void VEAsmPrinter::lowerGETGOTAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI) { | |
| MCSymbol *GOTLabel = | |
| OutContext.getOrCreateSymbol(Twine("_GLOBAL_OFFSET_TABLE_")); | |
| const MachineOperand &MO = MI->getOperand(0); | |
| MCOperand MCRegOP = MCOperand::createReg(MO.getReg()); | |
| if (!isPositionIndependent()) { | |
| // Just load the address of GOT to MCRegOP. | |
| switch (TM.getCodeModel()) { | |
| default: | |
| llvm_unreachable("Unsupported absolute code model"); | |
| case CodeModel::Small: | |
| case CodeModel::Medium: | |
| case CodeModel::Large: | |
| emitHiLo(*OutStreamer, GOTLabel, VEMCExpr::VK_VE_HI32, | |
| VEMCExpr::VK_VE_LO32, MCRegOP, OutContext, STI); | |
| break; | |
| } | |
| return; | |
| } | |
| MCOperand RegGOT = MCOperand::createReg(VE::SX15); // GOT | |
| MCOperand RegPLT = MCOperand::createReg(VE::SX16); // PLT | |
| // lea %got, _GLOBAL_OFFSET_TABLE_@PC_LO(-24) | |
| // and %got, %got, (32)0 | |
| // sic %plt | |
| // lea.sl %got, _GLOBAL_OFFSET_TABLE_@PC_HI(%plt, %got) | |
| MCOperand cim24 = MCOperand::createImm(-24); | |
| MCOperand loImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PC_LO32, GOTLabel, OutContext); | |
| emitLEAzii(*OutStreamer, cim24, loImm, MCRegOP, STI); | |
| MCOperand M032 = MCOperand::createImm(M0(32)); | |
| emitANDrm(*OutStreamer, MCRegOP, M032, MCRegOP, STI); | |
| emitSIC(*OutStreamer, RegPLT, STI); | |
| MCOperand hiImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PC_HI32, GOTLabel, OutContext); | |
| emitLEASLrri(*OutStreamer, RegGOT, RegPLT, hiImm, MCRegOP, STI); | |
| } | |
| void VEAsmPrinter::lowerGETFunPLTAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI) { | |
| const MachineOperand &MO = MI->getOperand(0); | |
| MCOperand MCRegOP = MCOperand::createReg(MO.getReg()); | |
| const MachineOperand &Addr = MI->getOperand(1); | |
| MCSymbol *AddrSym = nullptr; | |
| switch (Addr.getType()) { | |
| default: | |
| llvm_unreachable("<unknown operand type>"); | |
| return; | |
| case MachineOperand::MO_MachineBasicBlock: | |
| report_fatal_error("MBB is not supported yet"); | |
| return; | |
| case MachineOperand::MO_ConstantPoolIndex: | |
| report_fatal_error("ConstantPool is not supported yet"); | |
| return; | |
| case MachineOperand::MO_ExternalSymbol: | |
| AddrSym = GetExternalSymbolSymbol(Addr.getSymbolName()); | |
| break; | |
| case MachineOperand::MO_GlobalAddress: | |
| AddrSym = getSymbol(Addr.getGlobal()); | |
| break; | |
| } | |
| if (!isPositionIndependent()) { | |
| llvm_unreachable("Unsupported uses of %plt in not PIC code"); | |
| return; | |
| } | |
| MCOperand RegPLT = MCOperand::createReg(VE::SX16); // PLT | |
| // lea %dst, func@plt_lo(-24) | |
| // and %dst, %dst, (32)0 | |
| // sic %plt ; FIXME: is it safe to use %plt here? | |
| // lea.sl %dst, func@plt_hi(%plt, %dst) | |
| MCOperand cim24 = MCOperand::createImm(-24); | |
| MCOperand loImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PLT_LO32, AddrSym, OutContext); | |
| emitLEAzii(*OutStreamer, cim24, loImm, MCRegOP, STI); | |
| MCOperand M032 = MCOperand::createImm(M0(32)); | |
| emitANDrm(*OutStreamer, MCRegOP, M032, MCRegOP, STI); | |
| emitSIC(*OutStreamer, RegPLT, STI); | |
| MCOperand hiImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PLT_HI32, AddrSym, OutContext); | |
| emitLEASLrri(*OutStreamer, MCRegOP, RegPLT, hiImm, MCRegOP, STI); | |
| } | |
| void VEAsmPrinter::lowerGETTLSAddrAndEmitMCInsts(const MachineInstr *MI, | |
| const MCSubtargetInfo &STI) { | |
| const MachineOperand &Addr = MI->getOperand(0); | |
| MCSymbol *AddrSym = nullptr; | |
| switch (Addr.getType()) { | |
| default: | |
| llvm_unreachable("<unknown operand type>"); | |
| return; | |
| case MachineOperand::MO_MachineBasicBlock: | |
| report_fatal_error("MBB is not supported yet"); | |
| return; | |
| case MachineOperand::MO_ConstantPoolIndex: | |
| report_fatal_error("ConstantPool is not supported yet"); | |
| return; | |
| case MachineOperand::MO_ExternalSymbol: | |
| AddrSym = GetExternalSymbolSymbol(Addr.getSymbolName()); | |
| break; | |
| case MachineOperand::MO_GlobalAddress: | |
| AddrSym = getSymbol(Addr.getGlobal()); | |
| break; | |
| } | |
| MCOperand RegLR = MCOperand::createReg(VE::SX10); // LR | |
| MCOperand RegS0 = MCOperand::createReg(VE::SX0); // S0 | |
| MCOperand RegS12 = MCOperand::createReg(VE::SX12); // S12 | |
| MCSymbol *GetTLSLabel = OutContext.getOrCreateSymbol(Twine("__tls_get_addr")); | |
| // lea %s0, sym@tls_gd_lo(-24) | |
| // and %s0, %s0, (32)0 | |
| // sic %lr | |
| // lea.sl %s0, sym@tls_gd_hi(%lr, %s0) | |
| // lea %s12, __tls_get_addr@plt_lo(8) | |
| // and %s12, %s12, (32)0 | |
| // lea.sl %s12, __tls_get_addr@plt_hi(%s12, %lr) | |
| // bsic %lr, (, %s12) | |
| MCOperand cim24 = MCOperand::createImm(-24); | |
| MCOperand loImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_TLS_GD_LO32, AddrSym, OutContext); | |
| emitLEAzii(*OutStreamer, cim24, loImm, RegS0, STI); | |
| MCOperand M032 = MCOperand::createImm(M0(32)); | |
| emitANDrm(*OutStreamer, RegS0, M032, RegS0, STI); | |
| emitSIC(*OutStreamer, RegLR, STI); | |
| MCOperand hiImm = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_TLS_GD_HI32, AddrSym, OutContext); | |
| emitLEASLrri(*OutStreamer, RegS0, RegLR, hiImm, RegS0, STI); | |
| MCOperand ci8 = MCOperand::createImm(8); | |
| MCOperand loImm2 = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PLT_LO32, GetTLSLabel, OutContext); | |
| emitLEAzii(*OutStreamer, ci8, loImm2, RegS12, STI); | |
| emitANDrm(*OutStreamer, RegS12, M032, RegS12, STI); | |
| MCOperand hiImm2 = | |
| createGOTRelExprOp(VEMCExpr::VK_VE_PLT_HI32, GetTLSLabel, OutContext); | |
| emitLEASLrri(*OutStreamer, RegS12, RegLR, hiImm2, RegS12, STI); | |
| emitBSIC(*OutStreamer, RegLR, RegS12, STI); | |
| } | |
| void VEAsmPrinter::emitInstruction(const MachineInstr *MI) { | |
| VE_MC::verifyInstructionPredicates(MI->getOpcode(), | |
| getSubtargetInfo().getFeatureBits()); | |
| switch (MI->getOpcode()) { | |
| default: | |
| break; | |
| case TargetOpcode::DBG_VALUE: | |
| // FIXME: Debug Value. | |
| return; | |
| case VE::GETGOT: | |
| lowerGETGOTAndEmitMCInsts(MI, getSubtargetInfo()); | |
| return; | |
| case VE::GETFUNPLT: | |
| lowerGETFunPLTAndEmitMCInsts(MI, getSubtargetInfo()); | |
| return; | |
| case VE::GETTLSADDR: | |
| lowerGETTLSAddrAndEmitMCInsts(MI, getSubtargetInfo()); | |
| return; | |
| } | |
| MachineBasicBlock::const_instr_iterator I = MI->getIterator(); | |
| MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end(); | |
| do { | |
| MCInst TmpInst; | |
| LowerVEMachineInstrToMCInst(&*I, TmpInst, *this); | |
| EmitToStreamer(*OutStreamer, TmpInst); | |
| } while ((++I != E) && I->isInsideBundle()); // Delay slot check. | |
| } | |
| void VEAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, | |
| raw_ostream &O) { | |
| const MachineOperand &MO = MI->getOperand(OpNum); | |
| switch (MO.getType()) { | |
| case MachineOperand::MO_Register: | |
| O << "%" << StringRef(getRegisterName(MO.getReg())).lower(); | |
| break; | |
| case MachineOperand::MO_Immediate: | |
| O << (int)MO.getImm(); | |
| break; | |
| default: | |
| llvm_unreachable("<unknown operand type>"); | |
| } | |
| } | |
| // PrintAsmOperand - Print out an operand for an inline asm expression. | |
| bool VEAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &O) { | |
| if (ExtraCode && ExtraCode[0]) { | |
| if (ExtraCode[1] != 0) | |
| return true; // Unknown modifier. | |
| switch (ExtraCode[0]) { | |
| default: | |
| // See if this is a generic print operand | |
| return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O); | |
| case 'r': | |
| case 'v': | |
| break; | |
| } | |
| } | |
| printOperand(MI, OpNo, O); | |
| return false; | |
| } | |
| bool VEAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, | |
| raw_ostream &O) { | |
| if (ExtraCode && ExtraCode[0]) | |
| return true; // Unknown modifier | |
| if (MI->getOperand(OpNo+1).isImm() && | |
| MI->getOperand(OpNo+1).getImm() == 0) { | |
| // don't print "+0" | |
| } else { | |
| printOperand(MI, OpNo+1, O); | |
| } | |
| if (MI->getOperand(OpNo).isImm() && | |
| MI->getOperand(OpNo).getImm() == 0) { | |
| if (MI->getOperand(OpNo+1).isImm() && | |
| MI->getOperand(OpNo+1).getImm() == 0) { | |
| O << "0"; | |
| } else { | |
| // don't print "(0)" | |
| } | |
| } else { | |
| O << "("; | |
| printOperand(MI, OpNo, O); | |
| O << ")"; | |
| } | |
| return false; | |
| } | |
| // Force static initialization. | |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeVEAsmPrinter() { | |
| RegisterAsmPrinter<VEAsmPrinter> X(getTheVETarget()); | |
| } |