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