Bug Summary

File:build/source/llvm/include/llvm/ADT/FunctionExtras.h
Warning:line 202, column 5
Undefined or garbage value returned to caller

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name JITSymbol.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -D _DEBUG -D _GLIBCXX_ASSERTIONS -D _GNU_SOURCE -D _LIBCPP_ENABLE_ASSERTIONS -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I lib/ExecutionEngine/RuntimeDyld -I /build/source/llvm/lib/ExecutionEngine/RuntimeDyld -I include -I /build/source/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1683717183 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-05-10-133810-16478-1 -x c++ /build/source/llvm/lib/ExecutionEngine/RuntimeDyld/JITSymbol.cpp

/build/source/llvm/lib/ExecutionEngine/RuntimeDyld/JITSymbol.cpp

1//===----------- JITSymbol.cpp - JITSymbol class implementation -----------===//
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// JITSymbol class implementation plus helper functions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ExecutionEngine/JITSymbol.h"
14#include "llvm/IR/Function.h"
15#include "llvm/IR/GlobalAlias.h"
16#include "llvm/IR/GlobalValue.h"
17#include "llvm/IR/ModuleSummaryIndex.h"
18#include "llvm/Object/ObjectFile.h"
19
20using namespace llvm;
21
22JITSymbolFlags llvm::JITSymbolFlags::fromGlobalValue(const GlobalValue &GV) {
23 assert(GV.hasName() && "Can't get flags for anonymous symbol")(static_cast <bool> (GV.hasName() && "Can't get flags for anonymous symbol"
) ? void (0) : __assert_fail ("GV.hasName() && \"Can't get flags for anonymous symbol\""
, "llvm/lib/ExecutionEngine/RuntimeDyld/JITSymbol.cpp", 23, __extension__
__PRETTY_FUNCTION__))
;
24
25 JITSymbolFlags Flags = JITSymbolFlags::None;
26 if (GV.hasWeakLinkage() || GV.hasLinkOnceLinkage())
27 Flags |= JITSymbolFlags::Weak;
28 if (GV.hasCommonLinkage())
29 Flags |= JITSymbolFlags::Common;
30 if (!GV.hasLocalLinkage() && !GV.hasHiddenVisibility())
31 Flags |= JITSymbolFlags::Exported;
32
33 if (isa<Function>(GV))
34 Flags |= JITSymbolFlags::Callable;
35 else if (isa<GlobalAlias>(GV) &&
36 isa<Function>(cast<GlobalAlias>(GV).getAliasee()))
37 Flags |= JITSymbolFlags::Callable;
38
39 // Check for a linker-private-global-prefix on the symbol name, in which
40 // case it must be marked as non-exported.
41 if (auto *M = GV.getParent()) {
42 const auto &DL = M->getDataLayout();
43 StringRef LPGP = DL.getLinkerPrivateGlobalPrefix();
44 if (!LPGP.empty() && GV.getName().front() == '\01' &&
45 GV.getName().substr(1).startswith(LPGP))
46 Flags &= ~JITSymbolFlags::Exported;
47 }
48
49 return Flags;
50}
51
52JITSymbolFlags llvm::JITSymbolFlags::fromSummary(GlobalValueSummary *S) {
53 JITSymbolFlags Flags = JITSymbolFlags::None;
54 auto L = S->linkage();
55 if (GlobalValue::isWeakLinkage(L) || GlobalValue::isLinkOnceLinkage(L))
56 Flags |= JITSymbolFlags::Weak;
57 if (GlobalValue::isCommonLinkage(L))
58 Flags |= JITSymbolFlags::Common;
59 if (GlobalValue::isExternalLinkage(L) || GlobalValue::isExternalWeakLinkage(L))
60 Flags |= JITSymbolFlags::Exported;
61
62 if (isa<FunctionSummary>(S))
63 Flags |= JITSymbolFlags::Callable;
64
65 return Flags;
66}
67
68Expected<JITSymbolFlags>
69llvm::JITSymbolFlags::fromObjectSymbol(const object::SymbolRef &Symbol) {
70 Expected<uint32_t> SymbolFlagsOrErr = Symbol.getFlags();
71 if (!SymbolFlagsOrErr)
72 // TODO: Test this error.
73 return SymbolFlagsOrErr.takeError();
74
75 JITSymbolFlags Flags = JITSymbolFlags::None;
76 if (*SymbolFlagsOrErr & object::BasicSymbolRef::SF_Weak)
77 Flags |= JITSymbolFlags::Weak;
78 if (*SymbolFlagsOrErr & object::BasicSymbolRef::SF_Common)
79 Flags |= JITSymbolFlags::Common;
80 if (*SymbolFlagsOrErr & object::BasicSymbolRef::SF_Exported)
81 Flags |= JITSymbolFlags::Exported;
82
83 auto SymbolType = Symbol.getType();
84 if (!SymbolType)
85 return SymbolType.takeError();
86
87 if (*SymbolType == object::SymbolRef::ST_Function)
88 Flags |= JITSymbolFlags::Callable;
89
90 return Flags;
91}
92
93ARMJITSymbolFlags
94llvm::ARMJITSymbolFlags::fromObjectSymbol(const object::SymbolRef &Symbol) {
95 Expected<uint32_t> SymbolFlagsOrErr = Symbol.getFlags();
96 if (!SymbolFlagsOrErr)
97 // TODO: Actually report errors helpfully.
98 report_fatal_error(SymbolFlagsOrErr.takeError());
99 ARMJITSymbolFlags Flags;
100 if (*SymbolFlagsOrErr & object::BasicSymbolRef::SF_Thumb)
101 Flags |= ARMJITSymbolFlags::Thumb;
102 return Flags;
103}
104
105/// Performs lookup by, for each symbol, first calling
106/// findSymbolInLogicalDylib and if that fails calling
107/// findSymbol.
108void LegacyJITSymbolResolver::lookup(const LookupSet &Symbols,
109 OnResolvedFunction OnResolved) {
110 JITSymbolResolver::LookupResult Result;
111 for (auto &Symbol : Symbols) {
112 std::string SymName = Symbol.str();
113 if (auto Sym = findSymbolInLogicalDylib(SymName)) {
1
Taking true branch
114 if (auto AddrOrErr = Sym.getAddress())
2
Calling 'JITSymbol::getAddress'
115 Result[Symbol] = JITEvaluatedSymbol(*AddrOrErr, Sym.getFlags());
116 else {
117 OnResolved(AddrOrErr.takeError());
118 return;
119 }
120 } else if (auto Err = Sym.takeError()) {
121 OnResolved(std::move(Err));
122 return;
123 } else {
124 // findSymbolInLogicalDylib failed. Lets try findSymbol.
125 if (auto Sym = findSymbol(SymName)) {
126 if (auto AddrOrErr = Sym.getAddress())
127 Result[Symbol] = JITEvaluatedSymbol(*AddrOrErr, Sym.getFlags());
128 else {
129 OnResolved(AddrOrErr.takeError());
130 return;
131 }
132 } else if (auto Err = Sym.takeError()) {
133 OnResolved(std::move(Err));
134 return;
135 } else {
136 OnResolved(make_error<StringError>("Symbol not found: " + Symbol,
137 inconvertibleErrorCode()));
138 return;
139 }
140 }
141 }
142
143 OnResolved(std::move(Result));
144}
145
146/// Performs flags lookup by calling findSymbolInLogicalDylib and
147/// returning the flags value for that symbol.
148Expected<JITSymbolResolver::LookupSet>
149LegacyJITSymbolResolver::getResponsibilitySet(const LookupSet &Symbols) {
150 JITSymbolResolver::LookupSet Result;
151
152 for (auto &Symbol : Symbols) {
153 std::string SymName = Symbol.str();
154 if (auto Sym = findSymbolInLogicalDylib(SymName)) {
155 // If there's an existing def but it is not strong, then the caller is
156 // responsible for it.
157 if (!Sym.getFlags().isStrong())
158 Result.insert(Symbol);
159 } else if (auto Err = Sym.takeError())
160 return std::move(Err);
161 else {
162 // If there is no existing definition then the caller is responsible for
163 // it.
164 Result.insert(Symbol);
165 }
166 }
167
168 return std::move(Result);
169}

