diff options
-rw-r--r-- | src/sim/guest_abi.hh | 46 | ||||
-rw-r--r-- | src/sim/guest_abi.test.cc | 44 |
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; |