sim: Make it possible for a GuestABI to init its Position based on a TC.

It may be necessary to initialize the GuestABI Position type based on
the current state of the thread, for instance by reading the current
stack pointer.

This change makes it possible (but not mandantory) for an ABI to supply
a constructor for Position which accepts a ThreadContext * which it can
use to intiialize itself.

Change-Id: I5609b185f746368c5f9eb2a04074dcafa088f925
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23749
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh
index 2dd27c4..5432af3 100644
--- a/src/sim/guest_abi.hh
+++ b/src/sim/guest_abi.hh
@@ -61,6 +61,32 @@
  * 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 <typename ABI, typename Enabled=void>
+struct PositionInitializer
+{
+    static typename ABI::Position
+    init(const ThreadContext *tc)
+    {
+        return typename ABI::Position();
+    }
+};
+
+template <typename ABI>
+struct PositionInitializer<ABI, typename std::enable_if<
+    std::is_constructible<typename ABI::Position, const ThreadContext *>::value
+    >::type>
+{
+    static typename ABI::Position
+    init(const ThreadContext *tc)
+    {
+        return typename ABI::Position(tc);
+    }
+};
+
 template <typename ABI, typename Ret, typename Enabled=void>
 struct Result
 {
@@ -407,7 +433,7 @@
 {
     // Default construct a Position to track consumed resources. Built in
     // types will be zero initialized.
-    auto position = typename ABI::Position();
+    auto position = GuestABI::PositionInitializer<ABI>::init(tc);
     GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
     return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
 }
@@ -427,7 +453,7 @@
 {
     // Default construct a Position to track consumed resources. Built in
     // types will be zero initialized.
-    auto position = typename ABI::Position();
+    auto position = GuestABI::PositionInitializer<ABI>::init(tc);
     GuestABI::callFrom<ABI, Args...>(tc, position, target);
 }
 
@@ -450,7 +476,7 @@
             std::function<Ret(ThreadContext *, Args...)> target=
             std::function<Ret(ThreadContext *, Args...)>())
 {
-    auto position = typename ABI::Position();
+    auto position = GuestABI::PositionInitializer<ABI>::init(tc);
     std::ostringstream ss;
 
     GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
diff --git a/src/sim/guest_abi.test.cc b/src/sim/guest_abi.test.cc
index 2f896f9..506163e 100644
--- a/src/sim/guest_abi.test.cc
+++ b/src/sim/guest_abi.test.cc
@@ -46,6 +46,8 @@
 
     int intResult = DefaultIntResult;
     double floatResult = DefaultFloatResult;
+
+    int intOffset = 0;
 };
 
 const int ThreadContext::ints[] = {
@@ -80,6 +82,15 @@
     using Position = std::pair<int, int>;
 };
 
+struct TestABI_TcInit
+{
+    struct Position
+    {
+        int pos;
+        Position(const ThreadContext *tc) : pos(tc->intOffset) {}
+    };
+};
+
 namespace GuestABI
 {
 
@@ -188,6 +199,17 @@
     }
 };
 
+// Hooks for the TcInit ABI arguments.
+template <>
+struct Argument<TestABI_TcInit, int>
+{
+    static int
+    get(ThreadContext *tc, TestABI_TcInit::Position &position)
+    {
+        return tc->ints[position.pos++];
+    }
+};
+
 } // namespace GuestABI
 
 // Test function which verifies that its arguments reflect the 1D ABI and
@@ -237,6 +259,13 @@
     EXPECT_EQ(varargs.get<double>(), tc->floats[3]);
 }
 
+void
+testTcInit(ThreadContext *tc, int a)
+{
+    EXPECT_EQ(tc->intOffset, 2);
+    EXPECT_EQ(a, tc->ints[2]);
+}
+
 // Test functions which returns various types of values.
 const int IntRetValue = 50;
 const float FloatRetValue = 3.14;
@@ -271,6 +300,13 @@
     EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
 }
 
+TEST(GuestABI, ABI_TC_init)
+{
+    ThreadContext tc;
+    tc.intOffset = 2;
+    invokeSimcall<TestABI_TcInit>(&tc, testTcInit);
+}
+
 TEST(GuestABI, ABI_returns)
 {
     // 1D returns.