/build/source/llvm/include/llvm/ExecutionEngine/JITSymbol.h

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
30namespace llvm {
31
32class GlobalValue;
33class GlobalValueSummary;
34
35namespace object {
36
37class SymbolRef;
38
39} // end namespace object
40
41/// Represents an address in the target process's address space.
42using JITTargetAddress = uint64_t;
43
44/// Convert a JITTargetAddress to a pointer.
45///
46/// Note: This is a raw cast of the address bit pattern to the given pointer
47/// type. When casting to a function pointer in order to execute JIT'd code
48/// jitTargetAddressToFunction should be preferred, as it will also perform
49/// pointer signing on targets that require it.
50template <typename T> T jitTargetAddressToPointer(JITTargetAddress Addr) {
51 static_assert(std::is_pointer<T>::value, "T must be a pointer type");
52 uintptr_t IntPtr = static_cast<uintptr_t>(Addr);
53 assert(IntPtr == Addr && "JITTargetAddress value out of range for uintptr_t")(static_cast <bool> (IntPtr == Addr && "JITTargetAddress value out of range for uintptr_t"
) ? void (0) : __assert_fail ("IntPtr == Addr && \"JITTargetAddress value out of range for uintptr_t\""
, "llvm/include/llvm/ExecutionEngine/JITSymbol.h", 53, __extension__
__PRETTY_FUNCTION__))
;
54 return reinterpret_cast<T>(IntPtr);
55}
56
57/// Convert a JITTargetAddress to a callable function pointer.
58///
59/// Casts the given address to a callable function pointer. This operation
60/// will perform pointer signing for platforms that require it (e.g. arm64e).
61template <typename T> T jitTargetAddressToFunction(JITTargetAddress Addr) {
62 static_assert(std::is_pointer<T>::value &&
63 std::is_function<std::remove_pointer_t<T>>::value,
64 "T must be a function pointer type");
65 return jitTargetAddressToPointer<T>(Addr);
66}
67
68/// Convert a pointer to a JITTargetAddress.
69template <typename T> JITTargetAddress pointerToJITTargetAddress(T *Ptr) {
70 return static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(Ptr));
71}
72
73/// Flags for symbols in the JIT.
74class JITSymbolFlags {
75public:
76 using UnderlyingType = uint8_t;
77 using TargetFlagsType = uint8_t;
78
79 enum FlagNames : UnderlyingType {
80 None = 0,
81 HasError = 1U << 0,
82 Weak = 1U << 1,
83 Common = 1U << 2,
84 Absolute = 1U << 3,
85 Exported = 1U << 4,
86 Callable = 1U << 5,
87 MaterializationSideEffectsOnly = 1U << 6,
88 LLVM_MARK_AS_BITMASK_ENUM( // LargestValue =LLVM_BITMASK_LARGEST_ENUMERATOR = MaterializationSideEffectsOnly
89 MaterializationSideEffectsOnly)LLVM_BITMASK_LARGEST_ENUMERATOR = MaterializationSideEffectsOnly
90 };
91
92 /// Default-construct a JITSymbolFlags instance.
93 JITSymbolFlags() = default;
94
95 /// Construct a JITSymbolFlags instance from the given flags.
96 JITSymbolFlags(FlagNames Flags) : Flags(Flags) {}
97
98 /// Construct a JITSymbolFlags instance from the given flags and target
99 /// flags.
100 JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags)
101 : TargetFlags(TargetFlags), Flags(Flags) {}
102
103 /// Implicitly convert to bool. Returs true if any flag is set.
104 explicit operator bool() const { return Flags != None || TargetFlags != 0; }
105
106 /// Compare for equality.
107 bool operator==(const JITSymbolFlags &RHS) const {
108 return Flags == RHS.Flags && TargetFlags == RHS.TargetFlags;
109 }
110
111 /// Bitwise AND-assignment for FlagNames.
112 JITSymbolFlags &operator&=(const FlagNames &RHS) {
113 Flags &= RHS;
114 return *this;
115 }
116
117 /// Bitwise OR-assignment for FlagNames.
118 JITSymbolFlags &operator|=(const FlagNames &RHS) {
119 Flags |= RHS;
120 return *this;
121 }
122
123 /// Return true if there was an error retrieving this symbol.
124 bool hasError() const {
125 return (Flags & HasError) == HasError;
126 }
127
128 /// Returns true if the Weak flag is set.
129 bool isWeak() const {
130 return (Flags & Weak) == Weak;
131 }
132
133 /// Returns true if the Common flag is set.
134 bool isCommon() const {
135 return (Flags & Common) == Common;
136 }
137
138 /// Returns true if the symbol isn't weak or common.
139 bool isStrong() const {
140 return !isWeak() && !isCommon();
141 }
142
143 /// Returns true if the Exported flag is set.
144 bool isExported() const {
145 return (Flags & Exported) == Exported;
146 }
147
148 /// Returns true if the given symbol is known to be callable.
149 bool isCallable() const { return (Flags & Callable) == Callable; }
150
151 /// Returns true if this symbol is a materialization-side-effects-only
152 /// symbol. Such symbols do not have a real address. They exist to trigger
153 /// and support synchronization of materialization side effects, e.g. for
154 /// collecting initialization information. These symbols will vanish from
155 /// the symbol table immediately upon reaching the ready state, and will
156 /// appear to queries as if they were never defined (except that query
157 /// callback execution will be delayed until they reach the ready state).
158 /// MaterializationSideEffectOnly symbols should only be queried using the
159 /// SymbolLookupFlags::WeaklyReferencedSymbol flag (see
160 /// llvm/include/llvm/ExecutionEngine/Orc/Core.h).
161 bool hasMaterializationSideEffectsOnly() const {
162 return (Flags & MaterializationSideEffectsOnly) ==
163 MaterializationSideEffectsOnly;
164 }
165
166 /// Get the underlying flags value as an integer.
167 UnderlyingType getRawFlagsValue() const {
168 return static_cast<UnderlyingType>(Flags);
169 }
170
171 /// Return a reference to the target-specific flags.
172 TargetFlagsType& getTargetFlags() { return TargetFlags; }
173
174 /// Return a reference to the target-specific flags.
175 const TargetFlagsType& getTargetFlags() const { return TargetFlags; }
176
177 /// Construct a JITSymbolFlags value based on the flags of the given global
178 /// value.
179 static JITSymbolFlags fromGlobalValue(const GlobalValue &GV);
180
181 /// Construct a JITSymbolFlags value based on the flags of the given global
182 /// value summary.
183 static JITSymbolFlags fromSummary(GlobalValueSummary *S);
184
185 /// Construct a JITSymbolFlags value based on the flags of the given libobject
186 /// symbol.
187 static Expected<JITSymbolFlags>
188 fromObjectSymbol(const object::SymbolRef &Symbol);
189
190private:
191 TargetFlagsType TargetFlags = 0;
192 FlagNames Flags = None;
193};
194
195inline JITSymbolFlags operator&(const JITSymbolFlags &LHS,
196 const JITSymbolFlags::FlagNames &RHS) {
197 JITSymbolFlags Tmp = LHS;
198 Tmp &= RHS;
199 return Tmp;
200}
201
202inline JITSymbolFlags operator|(const JITSymbolFlags &LHS,
203 const JITSymbolFlags::FlagNames &RHS) {
204 JITSymbolFlags Tmp = LHS;
205 Tmp |= RHS;
206 return Tmp;
207}
208
209/// ARM-specific JIT symbol flags.
210/// FIXME: This should be moved into a target-specific header.
211class ARMJITSymbolFlags {
212public:
213 ARMJITSymbolFlags() = default;
214
215 enum FlagNames {
216 None = 0,
217 Thumb = 1 << 0
218 };
219
220 operator JITSymbolFlags::TargetFlagsType&() { return Flags; }
221
222 static ARMJITSymbolFlags fromObjectSymbol(const object::SymbolRef &Symbol);
223
224private:
225 JITSymbolFlags::TargetFlagsType Flags = 0;
226};
227
228/// Represents a symbol that has been evaluated to an address already.
229class JITEvaluatedSymbol {
230public:
231 JITEvaluatedSymbol() = default;
232
233 /// Create a 'null' symbol.
234 JITEvaluatedSymbol(std::nullptr_t) {}
235
236 /// Create a symbol for the given address and flags.
237 JITEvaluatedSymbol(JITTargetAddress Address, JITSymbolFlags Flags)
238 : Address(Address), Flags(Flags) {}
239
240 /// Create a symbol from the given pointer with the given flags.
241 template <typename T>
242 static JITEvaluatedSymbol
243 fromPointer(T *P, JITSymbolFlags Flags = JITSymbolFlags::Exported) {
244 return JITEvaluatedSymbol(pointerToJITTargetAddress(P), Flags);
245 }
246
247 /// An evaluated symbol converts to 'true' if its address is non-zero.
248 explicit operator bool() const { return Address != 0; }
249
250 /// Return the address of this symbol.
251 JITTargetAddress getAddress() const { return Address; }
252
253 /// Return the flags for this symbol.
254 JITSymbolFlags getFlags() const { return Flags; }
255
256 /// Set the flags for this symbol.
257 void setFlags(JITSymbolFlags Flags) { this->Flags = std::move(Flags); }
258
259private:
260 JITTargetAddress Address = 0;
261 JITSymbolFlags Flags;
262};
263
264/// Represents a symbol in the JIT.
265class JITSymbol {
266public:
267 using GetAddressFtor = unique_function<Expected<JITTargetAddress>()>;
268
269 /// Create a 'null' symbol, used to represent a "symbol not found"
270 /// result from a successful (non-erroneous) lookup.
271 JITSymbol(std::nullptr_t)
272 : CachedAddr(0) {}
273
274 /// Create a JITSymbol representing an error in the symbol lookup
275 /// process (e.g. a network failure during a remote lookup).
276 JITSymbol(Error Err)
277 : Err(std::move(Err)), Flags(JITSymbolFlags::HasError) {}
278
279 /// Create a symbol for a definition with a known address.
280 JITSymbol(JITTargetAddress Addr, JITSymbolFlags Flags)
281 : CachedAddr(Addr), Flags(Flags) {}
282
283 /// Construct a JITSymbol from a JITEvaluatedSymbol.
284 JITSymbol(JITEvaluatedSymbol Sym)
285 : CachedAddr(Sym.getAddress()), Flags(Sym.getFlags()) {}
286
287 /// Create a symbol for a definition that doesn't have a known address
288 /// yet.
289 /// @param GetAddress A functor to materialize a definition (fixing the
290 /// address) on demand.
291 ///
292 /// This constructor allows a JIT layer to provide a reference to a symbol
293 /// definition without actually materializing the definition up front. The
294 /// user can materialize the definition at any time by calling the getAddress
295 /// method.
296 JITSymbol(GetAddressFtor GetAddress, JITSymbolFlags Flags)
297 : GetAddress(std::move(GetAddress)), CachedAddr(0), Flags(Flags) {}
298
299 JITSymbol(const JITSymbol&) = delete;
300 JITSymbol& operator=(const JITSymbol&) = delete;
301
302 JITSymbol(JITSymbol &&Other)
303 : GetAddress(std::move(Other.GetAddress)), Flags(std::move(Other.Flags)) {
304 if (Flags.hasError())
305 Err = std::move(Other.Err);
306 else
307 CachedAddr = std::move(Other.CachedAddr);
308 }
309
310 JITSymbol& operator=(JITSymbol &&Other) {
311 GetAddress = std::move(Other.GetAddress);
312 Flags = std::move(Other.Flags);
313 if (Flags.hasError())
314 Err = std::move(Other.Err);
315 else
316 CachedAddr = std::move(Other.CachedAddr);
317 return *this;
318 }
319
320 ~JITSymbol() {
321 if (Flags.hasError())
322 Err.~Error();
323 else
324 CachedAddr.~JITTargetAddress();
325 }
326
327 /// Returns true if the symbol exists, false otherwise.
328 explicit operator bool() const {
329 return !Flags.hasError() && (CachedAddr || GetAddress);
330 }
331
332 /// Move the error field value out of this JITSymbol.
333 Error takeError() {
334 if (Flags.hasError())
335 return std::move(Err);
336 return Error::success();
337 }
338
339 /// Get the address of the symbol in the target address space. Returns
340 /// '0' if the symbol does not exist.
341 Expected<JITTargetAddress> getAddress() {
342 assert(!Flags.hasError() && "getAddress called on error value")(static_cast <bool> (!Flags.hasError() && "getAddress called on error value"
) ? void (0) : __assert_fail ("!Flags.hasError() && \"getAddress called on error value\""
, "llvm/include/llvm/ExecutionEngine/JITSymbol.h", 342, __extension__
__PRETTY_FUNCTION__))
;
3
'?' condition is true
343 if (GetAddress) {
4
Taking true branch
344 if (auto CachedAddrOrErr = GetAddress()) {
5
Taking true branch
345 GetAddress = nullptr;
6
Calling constructor for 'unique_function<llvm::Expected<unsigned long> ()>'
11
Returning from constructor for 'unique_function<llvm::Expected<unsigned long> ()>'
12
Calling defaulted move assignment operator for 'unique_function<llvm::Expected<unsigned long> ()>'
23
Returning from move assignment operator for 'unique_function<llvm::Expected<unsigned long> ()>'
24
Calling implicit destructor for 'unique_function<llvm::Expected<unsigned long> ()>'
25
Calling '~UniqueFunctionBase'
346 CachedAddr = *CachedAddrOrErr;
347 assert(CachedAddr && "Symbol could not be materialized.")(static_cast <bool> (CachedAddr && "Symbol could not be materialized."
) ? void (0) : __assert_fail ("CachedAddr && \"Symbol could not be materialized.\""
, "llvm/include/llvm/ExecutionEngine/JITSymbol.h", 347, __extension__
__PRETTY_FUNCTION__))
;
348 } else
349 return CachedAddrOrErr.takeError();
350 }
351 return CachedAddr;
352 }
353
354 JITSymbolFlags getFlags() const { return Flags; }
355
356private:
357 GetAddressFtor GetAddress;
358 union {
359 JITTargetAddress CachedAddr;
360 Error Err;
361 };
362 JITSymbolFlags Flags;
363};
364
365/// Symbol resolution interface.
366///
367/// Allows symbol flags and addresses to be looked up by name.
368/// Symbol queries are done in bulk (i.e. you request resolution of a set of
369/// symbols, rather than a single one) to reduce IPC overhead in the case of
370/// remote JITing, and expose opportunities for parallel compilation.
371class JITSymbolResolver {
372public:
373 using LookupSet = std::set<StringRef>;
374 using LookupResult = std::map<StringRef, JITEvaluatedSymbol>;
375 using OnResolvedFunction = unique_function<void(Expected<LookupResult>)>;
376
377 virtual ~JITSymbolResolver() = default;
378
379 /// Returns the fully resolved address and flags for each of the given
380 /// symbols.
381 ///
382 /// This method will return an error if any of the given symbols can not be
383 /// resolved, or if the resolution process itself triggers an error.
384 virtual void lookup(const LookupSet &Symbols,
385 OnResolvedFunction OnResolved) = 0;
386
387 /// Returns the subset of the given symbols that should be materialized by
388 /// the caller. Only weak/common symbols should be looked up, as strong
389 /// definitions are implicitly always part of the caller's responsibility.
390 virtual Expected<LookupSet>
391 getResponsibilitySet(const LookupSet &Symbols) = 0;
392
393 /// Specify if this resolver can return valid symbols with zero value.
394 virtual bool allowsZeroSymbols() { return false; }
395
396private:
397 virtual void anchor();
398};
399
400/// Legacy symbol resolution interface.
401class LegacyJITSymbolResolver : public JITSymbolResolver {
402public:
403 /// Performs lookup by, for each symbol, first calling
404 /// findSymbolInLogicalDylib and if that fails calling
405 /// findSymbol.
406 void lookup(const LookupSet &Symbols, OnResolvedFunction OnResolved) final;
407
408 /// Performs flags lookup by calling findSymbolInLogicalDylib and
409 /// returning the flags value for that symbol.
410 Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) final;
411
412 /// This method returns the address of the specified symbol if it exists
413 /// within the logical dynamic library represented by this JITSymbolResolver.
414 /// Unlike findSymbol, queries through this interface should return addresses
415 /// for hidden symbols.
416 ///
417 /// This is of particular importance for the Orc JIT APIs, which support lazy
418 /// compilation by breaking up modules: Each of those broken out modules
419 /// must be able to resolve hidden symbols provided by the others. Clients
420 /// writing memory managers for MCJIT can usually ignore this method.
421 ///
422 /// This method will be queried by RuntimeDyld when checking for previous
423 /// definitions of common symbols.
424 virtual JITSymbol findSymbolInLogicalDylib(const std::string &Name) = 0;
425
426 /// This method returns the address of the specified function or variable.
427 /// It is used to resolve symbols during module linking.
428 ///
429 /// If the returned symbol's address is equal to ~0ULL then RuntimeDyld will
430 /// skip all relocations for that symbol, and the client will be responsible
431 /// for handling them manually.
432 virtual JITSymbol findSymbol(const std::string &Name) = 0;
433
434private:
435 void anchor() override;
436};
437
438} // end namespace llvm
439
440#endif // LLVM_EXECUTIONENGINE_JITSYMBOL_H

