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"; 3106c3fb27SDimitry Andric case Pointer8: 3206c3fb27SDimitry Andric return "Pointer8"; 33fe6060f1SDimitry Andric case Delta64: 34fe6060f1SDimitry Andric return "Delta64"; 35fe6060f1SDimitry Andric case Delta32: 36fe6060f1SDimitry Andric return "Delta32"; 37*0fca6ea1SDimitry Andric case Delta8: 38*0fca6ea1SDimitry Andric return "Delta8"; 39fe6060f1SDimitry Andric case NegDelta64: 40fe6060f1SDimitry Andric return "NegDelta64"; 41fe6060f1SDimitry Andric case NegDelta32: 42fe6060f1SDimitry Andric return "NegDelta32"; 43349cc55cSDimitry Andric case Delta64FromGOT: 44349cc55cSDimitry Andric return "Delta64FromGOT"; 45972a253aSDimitry Andric case PCRel32: 46972a253aSDimitry Andric return "PCRel32"; 47fe6060f1SDimitry Andric case BranchPCRel32: 48fe6060f1SDimitry Andric return "BranchPCRel32"; 49fe6060f1SDimitry Andric case BranchPCRel32ToPtrJumpStub: 50fe6060f1SDimitry Andric return "BranchPCRel32ToPtrJumpStub"; 51349cc55cSDimitry Andric case BranchPCRel32ToPtrJumpStubBypassable: 52349cc55cSDimitry Andric return "BranchPCRel32ToPtrJumpStubBypassable"; 53fe6060f1SDimitry Andric case RequestGOTAndTransformToDelta32: 54fe6060f1SDimitry Andric return "RequestGOTAndTransformToDelta32"; 55349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64: 56349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64"; 57349cc55cSDimitry Andric case RequestGOTAndTransformToDelta64FromGOT: 58349cc55cSDimitry Andric return "RequestGOTAndTransformToDelta64FromGOT"; 59349cc55cSDimitry Andric case PCRel32GOTLoadREXRelaxable: 60349cc55cSDimitry Andric return "PCRel32GOTLoadREXRelaxable"; 61349cc55cSDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 62349cc55cSDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable"; 63fe6060f1SDimitry Andric case PCRel32GOTLoadRelaxable: 64fe6060f1SDimitry Andric return "PCRel32GOTLoadRelaxable"; 65fe6060f1SDimitry Andric case RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 66fe6060f1SDimitry Andric return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable"; 67349cc55cSDimitry Andric case PCRel32TLVPLoadREXRelaxable: 68349cc55cSDimitry Andric return "PCRel32TLVPLoadREXRelaxable"; 69349cc55cSDimitry Andric case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable: 70349cc55cSDimitry Andric return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable"; 71fe6060f1SDimitry Andric default: 72fe6060f1SDimitry Andric return getGenericEdgeKindName(static_cast<Edge::Kind>(K)); 73fe6060f1SDimitry Andric } 74fe6060f1SDimitry Andric } 75fe6060f1SDimitry Andric 76fe6060f1SDimitry Andric const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, 77fe6060f1SDimitry Andric 0x00, 0x00, 0x00, 0x00}; 78fe6060f1SDimitry Andric 79fe6060f1SDimitry Andric const char PointerJumpStubContent[6] = { 80fe6060f1SDimitry Andric static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; 81fe6060f1SDimitry Andric 82349cc55cSDimitry Andric Error optimizeGOTAndStubAccesses(LinkGraph &G) { 83349cc55cSDimitry Andric LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); 84349cc55cSDimitry Andric 85349cc55cSDimitry Andric for (auto *B : G.blocks()) 86349cc55cSDimitry Andric for (auto &E : B->edges()) { 87349cc55cSDimitry Andric if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable || 88349cc55cSDimitry Andric E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) { 89349cc55cSDimitry Andric #ifndef NDEBUG 90349cc55cSDimitry Andric bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable; 91349cc55cSDimitry Andric assert(E.getOffset() >= (REXPrefix ? 3u : 2u) && 92349cc55cSDimitry Andric "GOT edge occurs too early in block"); 93349cc55cSDimitry Andric #endif 94349cc55cSDimitry Andric auto *FixupData = reinterpret_cast<uint8_t *>( 95349cc55cSDimitry Andric const_cast<char *>(B->getContent().data())) + 96349cc55cSDimitry Andric E.getOffset(); 97349cc55cSDimitry Andric const uint8_t Op = FixupData[-2]; 98349cc55cSDimitry Andric const uint8_t ModRM = FixupData[-1]; 99349cc55cSDimitry Andric 100349cc55cSDimitry Andric auto &GOTEntryBlock = E.getTarget().getBlock(); 101349cc55cSDimitry Andric assert(GOTEntryBlock.getSize() == G.getPointerSize() && 102349cc55cSDimitry Andric "GOT entry block should be pointer sized"); 103349cc55cSDimitry Andric assert(GOTEntryBlock.edges_size() == 1 && 104349cc55cSDimitry Andric "GOT entry should only have one outgoing edge"); 105349cc55cSDimitry Andric auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget(); 10604eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 10704eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E); 108349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 10906c3fb27SDimitry Andric bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue()); 11006c3fb27SDimitry Andric bool DisplacementInRangeForImmS32 = isInt<32>(Displacement); 111349cc55cSDimitry Andric 112349cc55cSDimitry Andric // If both of the Target and displacement is out of range, then 113349cc55cSDimitry Andric // there isn't optimization chance. 114349cc55cSDimitry Andric if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32)) 115349cc55cSDimitry Andric continue; 116349cc55cSDimitry Andric 117349cc55cSDimitry Andric // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". 118349cc55cSDimitry Andric if (Op == 0x8b && DisplacementInRangeForImmS32) { 119349cc55cSDimitry Andric FixupData[-2] = 0x8d; 120349cc55cSDimitry Andric E.setKind(x86_64::Delta32); 121349cc55cSDimitry Andric E.setTarget(GOTTarget); 122349cc55cSDimitry Andric E.setAddend(E.getAddend() - 4); 123349cc55cSDimitry Andric LLVM_DEBUG({ 124349cc55cSDimitry Andric dbgs() << " Replaced GOT load wih LEA:\n "; 125349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 126349cc55cSDimitry Andric dbgs() << "\n"; 127349cc55cSDimitry Andric }); 128349cc55cSDimitry Andric continue; 129349cc55cSDimitry Andric } 130349cc55cSDimitry Andric 131349cc55cSDimitry Andric // Transform call/jmp instructions 132349cc55cSDimitry Andric if (Op == 0xff && TargetInRangeForImmU32) { 133349cc55cSDimitry Andric if (ModRM == 0x15) { 134349cc55cSDimitry Andric // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call 135349cc55cSDimitry Andric // foo" But lld convert it to "addr32 call foo, because that makes 136349cc55cSDimitry Andric // result expression to be a single instruction. 137349cc55cSDimitry Andric FixupData[-2] = 0x67; 138349cc55cSDimitry Andric FixupData[-1] = 0xe8; 139349cc55cSDimitry Andric LLVM_DEBUG({ 140349cc55cSDimitry Andric dbgs() << " replaced call instruction's memory operand wih imm " 141349cc55cSDimitry Andric "operand:\n "; 142349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 143349cc55cSDimitry Andric dbgs() << "\n"; 144349cc55cSDimitry Andric }); 145349cc55cSDimitry Andric } else { 146349cc55cSDimitry Andric // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop" 147349cc55cSDimitry Andric assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions"); 148349cc55cSDimitry Andric FixupData[-2] = 0xe9; 149349cc55cSDimitry Andric FixupData[3] = 0x90; 150349cc55cSDimitry Andric E.setOffset(E.getOffset() - 1); 151349cc55cSDimitry Andric LLVM_DEBUG({ 152349cc55cSDimitry Andric dbgs() << " replaced jmp instruction's memory operand wih imm " 153349cc55cSDimitry Andric "operand:\n "; 154349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 155349cc55cSDimitry Andric dbgs() << "\n"; 156349cc55cSDimitry Andric }); 157349cc55cSDimitry Andric } 158349cc55cSDimitry Andric E.setKind(x86_64::Pointer32); 159349cc55cSDimitry Andric E.setTarget(GOTTarget); 160349cc55cSDimitry Andric continue; 161349cc55cSDimitry Andric } 162349cc55cSDimitry Andric } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) { 163349cc55cSDimitry Andric auto &StubBlock = E.getTarget().getBlock(); 164349cc55cSDimitry Andric assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) && 165349cc55cSDimitry Andric "Stub block should be stub sized"); 166349cc55cSDimitry Andric assert(StubBlock.edges_size() == 1 && 167349cc55cSDimitry Andric "Stub block should only have one outgoing edge"); 168349cc55cSDimitry Andric 169349cc55cSDimitry Andric auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); 170349cc55cSDimitry Andric assert(GOTBlock.getSize() == G.getPointerSize() && 171349cc55cSDimitry Andric "GOT block should be pointer sized"); 172349cc55cSDimitry Andric assert(GOTBlock.edges_size() == 1 && 173349cc55cSDimitry Andric "GOT block should only have one outgoing edge"); 174349cc55cSDimitry Andric 175349cc55cSDimitry Andric auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); 17604eeddc0SDimitry Andric orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset(); 17704eeddc0SDimitry Andric orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 178349cc55cSDimitry Andric 179349cc55cSDimitry Andric int64_t Displacement = TargetAddr - EdgeAddr + 4; 18006c3fb27SDimitry Andric if (isInt<32>(Displacement)) { 181349cc55cSDimitry Andric E.setKind(x86_64::BranchPCRel32); 182349cc55cSDimitry Andric E.setTarget(GOTTarget); 183349cc55cSDimitry Andric LLVM_DEBUG({ 184349cc55cSDimitry Andric dbgs() << " Replaced stub branch with direct branch:\n "; 185349cc55cSDimitry Andric printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 186349cc55cSDimitry Andric dbgs() << "\n"; 187349cc55cSDimitry Andric }); 188349cc55cSDimitry Andric } 189349cc55cSDimitry Andric } 190349cc55cSDimitry Andric } 191349cc55cSDimitry Andric 192349cc55cSDimitry Andric return Error::success(); 193349cc55cSDimitry Andric } 194349cc55cSDimitry Andric 195fe6060f1SDimitry Andric } // end namespace x86_64 196fe6060f1SDimitry Andric } // end namespace jitlink 197fe6060f1SDimitry Andric } // end namespace llvm 198