Skip to content

Commit

Permalink
Re-commit r208025, reverted in r208030, with a fix for a conformance …
Browse files Browse the repository at this point in the history
…issue

which GCC detects and Clang does not!


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208033 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed May 6, 2014
1 parent f3a199b commit 36ecb2e
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 46 deletions.
2 changes: 2 additions & 0 deletions docs/CodingStandards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Expand Down
72 changes: 72 additions & 0 deletions docs/ProgrammersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename Callable>
void takeCallback(Callable Callback) {
Callback(1, 2, 3);
}

The ``function_ref`` class template
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``function_ref``
(`doxygen <http://llvm.org/doxygen/classllvm_1_1function_ref.html>`__) 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<Ret(Param1, Param2, ...)>`` 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<bool (BasicBlock*)> 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
Expand Down
125 changes: 125 additions & 0 deletions include/llvm/ADT/STLExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,131 @@ struct greater_ptr : public std::binary_function<Ty, Ty, bool> {
}
};

/// 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<typename std::remove_reference<Callable>::type>),
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<typename std::remove_reference<Callable>::type>),
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<typename std::remove_reference<Callable>::type>),
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<typename std::remove_reference<Callable>::type>),
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<typename std::remove_reference<Callable>::type>),
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:
//
Expand Down
33 changes: 10 additions & 23 deletions include/llvm/Support/CrashRecoveryContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -86,24 +77,20 @@ class CrashRecoveryContext {
/// 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
/// a protected context which is run in another thread (optionally with a
/// 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
Expand Down
9 changes: 3 additions & 6 deletions include/llvm/Transforms/Utils/CtorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,18 @@
#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 {

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

Expand Down
13 changes: 6 additions & 7 deletions lib/Support/CrashRecoveryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Expand All @@ -313,7 +313,7 @@ bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
}
}

Fn(UserData);
Fn();
return true;
}

Expand All @@ -334,8 +334,7 @@ const std::string &CrashRecoveryContext::getBacktrace() const {

namespace {
struct RunSafelyOnThreadInfo {
void (*Fn)(void*);
void *Data;
function_ref<void()> Fn;
CrashRecoveryContext *CRC;
bool Result;
};
Expand All @@ -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();
Expand Down
6 changes: 3 additions & 3 deletions lib/Transforms/IPO/GlobalDCE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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) {
Expand Down
7 changes: 3 additions & 4 deletions lib/Transforms/IPO/GlobalOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 36ecb2e

Please sign in to comment.