sim: Implement a varargs like mechanism for GuestABI system.
authorGabe Black <gabeblack@google.com>
Thu, 28 Nov 2019 05:34:53 +0000 (21:34 -0800)
committerGabe Black <gabeblack@google.com>
Mon, 13 Jan 2020 22:44:49 +0000 (22:44 +0000)
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 <jason@lowepower.com>
Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/sim/guest_abi.hh

index 109c8725264f19ee5f8430c20af21fecb9c9c320..b2176a6dfbdd2ebb81f6f592ba8681fd5fd78c62 100644 (file)
@@ -31,6 +31,7 @@
 #define __SIM_GUEST_ABI_HH__
 
 #include <functional>
+#include <memory>
 #include <type_traits>
 
 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<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;
+    }
+};
+
+// 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