From 5e0f5dbde7d83aad53fc357d09a2ea30786c2bb1 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 20 Dec 2019 21:42:43 -0800 Subject: [PATCH] sim: Split up the guest_abi.hh header. This header was getting pretty long, and could be broken up into a few headers which logically grouped related definitions and concepts. To maintain compatibility and keep things simple for users of the mechanism, there is still a top level header with the original name which defines the interface for using ABIs. It includes all the other new headers, and so can also be used when defining ABIs. Change-Id: I62a051b9bd982e0fcecfceeb3d658d1ff4d30c5e Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/24104 Tested-by: kokoro Reviewed-by: Bobby R. Bruce Maintainer: Gabe Black --- src/sim/guest_abi.hh | 454 +------------------------------- src/sim/guest_abi/definition.hh | 111 ++++++++ src/sim/guest_abi/dispatch.hh | 153 +++++++++++ src/sim/guest_abi/layout.hh | 135 ++++++++++ src/sim/guest_abi/varargs.hh | 197 ++++++++++++++ 5 files changed, 604 insertions(+), 446 deletions(-) create mode 100644 src/sim/guest_abi/definition.hh create mode 100644 src/sim/guest_abi/dispatch.hh create mode 100644 src/sim/guest_abi/layout.hh create mode 100644 src/sim/guest_abi/varargs.hh diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh index 99b47b796..97d3e21e3 100644 --- a/src/sim/guest_abi.hh +++ b/src/sim/guest_abi.hh @@ -29,451 +29,13 @@ #define __SIM_GUEST_ABI_HH__ #include -#include -#include -#include -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. - */ - -/* - * Position may need to be initialized based on the ThreadContext, for instance - * to find out where the stack pointer is initially. - */ -template -struct PositionInitializer -{ - static typename ABI::Position - init(const ThreadContext *tc) - { - return typename ABI::Position(); - } -}; - -template -struct PositionInitializer::value - >::type> -{ - static typename ABI::Position - init(const ThreadContext *tc) - { - return typename ABI::Position(tc); - } -}; - -template -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); - - /* - * Adjust the position of arguments based on the return type, if necessary. - * - * This method can be excluded if no adjustment is necessary. - */ - static void allocate(ThreadContext *tc, typename ABI::Position &position); -}; - -/* - * This partial specialization prevents having to special case 'void' when - * working with return types. - */ -template -struct Result -{}; - -template -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); - - /* - * Adjust the position of arguments based on the argument type, if - * necessary. - * - * This method can be excluded if no adjustment is necessary. - */ - static void allocate(ThreadContext *tc, typename ABI::Position &position); -}; - -/* - * This struct template provides a default allocate() method in case the - * Result or Argument template doesn't provide one. This is the default in - * cases where the return or argument type doesn't affect where things are - * stored. - */ -template class Role, - typename Type, typename Enabled=void> -struct Allocator -{ - static void - allocate(ThreadContext *tc, typename ABI::Position &position) - {} -}; - -/* - * If the return type is void, don't allocate anything. - */ -template class Role> -struct Allocator -{ - static void - allocate(ThreadContext *tc, typename ABI::Position &position) - {} -}; - -/* - * If the return or argument type isn't void and does affect where things - * are stored, the ABI can implement an allocate() method for the various - * argument and/or return types, and this specialization will call into it. - */ -template class Role, typename Type> -struct Allocator::allocate)> -{ - static void - allocate(ThreadContext *tc, typename ABI::Position &position) - { - Role::allocate(tc, position); - } -}; - -template -static void -allocateResult(ThreadContext *tc, typename ABI::Position &position) -{ - Allocator::allocate(tc, position); -} - -template -static void -allocateArguments(ThreadContext *tc, typename ABI::Position &position) -{ - return; -} - -template -static void -allocateArguments(ThreadContext *tc, typename ABI::Position &position) -{ - Allocator::allocate(tc, position); - allocateArguments(tc, position); -} - -template -static void -allocateSignature(ThreadContext *tc, typename ABI::Position &position) -{ - allocateResult(tc, position); - allocateArguments(tc, position); -} - - -/* - * 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; - } -}; - -template -struct IsVarArgs : public std::false_type {}; - -template -struct IsVarArgs> : public std::true_type {}; - -template -std::ostream & -operator << (std::ostream &os, const VarArgs &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 -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 - * 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 -static typename std::enable_if::value, Ret>::type -callFrom(ThreadContext *tc, typename ABI::Position &position, - std::function target) -{ - Ret ret = target(tc); - Result::store(tc, ret); - return ret; -} - -// With no arguments to gather and nothing to return, call the target function. -template -static void -callFrom(ThreadContext *tc, typename ABI::Position &position, - std::function target) -{ - target(tc); -} - -// Recursively gather arguments for target from tc until we get to the base -// case above. -template -static typename std::enable_if::value, Ret>::type -callFrom(ThreadContext *tc, typename ABI::Position &position, - std::function target) -{ - // Extract the next argument from the thread context. - NextArg next = Argument::get(tc, position); - - // Build a partial function which adds the next argument to the call. - std::function partial = - [target,next](ThreadContext *_tc, Args... args) { - return target(_tc, next, args...); - }; - - // Recursively handle any remaining arguments. - return callFrom(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 -static void -callFrom(ThreadContext *tc, typename ABI::Position &position, - std::function target) -{ - // Extract the next argument from the thread context. - NextArg next = Argument::get(tc, position); - - // Build a partial function which adds the next argument to the call. - std::function partial = - [target,next](ThreadContext *_tc, Args... args) { - target(_tc, next, args...); - }; - - // Recursively handle any remaining arguments. - callFrom(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 -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 -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::get(tc, position); - - // Add this argument to the list. - os << next; - - // Recursively handle any remaining arguments. - dumpArgsFrom(count + 1, os, tc, position); -} - -} // namespace GuestABI +#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 @@ -487,7 +49,7 @@ invokeSimcall(ThreadContext *tc, { // Default construct a Position to track consumed resources. Built in // types will be zero initialized. - auto position = GuestABI::PositionInitializer::init(tc); + auto position = GuestABI::initializePosition(tc); GuestABI::allocateSignature(tc, position); return GuestABI::callFrom(tc, position, target); } @@ -507,7 +69,7 @@ invokeSimcall(ThreadContext *tc, { // Default construct a Position to track consumed resources. Built in // types will be zero initialized. - auto position = GuestABI::PositionInitializer::init(tc); + auto position = GuestABI::initializePosition(tc); GuestABI::allocateArguments(tc, position); GuestABI::callFrom(tc, position, target); } @@ -531,7 +93,7 @@ dumpSimcall(std::string name, ThreadContext *tc, std::function target= std::function()) { - auto position = GuestABI::PositionInitializer::init(tc); + auto position = GuestABI::initializePosition(tc); std::ostringstream ss; GuestABI::allocateSignature(tc, position); diff --git a/src/sim/guest_abi/definition.hh b/src/sim/guest_abi/definition.hh new file mode 100644 index 000000000..ccebcc510 --- /dev/null +++ b/src/sim/guest_abi/definition.hh @@ -0,0 +1,111 @@ +/* + * 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_DEFINITION_HH__ +#define __SIM_GUEST_ABI_DEFINITION_HH__ + +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 +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); + + /* + * Adjust the position of arguments based on the return type, if necessary. + * + * This method can be excluded if no adjustment is necessary. + */ + static void allocate(ThreadContext *tc, typename ABI::Position &position); +}; + +/* + * This partial specialization prevents having to special case 'void' when + * working with return types. + */ +template +struct Result +{}; + +template +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); + + /* + * Adjust the position of arguments based on the argument type, if + * necessary. + * + * This method can be excluded if no adjustment is necessary. + */ + static void allocate(ThreadContext *tc, typename ABI::Position &position); +}; + +} // namespace GuestABI + +#endif // __SIM_GUEST_ABI_DEFINITION_HH__ diff --git a/src/sim/guest_abi/dispatch.hh b/src/sim/guest_abi/dispatch.hh new file mode 100644 index 000000000..c817f632c --- /dev/null +++ b/src/sim/guest_abi/dispatch.hh @@ -0,0 +1,153 @@ +/* + * 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_DISPATCH_HH__ +#define __SIM_GUEST_ABI_DISPATCH_HH__ + +#include +#include +#include + +#include "sim/guest_abi/definition.hh" + +class ThreadContext; + +namespace GuestABI +{ + +/* + * 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 +static typename std::enable_if::value, Ret>::type +callFrom(ThreadContext *tc, typename ABI::Position &position, + std::function target) +{ + Ret ret = target(tc); + Result::store(tc, ret); + return ret; +} + +// With no arguments to gather and nothing to return, call the target function. +template +static void +callFrom(ThreadContext *tc, typename ABI::Position &position, + std::function target) +{ + target(tc); +} + +// Recursively gather arguments for target from tc until we get to the base +// case above. +template +static typename std::enable_if::value, Ret>::type +callFrom(ThreadContext *tc, typename ABI::Position &position, + std::function target) +{ + // Extract the next argument from the thread context. + NextArg next = Argument::get(tc, position); + + // Build a partial function which adds the next argument to the call. + std::function partial = + [target,next](ThreadContext *_tc, Args... args) { + return target(_tc, next, args...); + }; + + // Recursively handle any remaining arguments. + return callFrom(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 +static void +callFrom(ThreadContext *tc, typename ABI::Position &position, + std::function target) +{ + // Extract the next argument from the thread context. + NextArg next = Argument::get(tc, position); + + // Build a partial function which adds the next argument to the call. + std::function partial = + [target,next](ThreadContext *_tc, Args... args) { + target(_tc, next, args...); + }; + + // Recursively handle any remaining arguments. + callFrom(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 +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 +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::get(tc, position); + + // Add this argument to the list. + os << next; + + // Recursively handle any remaining arguments. + dumpArgsFrom(count + 1, os, tc, position); +} + +} // namespace GuestABI + +#endif // __SIM_GUEST_ABI_DISPATCH_HH__ diff --git a/src/sim/guest_abi/layout.hh b/src/sim/guest_abi/layout.hh new file mode 100644 index 000000000..e7941f807 --- /dev/null +++ b/src/sim/guest_abi/layout.hh @@ -0,0 +1,135 @@ +/* + * 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_LAYOUT_HH__ +#define __SIM_GUEST_ABI_LAYOUT_HH__ + +#include + +#include "sim/guest_abi/definition.hh" + +class ThreadContext; + +namespace GuestABI +{ + +/* + * Position may need to be initialized based on the ThreadContext, for instance + * to find out where the stack pointer is initially. + */ +template +struct PositionInitializer +{ + static typename ABI::Position + init(const ThreadContext *tc) + { + return typename ABI::Position(); + } +}; + +template +struct PositionInitializer::value + >::type> +{ + static typename ABI::Position + init(const ThreadContext *tc) + { + return typename ABI::Position(tc); + } +}; + +template +static typename ABI::Position +initializePosition(const ThreadContext *tc) +{ + return PositionInitializer::init(tc); +} + +/* + * This struct template provides a default allocate() method in case the + * Result or Argument template doesn't provide one. This is the default in + * cases where the return or argument type doesn't affect where things are + * stored. + */ +template class Role, + typename Type, typename Enabled=void> +struct Allocator +{ + static void + allocate(ThreadContext *tc, typename ABI::Position &position) + {} +}; + +/* + * If the return or argument type isn't void and does affect where things + * are stored, the ABI can implement an allocate() method for the various + * argument and/or return types, and this specialization will call into it. + */ +template class Role, typename Type> +struct Allocator::allocate)> +{ + static void + allocate(ThreadContext *tc, typename ABI::Position &position) + { + Role::allocate(tc, position); + } +}; + +template +static void +allocateResult(ThreadContext *tc, typename ABI::Position &position) +{ + Allocator::allocate(tc, position); +} + +template +static void +allocateArguments(ThreadContext *tc, typename ABI::Position &position) +{ + return; +} + +template +static void +allocateArguments(ThreadContext *tc, typename ABI::Position &position) +{ + Allocator::allocate(tc, position); + allocateArguments(tc, position); +} + +template +static void +allocateSignature(ThreadContext *tc, typename ABI::Position &position) +{ + allocateResult(tc, position); + allocateArguments(tc, position); +} + +} // namespace GuestABI + +#endif // __SIM_GUEST_ABI_LAYOUT_HH__ diff --git a/src/sim/guest_abi/varargs.hh b/src/sim/guest_abi/varargs.hh new file mode 100644 index 000000000..c350a8128 --- /dev/null +++ b/src/sim/guest_abi/varargs.hh @@ -0,0 +1,197 @@ +/* + * 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_VARARGS_HH__ +#define __SIM_GUEST_ABI_VARRAGS_HH__ + +#include +#include +#include + +#include "sim/guest_abi/definition.hh" + +class ThreadContext; + +namespace GuestABI +{ + +/* + * 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; + } +}; + +template +struct IsVarArgs : public std::false_type {}; + +template +struct IsVarArgs> : public std::true_type {}; + +template +std::ostream & +operator << (std::ostream &os, const VarArgs &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 +struct Argument> +{ + static VarArgs + get(ThreadContext *tc, typename ABI::Position &position) + { + using Base = VarArgsBase; + using Impl = VarArgsImpl; + return VarArgs(new Impl(tc, position)); + } +}; + +} // namespace GuestABI + +#endif // __SIM_GUEST_ABI_VARARGS_HH__ -- 2.30.2