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"; 29fe6060f1SDimitry Andric case Delta64: 30fe6060f1SDimitry Andric return "Delta64"; 31fe6060f1SDimitry Andric case Delta32: 32fe6060f1SDimitry Andric return "Delta32"; 33fe6060f1SDimitry Andric case NegDelta64: 34fe6060f1SDimitry Andric return "NegDelta64"; 35fe6060f1SDimitry Andric case NegDelta32: 36fe6060f1SDimitry Andric return "NegDelta32"; 37349cc55cSDimitry Andric case Delta64FromGOT: 38349cc55cSDimitry Andric return "Delta64FromGOT"; 39*972a253aSDimitry Andric case PCRel32: 40*972a253aSDimitry Andric return "PCRel32"; 41fe6060f1SDimitry Andric case BranchPCRel32: 42fe6060f1SDimitry Andric return "BranchPCRel32"; 43fe6060f1SDimitry Andric case BranchPCRel32ToPtrJumpStub: 44fe6060f1SDimitry Andric return "BranchPCRel32ToPtrJumpStub"; 45349cc55cSDimitry Andric case BranchPCRel32ToPtrJumpStubBypassable: 46349cc55cSDimitry Andric return "BranchPCRel32ToPtrJumpStubBypassable"; 47fe6060f1SDimitry Andric case RequestGOTAndTransformToDelta32: 48fe6060f1SDimitry Andric return "RequestGOTAndTransformToDelta32"; 49349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64: 50349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64"; 51349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64FromGOT: 52349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64FromGOT"; 53349cc55cSDimitry Andric case PCRel32GOTLoadREXRelaxable: 54349cc55cSDimitry Andric return "PCRel32GOTLoadREXRelaxable"; 55349cc55cSDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 56349cc55cSDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable"; 57fe6060f1SDimitry Andric case PCRel32GOTLoadRelaxable: 58fe6060f1SDimitry Andric return "PCRel32GOTLoadRelaxable"; 59fe6060f1SDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 60fe6060f1SDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable"; 61349cc55cSDimitry Andric case PCRel32TLVPLoadREXRelaxable: 62349cc55cSDimitry Andric return "PCRel32TLVPLoadREXRelaxable"; 63349cc55cSDimitry Andric case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable: 64349cc55cSDimitry Andric return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable"; 65fe6060f1SDimitry Andric default: 66fe6060f1SDimitry Andric return getGenericEdgeKindName(static_cast<Edge::Kind>(K)); 67fe6060f1SDimitry Andric } 68fe6060f1SDimitry Andric } 69fe6060f1SDimitry Andric 70fe6060f1SDimitry Andric const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, 71fe6060f1SDimitry Andric 0x00, 0x00, 0x00, 0x00}; 72fe6060f1SDimitry Andric 73fe6060f1SDimitry Andric const char PointerJumpStubContent[6] = { 74fe6060f1SDimitry Andric static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; 75fe6060f1SDimitry Andric 76349cc55cSDimitry Andric Error optimizeGOTAndStubAccesses(LinkGraph &G) { 77349cc55cSDimitry Andric LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); 78349cc55cSDimitry Andric 79349cc55cSDimitry Andric for (auto *B : G.blocks()) 80349cc55cSDimitry Andric for (auto &E : B->edges()) { 81349cc55cSDimitry Andric if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable || 82349cc55cSDimitry Andric E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) { 83349cc55cSDimitry Andric #ifndef NDEBUG 84349cc55cSDimitry Andric bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable; 85349cc55cSDimitry Andric assert(E.getOffset() >= (REXPrefix ? 3u : 2u) && 86349cc55cSDimitry Andric "GOT edge occurs too early in block"); 87349cc55cSDimitry Andric #endif 88349cc55cSDimitry Andric auto *FixupData = reinterpret_cast<uint8_t *>( 89349cc55cSDimitry Andric const_cast<char *>(B->getContent().data())) + 90349cc55cSDimitry Andric E.getOffset(); 91349cc55cSDimitry Andric const uint8_t Op = FixupData[-2]; 92349cc55cSDimitry Andric const uint8_t ModRM = FixupData[-1]; 93349cc55cSDimitry Andric 94349cc55cSDimitry Andric auto &GOTEntryBlock = E.getTarget().getBlock(); 95349cc55cSDimitry Andric assert(GOTEntryBlock.getSize() == G.getPointerSize() && 96349cc55cSDimitry Andric "GOT entry block should be pointer sized"); 97349cc55cSDimitry Andric assert(GOTEntryBlock.edges_size() == 1 && 98349cc55cSDimitry Andric "GOT entry should only have one outgoing edge"); 99349cc55cSDimitry Andric auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget(); 10004eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 10104eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E); 102349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 10304eeddc0SDimitry Andric bool TargetInRangeForImmU32 = isInRangeForImmU32(TargetAddr.getValue()); 104349cc55cSDimitry Andric bool DisplacementInRangeForImmS32 = isInRangeForImmS32(Displacement); 105349cc55cSDimitry Andric 106349cc55cSDimitry Andric // If both of the Target and displacement is out of range, then 107349cc55cSDimitry Andric // there isn't optimization chance. 108349cc55cSDimitry Andric if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32)) 109349cc55cSDimitry Andric continue; 110349cc55cSDimitry Andric 111349cc55cSDimitry Andric // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". 112349cc55cSDimitry Andric if (Op == 0x8b && DisplacementInRangeForImmS32) { 113349cc55cSDimitry Andric FixupData[-2] = 0x8d; 114349cc55cSDimitry Andric E.setKind(x86_64::Delta32); 115349cc55cSDimitry Andric E.setTarget(GOTTarget); 116349cc55cSDimitry Andric E.setAddend(E.getAddend() - 4); 117349cc55cSDimitry Andric LLVM_DEBUG({ 118349cc55cSDimitry Andric dbgs() << " Replaced GOT load wih LEA:\n "; 119349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 120349cc55cSDimitry Andric dbgs() << "\n"; 121349cc55cSDimitry Andric }); 122349cc55cSDimitry Andric continue; 123349cc55cSDimitry Andric } 124349cc55cSDimitry Andric 125349cc55cSDimitry Andric // Transform call/jmp instructions 126349cc55cSDimitry Andric if (Op == 0xff && TargetInRangeForImmU32) { 127349cc55cSDimitry Andric if (ModRM == 0x15) { 128349cc55cSDimitry Andric // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call 129349cc55cSDimitry Andric // foo" But lld convert it to "addr32 call foo, because that makes 130349cc55cSDimitry Andric // result expression to be a single instruction. 131349cc55cSDimitry Andric FixupData[-2] = 0x67; 132349cc55cSDimitry Andric FixupData[-1] = 0xe8; 133349cc55cSDimitry Andric LLVM_DEBUG({ 134349cc55cSDimitry Andric dbgs() << " replaced call instruction's memory operand wih imm " 135349cc55cSDimitry Andric "operand:\n "; 136349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 137349cc55cSDimitry Andric dbgs() << "\n"; 138349cc55cSDimitry Andric }); 139349cc55cSDimitry Andric } else { 140349cc55cSDimitry Andric // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop" 141349cc55cSDimitry Andric assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions"); 142349cc55cSDimitry Andric FixupData[-2] = 0xe9; 143349cc55cSDimitry Andric FixupData[3] = 0x90; 144349cc55cSDimitry Andric E.setOffset(E.getOffset() - 1); 145349cc55cSDimitry Andric LLVM_DEBUG({ 146349cc55cSDimitry Andric dbgs() << " replaced jmp instruction's memory operand wih imm " 147349cc55cSDimitry Andric "operand:\n "; 148349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 149349cc55cSDimitry Andric dbgs() << "\n"; 150349cc55cSDimitry Andric }); 151349cc55cSDimitry Andric } 152349cc55cSDimitry Andric E.setKind(x86_64::Pointer32); 153349cc55cSDimitry Andric E.setTarget(GOTTarget); 154349cc55cSDimitry Andric continue; 155349cc55cSDimitry Andric } 156349cc55cSDimitry Andric } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) { 157349cc55cSDimitry Andric auto &StubBlock = E.getTarget().getBlock(); 158349cc55cSDimitry Andric assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) && 159349cc55cSDimitry Andric "Stub block should be stub sized"); 160349cc55cSDimitry Andric assert(StubBlock.edges_size() == 1 && 161349cc55cSDimitry Andric "Stub block should only have one outgoing edge"); 162349cc55cSDimitry Andric 163349cc55cSDimitry Andric auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); 164349cc55cSDimitry Andric assert(GOTBlock.getSize() == G.getPointerSize() && 165349cc55cSDimitry Andric "GOT block should be pointer sized"); 166349cc55cSDimitry Andric assert(GOTBlock.edges_size() == 1 && 167349cc55cSDimitry Andric "GOT block should only have one outgoing edge"); 168349cc55cSDimitry Andric 169349cc55cSDimitry Andric auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); 17004eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset(); 17104eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 172349cc55cSDimitry Andric 173349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 174349cc55cSDimitry Andric if (isInRangeForImmS32(Displacement)) { 175349cc55cSDimitry Andric E.setKind(x86_64::BranchPCRel32); 176349cc55cSDimitry Andric E.setTarget(GOTTarget); 177349cc55cSDimitry Andric LLVM_DEBUG({ 178349cc55cSDimitry Andric dbgs() << " Replaced stub branch with direct branch:\n "; 179349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 180349cc55cSDimitry Andric dbgs() << "\n"; 181349cc55cSDimitry Andric }); 182349cc55cSDimitry Andric } 183349cc55cSDimitry Andric } 184349cc55cSDimitry Andric } 185349cc55cSDimitry Andric 186349cc55cSDimitry Andric return Error::success(); 187349cc55cSDimitry Andric } 188349cc55cSDimitry Andric 189fe6060f1SDimitry Andric } // end namespace x86_64 190fe6060f1SDimitry Andric } // end namespace jitlink 191fe6060f1SDimitry Andric } // end namespace llvm 192