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