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