Line data Source code
1 : //===- JITSymbol.h - JIT symbol abstraction ---------------------*- C++ -*-===//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 : //
10 : // Abstraction for target process addresses.
11 : //
12 : //===----------------------------------------------------------------------===//
13 :
14 : #ifndef LLVM_EXECUTIONENGINE_JITSYMBOL_H
15 : #define LLVM_EXECUTIONENGINE_JITSYMBOL_H
16 :
17 : #include <algorithm>
18 : #include <cassert>
19 : #include <cstddef>
20 : #include <cstdint>
21 : #include <functional>
22 : #include <map>
23 : #include <set>
24 : #include <string>
25 :
26 : #include "llvm/ADT/BitmaskEnum.h"
27 : #include "llvm/ADT/StringRef.h"
28 : #include "llvm/Support/Error.h"
29 :
30 : namespace llvm {
31 :
32 : class GlobalValue;
33 :
34 : namespace object {
35 :
36 : class SymbolRef;
37 :
38 : } // end namespace object
39 :
40 : /// Represents an address in the target process's address space.
41 : using JITTargetAddress = uint64_t;
42 :
43 : /// Flags for symbols in the JIT.
44 : class JITSymbolFlags {
45 : public:
46 : using UnderlyingType = uint8_t;
47 : using TargetFlagsType = uint64_t;
48 :
49 : enum FlagNames : UnderlyingType {
50 : None = 0,
51 : HasError = 1U << 0,
52 : Weak = 1U << 1,
53 : Common = 1U << 2,
54 : Absolute = 1U << 3,
55 : Exported = 1U << 4,
56 : Callable = 1U << 5,
57 : Lazy = 1U << 6,
58 : Materializing = 1U << 7,
59 : LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Materializing)
60 : };
61 :
62 0 : static JITSymbolFlags stripTransientFlags(JITSymbolFlags Orig) {
63 0 : return static_cast<FlagNames>(Orig.Flags & ~Lazy & ~Materializing);
64 : }
65 :
66 : /// Default-construct a JITSymbolFlags instance.
67 1088 : JITSymbolFlags() = default;
68 :
69 : /// Construct a JITSymbolFlags instance from the given flags.
70 12 : JITSymbolFlags(FlagNames Flags) : Flags(Flags) {}
71 :
72 : /// Construct a JITSymbolFlags instance from the given flags and target
73 : /// flags.
74 : JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags)
75 : : Flags(Flags), TargetFlags(TargetFlags) {}
76 :
77 : /// Implicitly convert to bool. Returs true if any flag is set.
78 4 : explicit operator bool() const { return Flags != None || TargetFlags != 0; }
79 :
80 : /// Compare for equality.
81 0 : bool operator==(const JITSymbolFlags &RHS) const {
82 8 : return Flags == RHS.Flags && TargetFlags == RHS.TargetFlags;
83 : }
84 :
85 : /// Bitwise AND-assignment for FlagNames.
86 0 : JITSymbolFlags &operator&=(const FlagNames &RHS) {
87 0 : Flags &= RHS;
88 0 : return *this;
89 : }
90 :
91 : /// Bitwise OR-assignment for FlagNames.
92 0 : JITSymbolFlags &operator|=(const FlagNames &RHS) {
93 0 : Flags |= RHS;
94 0 : return *this;
95 : }
96 :
97 : /// Return true if there was an error retrieving this symbol.
98 0 : bool hasError() const {
99 0 : return (Flags & HasError) == HasError;
100 : }
101 :
102 : /// Returns true if this is a lazy symbol.
103 : /// This flag is used internally by the JIT APIs to track
104 : /// materialization states.
105 0 : bool isLazy() const { return Flags & Lazy; }
106 :
107 : /// Returns true if this symbol is in the process of being
108 : /// materialized.
109 0 : bool isMaterializing() const { return Flags & Materializing; }
110 :
111 : /// Returns true if this symbol is fully materialized.
112 : /// (i.e. neither lazy, nor materializing).
113 : bool isMaterialized() const { return !(Flags & (Lazy | Materializing)); }
114 :
115 : /// Returns true if the Weak flag is set.
116 0 : bool isWeak() const {
117 0 : return (Flags & Weak) == Weak;
118 : }
119 :
120 : /// Returns true if the Common flag is set.
121 0 : bool isCommon() const {
122 0 : return (Flags & Common) == Common;
123 : }
124 :
125 : /// Returns true if the symbol isn't weak or common.
126 : bool isStrong() const {
127 20 : return !isWeak() && !isCommon();
128 : }
129 :
130 : /// Returns true if the Exported flag is set.
131 0 : bool isExported() const {
132 0 : return (Flags & Exported) == Exported;
133 : }
134 :
135 : /// Returns true if the given symbol is known to be callable.
136 0 : bool isCallable() const { return (Flags & Callable) == Callable; }
137 :
138 : /// Get the underlying flags value as an integer.
139 0 : UnderlyingType getRawFlagsValue() const {
140 0 : return static_cast<UnderlyingType>(Flags);
141 : }
142 :
143 : /// Return a reference to the target-specific flags.
144 : TargetFlagsType& getTargetFlags() { return TargetFlags; }
145 :
146 : /// Return a reference to the target-specific flags.
147 3 : const TargetFlagsType& getTargetFlags() const { return TargetFlags; }
148 :
149 : /// Construct a JITSymbolFlags value based on the flags of the given global
150 : /// value.
151 : static JITSymbolFlags fromGlobalValue(const GlobalValue &GV);
152 :
153 : /// Construct a JITSymbolFlags value based on the flags of the given libobject
154 : /// symbol.
155 : static Expected<JITSymbolFlags>
156 : fromObjectSymbol(const object::SymbolRef &Symbol);
157 :
158 : private:
159 : FlagNames Flags = None;
160 : TargetFlagsType TargetFlags = 0;
161 : };
162 :
163 0 : inline JITSymbolFlags operator&(const JITSymbolFlags &LHS,
164 : const JITSymbolFlags::FlagNames &RHS) {
165 0 : JITSymbolFlags Tmp = LHS;
166 0 : Tmp &= RHS;
167 0 : return Tmp;
168 : }
169 :
170 0 : inline JITSymbolFlags operator|(const JITSymbolFlags &LHS,
171 : const JITSymbolFlags::FlagNames &RHS) {
172 0 : JITSymbolFlags Tmp = LHS;
173 0 : Tmp |= RHS;
174 0 : return Tmp;
175 : }
176 :
177 : /// ARM-specific JIT symbol flags.
178 : /// FIXME: This should be moved into a target-specific header.
179 : class ARMJITSymbolFlags {
180 : public:
181 : ARMJITSymbolFlags() = default;
182 :
183 : enum FlagNames {
184 : None = 0,
185 : Thumb = 1 << 0
186 : };
187 :
188 : operator JITSymbolFlags::TargetFlagsType&() { return Flags; }
189 :
190 : static ARMJITSymbolFlags fromObjectSymbol(const object::SymbolRef &Symbol);
191 :
192 : private:
193 : JITSymbolFlags::TargetFlagsType Flags = 0;
194 : };
195 :
196 : /// Represents a symbol that has been evaluated to an address already.
197 : class JITEvaluatedSymbol {
198 : public:
199 : JITEvaluatedSymbol() = default;
200 :
201 : /// Create a 'null' symbol.
202 199 : JITEvaluatedSymbol(std::nullptr_t) {}
203 :
204 : /// Create a symbol for the given address and flags.
205 : JITEvaluatedSymbol(JITTargetAddress Address, JITSymbolFlags Flags)
206 912 : : Address(Address), Flags(Flags) {}
207 :
208 : /// An evaluated symbol converts to 'true' if its address is non-zero.
209 0 : explicit operator bool() const { return Address != 0; }
210 :
211 : /// Return the address of this symbol.
212 0 : JITTargetAddress getAddress() const { return Address; }
213 :
214 : /// Return the flags for this symbol.
215 0 : JITSymbolFlags getFlags() const { return Flags; }
216 :
217 : /// Set the flags for this symbol.
218 157 : void setFlags(JITSymbolFlags Flags) { this->Flags = std::move(Flags); }
219 :
220 : private:
221 : JITTargetAddress Address = 0;
222 : JITSymbolFlags Flags;
223 : };
224 :
225 : /// Represents a symbol in the JIT.
226 : class JITSymbol {
227 : public:
228 : using GetAddressFtor = std::function<Expected<JITTargetAddress>()>;
229 :
230 : /// Create a 'null' symbol, used to represent a "symbol not found"
231 : /// result from a successful (non-erroneous) lookup.
232 : JITSymbol(std::nullptr_t)
233 817 : : CachedAddr(0) {}
234 :
235 : /// Create a JITSymbol representing an error in the symbol lookup
236 : /// process (e.g. a network failure during a remote lookup).
237 : JITSymbol(Error Err)
238 : : Err(std::move(Err)), Flags(JITSymbolFlags::HasError) {}
239 :
240 : /// Create a symbol for a definition with a known address.
241 : JITSymbol(JITTargetAddress Addr, JITSymbolFlags Flags)
242 283 : : CachedAddr(Addr), Flags(Flags) {}
243 :
244 : /// Construct a JITSymbol from a JITEvaluatedSymbol.
245 : JITSymbol(JITEvaluatedSymbol Sym)
246 443 : : CachedAddr(Sym.getAddress()), Flags(Sym.getFlags()) {}
247 :
248 : /// Create a symbol for a definition that doesn't have a known address
249 : /// yet.
250 : /// @param GetAddress A functor to materialize a definition (fixing the
251 : /// address) on demand.
252 : ///
253 : /// This constructor allows a JIT layer to provide a reference to a symbol
254 : /// definition without actually materializing the definition up front. The
255 : /// user can materialize the definition at any time by calling the getAddress
256 : /// method.
257 : JITSymbol(GetAddressFtor GetAddress, JITSymbolFlags Flags)
258 192 : : GetAddress(std::move(GetAddress)), CachedAddr(0), Flags(Flags) {}
259 :
260 : JITSymbol(const JITSymbol&) = delete;
261 : JITSymbol& operator=(const JITSymbol&) = delete;
262 :
263 556 : JITSymbol(JITSymbol &&Other)
264 556 : : GetAddress(std::move(Other.GetAddress)), Flags(std::move(Other.Flags)) {
265 1112 : if (Flags.hasError())
266 : Err = std::move(Other.Err);
267 : else
268 556 : CachedAddr = std::move(Other.CachedAddr);
269 556 : }
270 :
271 10 : JITSymbol& operator=(JITSymbol &&Other) {
272 10 : GetAddress = std::move(Other.GetAddress);
273 10 : Flags = std::move(Other.Flags);
274 20 : if (Flags.hasError())
275 : Err = std::move(Other.Err);
276 : else
277 10 : CachedAddr = std::move(Other.CachedAddr);
278 10 : return *this;
279 : }
280 :
281 4972 : ~JITSymbol() {
282 4972 : if (Flags.hasError())
283 : Err.~Error();
284 : else
285 : CachedAddr.~JITTargetAddress();
286 2486 : }
287 :
288 : /// Returns true if the symbol exists, false otherwise.
289 : explicit operator bool() const {
290 4142 : return !Flags.hasError() && (CachedAddr || GetAddress);
291 : }
292 :
293 : /// Move the error field value out of this JITSymbol.
294 : Error takeError() {
295 402 : if (Flags.hasError())
296 : return std::move(Err);
297 : return Error::success();
298 : }
299 :
300 : /// Get the address of the symbol in the target address space. Returns
301 : /// '0' if the symbol does not exist.
302 698 : Expected<JITTargetAddress> getAddress() {
303 : assert(!Flags.hasError() && "getAddress called on error value");
304 698 : if (GetAddress) {
305 190 : if (auto CachedAddrOrErr = GetAddress()) {
306 : GetAddress = nullptr;
307 190 : CachedAddr = *CachedAddrOrErr;
308 : assert(CachedAddr && "Symbol could not be materialized.");
309 : } else
310 : return CachedAddrOrErr.takeError();
311 : }
312 : return CachedAddr;
313 : }
314 :
315 0 : JITSymbolFlags getFlags() const { return Flags; }
316 :
317 : private:
318 : GetAddressFtor GetAddress;
319 : union {
320 : JITTargetAddress CachedAddr;
321 : Error Err;
322 : };
323 : JITSymbolFlags Flags;
324 : };
325 :
326 : /// Symbol resolution interface.
327 : ///
328 : /// Allows symbol flags and addresses to be looked up by name.
329 : /// Symbol queries are done in bulk (i.e. you request resolution of a set of
330 : /// symbols, rather than a single one) to reduce IPC overhead in the case of
331 : /// remote JITing, and expose opportunities for parallel compilation.
332 : class JITSymbolResolver {
333 : public:
334 : using LookupSet = std::set<StringRef>;
335 : using LookupResult = std::map<StringRef, JITEvaluatedSymbol>;
336 : using OnResolvedFunction = std::function<void(Expected<LookupResult>)>;
337 :
338 0 : virtual ~JITSymbolResolver() = default;
339 :
340 : /// Returns the fully resolved address and flags for each of the given
341 : /// symbols.
342 : ///
343 : /// This method will return an error if any of the given symbols can not be
344 : /// resolved, or if the resolution process itself triggers an error.
345 : virtual void lookup(const LookupSet &Symbols,
346 : OnResolvedFunction OnResolved) = 0;
347 :
348 : /// Returns the subset of the given symbols that should be materialized by
349 : /// the caller. Only weak/common symbols should be looked up, as strong
350 : /// definitions are implicitly always part of the caller's responsibility.
351 : virtual Expected<LookupSet>
352 : getResponsibilitySet(const LookupSet &Symbols) = 0;
353 :
354 : private:
355 : virtual void anchor();
356 : };
357 :
358 : /// Legacy symbol resolution interface.
359 : class LegacyJITSymbolResolver : public JITSymbolResolver {
360 : public:
361 : /// Performs lookup by, for each symbol, first calling
362 : /// findSymbolInLogicalDylib and if that fails calling
363 : /// findSymbol.
364 : void lookup(const LookupSet &Symbols, OnResolvedFunction OnResolved) final;
365 :
366 : /// Performs flags lookup by calling findSymbolInLogicalDylib and
367 : /// returning the flags value for that symbol.
368 : Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) final;
369 :
370 : /// This method returns the address of the specified symbol if it exists
371 : /// within the logical dynamic library represented by this JITSymbolResolver.
372 : /// Unlike findSymbol, queries through this interface should return addresses
373 : /// for hidden symbols.
374 : ///
375 : /// This is of particular importance for the Orc JIT APIs, which support lazy
376 : /// compilation by breaking up modules: Each of those broken out modules
377 : /// must be able to resolve hidden symbols provided by the others. Clients
378 : /// writing memory managers for MCJIT can usually ignore this method.
379 : ///
380 : /// This method will be queried by RuntimeDyld when checking for previous
381 : /// definitions of common symbols.
382 : virtual JITSymbol findSymbolInLogicalDylib(const std::string &Name) = 0;
383 :
384 : /// This method returns the address of the specified function or variable.
385 : /// It is used to resolve symbols during module linking.
386 : ///
387 : /// If the returned symbol's address is equal to ~0ULL then RuntimeDyld will
388 : /// skip all relocations for that symbol, and the client will be responsible
389 : /// for handling them manually.
390 : virtual JITSymbol findSymbol(const std::string &Name) = 0;
391 :
392 : private:
393 : virtual void anchor();
394 : };
395 :
396 : } // end namespace llvm
397 :
398 : #endif // LLVM_EXECUTIONENGINE_JITSYMBOL_H
|