* 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)
};
/*
- * 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
// 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);
}
// 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);
}
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();
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;
};
}
};
-// 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++;
}
// 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;
}
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)