xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
getEdgeKindName(Edge::Kind K)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 
optimizeGOTAndStubAccesses(LinkGraph & G)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