summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabe Black <gabeblack@google.com>2019-12-10 21:48:26 -0800
committerGabe Black <gabeblack@google.com>2020-01-25 07:47:17 +0000
commit13083a1c9cdd271d0cb4d8d2eb14161f5fe5fd03 (patch)
treee3aa5ba9c3cba5be3036fc3df44926ef41942ef8
parent377898c4034c72b84b2662ed252fa25079a4ea62 (diff)
downloadgem5-13083a1c9cdd271d0cb4d8d2eb14161f5fe5fd03.tar.xz
sim: Add a GuestABI mechanism to allocate space for a return value.
Some ABIs (including 32 bit ARM, 64 bit x86) allocate their argument registers differently depending on their return value. For instance, if the value needs to be returned in memory because it's too big, the caller could pass a pointer to where the result should be stored when the function returns. This pointer acts like an invisible first argument, offsetting where all the normal arguments actually live. This change adds a mechanism to handle that case. The Result templates can now declare an allocate() static method which is given a ThreadContext *, and a reference to the Position object. It can perform any adjustment it needs to before the normal argument extraction starts. Change-Id: Ibda9095f0e8c9882742d24f5effe309ccb514188 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23747 Maintainer: Gabe Black <gabeblack@google.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
-rw-r--r--src/sim/guest_abi.hh46
-rw-r--r--src/sim/guest_abi.test.cc44
2 files changed, 90 insertions, 0 deletions
diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh
index ff1464e8f..2dd27c424 100644
--- a/src/sim/guest_abi.hh
+++ b/src/sim/guest_abi.hh
@@ -74,8 +74,23 @@ struct Result
* 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 <typename ABI>
+struct Result<ABI, void>
+{};
+
template <typename ABI, typename Arg, typename Enabled=void>
struct Argument
{
@@ -92,6 +107,35 @@ struct Argument
/*
+ * This struct template provides a default allocate() method in case the
+ * Result template doesn't provide one. This is the default in cases where the
+ * return type doesn't affect how arguments are laid out.
+ */
+template <typename ABI, typename Ret, typename Enabled=void>
+struct ResultAllocator
+{
+ static void
+ allocate(ThreadContext *tc, typename ABI::Position &position)
+ {}
+};
+
+/*
+ * If the return type *does* affect how the arguments are laid out, the ABI
+ * can implement an allocate() method for the various return types, and this
+ * specialization will call into it.
+ */
+template <typename ABI, typename Ret>
+struct ResultAllocator<ABI, Ret, decltype((void)&Result<ABI, Ret>::allocate)>
+{
+ static void
+ allocate(ThreadContext *tc, typename ABI::Position &position)
+ {
+ Result<ABI, Ret>::allocate(tc, position);
+ }
+};
+
+
+/*
* These templates implement a variadic argument mechanism for guest ABI
* functions. A function might be written like this:
*
@@ -364,6 +408,7 @@ invokeSimcall(ThreadContext *tc,
// Default construct a Position to track consumed resources. Built in
// types will be zero initialized.
auto position = typename ABI::Position();
+ GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
}
@@ -408,6 +453,7 @@ dumpSimcall(std::string name, ThreadContext *tc,
auto position = typename ABI::Position();
std::ostringstream ss;
+ GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
ss << name;
GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
return ss.str();
diff --git a/src/sim/guest_abi.test.cc b/src/sim/guest_abi.test.cc
index 19efb7db2..2f896f9b3 100644
--- a/src/sim/guest_abi.test.cc
+++ b/src/sim/guest_abi.test.cc
@@ -66,6 +66,12 @@ struct TestABI_1D
using Position = int;
};
+// ABI anchor for an ABI which allocates a register for non-void return types.
+struct TestABI_RetReg
+{
+ using Position = int;
+};
+
// ABI anchor for an ABI which has 2D progress. Conceptually, this could be
// because integer and floating point arguments are stored in separate
// registers.
@@ -121,6 +127,22 @@ struct Result<TestABI_1D, Ret,
}
};
+// Hooks for the return value allocating ABI. It uses the same rules as the
+// 1D ABI for arguments, but allocates space for and discards return values.
+template <typename Arg>
+struct Argument<TestABI_RetReg, Arg> : public Argument<TestABI_1D, Arg> {};
+
+template <typename Ret>
+struct Result<TestABI_RetReg, Ret>
+{
+ static void store(ThreadContext *tc, const Ret &ret) {}
+ static void
+ allocate(ThreadContext *tc, TestABI_RetReg::Position &position)
+ {
+ position++;
+ }
+};
+
// Hooks for the 2D ABI arguments and return value. Add 2 or 2.0 to return
// values so we can tell they went through the right set of hooks.
@@ -184,6 +206,21 @@ testIntVoid(ThreadContext *tc, int a, float b, int c, double d,
EXPECT_EQ(varargs.get<double>(), tc->floats[6]);
}
+// Test functions which verify that the return allocating ABI allocates space
+// for its return value successfully.
+void
+testRetRegVoid(ThreadContext *tc, int a)
+{
+ EXPECT_EQ(a, tc->ints[0]);
+}
+
+int
+testRetRegInt(ThreadContext *tc, int a)
+{
+ EXPECT_EQ(a, tc->ints[1]);
+ return 0;
+}
+
// Test function which verifies that its arguments reflect the 2D ABI and
// which doesn't return anything.
void
@@ -219,6 +256,13 @@ TEST(GuestABI, ABI_1D_args)
EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
}
+TEST(GuestABI, ABI_RetReg)
+{
+ ThreadContext tc;
+ invokeSimcall<TestABI_RetReg>(&tc, testRetRegVoid);
+ invokeSimcall<TestABI_RetReg>(&tc, testRetRegInt);
+}
+
TEST(GuestABI, ABI_2D_args)
{
ThreadContext tc;