Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp
35271 views
1
//===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Generic utilities for graphs representing x86-64 objects.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
14
15
#define DEBUG_TYPE "jitlink"
16
17
namespace llvm {
18
namespace jitlink {
19
namespace x86_64 {
20
21
const char *getEdgeKindName(Edge::Kind K) {
22
switch (K) {
23
case Pointer64:
24
return "Pointer64";
25
case Pointer32:
26
return "Pointer32";
27
case Pointer32Signed:
28
return "Pointer32Signed";
29
case Pointer16:
30
return "Pointer16";
31
case Pointer8:
32
return "Pointer8";
33
case Delta64:
34
return "Delta64";
35
case Delta32:
36
return "Delta32";
37
case Delta8:
38
return "Delta8";
39
case NegDelta64:
40
return "NegDelta64";
41
case NegDelta32:
42
return "NegDelta32";
43
case Delta64FromGOT:
44
return "Delta64FromGOT";
45
case PCRel32:
46
return "PCRel32";
47
case BranchPCRel32:
48
return "BranchPCRel32";
49
case BranchPCRel32ToPtrJumpStub:
50
return "BranchPCRel32ToPtrJumpStub";
51
case BranchPCRel32ToPtrJumpStubBypassable:
52
return "BranchPCRel32ToPtrJumpStubBypassable";
53
case RequestGOTAndTransformToDelta32:
54
return "RequestGOTAndTransformToDelta32";
55
case RequestGOTAndTransformToDelta64:
56
return "RequestGOTAndTransformToDelta64";
57
case RequestGOTAndTransformToDelta64FromGOT:
58
return "RequestGOTAndTransformToDelta64FromGOT";
59
case PCRel32GOTLoadREXRelaxable:
60
return "PCRel32GOTLoadREXRelaxable";
61
case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:
62
return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable";
63
case PCRel32GOTLoadRelaxable:
64
return "PCRel32GOTLoadRelaxable";
65
case RequestGOTAndTransformToPCRel32GOTLoadRelaxable:
66
return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable";
67
case PCRel32TLVPLoadREXRelaxable:
68
return "PCRel32TLVPLoadREXRelaxable";
69
case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable:
70
return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable";
71
default:
72
return getGenericEdgeKindName(static_cast<Edge::Kind>(K));
73
}
74
}
75
76
const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,
77
0x00, 0x00, 0x00, 0x00};
78
79
const char PointerJumpStubContent[6] = {
80
static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};
81
82
Error optimizeGOTAndStubAccesses(LinkGraph &G) {
83
LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");
84
85
for (auto *B : G.blocks())
86
for (auto &E : B->edges()) {
87
if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable ||
88
E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) {
89
#ifndef NDEBUG
90
bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;
91
assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&
92
"GOT edge occurs too early in block");
93
#endif
94
auto *FixupData = reinterpret_cast<uint8_t *>(
95
const_cast<char *>(B->getContent().data())) +
96
E.getOffset();
97
const uint8_t Op = FixupData[-2];
98
const uint8_t ModRM = FixupData[-1];
99
100
auto &GOTEntryBlock = E.getTarget().getBlock();
101
assert(GOTEntryBlock.getSize() == G.getPointerSize() &&
102
"GOT entry block should be pointer sized");
103
assert(GOTEntryBlock.edges_size() == 1 &&
104
"GOT entry should only have one outgoing edge");
105
auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget();
106
orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
107
orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E);
108
int64_t Displacement = TargetAddr - EdgeAddr + 4;
109
bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue());
110
bool DisplacementInRangeForImmS32 = isInt<32>(Displacement);
111
112
// If both of the Target and displacement is out of range, then
113
// there isn't optimization chance.
114
if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32))
115
continue;
116
117
// Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
118
if (Op == 0x8b && DisplacementInRangeForImmS32) {
119
FixupData[-2] = 0x8d;
120
E.setKind(x86_64::Delta32);
121
E.setTarget(GOTTarget);
122
E.setAddend(E.getAddend() - 4);
123
LLVM_DEBUG({
124
dbgs() << " Replaced GOT load wih LEA:\n ";
125
printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
126
dbgs() << "\n";
127
});
128
continue;
129
}
130
131
// Transform call/jmp instructions
132
if (Op == 0xff && TargetInRangeForImmU32) {
133
if (ModRM == 0x15) {
134
// ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call
135
// foo" But lld convert it to "addr32 call foo, because that makes
136
// result expression to be a single instruction.
137
FixupData[-2] = 0x67;
138
FixupData[-1] = 0xe8;
139
LLVM_DEBUG({
140
dbgs() << " replaced call instruction's memory operand wih imm "
141
"operand:\n ";
142
printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
143
dbgs() << "\n";
144
});
145
} else {
146
// Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop"
147
assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions");
148
FixupData[-2] = 0xe9;
149
FixupData[3] = 0x90;
150
E.setOffset(E.getOffset() - 1);
151
LLVM_DEBUG({
152
dbgs() << " replaced jmp instruction's memory operand wih imm "
153
"operand:\n ";
154
printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
155
dbgs() << "\n";
156
});
157
}
158
E.setKind(x86_64::Pointer32);
159
E.setTarget(GOTTarget);
160
continue;
161
}
162
} else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) {
163
auto &StubBlock = E.getTarget().getBlock();
164
assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) &&
165
"Stub block should be stub sized");
166
assert(StubBlock.edges_size() == 1 &&
167
"Stub block should only have one outgoing edge");
168
169
auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();
170
assert(GOTBlock.getSize() == G.getPointerSize() &&
171
"GOT block should be pointer sized");
172
assert(GOTBlock.edges_size() == 1 &&
173
"GOT block should only have one outgoing edge");
174
175
auto &GOTTarget = GOTBlock.edges().begin()->getTarget();
176
orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset();
177
orc::ExecutorAddr TargetAddr = GOTTarget.getAddress();
178
179
int64_t Displacement = TargetAddr - EdgeAddr + 4;
180
if (isInt<32>(Displacement)) {
181
E.setKind(x86_64::BranchPCRel32);
182
E.setTarget(GOTTarget);
183
LLVM_DEBUG({
184
dbgs() << " Replaced stub branch with direct branch:\n ";
185
printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind()));
186
dbgs() << "\n";
187
});
188
}
189
}
190
}
191
192
return Error::success();
193
}
194
195
} // end namespace x86_64
196
} // end namespace jitlink
197
} // end namespace llvm
198
199