sim: Generalize the GuestABI Result::allocate() mechanism.
authorGabe Black <gabeblack@google.com>
Sat, 21 Dec 2019 05:04:19 +0000 (21:04 -0800)
committerGabe Black <gabeblack@google.com>
Thu, 12 Mar 2020 07:21:13 +0000 (07:21 +0000)
This change generalizes the GuestABI Result template family's
allocate() mechanism so that it can also be used with arguments. This
is useful in cases like the 32 bit ARM ISA which says that variadic
functions treat their floating point arguments differently than
non-variadic functions do. This mechanism will give the ABI a chance
to detect that the function is variadic (by checking for VarArgs) and
then to behave differently in that case.

This also makes the GuestABI mechanism a little more regular by
supporting allocate() on both arguments and return values instead of
just return values.

Change-Id: I67050a0ad7ccf15ee75b800dca4eae4fdc0e564e
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/24103
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu>
Maintainer: Gabe Black <gabeblack@google.com>

src/sim/guest_abi.hh
src/sim/guest_abi.test.cc

index ec4e358c604bd91681a0f90ca8b54a1f8cecc140..99b47b796bffe478a18cc9cec49b5e515a7e4f4a 100644 (file)
@@ -127,16 +127,25 @@ struct Argument
      * 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 template doesn't provide one. This is the default in cases where the
- * return type doesn't affect how arguments are laid out.
+ * 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 <typename ABI, typename Ret, typename Enabled=void>
-struct ResultAllocator
+template <typename ABI, template <class ...> class Role,
+          typename Type, typename Enabled=void>
+struct Allocator
 {
     static void
     allocate(ThreadContext *tc, typename ABI::Position &position)
@@ -144,20 +153,61 @@ struct ResultAllocator
 };
 
 /*
- * 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.
+ * If the return type is void, don't allocate anything.
  */
-template <typename ABI, typename Ret>
-struct ResultAllocator<ABI, Ret, decltype((void)&Result<ABI, Ret>::allocate)>
+template <typename ABI, template <class ...> class Role>
+struct Allocator<ABI, Role, void>
+{
+    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 <typename ABI, template <class ...> class Role, typename Type>
+struct Allocator<ABI, Role, Type, decltype((void)&Role<ABI, Type>::allocate)>
 {
     static void
     allocate(ThreadContext *tc, typename ABI::Position &position)
     {
-        Result<ABI, Ret>::allocate(tc, position);
+        Role<ABI, Type>::allocate(tc, position);
     }
 };
 
+template <typename ABI, typename Ret, typename Enabled=void>
+static void
+allocateResult(ThreadContext *tc, typename ABI::Position &position)
+{
+    Allocator<ABI, Result, Ret>::allocate(tc, position);
+}
+
+template <typename ABI>
+static void
+allocateArguments(ThreadContext *tc, typename ABI::Position &position)
+{
+    return;
+}
+
+template <typename ABI, typename NextArg, typename ...Args>
+static void
+allocateArguments(ThreadContext *tc, typename ABI::Position &position)
+{
+    Allocator<ABI, Argument, NextArg>::allocate(tc, position);
+    allocateArguments<ABI, Args...>(tc, position);
+}
+
+template <typename ABI, typename Ret, typename ...Args>
+static void
+allocateSignature(ThreadContext *tc, typename ABI::Position &position)
+{
+    allocateResult<ABI, Ret>(tc, position);
+    allocateArguments<ABI, Args...>(tc, position);
+}
+
 
 /*
  * These templates implement a variadic argument mechanism for guest ABI
@@ -438,7 +488,7 @@ invokeSimcall(ThreadContext *tc,
     // Default construct a Position to track consumed resources. Built in
     // types will be zero initialized.
     auto position = GuestABI::PositionInitializer<ABI>::init(tc);
-    GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
+    GuestABI::allocateSignature<ABI, Ret, Args...>(tc, position);
     return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
 }
 
@@ -458,6 +508,7 @@ invokeSimcall(ThreadContext *tc,
     // Default construct a Position to track consumed resources. Built in
     // types will be zero initialized.
     auto position = GuestABI::PositionInitializer<ABI>::init(tc);
+    GuestABI::allocateArguments<ABI, Args...>(tc, position);
     GuestABI::callFrom<ABI, Args...>(tc, position, target);
 }
 
@@ -483,7 +534,7 @@ dumpSimcall(std::string name, ThreadContext *tc,
     auto position = GuestABI::PositionInitializer<ABI>::init(tc);
     std::ostringstream ss;
 
-    GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
+    GuestABI::allocateSignature<ABI, Ret, Args...>(tc, position);
     ss << name;
     GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
     return ss.str();
index 439ad86e0b6b4ea75d85611cf9c55e6d231322ab..9b1fe6121740c1d4dd5fbc05e0f6179823c6acb7 100644 (file)
@@ -66,8 +66,8 @@ struct TestABI_1D
     using Position = int;
 };
 
-// ABI anchor for an ABI which allocates a register for non-void return types.
-struct TestABI_RetReg
+// ABI anchor for an ABI which uses the allocate() hook.
+struct TestABI_Allocate
 {
     using Position = int;
 };
@@ -136,17 +136,31 @@ 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> {};
+// Hooks for the ABI which uses allocate(). It uses the same rules as the
+// 1D ABI for arguments, but allocates space for and discards return values
+// and returns integer arguments in reverse order.
+template <>
+struct Argument<TestABI_Allocate, int>
+{
+    static int
+    get(ThreadContext *tc, TestABI_Allocate::Position &position)
+    {
+        return tc->ints[--position];
+    }
+
+    static void
+    allocate(ThreadContext *tc, TestABI_Allocate::Position &position)
+    {
+        position++;
+    }
+};
 
 template <typename Ret>
-struct Result<TestABI_RetReg, Ret>
+struct Result<TestABI_Allocate, Ret>
 {
     static void store(ThreadContext *tc, const Ret &ret) {}
     static void
-    allocate(ThreadContext *tc, TestABI_RetReg::Position &position)
+    allocate(ThreadContext *tc, TestABI_Allocate::Position &position)
     {
         position++;
     }
@@ -229,15 +243,17 @@ testIntVoid(ThreadContext *tc, int a, float b, int c, double d,
 // Test functions which verify that the return allocating ABI allocates space
 // for its return value successfully.
 void
-testRetRegVoid(ThreadContext *tc, int a)
+testAllocateVoid(ThreadContext *tc, int a, int b)
 {
-    EXPECT_EQ(a, tc->ints[0]);
+    EXPECT_EQ(a, tc->ints[1]);
+    EXPECT_EQ(b, tc->ints[0]);
 }
 
 int
-testRetRegInt(ThreadContext *tc, int a)
+testAllocateInt(ThreadContext *tc, int a, int b)
 {
-    EXPECT_EQ(a, tc->ints[1]);
+    EXPECT_EQ(a, tc->ints[2]);
+    EXPECT_EQ(b, tc->ints[1]);
     return 0;
 }
 
@@ -283,11 +299,11 @@ TEST(GuestABI, ABI_1D_args)
     EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
 }
 
-TEST(GuestABI, ABI_RetReg)
+TEST(GuestABI, ABI_Allocate)
 {
     ThreadContext tc;
-    invokeSimcall<TestABI_RetReg>(&tc, testRetRegVoid);
-    invokeSimcall<TestABI_RetReg>(&tc, testRetRegInt);
+    invokeSimcall<TestABI_Allocate>(&tc, testAllocateVoid);
+    invokeSimcall<TestABI_Allocate>(&tc, testAllocateInt);
 }
 
 TEST(GuestABI, ABI_2D_args)