[llvm] r208025 - Add llvm::function_ref (and a couple of uses of it), representing a type-erased reference to a callable object.

David Blaikie dblaikie at gmail.com
Sat May 10 00:10:45 PDT 2014


On Mon, May 5, 2014 at 6:01 PM, Richard Smith
<richard-llvm at metafoo.co.uk> wrote:
> Author: rsmith
> Date: Mon May  5 20:01:29 2014
> New Revision: 208025
>
> URL: http://llvm.org/viewvc/llvm-project?rev=208025&view=rev
> Log:
> Add llvm::function_ref (and a couple of uses of it), representing a type-erased reference to a callable object.
>
> Modified:
>     llvm/trunk/include/llvm/ADT/STLExtras.h
>     llvm/trunk/include/llvm/Support/CrashRecoveryContext.h
>     llvm/trunk/include/llvm/Transforms/Utils/CtorUtils.h
>     llvm/trunk/lib/Support/CrashRecoveryContext.cpp
>     llvm/trunk/lib/Transforms/IPO/GlobalDCE.cpp
>     llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
>     llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp
>
> Modified: llvm/trunk/include/llvm/ADT/STLExtras.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/STLExtras.h?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/ADT/STLExtras.h (original)
> +++ llvm/trunk/include/llvm/ADT/STLExtras.h Mon May  5 20:01:29 2014
> @@ -55,6 +55,131 @@ struct greater_ptr : public std::binary_
>    }
>  };
>
> +/// 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<typename Fn> class function_ref;
> +
> +#if LLVM_HAS_VARIADIC_TEMPLATES
> +
> +template<typename Ret, typename ...Params>
> +class function_ref<Ret(Params...)> {
> +  Ret (*callback)(void *callable, Params ...params);
> +  void *callable;
> +
> +  template<typename Callable>
> +  static Ret callback_fn(void *callable, Params ...params) {
> +    return reinterpret_cast<Callable&>(*callable)(
> +        std::forward<Params>(params)...);
> +  }
> +
> +public:
> +  template<typename Callable>
> +  function_ref(Callable &&callable)
> +      : callback(callback_fn<Callable>),
> +        callable(reinterpret_cast<void *>(&callable)) {}
> +  Ret operator()(Params ...params) const {
> +    return callback(callable, std::forward<Params>(params)...);
> +  }
> +};
> +
> +#else
> +
> +template<typename Ret>
> +class function_ref<Ret()> {
> +  Ret (*callback)(void *callable);
> +  void *callable;
> +
> +  template<typename Callable>
> +  static Ret callback_fn(void *callable) {
> +    return reinterpret_cast<Callable&>(*callable)();
> +  }
> +
> +public:
> +  template<typename Callable>
> +  function_ref(Callable &&callable)
> +      : callback(callback_fn<Callable>),
> +        callable(reinterpret_cast<void *>(&callable)) {}

Seems this presents a problem when the Callable binds to a reference
(rather than a pointer) to a function. GCC says:

In file included from llvm/src/include/llvm/Transforms/Utils/CtorUtils.h:17:0,
                 from llvm/src/lib/Transforms/IPO/GlobalDCE.cpp:24:
llvm/src/include/llvm/ADT/STLExtras.h: In instantiation of
‘llvm::function_ref<Ret(Param1)>::function_ref(Callable&&) [with
Callable = bool (&)(llvm::Function*); Ret = bool; Param1 =
llvm::Function*]’:
llvm/src/lib/Transforms/IPO/GlobalDCE.cpp:78:56:   required from here
llvm/src/include/llvm/ADT/STLExtras.h:124:53: warning: ISO C++ forbids
casting between pointer-to-function and pointer-to-object [enabled by
default]
llvm/src/include/llvm/ADT/STLExtras.h: In instantiation of ‘static Ret
llvm::function_ref<Ret(Param1)>::callback_fn(void*, Param1) [with
Callable = bool(llvm::Function*); Ret = bool; Param1 =
llvm::Function*]’:
llvm/src/include/llvm/ADT/STLExtras.h:124:53:   required from
‘llvm::function_ref<Ret(Param1)>::function_ref(Callable&&) [with
Callable = bool (&)(llvm::Function*); Ret = bool; Param1 =
llvm::Function*]’
llvm/src/lib/Transforms/IPO/GlobalDCE.cpp:78:56:   required from here
llvm/src/include/llvm/ADT/STLExtras.h:117:37: warning: ISO C++ forbids
casting between pointer-to-function and pointer-to-object [enabled by
default]

do we need a trait in here to take the address of the function? (but
then we'd have a pointer to a pointer to a function... which would
only be valid for the full expression, which might be a little bit
rough) - or a specialization of the template for functions? or...
something else?

> +  Ret operator()() const { return callback(callable); }
> +};
> +
> +template<typename Ret, typename Param1>
> +class function_ref<Ret(Param1)> {
> +  Ret (*callback)(void *callable, Param1 param1);
> +  void *callable;
> +
> +  template<typename Callable>
> +  static Ret callback_fn(void *callable, Param1 param1) {
> +    return reinterpret_cast<Callable&>(*callable)(
> +        std::forward<Param1>(param1));
> +  }
> +
> +public:
> +  template<typename Callable>
> +  function_ref(Callable &&callable)
> +      : callback(callback_fn<Callable>),
> +        callable(reinterpret_cast<void *>(&callable)) {}
> +  Ret operator()(Param1 param1) {
> +    return callback(callable, std::forward<Param1>(param1));
> +  }
> +};
> +
> +template<typename Ret, typename Param1, typename Param2>
> +class function_ref<Ret(Param1, Param2)> {
> +  Ret (*callback)(void *callable, Param1 param1, Param2 param2);
> +  void *callable;
> +
> +  template<typename Callable>
> +  static Ret callback_fn(void *callable, Param1 param1, Param2 param2) {
> +    return reinterpret_cast<Callable&>(*callable)(
> +        std::forward<Param1>(param1),
> +        std::forward<Param2>(param2));
> +  }
> +
> +public:
> +  template<typename Callable>
> +  function_ref(Callable &&callable)
> +      : callback(callback_fn<Callable>),
> +        callable(reinterpret_cast<void *>(&callable)) {}
> +  Ret operator()(Param1 param1, Param2 param2) {
> +    return callback(callable,
> +                    std::forward<Param1>(param1),
> +                    std::forward<Param2>(param2));
> +  }
> +};
> +
> +template<typename Ret, typename Param1, typename Param2, typename Param3>
> +class function_ref<Ret(Param1, Param2, Param3)> {
> +  Ret (*callback)(void *callable, Param1 param1, Param2 param2, Param3 param3);
> +  void *callable;
> +
> +  template<typename Callable>
> +  static Ret callback_fn(void *callable, Param1 param1, Param2 param2,
> +                         Param3 param3) {
> +    return reinterpret_cast<Callable&>(*callable)(
> +        std::forward<Param1>(param1),
> +        std::forward<Param2>(param2),
> +        std::forward<Param3>(param3));
> +  }
> +
> +public:
> +  template<typename Callable>
> +  function_ref(Callable &&callable)
> +      : callback(callback_fn<Callable>),
> +        callable(reinterpret_cast<void *>(&callable)) {}
> +  Ret operator()(Param1 param1, Param2 param2, Param3 param3) {
> +    return callback(callable,
> +                    std::forward<Param1>(param1),
> +                    std::forward<Param2>(param2),
> +                    std::forward<Param3>(param3));
> +  }
> +};
> +
> +#endif
> +
>  // deleter - Very very very simple method that is used to invoke operator
>  // delete on something.  It is used like this:
>  //
>
> Modified: llvm/trunk/include/llvm/Support/CrashRecoveryContext.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/CrashRecoveryContext.h?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/Support/CrashRecoveryContext.h (original)
> +++ llvm/trunk/include/llvm/Support/CrashRecoveryContext.h Mon May  5 20:01:29 2014
> @@ -12,11 +12,13 @@
>
>  #include <string>
>
> +#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<typename T> struct FunctorAdaptor {
> -    T Fn;
> -    static void invoke(void *Data) {
> -      return static_cast<FunctorAdaptor<T>*>(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<typename Functor>
> -  bool RunSafely(Functor Fn) {
> -    FunctorAdaptor<Functor> Adaptor = { Fn };
> -    return RunSafely(Adaptor.fn(), Adaptor.arg());
> +  bool RunSafely(function_ref<void()> 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<void()>, unsigned RequestedStackSize = 0);
>    bool RunSafelyOnThread(void (*Fn)(void*), void *UserData,
> -                         unsigned RequestedStackSize = 0);
> -  template<typename Functor>
> -  bool RunSafelyOnThread(Functor Fn, unsigned RequestedStackSize = 0) {
> -    FunctorAdaptor<Functor> 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
>
> Modified: llvm/trunk/include/llvm/Transforms/Utils/CtorUtils.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/CtorUtils.h?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/Transforms/Utils/CtorUtils.h (original)
> +++ llvm/trunk/include/llvm/Transforms/Utils/CtorUtils.h Mon May  5 20:01:29 2014
> @@ -14,8 +14,7 @@
>  #ifndef LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H
>  #define LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H
>
> -#include <functional>
> -#include <vector>
> +#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<bool(Function *)> ShouldRemove);
>
>  } // End llvm namespace
>
>
> Modified: llvm/trunk/lib/Support/CrashRecoveryContext.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CrashRecoveryContext.cpp?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Support/CrashRecoveryContext.cpp (original)
> +++ llvm/trunk/lib/Support/CrashRecoveryContext.cpp Mon May  5 20:01:29 2014
> @@ -301,7 +301,7 @@ void CrashRecoveryContext::Disable() {
>
>  #endif
>
> -bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
> +bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
>    // If crash recovery is disabled, do nothing.
>    if (gCrashRecoveryEnabled) {
>      assert(!Impl && "Crash recovery context already initialized!");
> @@ -313,7 +313,7 @@ bool CrashRecoveryContext::RunSafely(voi
>      }
>    }
>
> -  Fn(UserData);
> +  Fn();
>    return true;
>  }
>
> @@ -334,8 +334,7 @@ const std::string &CrashRecoveryContext:
>
>  namespace {
>  struct RunSafelyOnThreadInfo {
> -  void (*Fn)(void*);
> -  void *Data;
> +  function_ref<void()> Fn;
>    CrashRecoveryContext *CRC;
>    bool Result;
>  };
> @@ -344,11 +343,11 @@ struct RunSafelyOnThreadInfo {
>  static void RunSafelyOnThread_Dispatch(void *UserData) {
>    RunSafelyOnThreadInfo *Info =
>      reinterpret_cast<RunSafelyOnThreadInfo*>(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<void()> 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();
>
> Modified: llvm/trunk/lib/Transforms/IPO/GlobalDCE.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/GlobalDCE.cpp?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/GlobalDCE.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/GlobalDCE.cpp Mon May  5 20:01:29 2014
> @@ -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<ReturnInst>(Entry.front()))
>      return false;
>    ReturnInst &RI = cast<ReturnInst>(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) {
>
> Modified: llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp Mon May  5 20:01:29 2014
> @@ -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<GlobalOpt *>(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);
>
> Modified: llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp?rev=208025&r1=208024&r2=208025&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp (original)
> +++ llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp Mon May  5 20:01:29 2014
> @@ -132,8 +132,8 @@ GlobalVariable *findGlobalCtors(Module &
>
>  /// 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<bool(Function *)> ShouldRemove) {
>    GlobalVariable *GlobalCtors = findGlobalCtors(M);
>    if (!GlobalCtors)
>      return false;
> @@ -163,7 +163,7 @@ bool optimizeGlobalCtorsList(Module &M,
>        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;
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits




More information about the llvm-commits mailing list