//===-- PPCAsmPrinter.cpp - Print machine instrs to PowerPC assembly ------===// // // 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 PowerPC assembly language. This printer is // the output mechanism used by `llc'. // // Documentation at http://developer.apple.com/documentation/DeveloperTools/ // Reference/Assembler/ASMIntroduction/chapter_1_section_1.html // //===----------------------------------------------------------------------===// #include "MCTargetDesc/PPCInstPrinter.h" #include "MCTargetDesc/PPCMCExpr.h" #include "MCTargetDesc/PPCMCTargetDesc.h" #include "MCTargetDesc/PPCPredicates.h" #include "PPC.h" #include "PPCInstrInfo.h" #include "PPCMachineFunctionInfo.h" #include "PPCSubtarget.h" #include "PPCTargetMachine.h" #include "PPCTargetStreamer.h" #include "TargetInfo/PowerPCTargetInfo.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/StackMaps.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/SectionKind.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include #include #include #include #include using namespace llvm; using namespace llvm::XCOFF; #define DEBUG_TYPE "asmprinter" static cl::opt EnableSSPCanaryBitInTB( "aix-ssp-tb-bit", cl::init(false), cl::desc("Enable Passing SSP Canary info in Trackback on AIX"), cl::Hidden); // Specialize DenseMapInfo to allow // std::pair in DenseMap. // This specialization is needed here because that type is used as keys in the // map representing TOC entries. namespace llvm { template <> struct DenseMapInfo> { using TOCKey = std::pair; static inline TOCKey getEmptyKey() { return {nullptr, MCSymbolRefExpr::VariantKind::VK_None}; } static inline TOCKey getTombstoneKey() { return {nullptr, MCSymbolRefExpr::VariantKind::VK_Invalid}; } static unsigned getHashValue(const TOCKey &PairVal) { return detail::combineHashValue( DenseMapInfo::getHashValue(PairVal.first), DenseMapInfo::getHashValue(PairVal.second)); } static bool isEqual(const TOCKey &A, const TOCKey &B) { return A == B; } }; } // end namespace llvm namespace { enum { // GNU attribute tags for PowerPC ABI Tag_GNU_Power_ABI_FP = 4, Tag_GNU_Power_ABI_Vector = 8, Tag_GNU_Power_ABI_Struct_Return = 12, // GNU attribute values for PowerPC float ABI, as combination of two parts Val_GNU_Power_ABI_NoFloat = 0b00, Val_GNU_Power_ABI_HardFloat_DP = 0b01, Val_GNU_Power_ABI_SoftFloat_DP = 0b10, Val_GNU_Power_ABI_HardFloat_SP = 0b11, Val_GNU_Power_ABI_LDBL_IBM128 = 0b0100, Val_GNU_Power_ABI_LDBL_64 = 0b1000, Val_GNU_Power_ABI_LDBL_IEEE128 = 0b1100, }; class PPCAsmPrinter : public AsmPrinter { protected: // For TLS on AIX, we need to be able to identify TOC entries of specific // VariantKind so we can add the right relocations when we generate the // entries. So each entry is represented by a pair of MCSymbol and // VariantKind. For example, we need to be able to identify the following // entry as a TLSGD entry so we can add the @m relocation: // .tc .i[TC],i[TL]@m // By default, VK_None is used for the VariantKind. MapVector, MCSymbol *> TOC; const PPCSubtarget *Subtarget = nullptr; StackMaps SM; public: explicit PPCAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)), SM(*this) {} StringRef getPassName() const override { return "PowerPC Assembly Printer"; } MCSymbol *lookUpOrCreateTOCEntry(const MCSymbol *Sym, MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_None); bool doInitialization(Module &M) override { if (!TOC.empty()) TOC.clear(); return AsmPrinter::doInitialization(M); } void emitInstruction(const MachineInstr *MI) override; /// This function is for PrintAsmOperand and PrintAsmMemoryOperand, /// invoked by EmitMSInlineAsmStr and EmitGCCInlineAsmStr only. /// The \p MI would be INLINEASM ONLY. void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O); void PrintSymbolOperand(const MachineOperand &MO, raw_ostream &O) override; 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; void emitEndOfAsmFile(Module &M) override; void LowerSTACKMAP(StackMaps &SM, const MachineInstr &MI); void LowerPATCHPOINT(StackMaps &SM, const MachineInstr &MI); void EmitTlsCall(const MachineInstr *MI, MCSymbolRefExpr::VariantKind VK); bool runOnMachineFunction(MachineFunction &MF) override { Subtarget = &MF.getSubtarget(); bool Changed = AsmPrinter::runOnMachineFunction(MF); emitXRayTable(); return Changed; } }; /// PPCLinuxAsmPrinter - PowerPC assembly printer, customized for Linux class PPCLinuxAsmPrinter : public PPCAsmPrinter { public: explicit PPCLinuxAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) {} StringRef getPassName() const override { return "Linux PPC Assembly Printer"; } void emitGNUAttributes(Module &M); void emitStartOfAsmFile(Module &M) override; void emitEndOfAsmFile(Module &) override; void emitFunctionEntryLabel() override; void emitFunctionBodyStart() override; void emitFunctionBodyEnd() override; void emitInstruction(const MachineInstr *MI) override; }; class PPCAIXAsmPrinter : public PPCAsmPrinter { private: /// Symbols lowered from ExternalSymbolSDNodes, we will need to emit extern /// linkage for them in AIX. SmallPtrSet ExtSymSDNodeSymbols; /// A format indicator and unique trailing identifier to form part of the /// sinit/sterm function names. std::string FormatIndicatorAndUniqueModId; // Record a list of GlobalAlias associated with a GlobalObject. // This is used for AIX's extra-label-at-definition aliasing strategy. DenseMap> GOAliasMap; uint16_t getNumberOfVRSaved(); void emitTracebackTable(); SmallVector TOCDataGlobalVars; void emitGlobalVariableHelper(const GlobalVariable *); public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) { if (MAI->isLittleEndian()) report_fatal_error( "cannot create AIX PPC Assembly Printer for a little-endian target"); } StringRef getPassName() const override { return "AIX PPC Assembly Printer"; } bool doInitialization(Module &M) override; void emitXXStructorList(const DataLayout &DL, const Constant *List, bool IsCtor) override; void SetupMachineFunction(MachineFunction &MF) override; void emitGlobalVariable(const GlobalVariable *GV) override; void emitFunctionDescriptor() override; void emitFunctionEntryLabel() override; void emitFunctionBodyEnd() override; void emitEndOfAsmFile(Module &) override; void emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const override; void emitInstruction(const MachineInstr *MI) override; bool doFinalization(Module &M) override; void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override; }; } // end anonymous namespace void PPCAsmPrinter::PrintSymbolOperand(const MachineOperand &MO, raw_ostream &O) { // Computing the address of a global symbol, not calling it. const GlobalValue *GV = MO.getGlobal(); getSymbol(GV)->print(O, MAI); printOffset(MO.getOffset(), O); } void PPCAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O) { const DataLayout &DL = getDataLayout(); const MachineOperand &MO = MI->getOperand(OpNo); switch (MO.getType()) { case MachineOperand::MO_Register: { // The MI is INLINEASM ONLY and UseVSXReg is always false. const char *RegName = PPCInstPrinter::getRegisterName(MO.getReg()); // Linux assembler (Others?) does not take register mnemonics. // FIXME - What about special registers used in mfspr/mtspr? O << PPCRegisterInfo::stripRegisterPrefix(RegName); return; } case MachineOperand::MO_Immediate: O << MO.getImm(); return; case MachineOperand::MO_MachineBasicBlock: MO.getMBB()->getSymbol()->print(O, MAI); return; case MachineOperand::MO_ConstantPoolIndex: O << DL.getPrivateGlobalPrefix() << "CPI" << getFunctionNumber() << '_' << MO.getIndex(); return; case MachineOperand::MO_BlockAddress: GetBlockAddressSymbol(MO.getBlockAddress())->print(O, MAI); return; case MachineOperand::MO_GlobalAddress: { PrintSymbolOperand(MO, O); return; } default: O << ""; return; } } /// PrintAsmOperand - Print out an operand for an inline asm expression. /// bool PPCAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &O) { // Does this asm operand have a single letter operand modifier? 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 'L': // Write second word of DImode reference. // Verify that this operand has two consecutive registers. if (!MI->getOperand(OpNo).isReg() || OpNo+1 == MI->getNumOperands() || !MI->getOperand(OpNo+1).isReg()) return true; ++OpNo; // Return the high-part. break; case 'I': // Write 'i' if an integer constant, otherwise nothing. Used to print // addi vs add, etc. if (MI->getOperand(OpNo).isImm()) O << "i"; return false; case 'x': if(!MI->getOperand(OpNo).isReg()) return true; // This operand uses VSX numbering. // If the operand is a VMX register, convert it to a VSX register. Register Reg = MI->getOperand(OpNo).getReg(); if (PPCInstrInfo::isVRRegister(Reg)) Reg = PPC::VSX32 + (Reg - PPC::V0); else if (PPCInstrInfo::isVFRegister(Reg)) Reg = PPC::VSX32 + (Reg - PPC::VF0); const char *RegName; RegName = PPCInstPrinter::getRegisterName(Reg); RegName = PPCRegisterInfo::stripRegisterPrefix(RegName); O << RegName; return false; } } printOperand(MI, OpNo, O); return false; } // At the moment, all inline asm memory operands are a single register. // In any case, the output of this routine should always be just one // assembler operand. bool PPCAsmPrinter::PrintAsmMemoryOperand(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: return true; // Unknown modifier. case 'L': // A memory reference to the upper word of a double word op. O << getDataLayout().getPointerSize() << "("; printOperand(MI, OpNo, O); O << ")"; return false; case 'y': // A memory reference for an X-form instruction O << "0, "; printOperand(MI, OpNo, O); return false; case 'I': // Write 'i' if an integer constant, otherwise nothing. Used to print // addi vs add, etc. if (MI->getOperand(OpNo).isImm()) O << "i"; return false; case 'U': // Print 'u' for update form. case 'X': // Print 'x' for indexed form. // FIXME: Currently for PowerPC memory operands are always loaded // into a register, so we never get an update or indexed form. // This is bad even for offset forms, since even if we know we // have a value in -16(r1), we will generate a load into r // and then load from 0(r). Until that issue is fixed, // tolerate 'U' and 'X' but don't output anything. assert(MI->getOperand(OpNo).isReg()); return false; } } assert(MI->getOperand(OpNo).isReg()); O << "0("; printOperand(MI, OpNo, O); O << ")"; return false; } /// lookUpOrCreateTOCEntry -- Given a symbol, look up whether a TOC entry /// exists for it. If not, create one. Then return a symbol that references /// the TOC entry. MCSymbol * PPCAsmPrinter::lookUpOrCreateTOCEntry(const MCSymbol *Sym, MCSymbolRefExpr::VariantKind Kind) { MCSymbol *&TOCEntry = TOC[{Sym, Kind}]; if (!TOCEntry) TOCEntry = createTempSymbol("C"); return TOCEntry; } void PPCAsmPrinter::emitEndOfAsmFile(Module &M) { emitStackMaps(SM); } void PPCAsmPrinter::LowerSTACKMAP(StackMaps &SM, const MachineInstr &MI) { unsigned NumNOPBytes = MI.getOperand(1).getImm(); auto &Ctx = OutStreamer->getContext(); MCSymbol *MILabel = Ctx.createTempSymbol(); OutStreamer->emitLabel(MILabel); SM.recordStackMap(*MILabel, MI); assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); // Scan ahead to trim the shadow. const MachineBasicBlock &MBB = *MI.getParent(); MachineBasicBlock::const_iterator MII(MI); ++MII; while (NumNOPBytes > 0) { if (MII == MBB.end() || MII->isCall() || MII->getOpcode() == PPC::DBG_VALUE || MII->getOpcode() == TargetOpcode::PATCHPOINT || MII->getOpcode() == TargetOpcode::STACKMAP) break; ++MII; NumNOPBytes -= 4; } // Emit nops. for (unsigned i = 0; i < NumNOPBytes; i += 4) EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::NOP)); } // Lower a patchpoint of the form: // [], , , , void PPCAsmPrinter::LowerPATCHPOINT(StackMaps &SM, const MachineInstr &MI) { auto &Ctx = OutStreamer->getContext(); MCSymbol *MILabel = Ctx.createTempSymbol(); OutStreamer->emitLabel(MILabel); SM.recordPatchPoint(*MILabel, MI); PatchPointOpers Opers(&MI); unsigned EncodedBytes = 0; const MachineOperand &CalleeMO = Opers.getCallTarget(); if (CalleeMO.isImm()) { int64_t CallTarget = CalleeMO.getImm(); if (CallTarget) { assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget && "High 16 bits of call target should be zero."); Register ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg(); EncodedBytes = 0; // Materialize the jump address: EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LI8) .addReg(ScratchReg) .addImm((CallTarget >> 32) & 0xFFFF)); ++EncodedBytes; EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::RLDIC) .addReg(ScratchReg) .addReg(ScratchReg) .addImm(32).addImm(16)); ++EncodedBytes; EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ORIS8) .addReg(ScratchReg) .addReg(ScratchReg) .addImm((CallTarget >> 16) & 0xFFFF)); ++EncodedBytes; EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ORI8) .addReg(ScratchReg) .addReg(ScratchReg) .addImm(CallTarget & 0xFFFF)); // Save the current TOC pointer before the remote call. int TOCSaveOffset = Subtarget->getFrameLowering()->getTOCSaveOffset(); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::STD) .addReg(PPC::X2) .addImm(TOCSaveOffset) .addReg(PPC::X1)); ++EncodedBytes; // If we're on ELFv1, then we need to load the actual function pointer // from the function descriptor. if (!Subtarget->isELFv2ABI()) { // Load the new TOC pointer and the function address, but not r11 // (needing this is rare, and loading it here would prevent passing it // via a 'nest' parameter. EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LD) .addReg(PPC::X2) .addImm(8) .addReg(ScratchReg)); ++EncodedBytes; EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LD) .addReg(ScratchReg) .addImm(0) .addReg(ScratchReg)); ++EncodedBytes; } EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MTCTR8) .addReg(ScratchReg)); ++EncodedBytes; EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BCTRL8)); ++EncodedBytes; // Restore the TOC pointer after the call. EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LD) .addReg(PPC::X2) .addImm(TOCSaveOffset) .addReg(PPC::X1)); ++EncodedBytes; } } else if (CalleeMO.isGlobal()) { const GlobalValue *GValue = CalleeMO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymVar = MCSymbolRefExpr::create(MOSymbol, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL8_NOP) .addExpr(SymVar)); EncodedBytes += 2; } // Each instruction is 4 bytes. EncodedBytes *= 4; // Emit padding. unsigned NumBytes = Opers.getNumPatchBytes(); assert(NumBytes >= EncodedBytes && "Patchpoint can't request size less than the length of a call."); assert((NumBytes - EncodedBytes) % 4 == 0 && "Invalid number of NOP bytes requested!"); for (unsigned i = EncodedBytes; i < NumBytes; i += 4) EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::NOP)); } /// This helper function creates the TlsGetAddr MCSymbol for AIX. We will /// create the csect and use the qual-name symbol instead of creating just the /// external symbol. static MCSymbol *createMCSymbolForTlsGetAddr(MCContext &Ctx) { return Ctx .getXCOFFSection(".__tls_get_addr", SectionKind::getText(), XCOFF::CsectProperties(XCOFF::XMC_PR, XCOFF::XTY_ER)) ->getQualNameSymbol(); } /// EmitTlsCall -- Given a GETtls[ld]ADDR[32] instruction, print a /// call to __tls_get_addr to the current output stream. void PPCAsmPrinter::EmitTlsCall(const MachineInstr *MI, MCSymbolRefExpr::VariantKind VK) { MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; unsigned Opcode = PPC::BL8_NOP_TLS; assert(MI->getNumOperands() >= 3 && "Expecting at least 3 operands from MI"); if (MI->getOperand(2).getTargetFlags() == PPCII::MO_GOT_TLSGD_PCREL_FLAG || MI->getOperand(2).getTargetFlags() == PPCII::MO_GOT_TLSLD_PCREL_FLAG) { Kind = MCSymbolRefExpr::VK_PPC_NOTOC; Opcode = PPC::BL8_NOTOC_TLS; } const Module *M = MF->getFunction().getParent(); assert(MI->getOperand(0).isReg() && ((Subtarget->isPPC64() && MI->getOperand(0).getReg() == PPC::X3) || (!Subtarget->isPPC64() && MI->getOperand(0).getReg() == PPC::R3)) && "GETtls[ld]ADDR[32] must define GPR3"); assert(MI->getOperand(1).isReg() && ((Subtarget->isPPC64() && MI->getOperand(1).getReg() == PPC::X3) || (!Subtarget->isPPC64() && MI->getOperand(1).getReg() == PPC::R3)) && "GETtls[ld]ADDR[32] must read GPR3"); if (Subtarget->isAIXABI()) { // On AIX, the variable offset should already be in R4 and the region handle // should already be in R3. // For TLSGD, which currently is the only supported access model, we only // need to generate an absolute branch to .__tls_get_addr. Register VarOffsetReg = Subtarget->isPPC64() ? PPC::X4 : PPC::R4; (void)VarOffsetReg; assert(MI->getOperand(2).isReg() && MI->getOperand(2).getReg() == VarOffsetReg && "GETtls[ld]ADDR[32] must read GPR4"); MCSymbol *TlsGetAddr = createMCSymbolForTlsGetAddr(OutContext); const MCExpr *TlsRef = MCSymbolRefExpr::create( TlsGetAddr, MCSymbolRefExpr::VK_None, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BLA).addExpr(TlsRef)); return; } MCSymbol *TlsGetAddr = OutContext.getOrCreateSymbol("__tls_get_addr"); if (Subtarget->is32BitELFABI() && isPositionIndependent()) Kind = MCSymbolRefExpr::VK_PLT; const MCExpr *TlsRef = MCSymbolRefExpr::create(TlsGetAddr, Kind, OutContext); // Add 32768 offset to the symbol so we follow up the latest GOT/PLT ABI. if (Kind == MCSymbolRefExpr::VK_PLT && Subtarget->isSecurePlt() && M->getPICLevel() == PICLevel::BigPIC) TlsRef = MCBinaryExpr::createAdd( TlsRef, MCConstantExpr::create(32768, OutContext), OutContext); const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymVar = MCSymbolRefExpr::create(MOSymbol, VK, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(Subtarget->isPPC64() ? Opcode : (unsigned)PPC::BL_TLS) .addExpr(TlsRef) .addExpr(SymVar)); } /// Map a machine operand for a TOC pseudo-machine instruction to its /// corresponding MCSymbol. static MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO, AsmPrinter &AP) { switch (MO.getType()) { case MachineOperand::MO_GlobalAddress: return AP.getSymbol(MO.getGlobal()); case MachineOperand::MO_ConstantPoolIndex: return AP.GetCPISymbol(MO.getIndex()); case MachineOperand::MO_JumpTableIndex: return AP.GetJTISymbol(MO.getIndex()); case MachineOperand::MO_BlockAddress: return AP.GetBlockAddressSymbol(MO.getBlockAddress()); default: llvm_unreachable("Unexpected operand type to get symbol."); } } /// EmitInstruction -- Print out a single PowerPC MI in Darwin syntax to /// the current output stream. /// void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) { MCInst TmpInst; const bool IsPPC64 = Subtarget->isPPC64(); const bool IsAIX = Subtarget->isAIXABI(); const Module *M = MF->getFunction().getParent(); PICLevel::Level PL = M->getPICLevel(); #ifndef NDEBUG // Validate that SPE and FPU are mutually exclusive in codegen if (!MI->isInlineAsm()) { for (const MachineOperand &MO: MI->operands()) { if (MO.isReg()) { Register Reg = MO.getReg(); if (Subtarget->hasSPE()) { if (PPC::F4RCRegClass.contains(Reg) || PPC::F8RCRegClass.contains(Reg) || PPC::VFRCRegClass.contains(Reg) || PPC::VRRCRegClass.contains(Reg) || PPC::VSFRCRegClass.contains(Reg) || PPC::VSSRCRegClass.contains(Reg) ) llvm_unreachable("SPE targets cannot have FPRegs!"); } else { if (PPC::SPERCRegClass.contains(Reg)) llvm_unreachable("SPE register found in FPU-targeted code!"); } } } } #endif auto getTOCRelocAdjustedExprForXCOFF = [this](const MCExpr *Expr, ptrdiff_t OriginalOffset) { // Apply an offset to the TOC-based expression such that the adjusted // notional offset from the TOC base (to be encoded into the instruction's D // or DS field) is the signed 16-bit truncation of the original notional // offset from the TOC base. // This is consistent with the treatment used both by XL C/C++ and // by AIX ld -r. ptrdiff_t Adjustment = OriginalOffset - llvm::SignExtend32<16>(OriginalOffset); return MCBinaryExpr::createAdd( Expr, MCConstantExpr::create(-Adjustment, OutContext), OutContext); }; auto getTOCEntryLoadingExprForXCOFF = [IsPPC64, getTOCRelocAdjustedExprForXCOFF, this](const MCSymbol *MOSymbol, const MCExpr *Expr, MCSymbolRefExpr::VariantKind VK = MCSymbolRefExpr::VariantKind::VK_None) -> const MCExpr * { const unsigned EntryByteSize = IsPPC64 ? 8 : 4; const auto TOCEntryIter = TOC.find({MOSymbol, VK}); assert(TOCEntryIter != TOC.end() && "Could not find the TOC entry for this symbol."); const ptrdiff_t EntryDistanceFromTOCBase = (TOCEntryIter - TOC.begin()) * EntryByteSize; constexpr int16_t PositiveTOCRange = INT16_MAX; if (EntryDistanceFromTOCBase > PositiveTOCRange) return getTOCRelocAdjustedExprForXCOFF(Expr, EntryDistanceFromTOCBase); return Expr; }; auto GetVKForMO = [&](const MachineOperand &MO) { // For GD TLS access on AIX, we have two TOC entries for the symbol (one for // the variable offset and the other for the region handle). They are // differentiated by MO_TLSGD_FLAG and MO_TLSGDM_FLAG. if (MO.getTargetFlags() & PPCII::MO_TLSGDM_FLAG) return MCSymbolRefExpr::VariantKind::VK_PPC_AIX_TLSGDM; if (MO.getTargetFlags() & PPCII::MO_TLSGD_FLAG) return MCSymbolRefExpr::VariantKind::VK_PPC_AIX_TLSGD; return MCSymbolRefExpr::VariantKind::VK_None; }; // Lower multi-instruction pseudo operations. switch (MI->getOpcode()) { default: break; case TargetOpcode::DBG_VALUE: llvm_unreachable("Should be handled target independently"); case TargetOpcode::STACKMAP: return LowerSTACKMAP(SM, *MI); case TargetOpcode::PATCHPOINT: return LowerPATCHPOINT(SM, *MI); case PPC::MoveGOTtoLR: { // Transform %lr = MoveGOTtoLR // Into this: bl _GLOBAL_OFFSET_TABLE_@local-4 // _GLOBAL_OFFSET_TABLE_@local-4 (instruction preceding // _GLOBAL_OFFSET_TABLE_) has exactly one instruction: // blrl // This will return the pointer to _GLOBAL_OFFSET_TABLE_@local MCSymbol *GOTSymbol = OutContext.getOrCreateSymbol(StringRef("_GLOBAL_OFFSET_TABLE_")); const MCExpr *OffsExpr = MCBinaryExpr::createSub(MCSymbolRefExpr::create(GOTSymbol, MCSymbolRefExpr::VK_PPC_LOCAL, OutContext), MCConstantExpr::create(4, OutContext), OutContext); // Emit the 'bl'. EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL).addExpr(OffsExpr)); return; } case PPC::MovePCtoLR: case PPC::MovePCtoLR8: { // Transform %lr = MovePCtoLR // Into this, where the label is the PIC base: // bl L1$pb // L1$pb: MCSymbol *PICBase = MF->getPICBaseSymbol(); // Emit the 'bl'. EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL) // FIXME: We would like an efficient form for this, so we // don't have to do a lot of extra uniquing. .addExpr(MCSymbolRefExpr::create(PICBase, OutContext))); // Emit the label. OutStreamer->emitLabel(PICBase); return; } case PPC::UpdateGBR: { // Transform %rd = UpdateGBR(%rt, %ri) // Into: lwz %rt, .L0$poff - .L0$pb(%ri) // add %rd, %rt, %ri // or into (if secure plt mode is on): // addis r30, r30, {.LTOC,_GLOBAL_OFFSET_TABLE} - .L0$pb@ha // addi r30, r30, {.LTOC,_GLOBAL_OFFSET_TABLE} - .L0$pb@l // Get the offset from the GOT Base Register to the GOT LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); if (Subtarget->isSecurePlt() && isPositionIndependent() ) { unsigned PICR = TmpInst.getOperand(0).getReg(); MCSymbol *BaseSymbol = OutContext.getOrCreateSymbol( M->getPICLevel() == PICLevel::SmallPIC ? "_GLOBAL_OFFSET_TABLE_" : ".LTOC"); const MCExpr *PB = MCSymbolRefExpr::create(MF->getPICBaseSymbol(), OutContext); const MCExpr *DeltaExpr = MCBinaryExpr::createSub( MCSymbolRefExpr::create(BaseSymbol, OutContext), PB, OutContext); const MCExpr *DeltaHi = PPCMCExpr::createHa(DeltaExpr, OutContext); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::ADDIS).addReg(PICR).addReg(PICR).addExpr(DeltaHi)); const MCExpr *DeltaLo = PPCMCExpr::createLo(DeltaExpr, OutContext); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::ADDI).addReg(PICR).addReg(PICR).addExpr(DeltaLo)); return; } else { MCSymbol *PICOffset = MF->getInfo()->getPICOffsetSymbol(*MF); TmpInst.setOpcode(PPC::LWZ); const MCExpr *Exp = MCSymbolRefExpr::create(PICOffset, MCSymbolRefExpr::VK_None, OutContext); const MCExpr *PB = MCSymbolRefExpr::create(MF->getPICBaseSymbol(), MCSymbolRefExpr::VK_None, OutContext); const MCOperand TR = TmpInst.getOperand(1); const MCOperand PICR = TmpInst.getOperand(0); // Step 1: lwz %rt, .L$poff - .L$pb(%ri) TmpInst.getOperand(1) = MCOperand::createExpr(MCBinaryExpr::createSub(Exp, PB, OutContext)); TmpInst.getOperand(0) = TR; TmpInst.getOperand(2) = PICR; EmitToStreamer(*OutStreamer, TmpInst); TmpInst.setOpcode(PPC::ADD4); TmpInst.getOperand(0) = PICR; TmpInst.getOperand(1) = TR; TmpInst.getOperand(2) = PICR; EmitToStreamer(*OutStreamer, TmpInst); return; } } case PPC::LWZtoc: { // Transform %rN = LWZtoc @op1, %r2 LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to LWZ. TmpInst.setOpcode(PPC::LWZ); const MachineOperand &MO = MI->getOperand(1); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for LWZtoc."); // Map the operand to its corresponding MCSymbol. const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); // Create a reference to the GOT entry for the symbol. The GOT entry will be // synthesized later. if (PL == PICLevel::SmallPIC && !IsAIX) { const MCExpr *Exp = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_GOT, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); // Otherwise, use the TOC. 'TOCEntry' is a label used to reference the // storage allocated in the TOC which contains the address of // 'MOSymbol'. Said TOC entry will be synthesized later. MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol, VK); const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry, MCSymbolRefExpr::VK_None, OutContext); // AIX uses the label directly as the lwz displacement operand for // references into the toc section. The displacement value will be generated // relative to the toc-base. if (IsAIX) { assert( TM.getCodeModel() == CodeModel::Small && "This pseudo should only be selected for 32-bit small code model."); Exp = getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp, VK); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); // Print MO for better readability if (isVerbose()) OutStreamer->GetCommentOS() << MO << '\n'; EmitToStreamer(*OutStreamer, TmpInst); return; } // Create an explicit subtract expression between the local symbol and // '.LTOC' to manifest the toc-relative offset. const MCExpr *PB = MCSymbolRefExpr::create( OutContext.getOrCreateSymbol(Twine(".LTOC")), OutContext); Exp = MCBinaryExpr::createSub(Exp, PB, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::ADDItoc: case PPC::ADDItoc8: { assert(IsAIX && TM.getCodeModel() == CodeModel::Small && "PseudoOp only valid for small code model AIX"); // Transform %rN = ADDItoc/8 @op1, %r2. LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to load address. TmpInst.setOpcode((!IsPPC64) ? (PPC::LA) : (PPC::LA8)); const MachineOperand &MO = MI->getOperand(1); assert(MO.isGlobal() && "Invalid operand for ADDItoc[8]."); // Map the operand to its corresponding MCSymbol. const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); const MCExpr *Exp = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_None, OutContext); TmpInst.getOperand(1) = TmpInst.getOperand(2); TmpInst.getOperand(2) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::LDtocJTI: case PPC::LDtocCPT: case PPC::LDtocBA: case PPC::LDtoc: { // Transform %x3 = LDtoc @min1, %x2 LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to LD. TmpInst.setOpcode(PPC::LD); const MachineOperand &MO = MI->getOperand(1); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand!"); // Map the operand to its corresponding MCSymbol. const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); // Map the machine operand to its corresponding MCSymbol, then map the // global address operand to be a reference to the TOC entry we will // synthesize later. MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol, VK); MCSymbolRefExpr::VariantKind VKExpr = IsAIX ? MCSymbolRefExpr::VK_None : MCSymbolRefExpr::VK_PPC_TOC; const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry, VKExpr, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr( IsAIX ? getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp, VK) : Exp); // Print MO for better readability if (isVerbose() && IsAIX) OutStreamer->GetCommentOS() << MO << '\n'; EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::ADDIStocHA: { assert((IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large) && "This pseudo should only be selected for 32-bit large code model on" " AIX."); // Transform %rd = ADDIStocHA %rA, @sym(%r2) LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to ADDIS. TmpInst.setOpcode(PPC::ADDIS); const MachineOperand &MO = MI->getOperand(2); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for ADDIStocHA."); // Map the machine operand to its corresponding MCSymbol. MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); // Always use TOC on AIX. Map the global address operand to be a reference // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to // reference the storage allocated in the TOC which contains the address of // 'MOSymbol'. MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol, VK); const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry, MCSymbolRefExpr::VK_PPC_U, OutContext); TmpInst.getOperand(2) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::LWZtocL: { assert(IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large && "This pseudo should only be selected for 32-bit large code model on" " AIX."); // Transform %rd = LWZtocL @sym, %rs. LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to lwz. TmpInst.setOpcode(PPC::LWZ); const MachineOperand &MO = MI->getOperand(1); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for LWZtocL."); // Map the machine operand to its corresponding MCSymbol. MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); // Always use TOC on AIX. Map the global address operand to be a reference // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to // reference the storage allocated in the TOC which contains the address of // 'MOSymbol'. MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol, VK); const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry, MCSymbolRefExpr::VK_PPC_L, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::ADDIStocHA8: { // Transform %xd = ADDIStocHA8 %x2, @sym LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to ADDIS8. If the global address is the address of // an external symbol, is a jump table address, is a block address, or is a // constant pool index with large code model enabled, then generate a TOC // entry and reference that. Otherwise, reference the symbol directly. TmpInst.setOpcode(PPC::ADDIS8); const MachineOperand &MO = MI->getOperand(2); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for ADDIStocHA8!"); const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); const bool GlobalToc = MO.isGlobal() && Subtarget->isGVIndirectSymbol(MO.getGlobal()); if (GlobalToc || MO.isJTI() || MO.isBlockAddress() || (MO.isCPI() && TM.getCodeModel() == CodeModel::Large)) MOSymbol = lookUpOrCreateTOCEntry(MOSymbol, VK); VK = IsAIX ? MCSymbolRefExpr::VK_PPC_U : MCSymbolRefExpr::VK_PPC_TOC_HA; const MCExpr *Exp = MCSymbolRefExpr::create(MOSymbol, VK, OutContext); if (!MO.isJTI() && MO.getOffset()) Exp = MCBinaryExpr::createAdd(Exp, MCConstantExpr::create(MO.getOffset(), OutContext), OutContext); TmpInst.getOperand(2) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::LDtocL: { // Transform %xd = LDtocL @sym, %xs LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to LD. If the global address is the address of // an external symbol, is a jump table address, is a block address, or is // a constant pool index with large code model enabled, then generate a // TOC entry and reference that. Otherwise, reference the symbol directly. TmpInst.setOpcode(PPC::LD); const MachineOperand &MO = MI->getOperand(1); assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for LDtocL!"); LLVM_DEBUG(assert( (!MO.isGlobal() || Subtarget->isGVIndirectSymbol(MO.getGlobal())) && "LDtocL used on symbol that could be accessed directly is " "invalid. Must match ADDIStocHA8.")); const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO); if (!MO.isCPI() || TM.getCodeModel() == CodeModel::Large) MOSymbol = lookUpOrCreateTOCEntry(MOSymbol, VK); VK = IsAIX ? MCSymbolRefExpr::VK_PPC_L : MCSymbolRefExpr::VK_PPC_TOC_LO; const MCExpr *Exp = MCSymbolRefExpr::create(MOSymbol, VK, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::ADDItocL: { // Transform %xd = ADDItocL %xs, @sym LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to ADDI8. If the global address is external, then // generate a TOC entry and reference that. Otherwise, reference the // symbol directly. TmpInst.setOpcode(PPC::ADDI8); const MachineOperand &MO = MI->getOperand(2); assert((MO.isGlobal() || MO.isCPI()) && "Invalid operand for ADDItocL."); LLVM_DEBUG(assert( !(MO.isGlobal() && Subtarget->isGVIndirectSymbol(MO.getGlobal())) && "Interposable definitions must use indirect access.")); const MCExpr *Exp = MCSymbolRefExpr::create(getMCSymbolForTOCPseudoMO(MO, *this), MCSymbolRefExpr::VK_PPC_TOC_LO, OutContext); TmpInst.getOperand(2) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::ADDISgotTprelHA: { // Transform: %xd = ADDISgotTprelHA %x2, @sym // Into: %xd = ADDIS8 %x2, sym@got@tlsgd@ha assert(IsPPC64 && "Not supported for 32-bit PowerPC"); const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymGotTprel = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TPREL_HA, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS8) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymGotTprel)); return; } case PPC::LDgotTprelL: case PPC::LDgotTprelL32: { // Transform %xd = LDgotTprelL @sym, %xs LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); // Change the opcode to LD. TmpInst.setOpcode(IsPPC64 ? PPC::LD : PPC::LWZ); const MachineOperand &MO = MI->getOperand(1); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *Exp = MCSymbolRefExpr::create( MOSymbol, IsPPC64 ? MCSymbolRefExpr::VK_PPC_GOT_TPREL_LO : MCSymbolRefExpr::VK_PPC_GOT_TPREL, OutContext); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } case PPC::PPC32PICGOT: { MCSymbol *GOTSymbol = OutContext.getOrCreateSymbol(StringRef("_GLOBAL_OFFSET_TABLE_")); MCSymbol *GOTRef = OutContext.createTempSymbol(); MCSymbol *NextInstr = OutContext.createTempSymbol(); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL) // FIXME: We would like an efficient form for this, so we don't have to do // a lot of extra uniquing. .addExpr(MCSymbolRefExpr::create(NextInstr, OutContext))); const MCExpr *OffsExpr = MCBinaryExpr::createSub(MCSymbolRefExpr::create(GOTSymbol, OutContext), MCSymbolRefExpr::create(GOTRef, OutContext), OutContext); OutStreamer->emitLabel(GOTRef); OutStreamer->emitValue(OffsExpr, 4); OutStreamer->emitLabel(NextInstr); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MFLR) .addReg(MI->getOperand(0).getReg())); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LWZ) .addReg(MI->getOperand(1).getReg()) .addImm(0) .addReg(MI->getOperand(0).getReg())); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADD4) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addReg(MI->getOperand(0).getReg())); return; } case PPC::PPC32GOT: { MCSymbol *GOTSymbol = OutContext.getOrCreateSymbol(StringRef("_GLOBAL_OFFSET_TABLE_")); const MCExpr *SymGotTlsL = MCSymbolRefExpr::create( GOTSymbol, MCSymbolRefExpr::VK_PPC_LO, OutContext); const MCExpr *SymGotTlsHA = MCSymbolRefExpr::create( GOTSymbol, MCSymbolRefExpr::VK_PPC_HA, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LI) .addReg(MI->getOperand(0).getReg()) .addExpr(SymGotTlsL)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(0).getReg()) .addExpr(SymGotTlsHA)); return; } case PPC::ADDIStlsgdHA: { // Transform: %xd = ADDIStlsgdHA %x2, @sym // Into: %xd = ADDIS8 %x2, sym@got@tlsgd@ha assert(IsPPC64 && "Not supported for 32-bit PowerPC"); const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymGotTlsGD = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TLSGD_HA, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS8) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymGotTlsGD)); return; } case PPC::ADDItlsgdL: // Transform: %xd = ADDItlsgdL %xs, @sym // Into: %xd = ADDI8 %xs, sym@got@tlsgd@l case PPC::ADDItlsgdL32: { // Transform: %rd = ADDItlsgdL32 %rs, @sym // Into: %rd = ADDI %rs, sym@got@tlsgd const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymGotTlsGD = MCSymbolRefExpr::create( MOSymbol, IsPPC64 ? MCSymbolRefExpr::VK_PPC_GOT_TLSGD_LO : MCSymbolRefExpr::VK_PPC_GOT_TLSGD, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(IsPPC64 ? PPC::ADDI8 : PPC::ADDI) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymGotTlsGD)); return; } case PPC::GETtlsADDR: // Transform: %x3 = GETtlsADDR %x3, @sym // Into: BL8_NOP_TLS __tls_get_addr(sym at tlsgd) case PPC::GETtlsADDRPCREL: case PPC::GETtlsADDR32AIX: case PPC::GETtlsADDR64AIX: // Transform: %r3 = GETtlsADDRNNAIX %r3, %r4 (for NN == 32/64). // Into: BLA .__tls_get_addr() // Unlike on Linux, there is no symbol or relocation needed for this call. case PPC::GETtlsADDR32: { // Transform: %r3 = GETtlsADDR32 %r3, @sym // Into: BL_TLS __tls_get_addr(sym at tlsgd)@PLT EmitTlsCall(MI, MCSymbolRefExpr::VK_PPC_TLSGD); return; } case PPC::ADDIStlsldHA: { // Transform: %xd = ADDIStlsldHA %x2, @sym // Into: %xd = ADDIS8 %x2, sym@got@tlsld@ha assert(IsPPC64 && "Not supported for 32-bit PowerPC"); const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymGotTlsLD = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_GOT_TLSLD_HA, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS8) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymGotTlsLD)); return; } case PPC::ADDItlsldL: // Transform: %xd = ADDItlsldL %xs, @sym // Into: %xd = ADDI8 %xs, sym@got@tlsld@l case PPC::ADDItlsldL32: { // Transform: %rd = ADDItlsldL32 %rs, @sym // Into: %rd = ADDI %rs, sym@got@tlsld const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymGotTlsLD = MCSymbolRefExpr::create( MOSymbol, IsPPC64 ? MCSymbolRefExpr::VK_PPC_GOT_TLSLD_LO : MCSymbolRefExpr::VK_PPC_GOT_TLSLD, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(IsPPC64 ? PPC::ADDI8 : PPC::ADDI) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymGotTlsLD)); return; } case PPC::GETtlsldADDR: // Transform: %x3 = GETtlsldADDR %x3, @sym // Into: BL8_NOP_TLS __tls_get_addr(sym at tlsld) case PPC::GETtlsldADDRPCREL: case PPC::GETtlsldADDR32: { // Transform: %r3 = GETtlsldADDR32 %r3, @sym // Into: BL_TLS __tls_get_addr(sym at tlsld)@PLT EmitTlsCall(MI, MCSymbolRefExpr::VK_PPC_TLSLD); return; } case PPC::ADDISdtprelHA: // Transform: %xd = ADDISdtprelHA %xs, @sym // Into: %xd = ADDIS8 %xs, sym@dtprel@ha case PPC::ADDISdtprelHA32: { // Transform: %rd = ADDISdtprelHA32 %rs, @sym // Into: %rd = ADDIS %rs, sym@dtprel@ha const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymDtprel = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_DTPREL_HA, OutContext); EmitToStreamer( *OutStreamer, MCInstBuilder(IsPPC64 ? PPC::ADDIS8 : PPC::ADDIS) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymDtprel)); return; } case PPC::PADDIdtprel: { // Transform: %rd = PADDIdtprel %rs, @sym // Into: %rd = PADDI8 %rs, sym@dtprel const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymDtprel = MCSymbolRefExpr::create( MOSymbol, MCSymbolRefExpr::VK_DTPREL, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::PADDI8) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymDtprel)); return; } case PPC::ADDIdtprelL: // Transform: %xd = ADDIdtprelL %xs, @sym // Into: %xd = ADDI8 %xs, sym@dtprel@l case PPC::ADDIdtprelL32: { // Transform: %rd = ADDIdtprelL32 %rs, @sym // Into: %rd = ADDI %rs, sym@dtprel@l const MachineOperand &MO = MI->getOperand(2); const GlobalValue *GValue = MO.getGlobal(); MCSymbol *MOSymbol = getSymbol(GValue); const MCExpr *SymDtprel = MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_PPC_DTPREL_LO, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(IsPPC64 ? PPC::ADDI8 : PPC::ADDI) .addReg(MI->getOperand(0).getReg()) .addReg(MI->getOperand(1).getReg()) .addExpr(SymDtprel)); return; } case PPC::MFOCRF: case PPC::MFOCRF8: if (!Subtarget->hasMFOCRF()) { // Transform: %r3 = MFOCRF %cr7 // Into: %r3 = MFCR ;; cr7 unsigned NewOpcode = MI->getOpcode() == PPC::MFOCRF ? PPC::MFCR : PPC::MFCR8; OutStreamer->AddComment(PPCInstPrinter:: getRegisterName(MI->getOperand(1).getReg())); EmitToStreamer(*OutStreamer, MCInstBuilder(NewOpcode) .addReg(MI->getOperand(0).getReg())); return; } break; case PPC::MTOCRF: case PPC::MTOCRF8: if (!Subtarget->hasMFOCRF()) { // Transform: %cr7 = MTOCRF %r3 // Into: MTCRF mask, %r3 ;; cr7 unsigned NewOpcode = MI->getOpcode() == PPC::MTOCRF ? PPC::MTCRF : PPC::MTCRF8; unsigned Mask = 0x80 >> OutContext.getRegisterInfo() ->getEncodingValue(MI->getOperand(0).getReg()); OutStreamer->AddComment(PPCInstPrinter:: getRegisterName(MI->getOperand(0).getReg())); EmitToStreamer(*OutStreamer, MCInstBuilder(NewOpcode) .addImm(Mask) .addReg(MI->getOperand(1).getReg())); return; } break; case PPC::LD: case PPC::STD: case PPC::LWA_32: case PPC::LWA: { // Verify alignment is legal, so we don't create relocations // that can't be supported. unsigned OpNum = (MI->getOpcode() == PPC::STD) ? 2 : 1; const MachineOperand &MO = MI->getOperand(OpNum); if (MO.isGlobal()) { const DataLayout &DL = MO.getGlobal()->getParent()->getDataLayout(); if (MO.getGlobal()->getPointerAlignment(DL) < 4) llvm_unreachable("Global must be word-aligned for LD, STD, LWA!"); } // Now process the instruction normally. break; } case PPC::PseudoEIEIO: { EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::ORI).addReg(PPC::X2).addReg(PPC::X2).addImm(0)); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::ORI).addReg(PPC::X2).addReg(PPC::X2).addImm(0)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::EnforceIEIO)); return; } } LowerPPCMachineInstrToMCInst(MI, TmpInst, *this); EmitToStreamer(*OutStreamer, TmpInst); } void PPCLinuxAsmPrinter::emitGNUAttributes(Module &M) { // Emit float ABI into GNU attribute Metadata *MD = M.getModuleFlag("float-abi"); MDString *FloatABI = dyn_cast_or_null(MD); if (!FloatABI) return; StringRef flt = FloatABI->getString(); // TODO: Support emitting soft-fp and hard double/single attributes. if (flt == "doubledouble") OutStreamer->emitGNUAttribute(Tag_GNU_Power_ABI_FP, Val_GNU_Power_ABI_HardFloat_DP | Val_GNU_Power_ABI_LDBL_IBM128); else if (flt == "ieeequad") OutStreamer->emitGNUAttribute(Tag_GNU_Power_ABI_FP, Val_GNU_Power_ABI_HardFloat_DP | Val_GNU_Power_ABI_LDBL_IEEE128); else if (flt == "ieeedouble") OutStreamer->emitGNUAttribute(Tag_GNU_Power_ABI_FP, Val_GNU_Power_ABI_HardFloat_DP | Val_GNU_Power_ABI_LDBL_64); } void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) { if (!Subtarget->isPPC64()) return PPCAsmPrinter::emitInstruction(MI); switch (MI->getOpcode()) { default: return PPCAsmPrinter::emitInstruction(MI); case TargetOpcode::PATCHABLE_FUNCTION_ENTER: { // .begin: // b .end # lis 0, FuncId[16..32] // nop # li 0, FuncId[0..15] // std 0, -8(1) // mflr 0 // bl __xray_FunctionEntry // mtlr 0 // .end: // // Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number // of instructions change. MCSymbol *BeginOfSled = OutContext.createTempSymbol(); MCSymbol *EndOfSled = OutContext.createTempSymbol(); OutStreamer->emitLabel(BeginOfSled); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::B).addExpr( MCSymbolRefExpr::create(EndOfSled, OutContext))); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::NOP)); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::STD).addReg(PPC::X0).addImm(-8).addReg(PPC::X1)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MFLR8).addReg(PPC::X0)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL8_NOP) .addExpr(MCSymbolRefExpr::create( OutContext.getOrCreateSymbol("__xray_FunctionEntry"), OutContext))); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MTLR8).addReg(PPC::X0)); OutStreamer->emitLabel(EndOfSled); recordSled(BeginOfSled, *MI, SledKind::FUNCTION_ENTER, 2); break; } case TargetOpcode::PATCHABLE_RET: { unsigned RetOpcode = MI->getOperand(0).getImm(); MCInst RetInst; RetInst.setOpcode(RetOpcode); for (const auto &MO : llvm::drop_begin(MI->operands())) { MCOperand MCOp; if (LowerPPCMachineOperandToMCOperand(MO, MCOp, *this)) RetInst.addOperand(MCOp); } bool IsConditional; if (RetOpcode == PPC::BCCLR) { IsConditional = true; } else if (RetOpcode == PPC::TCRETURNdi8 || RetOpcode == PPC::TCRETURNri8 || RetOpcode == PPC::TCRETURNai8) { break; } else if (RetOpcode == PPC::BLR8 || RetOpcode == PPC::TAILB8) { IsConditional = false; } else { EmitToStreamer(*OutStreamer, RetInst); break; } MCSymbol *FallthroughLabel; if (IsConditional) { // Before: // bgtlr cr0 // // After: // ble cr0, .end // .p2align 3 // .begin: // blr # lis 0, FuncId[16..32] // nop # li 0, FuncId[0..15] // std 0, -8(1) // mflr 0 // bl __xray_FunctionExit // mtlr 0 // blr // .end: // // Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number // of instructions change. FallthroughLabel = OutContext.createTempSymbol(); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::BCC) .addImm(PPC::InvertPredicate( static_cast(MI->getOperand(1).getImm()))) .addReg(MI->getOperand(2).getReg()) .addExpr(MCSymbolRefExpr::create(FallthroughLabel, OutContext))); RetInst = MCInst(); RetInst.setOpcode(PPC::BLR8); } // .p2align 3 // .begin: // b(lr)? # lis 0, FuncId[16..32] // nop # li 0, FuncId[0..15] // std 0, -8(1) // mflr 0 // bl __xray_FunctionExit // mtlr 0 // b(lr)? // // Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number // of instructions change. OutStreamer->emitCodeAlignment(8, &getSubtargetInfo()); MCSymbol *BeginOfSled = OutContext.createTempSymbol(); OutStreamer->emitLabel(BeginOfSled); EmitToStreamer(*OutStreamer, RetInst); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::NOP)); EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::STD).addReg(PPC::X0).addImm(-8).addReg(PPC::X1)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MFLR8).addReg(PPC::X0)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::BL8_NOP) .addExpr(MCSymbolRefExpr::create( OutContext.getOrCreateSymbol("__xray_FunctionExit"), OutContext))); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MTLR8).addReg(PPC::X0)); EmitToStreamer(*OutStreamer, RetInst); if (IsConditional) OutStreamer->emitLabel(FallthroughLabel); recordSled(BeginOfSled, *MI, SledKind::FUNCTION_EXIT, 2); break; } case TargetOpcode::PATCHABLE_FUNCTION_EXIT: llvm_unreachable("PATCHABLE_FUNCTION_EXIT should never be emitted"); case TargetOpcode::PATCHABLE_TAIL_CALL: // TODO: Define a trampoline `__xray_FunctionTailExit` and differentiate a // normal function exit from a tail exit. llvm_unreachable("Tail call is handled in the normal case. See comments " "around this assert."); } } void PPCLinuxAsmPrinter::emitStartOfAsmFile(Module &M) { if (static_cast(TM).isELFv2ABI()) { PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); if (TS) TS->emitAbiVersion(2); } if (static_cast(TM).isPPC64() || !isPositionIndependent()) return AsmPrinter::emitStartOfAsmFile(M); if (M.getPICLevel() == PICLevel::SmallPIC) return AsmPrinter::emitStartOfAsmFile(M); OutStreamer->SwitchSection(OutContext.getELFSection( ".got2", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC)); MCSymbol *TOCSym = OutContext.getOrCreateSymbol(Twine(".LTOC")); MCSymbol *CurrentPos = OutContext.createTempSymbol(); OutStreamer->emitLabel(CurrentPos); // The GOT pointer points to the middle of the GOT, in order to reference the // entire 64kB range. 0x8000 is the midpoint. const MCExpr *tocExpr = MCBinaryExpr::createAdd(MCSymbolRefExpr::create(CurrentPos, OutContext), MCConstantExpr::create(0x8000, OutContext), OutContext); OutStreamer->emitAssignment(TOCSym, tocExpr); OutStreamer->SwitchSection(getObjFileLowering().getTextSection()); } void PPCLinuxAsmPrinter::emitFunctionEntryLabel() { // linux/ppc32 - Normal entry label. if (!Subtarget->isPPC64() && (!isPositionIndependent() || MF->getFunction().getParent()->getPICLevel() == PICLevel::SmallPIC)) return AsmPrinter::emitFunctionEntryLabel(); if (!Subtarget->isPPC64()) { const PPCFunctionInfo *PPCFI = MF->getInfo(); if (PPCFI->usesPICBase() && !Subtarget->isSecurePlt()) { MCSymbol *RelocSymbol = PPCFI->getPICOffsetSymbol(*MF); MCSymbol *PICBase = MF->getPICBaseSymbol(); OutStreamer->emitLabel(RelocSymbol); const MCExpr *OffsExpr = MCBinaryExpr::createSub( MCSymbolRefExpr::create(OutContext.getOrCreateSymbol(Twine(".LTOC")), OutContext), MCSymbolRefExpr::create(PICBase, OutContext), OutContext); OutStreamer->emitValue(OffsExpr, 4); OutStreamer->emitLabel(CurrentFnSym); return; } else return AsmPrinter::emitFunctionEntryLabel(); } // ELFv2 ABI - Normal entry label. if (Subtarget->isELFv2ABI()) { // In the Large code model, we allow arbitrary displacements between // the text section and its associated TOC section. We place the // full 8-byte offset to the TOC in memory immediately preceding // the function global entry point. if (TM.getCodeModel() == CodeModel::Large && !MF->getRegInfo().use_empty(PPC::X2)) { const PPCFunctionInfo *PPCFI = MF->getInfo(); MCSymbol *TOCSymbol = OutContext.getOrCreateSymbol(StringRef(".TOC.")); MCSymbol *GlobalEPSymbol = PPCFI->getGlobalEPSymbol(*MF); const MCExpr *TOCDeltaExpr = MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCSymbol, OutContext), MCSymbolRefExpr::create(GlobalEPSymbol, OutContext), OutContext); OutStreamer->emitLabel(PPCFI->getTOCOffsetSymbol(*MF)); OutStreamer->emitValue(TOCDeltaExpr, 8); } return AsmPrinter::emitFunctionEntryLabel(); } // Emit an official procedure descriptor. MCSectionSubPair Current = OutStreamer->getCurrentSection(); MCSectionELF *Section = OutStreamer->getContext().getELFSection( ".opd", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); OutStreamer->SwitchSection(Section); OutStreamer->emitLabel(CurrentFnSym); OutStreamer->emitValueToAlignment(8); MCSymbol *Symbol1 = CurrentFnSymForSize; // Generates a R_PPC64_ADDR64 (from FK_DATA_8) relocation for the function // entry point. OutStreamer->emitValue(MCSymbolRefExpr::create(Symbol1, OutContext), 8 /*size*/); MCSymbol *Symbol2 = OutContext.getOrCreateSymbol(StringRef(".TOC.")); // Generates a R_PPC64_TOC relocation for TOC base insertion. OutStreamer->emitValue( MCSymbolRefExpr::create(Symbol2, MCSymbolRefExpr::VK_PPC_TOCBASE, OutContext), 8/*size*/); // Emit a null environment pointer. OutStreamer->emitIntValue(0, 8 /* size */); OutStreamer->SwitchSection(Current.first, Current.second); } void PPCLinuxAsmPrinter::emitEndOfAsmFile(Module &M) { const DataLayout &DL = getDataLayout(); bool isPPC64 = DL.getPointerSizeInBits() == 64; PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); emitGNUAttributes(M); if (!TOC.empty()) { const char *Name = isPPC64 ? ".toc" : ".got2"; MCSectionELF *Section = OutContext.getELFSection( Name, ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); OutStreamer->SwitchSection(Section); if (!isPPC64) OutStreamer->emitValueToAlignment(4); for (const auto &TOCMapPair : TOC) { const MCSymbol *const TOCEntryTarget = TOCMapPair.first.first; MCSymbol *const TOCEntryLabel = TOCMapPair.second; OutStreamer->emitLabel(TOCEntryLabel); if (isPPC64 && TS != nullptr) TS->emitTCEntry(*TOCEntryTarget, TOCMapPair.first.second); else OutStreamer->emitSymbolValue(TOCEntryTarget, 4); } } PPCAsmPrinter::emitEndOfAsmFile(M); } /// EmitFunctionBodyStart - Emit a global entry point prefix for ELFv2. void PPCLinuxAsmPrinter::emitFunctionBodyStart() { // In the ELFv2 ABI, in functions that use the TOC register, we need to // provide two entry points. The ABI guarantees that when calling the // local entry point, r2 is set up by the caller to contain the TOC base // for this function, and when calling the global entry point, r12 is set // up by the caller to hold the address of the global entry point. We // thus emit a prefix sequence along the following lines: // // func: // .Lfunc_gepNN: // # global entry point // addis r2,r12,(.TOC.-.Lfunc_gepNN)@ha // addi r2,r2,(.TOC.-.Lfunc_gepNN)@l // .Lfunc_lepNN: // .localentry func, .Lfunc_lepNN-.Lfunc_gepNN // # local entry point, followed by function body // // For the Large code model, we create // // .Lfunc_tocNN: // .quad .TOC.-.Lfunc_gepNN # done by EmitFunctionEntryLabel // func: // .Lfunc_gepNN: // # global entry point // ld r2,.Lfunc_tocNN-.Lfunc_gepNN(r12) // add r2,r2,r12 // .Lfunc_lepNN: // .localentry func, .Lfunc_lepNN-.Lfunc_gepNN // # local entry point, followed by function body // // This ensures we have r2 set up correctly while executing the function // body, no matter which entry point is called. const PPCFunctionInfo *PPCFI = MF->getInfo(); const bool UsesX2OrR2 = !MF->getRegInfo().use_empty(PPC::X2) || !MF->getRegInfo().use_empty(PPC::R2); const bool PCrelGEPRequired = Subtarget->isUsingPCRelativeCalls() && UsesX2OrR2 && PPCFI->usesTOCBasePtr(); const bool NonPCrelGEPRequired = !Subtarget->isUsingPCRelativeCalls() && Subtarget->isELFv2ABI() && UsesX2OrR2; // Only do all that if the function uses R2 as the TOC pointer // in the first place. We don't need the global entry point if the // function uses R2 as an allocatable register. if (NonPCrelGEPRequired || PCrelGEPRequired) { // Note: The logic here must be synchronized with the code in the // branch-selection pass which sets the offset of the first block in the // function. This matters because it affects the alignment. MCSymbol *GlobalEntryLabel = PPCFI->getGlobalEPSymbol(*MF); OutStreamer->emitLabel(GlobalEntryLabel); const MCSymbolRefExpr *GlobalEntryLabelExp = MCSymbolRefExpr::create(GlobalEntryLabel, OutContext); if (TM.getCodeModel() != CodeModel::Large) { MCSymbol *TOCSymbol = OutContext.getOrCreateSymbol(StringRef(".TOC.")); const MCExpr *TOCDeltaExpr = MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCSymbol, OutContext), GlobalEntryLabelExp, OutContext); const MCExpr *TOCDeltaHi = PPCMCExpr::createHa(TOCDeltaExpr, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDIS) .addReg(PPC::X2) .addReg(PPC::X12) .addExpr(TOCDeltaHi)); const MCExpr *TOCDeltaLo = PPCMCExpr::createLo(TOCDeltaExpr, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADDI) .addReg(PPC::X2) .addReg(PPC::X2) .addExpr(TOCDeltaLo)); } else { MCSymbol *TOCOffset = PPCFI->getTOCOffsetSymbol(*MF); const MCExpr *TOCOffsetDeltaExpr = MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCOffset, OutContext), GlobalEntryLabelExp, OutContext); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::LD) .addReg(PPC::X2) .addExpr(TOCOffsetDeltaExpr) .addReg(PPC::X12)); EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::ADD8) .addReg(PPC::X2) .addReg(PPC::X2) .addReg(PPC::X12)); } MCSymbol *LocalEntryLabel = PPCFI->getLocalEPSymbol(*MF); OutStreamer->emitLabel(LocalEntryLabel); const MCSymbolRefExpr *LocalEntryLabelExp = MCSymbolRefExpr::create(LocalEntryLabel, OutContext); const MCExpr *LocalOffsetExp = MCBinaryExpr::createSub(LocalEntryLabelExp, GlobalEntryLabelExp, OutContext); PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); if (TS) TS->emitLocalEntry(cast(CurrentFnSym), LocalOffsetExp); } else if (Subtarget->isUsingPCRelativeCalls()) { // When generating the entry point for a function we have a few scenarios // based on whether or not that function uses R2 and whether or not that // function makes calls (or is a leaf function). // 1) A leaf function that does not use R2 (or treats it as callee-saved // and preserves it). In this case st_other=0 and both // the local and global entry points for the function are the same. // No special entry point code is required. // 2) A function uses the TOC pointer R2. This function may or may not have // calls. In this case st_other=[2,6] and the global and local entry // points are different. Code to correctly setup the TOC pointer in R2 // is put between the global and local entry points. This case is // covered by the if statatement above. // 3) A function does not use the TOC pointer R2 but does have calls. // In this case st_other=1 since we do not know whether or not any // of the callees clobber R2. This case is dealt with in this else if // block. Tail calls are considered calls and the st_other should also // be set to 1 in that case as well. // 4) The function does not use the TOC pointer but R2 is used inside // the function. In this case st_other=1 once again. // 5) This function uses inline asm. We mark R2 as reserved if the function // has inline asm as we have to assume that it may be used. if (MF->getFrameInfo().hasCalls() || MF->getFrameInfo().hasTailCall() || MF->hasInlineAsm() || (!PPCFI->usesTOCBasePtr() && UsesX2OrR2)) { PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); if (TS) TS->emitLocalEntry(cast(CurrentFnSym), MCConstantExpr::create(1, OutContext)); } } } /// EmitFunctionBodyEnd - Print the traceback table before the .size /// directive. /// void PPCLinuxAsmPrinter::emitFunctionBodyEnd() { // Only the 64-bit target requires a traceback table. For now, // we only emit the word of zeroes that GDB requires to find // the end of the function, and zeroes for the eight-byte // mandatory fields. // FIXME: We should fill in the eight-byte mandatory fields as described in // the PPC64 ELF ABI (this is a low-priority item because GDB does not // currently make use of these fields). if (Subtarget->isPPC64()) { OutStreamer->emitIntValue(0, 4/*size*/); OutStreamer->emitIntValue(0, 8/*size*/); } } void PPCAIXAsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const { assert(MAI->hasVisibilityOnlyWithLinkage() && "AIX's linkage directives take a visibility setting."); MCSymbolAttr LinkageAttr = MCSA_Invalid; switch (GV->getLinkage()) { case GlobalValue::ExternalLinkage: LinkageAttr = GV->isDeclaration() ? MCSA_Extern : MCSA_Global; break; case GlobalValue::LinkOnceAnyLinkage: case GlobalValue::LinkOnceODRLinkage: case GlobalValue::WeakAnyLinkage: case GlobalValue::WeakODRLinkage: case GlobalValue::ExternalWeakLinkage: LinkageAttr = MCSA_Weak; break; case GlobalValue::AvailableExternallyLinkage: LinkageAttr = MCSA_Extern; break; case GlobalValue::PrivateLinkage: return; case GlobalValue::InternalLinkage: assert(GV->getVisibility() == GlobalValue::DefaultVisibility && "InternalLinkage should not have other visibility setting."); LinkageAttr = MCSA_LGlobal; break; case GlobalValue::AppendingLinkage: llvm_unreachable("Should never emit this"); case GlobalValue::CommonLinkage: llvm_unreachable("CommonLinkage of XCOFF should not come to this path"); } assert(LinkageAttr != MCSA_Invalid && "LinkageAttr should not MCSA_Invalid."); MCSymbolAttr VisibilityAttr = MCSA_Invalid; if (!TM.getIgnoreXCOFFVisibility()) { switch (GV->getVisibility()) { // TODO: "exported" and "internal" Visibility needs to go here. case GlobalValue::DefaultVisibility: break; case GlobalValue::HiddenVisibility: VisibilityAttr = MAI->getHiddenVisibilityAttr(); break; case GlobalValue::ProtectedVisibility: VisibilityAttr = MAI->getProtectedVisibilityAttr(); break; } } OutStreamer->emitXCOFFSymbolLinkageWithVisibility(GVSym, LinkageAttr, VisibilityAttr); } void PPCAIXAsmPrinter::SetupMachineFunction(MachineFunction &MF) { // Setup CurrentFnDescSym and its containing csect. MCSectionXCOFF *FnDescSec = cast(getObjFileLowering().getSectionForFunctionDescriptor( &MF.getFunction(), TM)); FnDescSec->setAlignment(Align(Subtarget->isPPC64() ? 8 : 4)); CurrentFnDescSym = FnDescSec->getQualNameSymbol(); return AsmPrinter::SetupMachineFunction(MF); } uint16_t PPCAIXAsmPrinter::getNumberOfVRSaved() { // Calculate the number of VRs be saved. // Vector registers 20 through 31 are marked as reserved and cannot be used // in the default ABI. const PPCSubtarget &Subtarget = MF->getSubtarget(); if (Subtarget.isAIXABI() && Subtarget.hasAltivec() && TM.getAIXExtendedAltivecABI()) { const MachineRegisterInfo &MRI = MF->getRegInfo(); for (unsigned Reg = PPC::V20; Reg <= PPC::V31; ++Reg) if (MRI.isPhysRegModified(Reg)) // Number of VRs saved. return PPC::V31 - Reg + 1; } return 0; } void PPCAIXAsmPrinter::emitFunctionBodyEnd() { if (!TM.getXCOFFTracebackTable()) return; emitTracebackTable(); // If ShouldEmitEHBlock returns true, then the eh info table // will be emitted via `AIXException::endFunction`. Otherwise, we // need to emit a dumy eh info table when VRs are saved. We could not // consolidate these two places into one because there is no easy way // to access register information in `AIXException` class. if (!TargetLoweringObjectFileXCOFF::ShouldEmitEHBlock(MF) && (getNumberOfVRSaved() > 0)) { // Emit dummy EH Info Table. OutStreamer->SwitchSection(getObjFileLowering().getCompactUnwindSection()); MCSymbol *EHInfoLabel = TargetLoweringObjectFileXCOFF::getEHInfoTableSymbol(MF); OutStreamer->emitLabel(EHInfoLabel); // Version number. OutStreamer->emitInt32(0); const DataLayout &DL = MMI->getModule()->getDataLayout(); const unsigned PointerSize = DL.getPointerSize(); // Add necessary paddings in 64 bit mode. OutStreamer->emitValueToAlignment(PointerSize); OutStreamer->emitIntValue(0, PointerSize); OutStreamer->emitIntValue(0, PointerSize); OutStreamer->SwitchSection(MF->getSection()); } } void PPCAIXAsmPrinter::emitTracebackTable() { // Create a symbol for the end of function. MCSymbol *FuncEnd = createTempSymbol(MF->getName()); OutStreamer->emitLabel(FuncEnd); OutStreamer->AddComment("Traceback table begin"); // Begin with a fullword of zero. OutStreamer->emitIntValueInHexWithPadding(0, 4 /*size*/); SmallString<128> CommentString; raw_svector_ostream CommentOS(CommentString); auto EmitComment = [&]() { OutStreamer->AddComment(CommentOS.str()); CommentString.clear(); }; auto EmitCommentAndValue = [&](uint64_t Value, int Size) { EmitComment(); OutStreamer->emitIntValueInHexWithPadding(Value, Size); }; unsigned int Version = 0; CommentOS << "Version = " << Version; EmitCommentAndValue(Version, 1); // There is a lack of information in the IR to assist with determining the // source language. AIX exception handling mechanism would only search for // personality routine and LSDA area when such language supports exception // handling. So to be conservatively correct and allow runtime to do its job, // we need to set it to C++ for now. TracebackTable::LanguageID LanguageIdentifier = TracebackTable::CPlusPlus; // C++ CommentOS << "Language = " << getNameForTracebackTableLanguageId(LanguageIdentifier); EmitCommentAndValue(LanguageIdentifier, 1); // This is only populated for the third and fourth bytes. uint32_t FirstHalfOfMandatoryField = 0; // Emit the 3rd byte of the mandatory field. // We always set traceback offset bit to true. FirstHalfOfMandatoryField |= TracebackTable::HasTraceBackTableOffsetMask; const PPCFunctionInfo *FI = MF->getInfo(); const MachineRegisterInfo &MRI = MF->getRegInfo(); // Check the function uses floating-point processor instructions or not for (unsigned Reg = PPC::F0; Reg <= PPC::F31; ++Reg) { if (MRI.isPhysRegUsed(Reg, /* SkipRegMaskTest */ true)) { FirstHalfOfMandatoryField |= TracebackTable::IsFloatingPointPresentMask; break; } } #define GENBOOLCOMMENT(Prefix, V, Field) \ CommentOS << (Prefix) << ((V) & (TracebackTable::Field##Mask) ? "+" : "-") \ << #Field #define GENVALUECOMMENT(PrefixAndName, V, Field) \ CommentOS << (PrefixAndName) << " = " \ << static_cast(((V) & (TracebackTable::Field##Mask)) >> \ (TracebackTable::Field##Shift)) GENBOOLCOMMENT("", FirstHalfOfMandatoryField, IsGlobaLinkage); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsOutOfLineEpilogOrPrologue); EmitComment(); GENBOOLCOMMENT("", FirstHalfOfMandatoryField, HasTraceBackTableOffset); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsInternalProcedure); EmitComment(); GENBOOLCOMMENT("", FirstHalfOfMandatoryField, HasControlledStorage); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsTOCless); EmitComment(); GENBOOLCOMMENT("", FirstHalfOfMandatoryField, IsFloatingPointPresent); EmitComment(); GENBOOLCOMMENT("", FirstHalfOfMandatoryField, IsFloatingPointOperationLogOrAbortEnabled); EmitComment(); OutStreamer->emitIntValueInHexWithPadding( (FirstHalfOfMandatoryField & 0x0000ff00) >> 8, 1); // Set the 4th byte of the mandatory field. FirstHalfOfMandatoryField |= TracebackTable::IsFunctionNamePresentMask; const PPCRegisterInfo *RegInfo = static_cast(Subtarget->getRegisterInfo()); Register FrameReg = RegInfo->getFrameRegister(*MF); if (FrameReg == (Subtarget->isPPC64() ? PPC::X31 : PPC::R31)) FirstHalfOfMandatoryField |= TracebackTable::IsAllocaUsedMask; const SmallVectorImpl &MustSaveCRs = FI->getMustSaveCRs(); if (!MustSaveCRs.empty()) FirstHalfOfMandatoryField |= TracebackTable::IsCRSavedMask; if (FI->mustSaveLR()) FirstHalfOfMandatoryField |= TracebackTable::IsLRSavedMask; GENBOOLCOMMENT("", FirstHalfOfMandatoryField, IsInterruptHandler); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsFunctionNamePresent); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsAllocaUsed); EmitComment(); GENVALUECOMMENT("OnConditionDirective", FirstHalfOfMandatoryField, OnConditionDirective); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsCRSaved); GENBOOLCOMMENT(", ", FirstHalfOfMandatoryField, IsLRSaved); EmitComment(); OutStreamer->emitIntValueInHexWithPadding((FirstHalfOfMandatoryField & 0xff), 1); // Set the 5th byte of mandatory field. uint32_t SecondHalfOfMandatoryField = 0; // Always store back chain. SecondHalfOfMandatoryField |= TracebackTable::IsBackChainStoredMask; uint32_t FPRSaved = 0; for (unsigned Reg = PPC::F14; Reg <= PPC::F31; ++Reg) { if (MRI.isPhysRegModified(Reg)) { FPRSaved = PPC::F31 - Reg + 1; break; } } SecondHalfOfMandatoryField |= (FPRSaved << TracebackTable::FPRSavedShift) & TracebackTable::FPRSavedMask; GENBOOLCOMMENT("", SecondHalfOfMandatoryField, IsBackChainStored); GENBOOLCOMMENT(", ", SecondHalfOfMandatoryField, IsFixup); GENVALUECOMMENT(", NumOfFPRsSaved", SecondHalfOfMandatoryField, FPRSaved); EmitComment(); OutStreamer->emitIntValueInHexWithPadding( (SecondHalfOfMandatoryField & 0xff000000) >> 24, 1); // Set the 6th byte of mandatory field. // Check whether has Vector Instruction,We only treat instructions uses vector // register as vector instructions. bool HasVectorInst = false; for (unsigned Reg = PPC::V0; Reg <= PPC::V31; ++Reg) if (MRI.isPhysRegUsed(Reg, /* SkipRegMaskTest */ true)) { // Has VMX instruction. HasVectorInst = true; break; } if (FI->hasVectorParms() || HasVectorInst) SecondHalfOfMandatoryField |= TracebackTable::HasVectorInfoMask; uint16_t NumOfVRSaved = getNumberOfVRSaved(); bool ShouldEmitEHBlock = TargetLoweringObjectFileXCOFF::ShouldEmitEHBlock(MF) || NumOfVRSaved > 0; if (ShouldEmitEHBlock) SecondHalfOfMandatoryField |= TracebackTable::HasExtensionTableMask; uint32_t GPRSaved = 0; // X13 is reserved under 64-bit environment. unsigned GPRBegin = Subtarget->isPPC64() ? PPC::X14 : PPC::R13; unsigned GPREnd = Subtarget->isPPC64() ? PPC::X31 : PPC::R31; for (unsigned Reg = GPRBegin; Reg <= GPREnd; ++Reg) { if (MRI.isPhysRegModified(Reg)) { GPRSaved = GPREnd - Reg + 1; break; } } SecondHalfOfMandatoryField |= (GPRSaved << TracebackTable::GPRSavedShift) & TracebackTable::GPRSavedMask; GENBOOLCOMMENT("", SecondHalfOfMandatoryField, HasExtensionTable); GENBOOLCOMMENT(", ", SecondHalfOfMandatoryField, HasVectorInfo); GENVALUECOMMENT(", NumOfGPRsSaved", SecondHalfOfMandatoryField, GPRSaved); EmitComment(); OutStreamer->emitIntValueInHexWithPadding( (SecondHalfOfMandatoryField & 0x00ff0000) >> 16, 1); // Set the 7th byte of mandatory field. uint32_t NumberOfFixedParms = FI->getFixedParmsNum(); SecondHalfOfMandatoryField |= (NumberOfFixedParms << TracebackTable::NumberOfFixedParmsShift) & TracebackTable::NumberOfFixedParmsMask; GENVALUECOMMENT("NumberOfFixedParms", SecondHalfOfMandatoryField, NumberOfFixedParms); EmitComment(); OutStreamer->emitIntValueInHexWithPadding( (SecondHalfOfMandatoryField & 0x0000ff00) >> 8, 1); // Set the 8th byte of mandatory field. // Always set parameter on stack. SecondHalfOfMandatoryField |= TracebackTable::HasParmsOnStackMask; uint32_t NumberOfFPParms = FI->getFloatingPointParmsNum(); SecondHalfOfMandatoryField |= (NumberOfFPParms << TracebackTable::NumberOfFloatingPointParmsShift) & TracebackTable::NumberOfFloatingPointParmsMask; GENVALUECOMMENT("NumberOfFPParms", SecondHalfOfMandatoryField, NumberOfFloatingPointParms); GENBOOLCOMMENT(", ", SecondHalfOfMandatoryField, HasParmsOnStack); EmitComment(); OutStreamer->emitIntValueInHexWithPadding(SecondHalfOfMandatoryField & 0xff, 1); // Generate the optional fields of traceback table. // Parameter type. if (NumberOfFixedParms || NumberOfFPParms) { uint32_t ParmsTypeValue = FI->getParmsType(); Expected> ParmsType = FI->hasVectorParms() ? XCOFF::parseParmsTypeWithVecInfo( ParmsTypeValue, NumberOfFixedParms, NumberOfFPParms, FI->getVectorParmsNum()) : XCOFF::parseParmsType(ParmsTypeValue, NumberOfFixedParms, NumberOfFPParms); assert(ParmsType && toString(ParmsType.takeError()).c_str()); if (ParmsType) { CommentOS << "Parameter type = " << ParmsType.get(); EmitComment(); } OutStreamer->emitIntValueInHexWithPadding(ParmsTypeValue, sizeof(ParmsTypeValue)); } // Traceback table offset. OutStreamer->AddComment("Function size"); if (FirstHalfOfMandatoryField & TracebackTable::HasTraceBackTableOffsetMask) { MCSymbol *FuncSectSym = getObjFileLowering().getFunctionEntryPointSymbol( &(MF->getFunction()), TM); OutStreamer->emitAbsoluteSymbolDiff(FuncEnd, FuncSectSym, 4); } // Since we unset the Int_Handler. if (FirstHalfOfMandatoryField & TracebackTable::IsInterruptHandlerMask) report_fatal_error("Hand_Mask not implement yet"); if (FirstHalfOfMandatoryField & TracebackTable::HasControlledStorageMask) report_fatal_error("Ctl_Info not implement yet"); if (FirstHalfOfMandatoryField & TracebackTable::IsFunctionNamePresentMask) { StringRef Name = MF->getName().substr(0, INT16_MAX); int16_t NameLength = Name.size(); CommentOS << "Function name len = " << static_cast(NameLength); EmitCommentAndValue(NameLength, 2); OutStreamer->AddComment("Function Name"); OutStreamer->emitBytes(Name); } if (FirstHalfOfMandatoryField & TracebackTable::IsAllocaUsedMask) { uint8_t AllocReg = XCOFF::AllocRegNo; OutStreamer->AddComment("AllocaUsed"); OutStreamer->emitIntValueInHex(AllocReg, sizeof(AllocReg)); } if (SecondHalfOfMandatoryField & TracebackTable::HasVectorInfoMask) { uint16_t VRData = 0; if (NumOfVRSaved) { // Number of VRs saved. VRData |= (NumOfVRSaved << TracebackTable::NumberOfVRSavedShift) & TracebackTable::NumberOfVRSavedMask; // This bit is supposed to set only when the special register // VRSAVE is saved on stack. // However, IBM XL compiler sets the bit when any vector registers // are saved on the stack. We will follow XL's behavior on AIX // so that we don't get surprise behavior change for C code. VRData |= TracebackTable::IsVRSavedOnStackMask; } // Set has_varargs. if (FI->getVarArgsFrameIndex()) VRData |= TracebackTable::HasVarArgsMask; // Vector parameters number. unsigned VectorParmsNum = FI->getVectorParmsNum(); VRData |= (VectorParmsNum << TracebackTable::NumberOfVectorParmsShift) & TracebackTable::NumberOfVectorParmsMask; if (HasVectorInst) VRData |= TracebackTable::HasVMXInstructionMask; GENVALUECOMMENT("NumOfVRsSaved", VRData, NumberOfVRSaved); GENBOOLCOMMENT(", ", VRData, IsVRSavedOnStack); GENBOOLCOMMENT(", ", VRData, HasVarArgs); EmitComment(); OutStreamer->emitIntValueInHexWithPadding((VRData & 0xff00) >> 8, 1); GENVALUECOMMENT("NumOfVectorParams", VRData, NumberOfVectorParms); GENBOOLCOMMENT(", ", VRData, HasVMXInstruction); EmitComment(); OutStreamer->emitIntValueInHexWithPadding(VRData & 0x00ff, 1); uint32_t VecParmTypeValue = FI->getVecExtParmsType(); Expected> VecParmsType = XCOFF::parseVectorParmsType(VecParmTypeValue, VectorParmsNum); assert(VecParmsType && toString(VecParmsType.takeError()).c_str()); if (VecParmsType) { CommentOS << "Vector Parameter type = " << VecParmsType.get(); EmitComment(); } OutStreamer->emitIntValueInHexWithPadding(VecParmTypeValue, sizeof(VecParmTypeValue)); // Padding 2 bytes. CommentOS << "Padding"; EmitCommentAndValue(0, 2); } uint8_t ExtensionTableFlag = 0; if (SecondHalfOfMandatoryField & TracebackTable::HasExtensionTableMask) { if (ShouldEmitEHBlock) ExtensionTableFlag |= ExtendedTBTableFlag::TB_EH_INFO; if (EnableSSPCanaryBitInTB && TargetLoweringObjectFileXCOFF::ShouldSetSSPCanaryBitInTB(MF)) ExtensionTableFlag |= ExtendedTBTableFlag::TB_SSP_CANARY; CommentOS << "ExtensionTableFlag = " << getExtendedTBTableFlagString(ExtensionTableFlag); EmitCommentAndValue(ExtensionTableFlag, sizeof(ExtensionTableFlag)); } if (ExtensionTableFlag & ExtendedTBTableFlag::TB_EH_INFO) { auto &Ctx = OutStreamer->getContext(); MCSymbol *EHInfoSym = TargetLoweringObjectFileXCOFF::getEHInfoTableSymbol(MF); MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(EHInfoSym); const MCSymbol *TOCBaseSym = cast(getObjFileLowering().getTOCBaseSection()) ->getQualNameSymbol(); const MCExpr *Exp = MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCEntry, Ctx), MCSymbolRefExpr::create(TOCBaseSym, Ctx), Ctx); const DataLayout &DL = getDataLayout(); OutStreamer->emitValueToAlignment(4); OutStreamer->AddComment("EHInfo Table"); OutStreamer->emitValue(Exp, DL.getPointerSize()); } #undef GENBOOLCOMMENT #undef GENVALUECOMMENT } static bool isSpecialLLVMGlobalArrayToSkip(const GlobalVariable *GV) { return GV->hasAppendingLinkage() && StringSwitch(GV->getName()) // TODO: Linker could still eliminate the GV if we just skip // handling llvm.used array. Skipping them for now until we or the // AIX OS team come up with a good solution. .Case("llvm.used", true) // It's correct to just skip llvm.compiler.used array here. .Case("llvm.compiler.used", true) .Default(false); } static bool isSpecialLLVMGlobalArrayForStaticInit(const GlobalVariable *GV) { return StringSwitch(GV->getName()) .Cases("llvm.global_ctors", "llvm.global_dtors", true) .Default(false); } void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { // Special LLVM global arrays have been handled at the initialization. if (isSpecialLLVMGlobalArrayToSkip(GV) || isSpecialLLVMGlobalArrayForStaticInit(GV)) return; // If the Global Variable has the toc-data attribute, it needs to be emitted // when we emit the .toc section. if (GV->hasAttribute("toc-data")) { TOCDataGlobalVars.push_back(GV); return; } emitGlobalVariableHelper(GV); } void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) { assert(!GV->getName().startswith("llvm.") && "Unhandled intrinsic global variable."); if (GV->hasComdat()) report_fatal_error("COMDAT not yet supported by AIX."); MCSymbolXCOFF *GVSym = cast(getSymbol(GV)); if (GV->isDeclarationForLinker()) { emitLinkage(GV, GVSym); return; } SectionKind GVKind = getObjFileLowering().getKindForGlobal(GV, TM); if (!GVKind.isGlobalWriteableData() && !GVKind.isReadOnly() && !GVKind.isThreadLocal()) // Checks for both ThreadData and ThreadBSS. report_fatal_error("Encountered a global variable kind that is " "not supported yet."); // Print GV in verbose mode if (isVerbose()) { if (GV->hasInitializer()) { GV->printAsOperand(OutStreamer->GetCommentOS(), /*PrintType=*/false, GV->getParent()); OutStreamer->GetCommentOS() << '\n'; } } MCSectionXCOFF *Csect = cast( getObjFileLowering().SectionForGlobal(GV, GVKind, TM)); // Switch to the containing csect. OutStreamer->SwitchSection(Csect); const DataLayout &DL = GV->getParent()->getDataLayout(); // Handle common and zero-initialized local symbols. if (GV->hasCommonLinkage() || GVKind.isBSSLocal() || GVKind.isThreadBSSLocal()) { Align Alignment = GV->getAlign().getValueOr(DL.getPreferredAlign(GV)); uint64_t Size = DL.getTypeAllocSize(GV->getValueType()); GVSym->setStorageClass( TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV)); if (GVKind.isBSSLocal() || GVKind.isThreadBSSLocal()) OutStreamer->emitXCOFFLocalCommonSymbol( OutContext.getOrCreateSymbol(GVSym->getSymbolTableName()), Size, GVSym, Alignment.value()); else OutStreamer->emitCommonSymbol(GVSym, Size, Alignment.value()); return; } MCSymbol *EmittedInitSym = GVSym; emitLinkage(GV, EmittedInitSym); emitAlignment(getGVAlignment(GV, DL), GV); // When -fdata-sections is enabled, every GlobalVariable will // be put into its own csect; therefore, label is not necessary here. if (!TM.getDataSections() || GV->hasSection()) { OutStreamer->emitLabel(EmittedInitSym); } // Emit aliasing label for global variable. llvm::for_each(GOAliasMap[GV], [this](const GlobalAlias *Alias) { OutStreamer->emitLabel(getSymbol(Alias)); }); emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } void PPCAIXAsmPrinter::emitFunctionDescriptor() { const DataLayout &DL = getDataLayout(); const unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; MCSectionSubPair Current = OutStreamer->getCurrentSection(); // Emit function descriptor. OutStreamer->SwitchSection( cast(CurrentFnDescSym)->getRepresentedCsect()); // Emit aliasing label for function descriptor csect. llvm::for_each(GOAliasMap[&MF->getFunction()], [this](const GlobalAlias *Alias) { OutStreamer->emitLabel(getSymbol(Alias)); }); // Emit function entry point address. OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), PointerSize); // Emit TOC base address. const MCSymbol *TOCBaseSym = cast(getObjFileLowering().getTOCBaseSection()) ->getQualNameSymbol(); OutStreamer->emitValue(MCSymbolRefExpr::create(TOCBaseSym, OutContext), PointerSize); // Emit a null environment pointer. OutStreamer->emitIntValue(0, PointerSize); OutStreamer->SwitchSection(Current.first, Current.second); } void PPCAIXAsmPrinter::emitFunctionEntryLabel() { // It's not necessary to emit the label when we have individual // function in its own csect. if (!TM.getFunctionSections()) PPCAsmPrinter::emitFunctionEntryLabel(); // Emit aliasing label for function entry point label. llvm::for_each( GOAliasMap[&MF->getFunction()], [this](const GlobalAlias *Alias) { OutStreamer->emitLabel( getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM)); }); } void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) { // If there are no functions and there are no toc-data definitions in this // module, we will never need to reference the TOC base. if (M.empty() && TOCDataGlobalVars.empty()) return; // Switch to section to emit TOC base. OutStreamer->SwitchSection(getObjFileLowering().getTOCBaseSection()); PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); for (auto &I : TOC) { MCSectionXCOFF *TCEntry; // Setup the csect for the current TC entry. If the variant kind is // VK_PPC_AIX_TLSGDM the entry represents the region handle, we create a // new symbol to prefix the name with a dot. if (I.first.second == MCSymbolRefExpr::VariantKind::VK_PPC_AIX_TLSGDM) { SmallString<128> Name; StringRef Prefix = "."; Name += Prefix; Name += I.first.first->getName(); MCSymbol *S = OutContext.getOrCreateSymbol(Name); TCEntry = cast( getObjFileLowering().getSectionForTOCEntry(S, TM)); } else { TCEntry = cast( getObjFileLowering().getSectionForTOCEntry(I.first.first, TM)); } OutStreamer->SwitchSection(TCEntry); OutStreamer->emitLabel(I.second); if (TS != nullptr) TS->emitTCEntry(*I.first.first, I.first.second); } for (const auto *GV : TOCDataGlobalVars) emitGlobalVariableHelper(GV); } bool PPCAIXAsmPrinter::doInitialization(Module &M) { const bool Result = PPCAsmPrinter::doInitialization(M); auto setCsectAlignment = [this](const GlobalObject *GO) { // Declarations have 0 alignment which is set by default. if (GO->isDeclarationForLinker()) return; SectionKind GOKind = getObjFileLowering().getKindForGlobal(GO, TM); MCSectionXCOFF *Csect = cast( getObjFileLowering().SectionForGlobal(GO, GOKind, TM)); Align GOAlign = getGVAlignment(GO, GO->getParent()->getDataLayout()); if (GOAlign > Csect->getAlignment()) Csect->setAlignment(GOAlign); }; // We need to know, up front, the alignment of csects for the assembly path, // because once a .csect directive gets emitted, we could not change the // alignment value on it. for (const auto &G : M.globals()) { if (isSpecialLLVMGlobalArrayToSkip(&G)) continue; if (isSpecialLLVMGlobalArrayForStaticInit(&G)) { // Generate a format indicator and a unique module id to be a part of // the sinit and sterm function names. if (FormatIndicatorAndUniqueModId.empty()) { std::string UniqueModuleId = getUniqueModuleId(&M); if (UniqueModuleId != "") // TODO: Use source file full path to generate the unique module id // and add a format indicator as a part of function name in case we // will support more than one format. FormatIndicatorAndUniqueModId = "clang_" + UniqueModuleId.substr(1); else // Use the Pid and current time as the unique module id when we cannot // generate one based on a module's strong external symbols. // FIXME: Adjust the comment accordingly after we use source file full // path instead. FormatIndicatorAndUniqueModId = "clangPidTime_" + llvm::itostr(sys::Process::getProcessId()) + "_" + llvm::itostr(time(nullptr)); } emitSpecialLLVMGlobal(&G); continue; } setCsectAlignment(&G); } for (const auto &F : M) setCsectAlignment(&F); // Construct an aliasing list for each GlobalObject. for (const auto &Alias : M.aliases()) { const GlobalObject *Base = Alias.getAliaseeObject(); if (!Base) report_fatal_error( "alias without a base object is not yet supported on AIX"); GOAliasMap[Base].push_back(&Alias); } return Result; } void PPCAIXAsmPrinter::emitInstruction(const MachineInstr *MI) { switch (MI->getOpcode()) { default: break; case PPC::GETtlsADDR64AIX: case PPC::GETtlsADDR32AIX: { // The reference to .__tls_get_addr is unknown to the assembler // so we need to emit an external symbol reference. MCSymbol *TlsGetAddr = createMCSymbolForTlsGetAddr(OutContext); ExtSymSDNodeSymbols.insert(TlsGetAddr); break; } case PPC::BL8: case PPC::BL: case PPC::BL8_NOP: case PPC::BL_NOP: { const MachineOperand &MO = MI->getOperand(0); if (MO.isSymbol()) { MCSymbolXCOFF *S = cast(OutContext.getOrCreateSymbol(MO.getSymbolName())); ExtSymSDNodeSymbols.insert(S); } } break; case PPC::BL_TLS: case PPC::BL8_TLS: case PPC::BL8_TLS_: case PPC::BL8_NOP_TLS: report_fatal_error("TLS call not yet implemented"); case PPC::TAILB: case PPC::TAILB8: case PPC::TAILBA: case PPC::TAILBA8: case PPC::TAILBCTR: case PPC::TAILBCTR8: if (MI->getOperand(0).isSymbol()) report_fatal_error("Tail call for extern symbol not yet supported."); break; case PPC::DST: case PPC::DST64: case PPC::DSTT: case PPC::DSTT64: case PPC::DSTST: case PPC::DSTST64: case PPC::DSTSTT: case PPC::DSTSTT64: EmitToStreamer( *OutStreamer, MCInstBuilder(PPC::ORI).addReg(PPC::R0).addReg(PPC::R0).addImm(0)); return; } return PPCAsmPrinter::emitInstruction(MI); } bool PPCAIXAsmPrinter::doFinalization(Module &M) { // Do streamer related finalization for DWARF. if (!MAI->usesDwarfFileAndLocDirectives() && MMI->hasDebugInfo()) OutStreamer->doFinalizationAtSectionEnd( OutStreamer->getContext().getObjectFileInfo()->getTextSection()); for (MCSymbol *Sym : ExtSymSDNodeSymbols) OutStreamer->emitSymbolAttribute(Sym, MCSA_Extern); return PPCAsmPrinter::doFinalization(M); } static unsigned mapToSinitPriority(int P) { if (P < 0 || P > 65535) report_fatal_error("invalid init priority"); if (P <= 20) return P; if (P < 81) return 20 + (P - 20) * 16; if (P <= 1124) return 1004 + (P - 81); if (P < 64512) return 2047 + (P - 1124) * 33878; return 2147482625u + (P - 64512); } static std::string convertToSinitPriority(int Priority) { // This helper function converts clang init priority to values used in sinit // and sterm functions. // // The conversion strategies are: // We map the reserved clang/gnu priority range [0, 100] into the sinit/sterm // reserved priority range [0, 1023] by // - directly mapping the first 21 and the last 20 elements of the ranges // - linear interpolating the intermediate values with a step size of 16. // // We map the non reserved clang/gnu priority range of [101, 65535] into the // sinit/sterm priority range [1024, 2147483648] by: // - directly mapping the first and the last 1024 elements of the ranges // - linear interpolating the intermediate values with a step size of 33878. unsigned int P = mapToSinitPriority(Priority); std::string PrioritySuffix; llvm::raw_string_ostream os(PrioritySuffix); os << llvm::format_hex_no_prefix(P, 8); os.flush(); return PrioritySuffix; } void PPCAIXAsmPrinter::emitXXStructorList(const DataLayout &DL, const Constant *List, bool IsCtor) { SmallVector Structors; preprocessXXStructorList(DL, List, Structors); if (Structors.empty()) return; unsigned Index = 0; for (Structor &S : Structors) { if (const ConstantExpr *CE = dyn_cast(S.Func)) S.Func = CE->getOperand(0); llvm::GlobalAlias::create( GlobalValue::ExternalLinkage, (IsCtor ? llvm::Twine("__sinit") : llvm::Twine("__sterm")) + llvm::Twine(convertToSinitPriority(S.Priority)) + llvm::Twine("_", FormatIndicatorAndUniqueModId) + llvm::Twine("_", llvm::utostr(Index++)), cast(S.Func)); } } void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, unsigned Encoding) { if (GV) { MCSymbol *TypeInfoSym = TM.getSymbol(GV); MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(TypeInfoSym); const MCSymbol *TOCBaseSym = cast(getObjFileLowering().getTOCBaseSection()) ->getQualNameSymbol(); auto &Ctx = OutStreamer->getContext(); const MCExpr *Exp = MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCEntry, Ctx), MCSymbolRefExpr::create(TOCBaseSym, Ctx), Ctx); OutStreamer->emitValue(Exp, GetSizeOfEncodedValue(Encoding)); } else OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding)); } // Return a pass that prints the PPC assembly code for a MachineFunction to the // given output stream. static AsmPrinter * createPPCAsmPrinterPass(TargetMachine &tm, std::unique_ptr &&Streamer) { if (tm.getTargetTriple().isOSAIX()) return new PPCAIXAsmPrinter(tm, std::move(Streamer)); return new PPCLinuxAsmPrinter(tm, std::move(Streamer)); } // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializePowerPCAsmPrinter() { TargetRegistry::RegisterAsmPrinter(getThePPC32Target(), createPPCAsmPrinterPass); TargetRegistry::RegisterAsmPrinter(getThePPC32LETarget(), createPPCAsmPrinterPass); TargetRegistry::RegisterAsmPrinter(getThePPC64Target(), createPPCAsmPrinterPass); TargetRegistry::RegisterAsmPrinter(getThePPC64LETarget(), createPPCAsmPrinterPass); }