From 6c9da52bea73d43f3384dd57594b74dc7fd031c3 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Wed, 27 Nov 2019 21:34:53 -0800 Subject: [PATCH] sim: Implement a varargs like mechanism for GuestABI system. This will let a function called with a GuestABI emulate the ... mechanism available in C. To make that possible without the functions knowing anything about the ABI and to follow C++'s (sensible) templating and virtual function rules, you have to tell VarArgs what types you might want to extract from it, unlike the pure ... varargs style mechanism. Also unlike ..., there is no mechanism in place to force the varargs to appear last in the argument list. It will pick up the progress through the arguments at the point it's reached, and will ignore any later arguments. It would be possible to be more rigorous about this by changing the callFrom templates, but the overhead in complexity is probably not worth it. Also, retrieving arguments through a VarArgs happens live, meaning at the point that the argument is asked for. If the ThreadContext or memory the argument lives in is modified before that point, the retrieved value will reflect that modification and not what the function was originally called with. Care should be taken so that this doesn't cause corrupted arguments. Finally, this mechansim (and the Guest ABI mechanism in general) is complex and should have tests written for it. That should be possible since ThreadContext is forward declared and so the test can say it works however it wants or even ignore it completely. If that changes in the future, we may need a mock ThreadContext implementation. Jira Issue: https://gem5.atlassian.net/browse/GEM5-187 Change-Id: I37484b50a3e8c0d259d9590e32fecbb5f76670c1 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23195 Reviewed-by: Jason Lowe-Power Reviewed-by: Bobby R. Bruce Maintainer: Gabe Black Tested-by: kokoro --- src/sim/guest_abi.hh | 141 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh index 109c87252..b2176a6df 100644 --- a/src/sim/guest_abi.hh +++ b/src/sim/guest_abi.hh @@ -31,6 +31,7 @@ #define __SIM_GUEST_ABI_HH__ #include +#include #include class ThreadContext; @@ -89,6 +90,146 @@ struct Argument }; +/* + * These templates implement a variadic argument mechanism for guest ABI + * functions. A function might be written like this: + * + * void + * func(ThreadContext *tc, VarArgs varargs) + * { + * warn("Address = %#x, int = %d.", + * varargs.get(), varargs.get()); + * } + * + * 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 +class VarArgsBase; + +template +class VarArgsBase : public VarArgsBase +{ + 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::_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 +class VarArgsImpl; + +template +class VarArgsImpl : + public VarArgsImpl +{ + protected: + // Bring forward the base class constructor. + using VarArgsImpl::VarArgsImpl; + // Make sure base class _getImpl-es don't get hidden by ours. + using VarArgsImpl::_getImpl; + + // Implement a version of _getImple, using the ABI specialized version of + // the Argument class. + void + _getImpl(First &first) override + { + first = Argument::get(this->tc, this->position); + } +}; + +// The base case of the recursion, which inherits from the interface class. +template +class VarArgsImpl : 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 +class VarArgs +{ + private: + // This points to the implementation which knows how to read arguments + // based on the ABI being used. + std::shared_ptr> _ptr; + + public: + VarArgs(VarArgsBase *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 + Arg + get() + { + Arg arg; + _ptr->_getImpl(arg); + return arg; + } +}; + +// 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 +struct Argument> +{ + static VarArgs + get(ThreadContext *tc, typename ABI::Position &position) + { + using Base = VarArgsBase; + using Impl = VarArgsImpl; + return VarArgs(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 -- 2.30.2