1fe6060f1SDimitry Andric //===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===// 2fe6060f1SDimitry Andric // 3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fe6060f1SDimitry Andric // 7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8fe6060f1SDimitry Andric // 9fe6060f1SDimitry Andric // Generic utilities for graphs representing x86-64 objects. 10fe6060f1SDimitry Andric // 11fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 12fe6060f1SDimitry Andric 13fe6060f1SDimitry Andric #include "llvm/ExecutionEngine/JITLink/x86_64.h" 14fe6060f1SDimitry Andric 15fe6060f1SDimitry Andric #define DEBUG_TYPE "jitlink" 16fe6060f1SDimitry Andric 17fe6060f1SDimitry Andric namespace llvm { 18fe6060f1SDimitry Andric namespace jitlink { 19fe6060f1SDimitry Andric namespace x86_64 { 20fe6060f1SDimitry Andric 21fe6060f1SDimitry Andric const char *getEdgeKindName(Edge::Kind K) { 22fe6060f1SDimitry Andric switch (K) { 23fe6060f1SDimitry Andric case Pointer64: 24fe6060f1SDimitry Andric return "Pointer64"; 25fe6060f1SDimitry Andric case Pointer32: 26fe6060f1SDimitry Andric return "Pointer32"; 27349cc55cSDimitry Andric case Pointer32Signed: 28349cc55cSDimitry Andric return "Pointer32Signed"; 29bdd1243dSDimitry Andric case Pointer16: 30bdd1243dSDimitry Andric return "Pointer16"; 31*06c3fb27SDimitry Andric case Pointer8: 32*06c3fb27SDimitry Andric return "Pointer8"; 33fe6060f1SDimitry Andric case Delta64: 34fe6060f1SDimitry Andric return "Delta64"; 35fe6060f1SDimitry Andric case Delta32: 36fe6060f1SDimitry Andric return "Delta32"; 37fe6060f1SDimitry Andric case NegDelta64: 38fe6060f1SDimitry Andric return "NegDelta64"; 39fe6060f1SDimitry Andric case NegDelta32: 40fe6060f1SDimitry Andric return "NegDelta32"; 41349cc55cSDimitry Andric case Delta64FromGOT: 42349cc55cSDimitry Andric return "Delta64FromGOT"; 43972a253aSDimitry Andric case PCRel32: 44972a253aSDimitry Andric return "PCRel32"; 45fe6060f1SDimitry Andric case BranchPCRel32: 46fe6060f1SDimitry Andric return "BranchPCRel32"; 47fe6060f1SDimitry Andric case BranchPCRel32ToPtrJumpStub: 48fe6060f1SDimitry Andric return "BranchPCRel32ToPtrJumpStub"; 49349cc55cSDimitry Andric case BranchPCRel32ToPtrJumpStubBypassable: 50349cc55cSDimitry Andric return "BranchPCRel32ToPtrJumpStubBypassable"; 51fe6060f1SDimitry Andric case RequestGOTAndTransformToDelta32: 52fe6060f1SDimitry Andric return "RequestGOTAndTransformToDelta32"; 53349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64: 54349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64"; 55349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64FromGOT: 56349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64FromGOT"; 57349cc55cSDimitry Andric case PCRel32GOTLoadREXRelaxable: 58349cc55cSDimitry Andric return "PCRel32GOTLoadREXRelaxable"; 59349cc55cSDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 60349cc55cSDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable"; 61fe6060f1SDimitry Andric case PCRel32GOTLoadRelaxable: 62fe6060f1SDimitry Andric return "PCRel32GOTLoadRelaxable"; 63fe6060f1SDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 64fe6060f1SDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable"; 65349cc55cSDimitry Andric case PCRel32TLVPLoadREXRelaxable: 66349cc55cSDimitry Andric return "PCRel32TLVPLoadREXRelaxable"; 67349cc55cSDimitry Andric case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable: 68349cc55cSDimitry Andric return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable"; 69fe6060f1SDimitry Andric default: 70fe6060f1SDimitry Andric return getGenericEdgeKindName(static_cast<Edge::Kind>(K)); 71fe6060f1SDimitry Andric } 72fe6060f1SDimitry Andric } 73fe6060f1SDimitry Andric 74fe6060f1SDimitry Andric const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, 75fe6060f1SDimitry Andric 0x00, 0x00, 0x00, 0x00}; 76fe6060f1SDimitry Andric 77fe6060f1SDimitry Andric const char PointerJumpStubContent[6] = { 78fe6060f1SDimitry Andric static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; 79fe6060f1SDimitry Andric 80349cc55cSDimitry Andric Error optimizeGOTAndStubAccesses(LinkGraph &G) { 81349cc55cSDimitry Andric LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); 82349cc55cSDimitry Andric 83349cc55cSDimitry Andric for (auto *B : G.blocks()) 84349cc55cSDimitry Andric for (auto &E : B->edges()) { 85349cc55cSDimitry Andric if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable || 86349cc55cSDimitry Andric E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) { 87349cc55cSDimitry Andric #ifndef NDEBUG 88349cc55cSDimitry Andric bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable; 89349cc55cSDimitry Andric assert(E.getOffset() >= (REXPrefix ? 3u : 2u) && 90349cc55cSDimitry Andric "GOT edge occurs too early in block"); 91349cc55cSDimitry Andric #endif 92349cc55cSDimitry Andric auto *FixupData = reinterpret_cast<uint8_t *>( 93349cc55cSDimitry Andric const_cast<char *>(B->getContent().data())) + 94349cc55cSDimitry Andric E.getOffset(); 95349cc55cSDimitry Andric const uint8_t Op = FixupData[-2]; 96349cc55cSDimitry Andric const uint8_t ModRM = FixupData[-1]; 97349cc55cSDimitry Andric 98349cc55cSDimitry Andric auto &GOTEntryBlock = E.getTarget().getBlock(); 99349cc55cSDimitry Andric assert(GOTEntryBlock.getSize() == G.getPointerSize() && 100349cc55cSDimitry Andric "GOT entry block should be pointer sized"); 101349cc55cSDimitry Andric assert(GOTEntryBlock.edges_size() == 1 && 102349cc55cSDimitry Andric "GOT entry should only have one outgoing edge"); 103349cc55cSDimitry Andric auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget(); 10404eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 10504eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E); 106349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 107*06c3fb27SDimitry Andric bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue()); 108*06c3fb27SDimitry Andric bool DisplacementInRangeForImmS32 = isInt<32>(Displacement); 109349cc55cSDimitry Andric 110349cc55cSDimitry Andric // If both of the Target and displacement is out of range, then 111349cc55cSDimitry Andric // there isn't optimization chance. 112349cc55cSDimitry Andric if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32)) 113349cc55cSDimitry Andric continue; 114349cc55cSDimitry Andric 115349cc55cSDimitry Andric // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". 116349cc55cSDimitry Andric if (Op == 0x8b && DisplacementInRangeForImmS32) { 117349cc55cSDimitry Andric FixupData[-2] = 0x8d; 118349cc55cSDimitry Andric E.setKind(x86_64::Delta32); 119349cc55cSDimitry Andric E.setTarget(GOTTarget); 120349cc55cSDimitry Andric E.setAddend(E.getAddend() - 4); 121349cc55cSDimitry Andric LLVM_DEBUG({ 122349cc55cSDimitry Andric dbgs() << " Replaced GOT load wih LEA:\n "; 123349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 124349cc55cSDimitry Andric dbgs() << "\n"; 125349cc55cSDimitry Andric }); 126349cc55cSDimitry Andric continue; 127349cc55cSDimitry Andric } 128349cc55cSDimitry Andric 129349cc55cSDimitry Andric // Transform call/jmp instructions 130349cc55cSDimitry Andric if (Op == 0xff && TargetInRangeForImmU32) { 131349cc55cSDimitry Andric if (ModRM == 0x15) { 132349cc55cSDimitry Andric // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call 133349cc55cSDimitry Andric // foo" But lld convert it to "addr32 call foo, because that makes 134349cc55cSDimitry Andric // result expression to be a single instruction. 135349cc55cSDimitry Andric FixupData[-2] = 0x67; 136349cc55cSDimitry Andric FixupData[-1] = 0xe8; 137349cc55cSDimitry Andric LLVM_DEBUG({ 138349cc55cSDimitry Andric dbgs() << " replaced call instruction's memory operand wih imm " 139349cc55cSDimitry Andric "operand:\n "; 140349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 141349cc55cSDimitry Andric dbgs() << "\n"; 142349cc55cSDimitry Andric }); 143349cc55cSDimitry Andric } else { 144349cc55cSDimitry Andric // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop" 145349cc55cSDimitry Andric assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions"); 146349cc55cSDimitry Andric FixupData[-2] = 0xe9; 147349cc55cSDimitry Andric FixupData[3] = 0x90; 148349cc55cSDimitry Andric E.setOffset(E.getOffset() - 1); 149349cc55cSDimitry Andric LLVM_DEBUG({ 150349cc55cSDimitry Andric dbgs() << " replaced jmp instruction's memory operand wih imm " 151349cc55cSDimitry Andric "operand:\n "; 152349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 153349cc55cSDimitry Andric dbgs() << "\n"; 154349cc55cSDimitry Andric }); 155349cc55cSDimitry Andric } 156349cc55cSDimitry Andric E.setKind(x86_64::Pointer32); 157349cc55cSDimitry Andric E.setTarget(GOTTarget); 158349cc55cSDimitry Andric continue; 159349cc55cSDimitry Andric } 160349cc55cSDimitry Andric } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) { 161349cc55cSDimitry Andric auto &StubBlock = E.getTarget().getBlock(); 162349cc55cSDimitry Andric assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) && 163349cc55cSDimitry Andric "Stub block should be stub sized"); 164349cc55cSDimitry Andric assert(StubBlock.edges_size() == 1 && 165349cc55cSDimitry Andric "Stub block should only have one outgoing edge"); 166349cc55cSDimitry Andric 167349cc55cSDimitry Andric auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); 168349cc55cSDimitry Andric assert(GOTBlock.getSize() == G.getPointerSize() && 169349cc55cSDimitry Andric "GOT block should be pointer sized"); 170349cc55cSDimitry Andric assert(GOTBlock.edges_size() == 1 && 171349cc55cSDimitry Andric "GOT block should only have one outgoing edge"); 172349cc55cSDimitry Andric 173349cc55cSDimitry Andric auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); 17404eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset(); 17504eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 176349cc55cSDimitry Andric 177349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 178*06c3fb27SDimitry Andric if (isInt<32>(Displacement)) { 179349cc55cSDimitry Andric E.setKind(x86_64::BranchPCRel32); 180349cc55cSDimitry Andric E.setTarget(GOTTarget); 181349cc55cSDimitry Andric LLVM_DEBUG({ 182349cc55cSDimitry Andric dbgs() << " Replaced stub branch with direct branch:\n "; 183349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 184349cc55cSDimitry Andric dbgs() << "\n"; 185349cc55cSDimitry Andric }); 186349cc55cSDimitry Andric } 187349cc55cSDimitry Andric } 188349cc55cSDimitry Andric } 189349cc55cSDimitry Andric 190349cc55cSDimitry Andric return Error::success(); 191349cc55cSDimitry Andric } 192349cc55cSDimitry Andric 193fe6060f1SDimitry Andric } // end namespace x86_64 194fe6060f1SDimitry Andric } // end namespace jitlink 195fe6060f1SDimitry Andric } // end namespace llvm 196