xref: /freebsd-src/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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