Files
gem5/src/sim/guest_abi.hh
Gabe Black ad204d9de0 sim: Simplify some code in the guest ABI mechanism.
Instead of using recursively applied templates to accumulate a series of
wrapper lambdas which dispatch to a call, use pure parameter pack
expansion. This has two benefits. One, it makes the code simpler(ish) and
easier to understand. The parameter pack machinery is still intrinsically
fairly tricky, but there's less of it and it's a fairly straightforward
application of that mechanism.

Also, a nice side benefit is that the template for simcall dispatch will
expand to a small fixed number of functions which do all their work
locally, instead of having a new function for each layer of the onion,
one per parameter, and no calls through lambdas. That should hopefully
make debugging easier, and produce less bookkeeping overhead as far as
really long names, lots of functions, etc.

This code, specifically the code in dispatch.hh, can be simplified even
further in the future once we start using c++17 which is if constexpr,
and std::apply which explodes a tuple and uses its components as
arguments to a function, something I'm doing manually here.

Change-Id: If7c9234cc1014101211474c2ec20362702cf78c2
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/41600
Reviewed-by: Gabe Black <gabe.black@gmail.com>
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
2021-03-04 10:19:05 +00:00

130 lines
4.8 KiB
C++

/*
* Copyright 2019 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SIM_GUEST_ABI_HH__
#define __SIM_GUEST_ABI_HH__
#include <functional>
#include "sim/guest_abi/definition.hh"
#include "sim/guest_abi/dispatch.hh"
#include "sim/guest_abi/layout.hh"
#include "sim/guest_abi/varargs.hh"
class ThreadContext;
// These functions wrap a simulator level function with the given signature.
// The wrapper takes one argument, a thread context to extract arguments from
// and write a result (if any) back to. For convenience, the wrapper also
// returns the result of the wrapped function.
template <typename ABI, bool store_ret, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc,
std::function<Ret(ThreadContext *, Args...)> target)
{
// Default construct a State to track consumed resources. Built in
// types will be zero initialized.
auto state = GuestABI::initializeState<ABI>(tc);
GuestABI::prepareForFunction<ABI, Ret, Args...>(tc, state);
return GuestABI::callFrom<ABI, Ret, store_ret, Args...>(tc, state, target);
}
template <typename ABI, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc,
std::function<Ret(ThreadContext *, Args...)> target)
{
return invokeSimcall<ABI, true>(tc, target);
}
template <typename ABI, bool store_ret, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc, Ret (*target)(ThreadContext *, Args...))
{
return invokeSimcall<ABI, store_ret>(
tc, std::function<Ret(ThreadContext *, Args...)>(target));
}
template <typename ABI, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc, Ret (*target)(ThreadContext *, Args...))
{
return invokeSimcall<ABI, true>(tc, target);
}
template <typename ABI, typename ...Args>
void
invokeSimcall(ThreadContext *tc,
std::function<void(ThreadContext *, Args...)> target)
{
// Default construct a State to track consumed resources. Built in
// types will be zero initialized.
auto state = GuestABI::initializeState<ABI>(tc);
GuestABI::prepareForArguments<ABI, Args...>(tc, state);
GuestABI::callFrom<ABI, void, false, Args...>(tc, state, target);
}
template <typename ABI, typename ...Args>
void
invokeSimcall(ThreadContext *tc, void (*target)(ThreadContext *, Args...))
{
invokeSimcall<ABI>(
tc, std::function<void(ThreadContext *, Args...)>(target));
}
// These functions also wrap a simulator level function. Instead of running the
// function, they return a string which shows what arguments the function would
// be invoked with if it were called from the given context.
template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
std::function<Ret(ThreadContext *, Args...)> target=
std::function<Ret(ThreadContext *, Args...)>())
{
auto state = GuestABI::initializeState<ABI>(tc);
std::ostringstream ss;
GuestABI::prepareForFunction<ABI, Ret, Args...>(tc, state);
ss << name;
GuestABI::dumpArgsFrom<ABI, Ret, Args...>(ss, tc, state);
return ss.str();
}
template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
Ret (*target)(ThreadContext *, Args...))
{
return dumpSimcall<ABI>(
name, tc, std::function<Ret(ThreadContext *, Args...)>(target));
}
#endif // __SIM_GUEST_ABI_HH__