File indexing completed on 2026-05-10 08:43:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 #ifndef LLVM_ADT_FUNCTIONEXTRAS_H
0033 #define LLVM_ADT_FUNCTIONEXTRAS_H
0034
0035 #include "llvm/ADT/PointerIntPair.h"
0036 #include "llvm/ADT/PointerUnion.h"
0037 #include "llvm/ADT/STLForwardCompat.h"
0038 #include "llvm/Support/Compiler.h"
0039 #include "llvm/Support/MemAlloc.h"
0040 #include "llvm/Support/type_traits.h"
0041 #include <cstring>
0042 #include <memory>
0043 #include <type_traits>
0044
0045 namespace llvm {
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057 template <typename FunctionT> class unique_function;
0058
0059 namespace detail {
0060
0061 template <typename T>
0062 using EnableIfTrivial =
0063 std::enable_if_t<std::is_trivially_move_constructible<T>::value &&
0064 std::is_trivially_destructible<T>::value>;
0065 template <typename CallableT, typename ThisT>
0066 using EnableUnlessSameType =
0067 std::enable_if_t<!std::is_same<remove_cvref_t<CallableT>, ThisT>::value>;
0068 template <typename CallableT, typename Ret, typename... Params>
0069 using EnableIfCallable = std::enable_if_t<std::disjunction<
0070 std::is_void<Ret>,
0071 std::is_same<decltype(std::declval<CallableT>()(std::declval<Params>()...)),
0072 Ret>,
0073 std::is_same<const decltype(std::declval<CallableT>()(
0074 std::declval<Params>()...)),
0075 Ret>,
0076 std::is_convertible<decltype(std::declval<CallableT>()(
0077 std::declval<Params>()...)),
0078 Ret>>::value>;
0079
0080 template <typename ReturnT, typename... ParamTs> class UniqueFunctionBase {
0081 protected:
0082 static constexpr size_t InlineStorageSize = sizeof(void *) * 3;
0083 static constexpr size_t InlineStorageAlign = alignof(void *);
0084
0085 template <typename T, class = void>
0086 struct IsSizeLessThanThresholdT : std::false_type {};
0087
0088 template <typename T>
0089 struct IsSizeLessThanThresholdT<
0090 T, std::enable_if_t<sizeof(T) <= 2 * sizeof(void *)>> : std::true_type {};
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 template <typename T> struct AdjustedParamTBase {
0102 static_assert(!std::is_reference<T>::value,
0103 "references should be handled by template specialization");
0104 using type =
0105 std::conditional_t<std::is_trivially_copy_constructible<T>::value &&
0106 std::is_trivially_move_constructible<T>::value &&
0107 IsSizeLessThanThresholdT<T>::value,
0108 T, T &>;
0109 };
0110
0111
0112
0113
0114 template <typename T> struct AdjustedParamTBase<T &> { using type = T &; };
0115 template <typename T> struct AdjustedParamTBase<T &&> { using type = T &; };
0116
0117 template <typename T>
0118 using AdjustedParamT = typename AdjustedParamTBase<T>::type;
0119
0120
0121
0122 using CallPtrT = ReturnT (*)(void *CallableAddr,
0123 AdjustedParamT<ParamTs>... Params);
0124 using MovePtrT = void (*)(void *LHSCallableAddr, void *RHSCallableAddr);
0125 using DestroyPtrT = void (*)(void *CallableAddr);
0126
0127
0128
0129 struct alignas(8) TrivialCallback {
0130 CallPtrT CallPtr;
0131 };
0132
0133
0134
0135 struct alignas(8) NonTrivialCallbacks {
0136 CallPtrT CallPtr;
0137 MovePtrT MovePtr;
0138 DestroyPtrT DestroyPtr;
0139 };
0140
0141
0142
0143
0144 using CallbackPointerUnionT =
0145 PointerUnion<TrivialCallback *, NonTrivialCallbacks *>;
0146
0147
0148
0149 union StorageUnionT {
0150
0151
0152 struct OutOfLineStorageT {
0153 void *StoragePtr;
0154 size_t Size;
0155 size_t Alignment;
0156 } OutOfLineStorage;
0157 static_assert(
0158 sizeof(OutOfLineStorageT) <= InlineStorageSize,
0159 "Should always use all of the out-of-line storage for inline storage!");
0160
0161
0162
0163
0164
0165 alignas(InlineStorageAlign) mutable std::byte
0166 InlineStorage[InlineStorageSize];
0167 } StorageUnion;
0168
0169
0170
0171
0172 PointerIntPair<CallbackPointerUnionT, 1, bool> CallbackAndInlineFlag;
0173
0174 bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); }
0175
0176 bool isTrivialCallback() const {
0177 return isa<TrivialCallback *>(CallbackAndInlineFlag.getPointer());
0178 }
0179
0180 CallPtrT getTrivialCallback() const {
0181 return cast<TrivialCallback *>(CallbackAndInlineFlag.getPointer())->CallPtr;
0182 }
0183
0184 NonTrivialCallbacks *getNonTrivialCallbacks() const {
0185 return cast<NonTrivialCallbacks *>(CallbackAndInlineFlag.getPointer());
0186 }
0187
0188 CallPtrT getCallPtr() const {
0189 return isTrivialCallback() ? getTrivialCallback()
0190 : getNonTrivialCallbacks()->CallPtr;
0191 }
0192
0193
0194
0195
0196
0197
0198
0199 void *getCalleePtr() const {
0200 return isInlineStorage() ? getInlineStorage() : getOutOfLineStorage();
0201 }
0202 void *getInlineStorage() const { return &StorageUnion.InlineStorage; }
0203 void *getOutOfLineStorage() const {
0204 return StorageUnion.OutOfLineStorage.StoragePtr;
0205 }
0206
0207 size_t getOutOfLineStorageSize() const {
0208 return StorageUnion.OutOfLineStorage.Size;
0209 }
0210 size_t getOutOfLineStorageAlignment() const {
0211 return StorageUnion.OutOfLineStorage.Alignment;
0212 }
0213
0214 void setOutOfLineStorage(void *Ptr, size_t Size, size_t Alignment) {
0215 StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment};
0216 }
0217
0218 template <typename CalledAsT>
0219 static ReturnT CallImpl(void *CallableAddr,
0220 AdjustedParamT<ParamTs>... Params) {
0221 auto &Func = *reinterpret_cast<CalledAsT *>(CallableAddr);
0222 return Func(std::forward<ParamTs>(Params)...);
0223 }
0224
0225 template <typename CallableT>
0226 static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept {
0227 new (LHSCallableAddr)
0228 CallableT(std::move(*reinterpret_cast<CallableT *>(RHSCallableAddr)));
0229 }
0230
0231 template <typename CallableT>
0232 static void DestroyImpl(void *CallableAddr) noexcept {
0233 reinterpret_cast<CallableT *>(CallableAddr)->~CallableT();
0234 }
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244 template <typename CallableT, typename CalledAs, typename Enable = void>
0245 struct CallbacksHolder {
0246 static NonTrivialCallbacks Callbacks;
0247 };
0248
0249
0250
0251 template <typename CallableT, typename CalledAs>
0252 struct CallbacksHolder<CallableT, CalledAs, EnableIfTrivial<CallableT>> {
0253 static TrivialCallback Callbacks;
0254 };
0255
0256
0257 template <typename T> struct CalledAs {};
0258
0259
0260
0261
0262 template <typename CallableT, typename CalledAsT>
0263 UniqueFunctionBase(CallableT Callable, CalledAs<CalledAsT>) {
0264 bool IsInlineStorage = true;
0265 void *CallableAddr = getInlineStorage();
0266 if (sizeof(CallableT) > InlineStorageSize ||
0267 alignof(CallableT) > InlineStorageAlign) {
0268 IsInlineStorage = false;
0269
0270
0271 auto Size = sizeof(CallableT);
0272 auto Alignment = alignof(CallableT);
0273 CallableAddr = allocate_buffer(Size, Alignment);
0274 setOutOfLineStorage(CallableAddr, Size, Alignment);
0275 }
0276
0277
0278 new (CallableAddr) CallableT(std::move(Callable));
0279 CallbackAndInlineFlag.setPointerAndInt(
0280 &CallbacksHolder<CallableT, CalledAsT>::Callbacks, IsInlineStorage);
0281 }
0282
0283 ~UniqueFunctionBase() {
0284 if (!CallbackAndInlineFlag.getPointer())
0285 return;
0286
0287
0288 bool IsInlineStorage = isInlineStorage();
0289
0290 if (!isTrivialCallback())
0291 getNonTrivialCallbacks()->DestroyPtr(
0292 IsInlineStorage ? getInlineStorage() : getOutOfLineStorage());
0293
0294 if (!IsInlineStorage)
0295 deallocate_buffer(getOutOfLineStorage(), getOutOfLineStorageSize(),
0296 getOutOfLineStorageAlignment());
0297 }
0298
0299 UniqueFunctionBase(UniqueFunctionBase &&RHS) noexcept {
0300
0301 CallbackAndInlineFlag = RHS.CallbackAndInlineFlag;
0302
0303
0304 if (!RHS)
0305 return;
0306
0307 if (!isInlineStorage()) {
0308
0309 StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage;
0310 } else if (isTrivialCallback()) {
0311
0312 memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize);
0313 } else {
0314
0315 getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
0316 RHS.getInlineStorage());
0317 getNonTrivialCallbacks()->DestroyPtr(RHS.getInlineStorage());
0318 }
0319
0320
0321 RHS.CallbackAndInlineFlag = {};
0322
0323 #if !defined(NDEBUG) && !LLVM_ADDRESS_SANITIZER_BUILD
0324
0325
0326
0327 memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize);
0328 #endif
0329 }
0330
0331 UniqueFunctionBase &operator=(UniqueFunctionBase &&RHS) noexcept {
0332 if (this == &RHS)
0333 return *this;
0334
0335
0336
0337
0338 this->~UniqueFunctionBase();
0339 new (this) UniqueFunctionBase(std::move(RHS));
0340 return *this;
0341 }
0342
0343 UniqueFunctionBase() = default;
0344
0345 public:
0346 explicit operator bool() const {
0347 return (bool)CallbackAndInlineFlag.getPointer();
0348 }
0349 };
0350
0351 template <typename R, typename... P>
0352 template <typename CallableT, typename CalledAsT, typename Enable>
0353 typename UniqueFunctionBase<R, P...>::NonTrivialCallbacks UniqueFunctionBase<
0354 R, P...>::CallbacksHolder<CallableT, CalledAsT, Enable>::Callbacks = {
0355 &CallImpl<CalledAsT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
0356
0357 template <typename R, typename... P>
0358 template <typename CallableT, typename CalledAsT>
0359 typename UniqueFunctionBase<R, P...>::TrivialCallback
0360 UniqueFunctionBase<R, P...>::CallbacksHolder<
0361 CallableT, CalledAsT, EnableIfTrivial<CallableT>>::Callbacks{
0362 &CallImpl<CalledAsT>};
0363
0364 }
0365
0366 template <typename R, typename... P>
0367 class unique_function<R(P...)> : public detail::UniqueFunctionBase<R, P...> {
0368 using Base = detail::UniqueFunctionBase<R, P...>;
0369
0370 public:
0371 unique_function() = default;
0372 unique_function(std::nullptr_t) {}
0373 unique_function(unique_function &&) = default;
0374 unique_function(const unique_function &) = delete;
0375 unique_function &operator=(unique_function &&) = default;
0376 unique_function &operator=(const unique_function &) = delete;
0377
0378 template <typename CallableT>
0379 unique_function(
0380 CallableT Callable,
0381 detail::EnableUnlessSameType<CallableT, unique_function> * = nullptr,
0382 detail::EnableIfCallable<CallableT, R, P...> * = nullptr)
0383 : Base(std::forward<CallableT>(Callable),
0384 typename Base::template CalledAs<CallableT>{}) {}
0385
0386 R operator()(P... Params) {
0387 return this->getCallPtr()(this->getCalleePtr(), Params...);
0388 }
0389 };
0390
0391 template <typename R, typename... P>
0392 class unique_function<R(P...) const>
0393 : public detail::UniqueFunctionBase<R, P...> {
0394 using Base = detail::UniqueFunctionBase<R, P...>;
0395
0396 public:
0397 unique_function() = default;
0398 unique_function(std::nullptr_t) {}
0399 unique_function(unique_function &&) = default;
0400 unique_function(const unique_function &) = delete;
0401 unique_function &operator=(unique_function &&) = default;
0402 unique_function &operator=(const unique_function &) = delete;
0403
0404 template <typename CallableT>
0405 unique_function(
0406 CallableT Callable,
0407 detail::EnableUnlessSameType<CallableT, unique_function> * = nullptr,
0408 detail::EnableIfCallable<const CallableT, R, P...> * = nullptr)
0409 : Base(std::forward<CallableT>(Callable),
0410 typename Base::template CalledAs<const CallableT>{}) {}
0411
0412 R operator()(P... Params) const {
0413 return this->getCallPtr()(this->getCalleePtr(), Params...);
0414 }
0415 };
0416
0417 }
0418
0419 #endif