[PATCH] Add llvm::function_ref as a type-erased non-owning reference to a callable
Richard Smith
richard at metafoo.co.uk
Mon May 5 14:22:24 PDT 2014
On Thu, May 1, 2014 at 11:23 PM, Richard Smith <richard at metafoo.co.uk>wrote:
> On Thu, May 1, 2014 at 9:29 PM, Nico Weber <thakis at chromium.org> wrote:
>
>> On Thu, May 1, 2014 at 8:31 PM, Rafael Espíndola
>> <rafael.espindola at gmail.com> wrote:
>> > OptimizeGlobalCtorsList
>> >
>> > start the name with a lowercase.
>>
>> Done. (I'm just moving them around, but since that modifies "svn
>> blame" output already, renaming at the same time is a good idea.)
>>
>> >
>> > +bool IsEmptyFunction(Function* F) {
>> > + BasicBlock &Entry = F->getEntryBlock();
>> > + return Entry.size() == 1 && isa<ReturnInst>(Entry.front());
>> > }
>> >
>> > There is no way to cast to void in llvm, right? The thing I am afraid
>> > of is if a trapping constant can be hidden in there.
>>
>> global_ctors are always arrray of { i32, void ()* }…oooh I see what
>> you're saying. I think it should be safe as-is, but I added a check
>> for RI.getReturnValue() too.
>>
>> > +/// Given a specified llvm.global_ctors list, install the
>> > +/// specified array, returning the new global to use.
>> > +void InstallGlobalCtors(GlobalVariable *GCL,
>> >
>> > lowercase. Returning? install?
>>
>> Done.
>>
>> > ParseGlobalCtors
>> >
>> > lowercase.
>>
>> Done.
>>
>> > + if (!GV->hasUniqueInitializer()) return nullptr;
>> >
>> > please clang-format.
>>
>> Done.
>>
>> > + // Init priority must be standard.
>> >
>> > Do you know why? At least for the empty case it should be valid to
>> > drop constructors of any priority.
>>
>> I don't know. Constructors with init priority are rare, and empty
>> constructors with init priority are probably rarer still though.
>>
>> > + ... Call "Remove" for
>> >
>> >
>> > It is actually a predicate, so maybe ShouldRemove?
>>
>> Done. I also changed this to a function pointer with a context
>> argument, since
>>
>> http://llvm.org/docs/CodingStandards.html#supported-c-11-language-and-library-features
>> says that std::function cannot be used yet.
>
>
> It looks like you don't actually want std::function (you don't want an
> owned and managed copy of a callable), you just want a non-owning,
> type-erasing callable reference. I wanted one of those about a month ago,
> and wrote a special-case handler for it at the time, but you've motivated
> me to implement the general case.
>
> Attached is a patch for an llvm::function_ref (a non-owning, type-erased
> callable) and a sample use. This would be usable as a drop-in replacement
> for std::function in your previous patch.
>
Revised patch attached. This also converts optimizeGlobalCtorsList to use
function_ref.
> > This is a case where an integration test might be a good idea. Do you
>> > have a reasonably size test where this change causes a ctor to be
>> > deleted at -O2? We might want to test that, so that even if we go with
>> > a different optimization strategy in the future we still have a test
>> > of the end result (the ctor gets deleted somewhere in -O2).
>>
>> Yes, there's one in the bug. I can check that in on the clang side
>> once this patch is in.
>>
>> Thanks!
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20140505/4f6f59f3/attachment.html>
-------------- next part --------------
Index: include/llvm/Transforms/Utils/CtorUtils.h
===================================================================
--- include/llvm/Transforms/Utils/CtorUtils.h (revision 207998)
+++ include/llvm/Transforms/Utils/CtorUtils.h (working copy)
@@ -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 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
Index: include/llvm/ADT/STLExtras.h
===================================================================
--- include/llvm/ADT/STLExtras.h (revision 207998)
+++ include/llvm/ADT/STLExtras.h (working copy)
@@ -55,6 +55,131 @@
}
};
+/// 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)) {}
+ 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:
//
Index: include/llvm/Support/CrashRecoveryContext.h
===================================================================
--- include/llvm/Support/CrashRecoveryContext.h (revision 207998)
+++ include/llvm/Support/CrashRecoveryContext.h (working copy)
@@ -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 @@
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 @@
/// 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 @@
/// 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
Index: lib/Support/CrashRecoveryContext.cpp
===================================================================
--- lib/Support/CrashRecoveryContext.cpp (revision 207998)
+++ lib/Support/CrashRecoveryContext.cpp (working copy)
@@ -301,7 +301,7 @@
#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 @@
}
}
- Fn(UserData);
+ Fn();
return true;
}
@@ -334,8 +334,7 @@
namespace {
struct RunSafelyOnThreadInfo {
- void (*Fn)(void*);
- void *Data;
+ function_ref<void()> Fn;
CrashRecoveryContext *CRC;
bool Result;
};
@@ -344,11 +343,11 @@
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();
Index: lib/Transforms/IPO/GlobalDCE.cpp
===================================================================
--- lib/Transforms/IPO/GlobalDCE.cpp (revision 207998)
+++ lib/Transforms/IPO/GlobalDCE.cpp (working copy)
@@ -54,16 +54,7 @@
bool RemoveUnusedGlobalValue(GlobalValue &GV);
};
-
-/// Returns true if F contains only a single "ret" instruction.
-bool isEmptyFunction(void *Context, 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 +66,13 @@
bool Changed = false;
// Remove empty functions from the global ctors list.
- Changed |= optimizeGlobalCtorsList(M, isEmptyFunction, nullptr);
+ Changed |= optimizeGlobalCtorsList(M, [](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;
+ });
// Loop over the module, adding globals which are obviously necessary.
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
Index: lib/Transforms/IPO/GlobalOpt.cpp
===================================================================
--- lib/Transforms/IPO/GlobalOpt.cpp (revision 207998)
+++ lib/Transforms/IPO/GlobalOpt.cpp (working copy)
@@ -3022,10 +3022,9 @@
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);
Index: lib/Transforms/Utils/CtorUtils.cpp
===================================================================
--- lib/Transforms/Utils/CtorUtils.cpp (revision 207998)
+++ lib/Transforms/Utils/CtorUtils.cpp (working copy)
@@ -132,8 +132,8 @@
/// 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 @@
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;
More information about the llvm-commits
mailing list