| //===-------- PPCELFStreamer.cpp - ELF Object Output ---------------------===// | |
| // | |
| // 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 is a custom MCELFStreamer for PowerPC. | |
| // | |
| // The purpose of the custom ELF streamer is to allow us to intercept | |
| // instructions as they are being emitted and align all 8 byte instructions | |
| // to a 64 byte boundary if required (by adding a 4 byte nop). This is important | |
| // because 8 byte instructions are not allowed to cross 64 byte boundaries | |
| // and by aliging anything that is within 4 bytes of the boundary we can | |
| // guarantee that the 8 byte instructions do not cross that boundary. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "PPCELFStreamer.h" | |
| #include "PPCFixupKinds.h" | |
| #include "PPCInstrInfo.h" | |
| #include "PPCMCCodeEmitter.h" | |
| #include "llvm/BinaryFormat/ELF.h" | |
| #include "llvm/MC/MCAsmBackend.h" | |
| #include "llvm/MC/MCAssembler.h" | |
| #include "llvm/MC/MCCodeEmitter.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstrDesc.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCSymbolELF.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| using namespace llvm; | |
| PPCELFStreamer::PPCELFStreamer(MCContext &Context, | |
| std::unique_ptr<MCAsmBackend> MAB, | |
| std::unique_ptr<MCObjectWriter> OW, | |
| std::unique_ptr<MCCodeEmitter> Emitter) | |
| : MCELFStreamer(Context, std::move(MAB), std::move(OW), std::move(Emitter)), | |
| LastLabel(nullptr) {} | |
| void PPCELFStreamer::emitPrefixedInstruction(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| // Prefixed instructions must not cross a 64-byte boundary (i.e. prefix is | |
| // before the boundary and the remaining 4-bytes are after the boundary). In | |
| // order to achieve this, a nop is added prior to any such boundary-crossing | |
| // prefixed instruction. Align to 64 bytes if possible but add a maximum of 4 | |
| // bytes when trying to do that. If alignment requires adding more than 4 | |
| // bytes then the instruction won't be aligned. When emitting a code alignment | |
| // a new fragment is created for this alignment. This fragment will contain | |
| // all of the nops required as part of the alignment operation. In the cases | |
| // when no nops are added then The fragment is still created but it remains | |
| // empty. | |
| emitCodeAlignment(Align(64), &STI, 4); | |
| // Emit the instruction. | |
| // Since the previous emit created a new fragment then adding this instruction | |
| // also forces the addition of a new fragment. Inst is now the first | |
| // instruction in that new fragment. | |
| MCELFStreamer::emitInstruction(Inst, STI); | |
| // The above instruction is forced to start a new fragment because it | |
| // comes after a code alignment fragment. Get that new fragment. | |
| MCFragment *InstructionFragment = getCurrentFragment(); | |
| SMLoc InstLoc = Inst.getLoc(); | |
| // Check if there was a last label emitted. | |
| if (LastLabel && !LastLabel->isUnset() && LastLabelLoc.isValid() && | |
| InstLoc.isValid()) { | |
| const SourceMgr *SourceManager = getContext().getSourceManager(); | |
| unsigned InstLine = SourceManager->FindLineNumber(InstLoc); | |
| unsigned LabelLine = SourceManager->FindLineNumber(LastLabelLoc); | |
| // If the Label and the Instruction are on the same line then move the | |
| // label to the top of the fragment containing the aligned instruction that | |
| // was just added. | |
| if (InstLine == LabelLine) { | |
| assignFragment(LastLabel, InstructionFragment); | |
| LastLabel->setOffset(0); | |
| } | |
| } | |
| } | |
| void PPCELFStreamer::emitInstruction(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| PPCMCCodeEmitter *Emitter = | |
| static_cast<PPCMCCodeEmitter*>(getAssembler().getEmitterPtr()); | |
| // If the instruction is a part of the GOT to PC-Rel link time optimization | |
| // instruction pair, return a value, otherwise return std::nullopt. A true | |
| // returned value means the instruction is the PLDpc and a false value means | |
| // it is the user instruction. | |
| std::optional<bool> IsPartOfGOTToPCRelPair = | |
| isPartOfGOTToPCRelPair(Inst, STI); | |
| // User of the GOT-indirect address. | |
| // For example, the load that will get the relocation as follows: | |
| // .reloc .Lpcrel1-8,R_PPC64_PCREL_OPT,.-(.Lpcrel1-8) | |
| // lwa 3, 4(3) | |
| if (IsPartOfGOTToPCRelPair && !*IsPartOfGOTToPCRelPair) | |
| emitGOTToPCRelReloc(Inst); | |
| // Special handling is only for prefixed instructions. | |
| if (!Emitter->isPrefixedInstruction(Inst)) { | |
| MCELFStreamer::emitInstruction(Inst, STI); | |
| return; | |
| } | |
| emitPrefixedInstruction(Inst, STI); | |
| // Producer of the GOT-indirect address. | |
| // For example, the prefixed load from the got that will get the label as | |
| // follows: | |
| // pld 3, vec@got@pcrel(0), 1 | |
| // .Lpcrel1: | |
| if (IsPartOfGOTToPCRelPair && *IsPartOfGOTToPCRelPair) | |
| emitGOTToPCRelLabel(Inst); | |
| } | |
| void PPCELFStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { | |
| LastLabel = Symbol; | |
| LastLabelLoc = Loc; | |
| MCELFStreamer::emitLabel(Symbol); | |
| } | |
| // This linker time GOT PC Relative optimization relocation will look like this: | |
| // pld <reg> symbol@got@pcrel | |
| // <Label###>: | |
| // .reloc Label###-8,R_PPC64_PCREL_OPT,.-(Label###-8) | |
| // load <loadedreg>, 0(<reg>) | |
| // The reason we place the label after the PLDpc instruction is that there | |
| // may be an alignment nop before it since prefixed instructions must not | |
| // cross a 64-byte boundary (please see | |
| // PPCELFStreamer::emitPrefixedInstruction()). When referring to the | |
| // label, we subtract the width of a prefixed instruction (8 bytes) to ensure | |
| // we refer to the PLDpc. | |
| void PPCELFStreamer::emitGOTToPCRelReloc(const MCInst &Inst) { | |
| // Get the last operand which contains the symbol. | |
| const MCOperand &Operand = Inst.getOperand(Inst.getNumOperands() - 1); | |
| assert(Operand.isExpr() && "Expecting an MCExpr."); | |
| // Cast the last operand to MCSymbolRefExpr to get the symbol. | |
| const MCExpr *Expr = Operand.getExpr(); | |
| const MCSymbolRefExpr *SymExpr = static_cast<const MCSymbolRefExpr *>(Expr); | |
| assert(SymExpr->getKind() == MCSymbolRefExpr::VK_PPC_PCREL_OPT && | |
| "Expecting a symbol of type VK_PPC_PCREL_OPT"); | |
| MCSymbol *LabelSym = | |
| getContext().getOrCreateSymbol(SymExpr->getSymbol().getName()); | |
| const MCExpr *LabelExpr = MCSymbolRefExpr::create(LabelSym, getContext()); | |
| const MCExpr *Eight = MCConstantExpr::create(8, getContext()); | |
| // SubExpr is just Label###-8 | |
| const MCExpr *SubExpr = | |
| MCBinaryExpr::createSub(LabelExpr, Eight, getContext()); | |
| MCSymbol *CurrentLocation = getContext().createTempSymbol(); | |
| const MCExpr *CurrentLocationExpr = | |
| MCSymbolRefExpr::create(CurrentLocation, getContext()); | |
| // SubExpr2 is .-(Label###-8) | |
| const MCExpr *SubExpr2 = | |
| MCBinaryExpr::createSub(CurrentLocationExpr, SubExpr, getContext()); | |
| MCDataFragment *DF = static_cast<MCDataFragment *>(LabelSym->getFragment()); | |
| assert(DF && "Expecting a valid data fragment."); | |
| MCFixupKind FixupKind = static_cast<MCFixupKind>(FirstLiteralRelocationKind + | |
| ELF::R_PPC64_PCREL_OPT); | |
| DF->getFixups().push_back( | |
| MCFixup::create(LabelSym->getOffset() - 8, SubExpr2, | |
| FixupKind, Inst.getLoc())); | |
| emitLabel(CurrentLocation, Inst.getLoc()); | |
| } | |
| // Emit the label that immediately follows the PLDpc for a link time GOT PC Rel | |
| // optimization. | |
| void PPCELFStreamer::emitGOTToPCRelLabel(const MCInst &Inst) { | |
| // Get the last operand which contains the symbol. | |
| const MCOperand &Operand = Inst.getOperand(Inst.getNumOperands() - 1); | |
| assert(Operand.isExpr() && "Expecting an MCExpr."); | |
| // Cast the last operand to MCSymbolRefExpr to get the symbol. | |
| const MCExpr *Expr = Operand.getExpr(); | |
| const MCSymbolRefExpr *SymExpr = static_cast<const MCSymbolRefExpr *>(Expr); | |
| assert(SymExpr->getKind() == MCSymbolRefExpr::VK_PPC_PCREL_OPT && | |
| "Expecting a symbol of type VK_PPC_PCREL_OPT"); | |
| MCSymbol *LabelSym = | |
| getContext().getOrCreateSymbol(SymExpr->getSymbol().getName()); | |
| emitLabel(LabelSym, Inst.getLoc()); | |
| } | |
| // This funciton checks if the parameter Inst is part of the setup for a link | |
| // time GOT PC Relative optimization. For example in this situation: | |
| // <MCInst PLDpc <MCOperand Reg:282> <MCOperand Expr:(glob_double@got@pcrel)> | |
| // <MCOperand Imm:0> <MCOperand Expr:(.Lpcrel@<<invalid>>)>> | |
| // <MCInst SOME_LOAD <MCOperand Reg:22> <MCOperand Imm:0> <MCOperand Reg:282> | |
| // <MCOperand Expr:(.Lpcrel@<<invalid>>)>> | |
| // The above is a pair of such instructions and this function will not return | |
| // std::nullopt for either one of them. In both cases we are looking for the | |
| // last operand <MCOperand Expr:(.Lpcrel@<<invalid>>)> which needs to be an | |
| // MCExpr and has the flag MCSymbolRefExpr::VK_PPC_PCREL_OPT. After that we just | |
| // look at the opcode and in the case of PLDpc we will return true. For the load | |
| // (or store) this function will return false indicating it has found the second | |
| // instruciton in the pair. | |
| std::optional<bool> llvm::isPartOfGOTToPCRelPair(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| // Need at least two operands. | |
| if (Inst.getNumOperands() < 2) | |
| return std::nullopt; | |
| unsigned LastOp = Inst.getNumOperands() - 1; | |
| // The last operand needs to be an MCExpr and it needs to have a variant kind | |
| // of VK_PPC_PCREL_OPT. If it does not satisfy these conditions it is not a | |
| // link time GOT PC Rel opt instruction and we can ignore it and return | |
| // std::nullopt. | |
| const MCOperand &Operand = Inst.getOperand(LastOp); | |
| if (!Operand.isExpr()) | |
| return std::nullopt; | |
| // Check for the variant kind VK_PPC_PCREL_OPT in this expression. | |
| const MCExpr *Expr = Operand.getExpr(); | |
| const MCSymbolRefExpr *SymExpr = static_cast<const MCSymbolRefExpr *>(Expr); | |
| if (!SymExpr || SymExpr->getKind() != MCSymbolRefExpr::VK_PPC_PCREL_OPT) | |
| return std::nullopt; | |
| return (Inst.getOpcode() == PPC::PLDpc); | |
| } | |
| MCELFStreamer *llvm::createPPCELFStreamer( | |
| MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, | |
| std::unique_ptr<MCObjectWriter> OW, | |
| std::unique_ptr<MCCodeEmitter> Emitter) { | |
| return new PPCELFStreamer(Context, std::move(MAB), std::move(OW), | |
| std::move(Emitter)); | |
| } |