blob: ff1464e8fe76f3485e4d7bee8b93325f9e8a5f47 [file] [log] [blame]
/*
* 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.
*
* Authors: Gabe Black
*/
#ifndef __SIM_GUEST_ABI_HH__
#define __SIM_GUEST_ABI_HH__
#include <functional>
#include <memory>
#include <sstream>
#include <type_traits>
class ThreadContext;
namespace GuestABI
{
/*
* To implement an ABI, a subclass needs to implement a system of
* specializations of these two templates Result and Argument, and define a
* "Position" type.
*
* The Position type carries information about, for instance, how many
* integer registers have been consumed gathering earlier arguments. It
* may contain multiple elements if there are multiple dimensions to track,
* for instance the number of integer and floating point registers used so far.
*
* Result and Argument are class templates instead of function templates so
* that they can be partially specialized if necessary. C++ doesn't let you
* partially specialize function templates because that conflicts with
* template resolution using the function's arguments. Since we already know
* what type we want and we don't need argument based resolution, we can just
* wrap the desired functionality in classes and sidestep the problem.
*
* Also note that these templates have an "Enabled" parameter to support
* std::enable_if style conditional specializations.
*/
template <typename ABI, typename Ret, typename Enabled=void>
struct Result
{
private:
/*
* Store result "ret" into the state accessible through tc.
*
* Note that the declaration below is only to document the expected
* signature and is private so it won't be used by accident.
* Specializations of this Result class should define their own version
* of this method which actually does something and is public.
*/
static void store(ThreadContext *tc, const Ret &ret);
};
template <typename ABI, typename Arg, typename Enabled=void>
struct Argument
{
/*
* Retrieve an argument of type Arg from the state accessible through tc,
* assuming the state represented by "position" has already been used.
* Also update position to account for this argument as well.
*
* Like Result::store above, the declaration below is only to document
* the expected method signature.
*/
static Arg get(ThreadContext *tc, typename ABI::Position &position);
};
/*
* These templates implement a variadic argument mechanism for guest ABI
* functions. A function might be written like this:
*
* void
* func(ThreadContext *tc, VarArgs<Addr, int> varargs)
* {
* warn("Address = %#x, int = %d.",
* varargs.get<Addr>(), varargs.get<int>());
* }
*
* where an object of type VarArgs<...> is its last argument. The types given
* to the template specify what types the function might need to retrieve from
* varargs. The varargs object will then have get<> methods for each of those
* types.
*
* Note that each get<> will happen live. If you modify values through the
* ThreadContext *tc and then run get<>(), you may alter one of your arguments.
* If you're going to use tc to modify state, it would be a good idea to use
* get<>() as soon as possible to avoid corrupting the functions arguments.
*/
// A recursive template which defines virtual functions to retrieve each of the
// requested types. This provides the ABI agnostic interface the function uses.
template <typename ...Types>
class VarArgsBase;
template <typename First, typename ...Types>
class VarArgsBase<First, Types...> : public VarArgsBase<Types...>
{
public:
// The virtual function takes a reference parameter so that the different
// _getImpl methods can co-exist through overloading.
virtual void _getImpl(First &) = 0;
// Make sure base class _getImpl-es aren't hidden by this one.
using VarArgsBase<Types...>::_getImpl;
};
// The base case of the recursion.
template <>
class VarArgsBase<>
{
protected:
// This just gives the "using" statement in the non base case something to
// refer to.
void _getImpl();
};
// A recursive template which defines the ABI specific implementation of the
// interface defined above.
//
// The types in Types are consumed one by one, and by
// the time we get down to the base case we'd have lost track of the complete
// set we need to know what interface to inherit. The Base parameter keeps
// track of that through the recursion.
template <typename ABI, typename Base, typename ...Types>
class VarArgsImpl;
template <typename ABI, typename Base, typename First, typename ...Types>
class VarArgsImpl<ABI, Base, First, Types...> :
public VarArgsImpl<ABI, Base, Types...>
{
protected:
// Bring forward the base class constructor.
using VarArgsImpl<ABI, Base, Types...>::VarArgsImpl;
// Make sure base class _getImpl-es don't get hidden by ours.
using VarArgsImpl<ABI, Base, Types...>::_getImpl;
// Implement a version of _getImple, using the ABI specialized version of
// the Argument class.
void
_getImpl(First &first) override
{
first = Argument<ABI, First>::get(this->tc, this->position);
}
};
// The base case of the recursion, which inherits from the interface class.
template <typename ABI, typename Base>
class VarArgsImpl<ABI, Base> : public Base
{
protected:
// Declare state to pass to the Argument<>::get methods.
ThreadContext *tc;
typename ABI::Position position;
// Give the "using" statement in our subclass something to refer to.
void _getImpl();
public:
VarArgsImpl(ThreadContext *_tc, const typename ABI::Position &_pos) :
tc(_tc), position(_pos)
{}
};
// A wrapper which provides a nice interface to the virtual functions, and a
// hook for the Argument template mechanism.
template <typename ...Types>
class VarArgs
{
private:
// This points to the implementation which knows how to read arguments
// based on the ABI being used.
std::shared_ptr<VarArgsBase<Types...>> _ptr;
public:
VarArgs(VarArgsBase<Types...> *ptr) : _ptr(ptr) {}
// This template is a friendlier wrapper around the virtual functions the
// raw interface provides. This version lets you pick a type which it then
// returns, instead of having to pre-declare a variable to pass in.
template <typename Arg>
Arg
get()
{
Arg arg;
_ptr->_getImpl(arg);
return arg;
}
};
template <typename ...Types>
std::ostream &
operator << (std::ostream &os, const VarArgs<Types...> &va)
{
os << "...";
return os;
}
// The ABI independent hook which tells the GuestABI mechanism what to do with
// a VarArgs argument. It constructs the underlying implementation which knows
// about the ABI, and installs it in the VarArgs wrapper to give to the
// function.
template <typename ABI, typename ...Types>
struct Argument<ABI, VarArgs<Types...>>
{
static VarArgs<Types...>
get(ThreadContext *tc, typename ABI::Position &position)
{
using Base = VarArgsBase<Types...>;
using Impl = VarArgsImpl<ABI, Base, Types...>;
return VarArgs<Types...>(new Impl(tc, position));
}
};
/*
* These functions will likely be common among all ABIs and implement the
* mechanism of gathering arguments, calling the target function, and then
* storing the result. They might need to be overridden if, for instance,
* the location of arguments need to be determined in a different order.
* For example, there might be an ABI which gathers arguments starting
* from the last in the list instead of the first. This is unlikely but
* still possible to support by redefining these functions..
*/
// With no arguments to gather, call the target function and store the
// result.
template <typename ABI, typename Ret>
static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
callFrom(ThreadContext *tc, typename ABI::Position &position,
std::function<Ret(ThreadContext *)> target)
{
Ret ret = target(tc);
Result<ABI, Ret>::store(tc, ret);
return ret;
}
// With no arguments to gather and nothing to return, call the target function.
template <typename ABI>
static void
callFrom(ThreadContext *tc, typename ABI::Position &position,
std::function<void(ThreadContext *)> target)
{
target(tc);
}
// Recursively gather arguments for target from tc until we get to the base
// case above.
template <typename ABI, typename Ret, typename NextArg, typename ...Args>
static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
callFrom(ThreadContext *tc, typename ABI::Position &position,
std::function<Ret(ThreadContext *, NextArg, Args...)> target)
{
// Extract the next argument from the thread context.
NextArg next = Argument<ABI, NextArg>::get(tc, position);
// Build a partial function which adds the next argument to the call.
std::function<Ret(ThreadContext *, Args...)> partial =
[target,next](ThreadContext *_tc, Args... args) {
return target(_tc, next, args...);
};
// Recursively handle any remaining arguments.
return callFrom<ABI, Ret, Args...>(tc, position, partial);
}
// Recursively gather arguments for target from tc until we get to the base
// case above. This version is for functions that don't return anything.
template <typename ABI, typename NextArg, typename ...Args>
static void
callFrom(ThreadContext *tc, typename ABI::Position &position,
std::function<void(ThreadContext *, NextArg, Args...)> target)
{
// Extract the next argument from the thread context.
NextArg next = Argument<ABI, NextArg>::get(tc, position);
// Build a partial function which adds the next argument to the call.
std::function<void(ThreadContext *, Args...)> partial =
[target,next](ThreadContext *_tc, Args... args) {
target(_tc, next, args...);
};
// Recursively handle any remaining arguments.
callFrom<ABI, Args...>(tc, position, partial);
}
/*
* These functions are like the ones above, except they print the arguments
* a target function would be called with instead of actually calling it.
*/
// With no arguments to print, add the closing parenthesis and return.
template <typename ABI, typename Ret>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
typename ABI::Position &position)
{
os << ")";
}
// Recursively gather arguments for target from tc until we get to the base
// case above, and append those arguments to the string stream being
// constructed.
template <typename ABI, typename Ret, typename NextArg, typename ...Args>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
typename ABI::Position &position)
{
// Either open the parenthesis or add a comma, depending on where we are
// in the argument list.
os << (count ? ", " : "(");
// Extract the next argument from the thread context.
NextArg next = Argument<ABI, NextArg>::get(tc, position);
// Add this argument to the list.
os << next;
// Recursively handle any remaining arguments.
dumpArgsFrom<ABI, Ret, Args...>(count + 1, os, tc, position);
}
} // namespace GuestABI
// 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, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc,
std::function<Ret(ThreadContext *, Args...)> target)
{
// Default construct a Position to track consumed resources. Built in
// types will be zero initialized.
auto position = typename ABI::Position();
return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
}
template <typename ABI, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc, Ret (*target)(ThreadContext *, Args...))
{
return invokeSimcall<ABI>(
tc, std::function<Ret(ThreadContext *, Args...)>(target));
}
template <typename ABI, typename ...Args>
void
invokeSimcall(ThreadContext *tc,
std::function<void(ThreadContext *, Args...)> target)
{
// Default construct a Position to track consumed resources. Built in
// types will be zero initialized.
auto position = typename ABI::Position();
GuestABI::callFrom<ABI, Args...>(tc, position, 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 position = typename ABI::Position();
std::ostringstream ss;
ss << name;
GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
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__