diff --git a/docs/CodingStandards.rst b/docs/CodingStandards.rst index 0fcca0330f4..edbef3ace53 100644 --- a/docs/CodingStandards.rst +++ b/docs/CodingStandards.rst @@ -108,6 +108,8 @@ unlikely to be supported by our host compilers. * Lambdas: N2927_ * But *not* ``std::function``, until Clang implements `MSVC-compatible RTTI`_. + In many cases, you may be able to use ``llvm::function_ref`` instead, and it + is a superior choice in those cases. * And *not* lambdas with default arguments. * ``decltype``: N2343_ diff --git a/docs/ProgrammersManual.rst b/docs/ProgrammersManual.rst index 473d95df78c..7e46ac4e8e6 100644 --- a/docs/ProgrammersManual.rst +++ b/docs/ProgrammersManual.rst @@ -263,6 +263,78 @@ almost never be stored or mentioned directly. They are intended solely for use when defining a function which should be able to efficiently accept concatenated strings. +.. _function_apis: + +Passing functions and other callable objects +-------------------------------------------- + +Sometimes you may want a function to be passed a callback object. In order to +support lambda expressions and other function objects, you should not use the +traditional C approach of taking a function pointer and an opaque cookie: + +.. code-block:: c++ + + void takeCallback(bool (*Callback)(Function *, void *), void *Cookie); + +Instead, use one of the following approaches: + +Function template +^^^^^^^^^^^^^^^^^ + +If you don't mind putting the definition of your function into a header file, +make it a function template that is templated on the callable type. + +.. code-block:: c++ + + template + void takeCallback(Callable Callback) { + Callback(1, 2, 3); + } + +The ``function_ref`` class template +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``function_ref`` +(`doxygen `__) class +template represents a reference to a callable object, templated over the type +of the callable. This is a good choice for passing a callback to a function, +if you don't need to hold onto the callback after the function returns. + +``function_ref`` can be implicitly constructed from +any callable object that can be called with arguments of type ``Param1``, +``Param2``, ..., and returns a value that can be converted to type ``Ret``. +For example: + +.. code-block:: c++ + + void visitBasicBlocks(Function *F, function_ref Callback) { + for (BasicBlock &BB : *F) + if (Callback(&BB)) + return; + } + +can be called using: + +.. code-block:: c++ + + visitBasicBlocks(F, [&](BasicBlock *BB) { + if (process(BB)) + return isEmpty(BB); + return false; + }); + +Note that a ``function_ref`` object contains pointers to external memory, so +it is not generally safe to store an instance of the class (unless you know +that the external storage will not be freed). +``function_ref`` is small enough that it should always be passed by value. + +``std::function`` +^^^^^^^^^^^^^^^^^ + +You cannot use ``std::function`` within LLVM code, because it is not supported +by all our target toolchains. + + .. _DEBUG: The ``DEBUG()`` macro and ``-debug`` option diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 7c2a147bc88..807ec59061d 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -55,6 +55,131 @@ struct greater_ptr : public std::binary_function { } }; +/// An efficient, type-erasing, non-owning reference to a callable. This is +/// intended for use as the type of a function parameter that is not used +/// after the function in question returns. +/// +/// This class does not own the callable, so it is not in general safe to store +/// a function_ref. +template class function_ref; + +#if LLVM_HAS_VARIADIC_TEMPLATES + +template +class function_ref { + Ret (*callback)(void *callable, Params ...params); + void *callable; + + template + static Ret callback_fn(void *callable, Params ...params) { + return (*reinterpret_cast(callable))( + std::forward(params)...); + } + +public: + template + function_ref(Callable &&callable) + : callback(callback_fn::type>), + callable(reinterpret_cast(&callable)) {} + Ret operator()(Params ...params) const { + return callback(callable, std::forward(params)...); + } +}; + +#else + +template +class function_ref { + Ret (*callback)(void *callable); + void *callable; + + template + static Ret callback_fn(void *callable) { + return (*reinterpret_cast(callable))(); + } + +public: + template + function_ref(Callable &&callable) + : callback(callback_fn::type>), + callable(reinterpret_cast(&callable)) {} + Ret operator()() const { return callback(callable); } +}; + +template +class function_ref { + Ret (*callback)(void *callable, Param1 param1); + void *callable; + + template + static Ret callback_fn(void *callable, Param1 param1) { + return (*reinterpret_cast(callable))( + std::forward(param1)); + } + +public: + template + function_ref(Callable &&callable) + : callback(callback_fn::type>), + callable(reinterpret_cast(&callable)) {} + Ret operator()(Param1 param1) { + return callback(callable, std::forward(param1)); + } +}; + +template +class function_ref { + Ret (*callback)(void *callable, Param1 param1, Param2 param2); + void *callable; + + template + static Ret callback_fn(void *callable, Param1 param1, Param2 param2) { + return (*reinterpret_cast(callable))( + std::forward(param1), + std::forward(param2)); + } + +public: + template + function_ref(Callable &&callable) + : callback(callback_fn::type>), + callable(reinterpret_cast(&callable)) {} + Ret operator()(Param1 param1, Param2 param2) { + return callback(callable, + std::forward(param1), + std::forward(param2)); + } +}; + +template +class function_ref { + Ret (*callback)(void *callable, Param1 param1, Param2 param2, Param3 param3); + void *callable; + + template + static Ret callback_fn(void *callable, Param1 param1, Param2 param2, + Param3 param3) { + return (*reinterpret_cast(callable))( + std::forward(param1), + std::forward(param2), + std::forward(param3)); + } + +public: + template + function_ref(Callable &&callable) + : callback(callback_fn::type>), + callable(reinterpret_cast(&callable)) {} + Ret operator()(Param1 param1, Param2 param2, Param3 param3) { + return callback(callable, + std::forward(param1), + std::forward(param2), + std::forward(param3)); + } +}; + +#endif + // deleter - Very very very simple method that is used to invoke operator // delete on something. It is used like this: // diff --git a/include/llvm/Support/CrashRecoveryContext.h b/include/llvm/Support/CrashRecoveryContext.h index 6b9e0439790..c132373e91d 100644 --- a/include/llvm/Support/CrashRecoveryContext.h +++ b/include/llvm/Support/CrashRecoveryContext.h @@ -12,11 +12,13 @@ #include +#include "llvm/ADT/STLExtras.h" + namespace llvm { class StringRef; class CrashRecoveryContextCleanup; - + /// \brief Crash recovery helper object. /// /// This class implements support for running operations in a safe context so @@ -46,21 +48,10 @@ class CrashRecoveryContext { void *Impl; CrashRecoveryContextCleanup *head; - /// An adaptor to convert an arbitrary functor into a void(void*), void* pair. - template struct FunctorAdaptor { - T Fn; - static void invoke(void *Data) { - return static_cast*>(Data)->Fn(); - } - typedef void Callback(void*); - Callback *fn() { return &invoke; } - void *arg() { return this; } - }; - public: CrashRecoveryContext() : Impl(nullptr), head(nullptr) {} ~CrashRecoveryContext(); - + void registerCleanup(CrashRecoveryContextCleanup *cleanup); void unregisterCleanup(CrashRecoveryContextCleanup *cleanup); @@ -86,11 +77,9 @@ public: /// make as little assumptions as possible about the program state when /// RunSafely has returned false. Clients can use getBacktrace() to retrieve /// the backtrace of the crash on failures. - bool RunSafely(void (*Fn)(void*), void *UserData); - template - bool RunSafely(Functor Fn) { - FunctorAdaptor Adaptor = { Fn }; - return RunSafely(Adaptor.fn(), Adaptor.arg()); + bool RunSafely(function_ref Fn); + bool RunSafely(void (*Fn)(void*), void *UserData) { + return RunSafely([&]() { Fn(UserData); }); } /// \brief Execute the provide callback function (with the given arguments) in @@ -98,12 +87,10 @@ public: /// requested stack size). /// /// See RunSafely() and llvm_execute_on_thread(). + bool RunSafelyOnThread(function_ref, unsigned RequestedStackSize = 0); bool RunSafelyOnThread(void (*Fn)(void*), void *UserData, - unsigned RequestedStackSize = 0); - template - bool RunSafelyOnThread(Functor Fn, unsigned RequestedStackSize = 0) { - FunctorAdaptor Adaptor = { Fn }; - return RunSafelyOnThread(Adaptor.fn(), Adaptor.arg(), RequestedStackSize); + unsigned RequestedStackSize = 0) { + return RunSafelyOnThread([&]() { Fn(UserData); }, RequestedStackSize); } /// \brief Explicitly trigger a crash recovery in the current process, and diff --git a/include/llvm/Transforms/Utils/CtorUtils.h b/include/llvm/Transforms/Utils/CtorUtils.h index a96ba2b8458..81e7b951c25 100644 --- a/include/llvm/Transforms/Utils/CtorUtils.h +++ b/include/llvm/Transforms/Utils/CtorUtils.h @@ -14,8 +14,7 @@ #ifndef LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H #define LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H -#include -#include +#include "llvm/ADT/STLExtras.h" namespace llvm { @@ -23,12 +22,10 @@ class GlobalVariable; class Function; class Module; -typedef bool (*ShouldRemoveCtor)(void *, Function *); - /// Call "ShouldRemove" for every entry in M's global_ctor list and remove the /// entries for which it returns true. Return true if anything changed. -bool optimizeGlobalCtorsList(Module &M, ShouldRemoveCtor ShouldRemove, - void *Context); +bool optimizeGlobalCtorsList(Module &M, + function_ref ShouldRemove); } // End llvm namespace diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp index 513875d4de4..a426377042d 100644 --- a/lib/Support/CrashRecoveryContext.cpp +++ b/lib/Support/CrashRecoveryContext.cpp @@ -301,7 +301,7 @@ void CrashRecoveryContext::Disable() { #endif -bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { +bool CrashRecoveryContext::RunSafely(function_ref Fn) { // If crash recovery is disabled, do nothing. if (gCrashRecoveryEnabled) { assert(!Impl && "Crash recovery context already initialized!"); @@ -313,7 +313,7 @@ bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { } } - Fn(UserData); + Fn(); return true; } @@ -334,8 +334,7 @@ const std::string &CrashRecoveryContext::getBacktrace() const { namespace { struct RunSafelyOnThreadInfo { - void (*Fn)(void*); - void *Data; + function_ref Fn; CrashRecoveryContext *CRC; bool Result; }; @@ -344,11 +343,11 @@ struct RunSafelyOnThreadInfo { static void RunSafelyOnThread_Dispatch(void *UserData) { RunSafelyOnThreadInfo *Info = reinterpret_cast(UserData); - Info->Result = Info->CRC->RunSafely(Info->Fn, Info->Data); + Info->Result = Info->CRC->RunSafely(Info->Fn); } -bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData, +bool CrashRecoveryContext::RunSafelyOnThread(function_ref Fn, unsigned RequestedStackSize) { - RunSafelyOnThreadInfo Info = { Fn, UserData, this, false }; + RunSafelyOnThreadInfo Info = { Fn, this, false }; llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) CRC->setSwitchedThread(); diff --git a/lib/Transforms/IPO/GlobalDCE.cpp b/lib/Transforms/IPO/GlobalDCE.cpp index 03b17d52386..9decddcff80 100644 --- a/lib/Transforms/IPO/GlobalDCE.cpp +++ b/lib/Transforms/IPO/GlobalDCE.cpp @@ -54,16 +54,16 @@ namespace { bool RemoveUnusedGlobalValue(GlobalValue &GV); }; +} /// Returns true if F contains only a single "ret" instruction. -bool isEmptyFunction(void *Context, Function *F) { +static bool isEmptyFunction(Function *F) { BasicBlock &Entry = F->getEntryBlock(); if (Entry.size() != 1 || !isa(Entry.front())) return false; ReturnInst &RI = cast(Entry.front()); return RI.getReturnValue() == NULL; } -} char GlobalDCE::ID = 0; INITIALIZE_PASS(GlobalDCE, "globaldce", @@ -75,7 +75,7 @@ bool GlobalDCE::runOnModule(Module &M) { bool Changed = false; // Remove empty functions from the global ctors list. - Changed |= optimizeGlobalCtorsList(M, isEmptyFunction, nullptr); + Changed |= optimizeGlobalCtorsList(M, isEmptyFunction); // Loop over the module, adding globals which are obviously necessary. for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp index 3db0abf89f5..a8471bacb49 100644 --- a/lib/Transforms/IPO/GlobalOpt.cpp +++ b/lib/Transforms/IPO/GlobalOpt.cpp @@ -3022,10 +3022,9 @@ bool GlobalOpt::runOnModule(Module &M) { LocalChange |= OptimizeFunctions(M); // Optimize global_ctors list. - LocalChange |= optimizeGlobalCtorsList(M, [](void *C, Function *F) -> bool { - GlobalOpt *self = static_cast(C); - return EvaluateStaticConstructor(F, self->DL, self->TLI); - }, this); + LocalChange |= optimizeGlobalCtorsList(M, [&](Function *F) { + return EvaluateStaticConstructor(F, DL, TLI); + }); // Optimize non-address-taken globals. LocalChange |= OptimizeGlobalVars(M); diff --git a/lib/Transforms/Utils/CtorUtils.cpp b/lib/Transforms/Utils/CtorUtils.cpp index 0082df01214..7cf793f6266 100644 --- a/lib/Transforms/Utils/CtorUtils.cpp +++ b/lib/Transforms/Utils/CtorUtils.cpp @@ -132,8 +132,8 @@ GlobalVariable *findGlobalCtors(Module &M) { /// Call "ShouldRemove" for every entry in M's global_ctor list and remove the /// entries for which it returns true. Return true if anything changed. -bool optimizeGlobalCtorsList(Module &M, ShouldRemoveCtor ShouldRemove, - void *Context) { +bool optimizeGlobalCtorsList(Module &M, + function_ref ShouldRemove) { GlobalVariable *GlobalCtors = findGlobalCtors(M); if (!GlobalCtors) return false; @@ -163,7 +163,7 @@ bool optimizeGlobalCtorsList(Module &M, ShouldRemoveCtor ShouldRemove, continue; // If we can evaluate the ctor at compile time, do. - if (ShouldRemove(Context, F)) { + if (ShouldRemove(F)) { Ctors.erase(Ctors.begin() + i); MadeChange = true; --i;