/build/source/llvm/include/llvm/ADT/FunctionExtras.h

1//===- FunctionExtras.h - Function type erasure utilities -------*- 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/// \file
9/// This file provides a collection of function (or more generally, callable)
10/// type erasure utilities supplementing those provided by the standard library
11/// in `<function>`.
12///
13/// It provides `unique_function`, which works like `std::function` but supports
14/// move-only callable objects and const-qualification.
15///
16/// Future plans:
17/// - Add a `function` that provides ref-qualified support, which doesn't work
18/// with `std::function`.
19/// - Provide support for specifying multiple signatures to type erase callable
20/// objects with an overload set, such as those produced by generic lambdas.
21/// - Expand to include a copyable utility that directly replaces std::function
22/// but brings the above improvements.
23///
24/// Note that LLVM's utilities are greatly simplified by not supporting
25/// allocators.
26///
27/// If the standard library ever begins to provide comparable facilities we can
28/// consider switching to those.
29///
30//===----------------------------------------------------------------------===//
31
32#ifndef LLVM_ADT_FUNCTIONEXTRAS_H
33#define LLVM_ADT_FUNCTIONEXTRAS_H
34
35#include "llvm/ADT/PointerIntPair.h"
36#include "llvm/ADT/PointerUnion.h"
37#include "llvm/ADT/STLForwardCompat.h"
38#include "llvm/Support/MemAlloc.h"
39#include "llvm/Support/type_traits.h"
40#include <cstring>
41#include <memory>
42#include <type_traits>
43
44namespace llvm {
45
46/// unique_function is a type-erasing functor similar to std::function.
47///
48/// It can hold move-only function objects, like lambdas capturing unique_ptrs.
49/// Accordingly, it is movable but not copyable.
50///
51/// It supports const-qualification:
52/// - unique_function<int() const> has a const operator().
53/// It can only hold functions which themselves have a const operator().
54/// - unique_function<int()> has a non-const operator().
55/// It can hold functions with a non-const operator(), like mutable lambdas.
56template <typename FunctionT> class unique_function;
57
58namespace detail {
59
60template <typename T>
61using EnableIfTrivial =
62 std::enable_if_t<llvm::is_trivially_move_constructible<T>::value &&
63 std::is_trivially_destructible<T>::value>;
64template <typename CallableT, typename ThisT>
65using EnableUnlessSameType =
66 std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
67template <typename CallableT, typename Ret, typename... Params>
68using EnableIfCallable = std::enable_if_t<std::disjunction<
69 std::is_void<Ret>,
70 std::is_same<decltype(std::declval<CallableT>()(std::declval<Params>()...)),
71 Ret>,
72 std::is_same<const decltype(std::declval<CallableT>()(
73 std::declval<Params>()...)),
74 Ret>,
75 std::is_convertible<decltype(std::declval<CallableT>()(
76 std::declval<Params>()...)),
77 Ret>>::value>;
78
79template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
80protected:
81 static constexpr size_t InlineStorageSize = sizeof(void *) * 3;
82
83 template <typename T, class = void>
84 struct IsSizeLessThanThresholdT : std::false_type {};
85
86 template <typename T>
87 struct IsSizeLessThanThresholdT<
88 T, std::enable_if_t<sizeof(T) <= 2 * sizeof(void *)>> : std::true_type {};
89
90 // Provide a type function to map parameters that won't observe extra copies
91 // or moves and which are small enough to likely pass in register to values
92 // and all other types to l-value reference types. We use this to compute the
93 // types used in our erased call utility to minimize copies and moves unless
94 // doing so would force things unnecessarily into memory.
95 //
96 // The heuristic used is related to common ABI register passing conventions.
97 // It doesn't have to be exact though, and in one way it is more strict
98 // because we want to still be able to observe either moves *or* copies.
99 template <typename T> struct AdjustedParamTBase {
100 static_assert(!std::is_reference<T>::value,
101 "references should be handled by template specialization");
102 using type = std::conditional_t<
103 llvm::is_trivially_copy_constructible<T>::value &&
104 llvm::is_trivially_move_constructible<T>::value &&
105 IsSizeLessThanThresholdT<T>::value,
106 T, T &>;
107 };
108
109 // This specialization ensures that 'AdjustedParam<V<T>&>' or
110 // 'AdjustedParam<V<T>&&>' does not trigger a compile-time error when 'T' is
111 // an incomplete type and V a templated type.
112 template <typename T> struct AdjustedParamTBase<T &> { using type = T &; };
113 template <typename T> struct AdjustedParamTBase<T &&> { using type = T &; };
114
115 template <typename T>
116 using AdjustedParamT = typename AdjustedParamTBase<T>::type;
117
118 // The type of the erased function pointer we use as a callback to dispatch to
119 // the stored callable when it is trivial to move and destroy.
120 using CallPtrT = ReturnT (*)(void *CallableAddr,
121 AdjustedParamT<ParamTs>... Params);
122 using MovePtrT = void (*)(void *LHSCallableAddr, void *RHSCallableAddr);
123 using DestroyPtrT = void (*)(void *CallableAddr);
124
125 /// A struct to hold a single trivial callback with sufficient alignment for
126 /// our bitpacking.
127 struct alignas(8) TrivialCallback {
128 CallPtrT CallPtr;
129 };
130
131 /// A struct we use to aggregate three callbacks when we need full set of
132 /// operations.
133 struct alignas(8) NonTrivialCallbacks {
134 CallPtrT CallPtr;
135 MovePtrT MovePtr;
136 DestroyPtrT DestroyPtr;
137 };
138
139 // Create a pointer union between either a pointer to a static trivial call
140 // pointer in a struct or a pointer to a static struct of the call, move, and
141 // destroy pointers.
142 using CallbackPointerUnionT =
143 PointerUnion<TrivialCallback *, NonTrivialCallbacks *>;
144
145 // The main storage buffer. This will either have a pointer to out-of-line
146 // storage or an inline buffer storing the callable.
147 union StorageUnionT {
148 // For out-of-line storage we keep a pointer to the underlying storage and
149 // the size. This is enough to deallocate the memory.
150 struct OutOfLineStorageT {
151 void *StoragePtr;
152 size_t Size;
153 size_t Alignment;
154 } OutOfLineStorage;
155 static_assert(
156 sizeof(OutOfLineStorageT) <= InlineStorageSize,
157 "Should always use all of the out-of-line storage for inline storage!");
158
159 // For in-line storage, we just provide an aligned character buffer. We
160 // provide three pointers worth of storage here.
161 // This is mutable as an inlined `const unique_function<void() const>` may
162 // still modify its own mutable members.
163 mutable std::aligned_storage_t<InlineStorageSize, alignof(void *)>
164 InlineStorage;
165 } StorageUnion;
166
167 // A compressed pointer to either our dispatching callback or our table of
168 // dispatching callbacks and the flag for whether the callable itself is
169 // stored inline or not.
170 PointerIntPair<CallbackPointerUnionT, 1, bool> CallbackAndInlineFlag;
171
172 bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); }
173
174 bool isTrivialCallback() const {
175 return isa<TrivialCallback *>(CallbackAndInlineFlag.getPointer());
176 }
177
178 CallPtrT getTrivialCallback() const {
179 return cast<TrivialCallback *>(CallbackAndInlineFlag.getPointer())->CallPtr;
180 }
181
182 NonTrivialCallbacks *getNonTrivialCallbacks() const {
183 return cast<NonTrivialCallbacks *>(CallbackAndInlineFlag.getPointer());
184 }
185
186 CallPtrT getCallPtr() const {
187 return isTrivialCallback() ? getTrivialCallback()
188 : getNonTrivialCallbacks()->CallPtr;
189 }
190
191 // These three functions are only const in the narrow sense. They return
192 // mutable pointers to function state.
193 // This allows unique_function<T const>::operator() to be const, even if the
194 // underlying functor may be internally mutable.
195 //
196 // const callers must ensure they're only used in const-correct ways.
197 void *getCalleePtr() const {
198 return isInlineStorage() ? getInlineStorage() : getOutOfLineStorage();
199 }
200 void *getInlineStorage() const { return &StorageUnion.InlineStorage; }
201 void *getOutOfLineStorage() const {
202 return StorageUnion.OutOfLineStorage.StoragePtr;
31
Undefined or garbage value returned to caller
203 }
204
205 size_t getOutOfLineStorageSize() const {
206 return StorageUnion.OutOfLineStorage.Size;
207 }
208 size_t getOutOfLineStorageAlignment() const {
209 return StorageUnion.OutOfLineStorage.Alignment;
210 }
211
212 void setOutOfLineStorage(void *Ptr, size_t Size, size_t Alignment) {
213 StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment};
214 }
215
216 template <typename CalledAsT>
217 static ReturnT CallImpl(void *CallableAddr,
218 AdjustedParamT<ParamTs>... Params) {
219 auto &Func = *reinterpret_cast<CalledAsT *>(CallableAddr);
220 return Func(std::forward<ParamTs>(Params)...);
221 }
222
223 template <typename CallableT>
224 static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept {
225 new (LHSCallableAddr)
226 CallableT(std::move(*reinterpret_cast<CallableT *>(RHSCallableAddr)));
227 }
228
229 template <typename CallableT>
230 static void DestroyImpl(void *CallableAddr) noexcept {
231 reinterpret_cast<CallableT *>(CallableAddr)->~CallableT();
232 }
233
234 // The pointers to call/move/destroy functions are determined for each
235 // callable type (and called-as type, which determines the overload chosen).
236 // (definitions are out-of-line).
237
238 // By default, we need an object that contains all the different
239 // type erased behaviors needed. Create a static instance of the struct type
240 // here and each instance will contain a pointer to it.
241 // Wrap in a struct to avoid https://gcc.gnu.org/PR71954
242 template <typename CallableT, typename CalledAs, typename Enable = void>
243 struct CallbacksHolder {
244 static NonTrivialCallbacks Callbacks;
245 };
246 // See if we can create a trivial callback. We need the callable to be
247 // trivially moved and trivially destroyed so that we don't have to store
248 // type erased callbacks for those operations.
249 template <typename CallableT, typename CalledAs>
250 struct CallbacksHolder<CallableT, CalledAs, EnableIfTrivial<CallableT>> {
251 static TrivialCallback Callbacks;
252 };
253
254 // A simple tag type so the call-as type to be passed to the constructor.
255 template <typename T> struct CalledAs {};
256
257 // Essentially the "main" unique_function constructor, but subclasses
258 // provide the qualified type to be used for the call.
259 // (We always store a T, even if the call will use a pointer to const T).
260 template <typename CallableT, typename CalledAsT>
261 UniqueFunctionBase(CallableT Callable, CalledAs<CalledAsT>) {
262 bool IsInlineStorage = true;
263 void *CallableAddr = getInlineStorage();
264 if (sizeof(CallableT) > InlineStorageSize ||
265 alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) {
266 IsInlineStorage = false;
267 // Allocate out-of-line storage. FIXME: Use an explicit alignment
268 // parameter in C++17 mode.
269 auto Size = sizeof(CallableT);
270 auto Alignment = alignof(CallableT);
271 CallableAddr = allocate_buffer(Size, Alignment);
272 setOutOfLineStorage(CallableAddr, Size, Alignment);
273 }
274
275 // Now move into the storage.
276 new (CallableAddr) CallableT(std::move(Callable));
277 CallbackAndInlineFlag.setPointerAndInt(
278 &CallbacksHolder<CallableT, CalledAsT>::Callbacks, IsInlineStorage);
279 }
280
281 ~UniqueFunctionBase() {
282 if (!CallbackAndInlineFlag.getPointer())
26
Taking false branch
283 return;
284
285 // Cache this value so we don't re-check it after type-erased operations.
286 bool IsInlineStorage = isInlineStorage();
287
288 if (!isTrivialCallback())
27
Taking false branch
289 getNonTrivialCallbacks()->DestroyPtr(
290 IsInlineStorage ? getInlineStorage() : getOutOfLineStorage());
291
292 if (!IsInlineStorage)
28
Assuming 'IsInlineStorage' is false
29
Taking true branch
293 deallocate_buffer(getOutOfLineStorage(), getOutOfLineStorageSize(),
30
Calling 'UniqueFunctionBase::getOutOfLineStorage'
294 getOutOfLineStorageAlignment());
295 }
296
297 UniqueFunctionBase(UniqueFunctionBase &&RHS) noexcept {
298 // Copy the callback and inline flag.
299 CallbackAndInlineFlag = RHS.CallbackAndInlineFlag;
300
301 // If the RHS is empty, just copying the above is sufficient.
302 if (!RHS)
18
Taking true branch
303 return;
304
305 if (!isInlineStorage()) {
306 // The out-of-line case is easiest to move.
307 StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage;
308 } else if (isTrivialCallback()) {
309 // Move is trivial, just memcpy the bytes across.
310 memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize);
311 } else {
312 // Non-trivial move, so dispatch to a type-erased implementation.
313 getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
314 RHS.getInlineStorage());
315 }
316
317 // Clear the old callback and inline flag to get back to as-if-null.
318 RHS.CallbackAndInlineFlag = {};
319
320#ifndef NDEBUG
321 // In debug builds, we also scribble across the rest of the storage.
322 memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize);
323#endif
324 }
325
326 UniqueFunctionBase &operator=(UniqueFunctionBase &&RHS) noexcept {
327 if (this == &RHS)
14
Taking false branch
328 return *this;
329
330 // Because we don't try to provide any exception safety guarantees we can
331 // implement move assignment very simply by first destroying the current
332 // object and then move-constructing over top of it.
333 this->~UniqueFunctionBase();
334 new (this) UniqueFunctionBase(std::move(RHS));
15
Calling 'move<llvm::detail::UniqueFunctionBase<llvm::Expected<unsigned long>> &>'
16
Returning from 'move<llvm::detail::UniqueFunctionBase<llvm::Expected<unsigned long>> &>'
17
Calling move constructor for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
19
Returning from move constructor for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
335 return *this;
20
Returning without writing to 'RHS.StorageUnion.OutOfLineStorage.StoragePtr'
336 }
337
338 UniqueFunctionBase() = default;
8
Returning without writing to 'this->StorageUnion.OutOfLineStorage.StoragePtr'
339
340public:
341 explicit operator bool() const {
342 return (bool)CallbackAndInlineFlag.getPointer();
343 }
344};
345
346template <typename R, typename... P>
347template <typename CallableT, typename CalledAsT, typename Enable>
348typename UniqueFunctionBase<R, P...>::NonTrivialCallbacks UniqueFunctionBase<
349 R, P...>::CallbacksHolder<CallableT, CalledAsT, Enable>::Callbacks = {
350 &CallImpl<CalledAsT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
351
352template <typename R, typename... P>
353template <typename CallableT, typename CalledAsT>
354typename UniqueFunctionBase<R, P...>::TrivialCallback
355 UniqueFunctionBase<R, P...>::CallbacksHolder<
356 CallableT, CalledAsT, EnableIfTrivial<CallableT>>::Callbacks{
357 &CallImpl<CalledAsT>};
358
359} // namespace detail
360
361template <typename R, typename... P>
362class unique_function<R(P...)> : public detail::UniqueFunctionBase<R, P...> {
363 using Base = detail::UniqueFunctionBase<R, P...>;
364
365public:
366 unique_function() = default;
367 unique_function(std::nullptr_t) {}
7
Calling defaulted default constructor for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
9
Returning from default constructor for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
10
Returning without writing to 'this->StorageUnion.OutOfLineStorage.StoragePtr'
368 unique_function(unique_function &&) = default;
369 unique_function(const unique_function &) = delete;
370 unique_function &operator=(unique_function &&) = default;
13
Calling move assignment operator for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
21
Returning from move assignment operator for 'UniqueFunctionBase<llvm::Expected<unsigned long>, >'
22
Returning without writing to '.StorageUnion.OutOfLineStorage.StoragePtr'
371 unique_function &operator=(const unique_function &) = delete;
372
373 template <typename CallableT>
374 unique_function(
375 CallableT Callable,
376 detail::EnableUnlessSameType<CallableT, unique_function> * = nullptr,
377 detail::EnableIfCallable<CallableT, R, P...> * = nullptr)
378 : Base(std::forward<CallableT>(Callable),
379 typename Base::template CalledAs<CallableT>{}) {}
380
381 R operator()(P... Params) {
382 return this->getCallPtr()(this->getCalleePtr(), Params...);
383 }
384};
385
386template <typename R, typename... P>
387class unique_function<R(P...) const>
388 : public detail::UniqueFunctionBase<R, P...> {
389 using Base = detail::UniqueFunctionBase<R, P...>;
390
391public:
392 unique_function() = default;
393 unique_function(std::nullptr_t) {}
394 unique_function(unique_function &&) = default;
395 unique_function(const unique_function &) = delete;
396 unique_function &operator=(unique_function &&) = default;
397 unique_function &operator=(const unique_function &) = delete;
398
399 template <typename CallableT>
400 unique_function(
401 CallableT Callable,
402 detail::EnableUnlessSameType<CallableT, unique_function> * = nullptr,
403 detail::EnableIfCallable<const CallableT, R, P...> * = nullptr)
404 : Base(std::forward<CallableT>(Callable),
405 typename Base::template CalledAs<const CallableT>{}) {}
406
407 R operator()(P... Params) const {
408 return this->getCallPtr()(this->getCalleePtr(), Params...);
409 }
410};
411
412} // end namespace llvm
413
414#endif // LLVM_ADT_FUNCTIONEXTRAS_H