From 61f3b66214378d6abda970fba7a9aadf8e925843 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 20 Dec 2019 21:04:19 -0800 Subject: [PATCH] sim: Generalize the GuestABI Result::allocate() mechanism. 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 Reviewed-by: Bobby R. Bruce Maintainer: Gabe Black --- src/sim/guest_abi.hh | 77 ++++++++++++++++++++++++++++++++------- src/sim/guest_abi.test.cc | 46 +++++++++++++++-------- 2 files changed, 95 insertions(+), 28 deletions(-) diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh index ec4e358c6..99b47b796 100644 --- a/src/sim/guest_abi.hh +++ b/src/sim/guest_abi.hh @@ -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 -struct ResultAllocator +template 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 -struct ResultAllocator::allocate)> +template class Role> +struct Allocator +{ + 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 class Role, typename Type> +struct Allocator::allocate)> { static void allocate(ThreadContext *tc, typename ABI::Position &position) { - Result::allocate(tc, position); + Role::allocate(tc, position); } }; +template +static void +allocateResult(ThreadContext *tc, typename ABI::Position &position) +{ + Allocator::allocate(tc, position); +} + +template +static void +allocateArguments(ThreadContext *tc, typename ABI::Position &position) +{ + return; +} + +template +static void +allocateArguments(ThreadContext *tc, typename ABI::Position &position) +{ + Allocator::allocate(tc, position); + allocateArguments(tc, position); +} + +template +static void +allocateSignature(ThreadContext *tc, typename ABI::Position &position) +{ + allocateResult(tc, position); + allocateArguments(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::init(tc); - GuestABI::ResultAllocator::allocate(tc, position); + GuestABI::allocateSignature(tc, position); return GuestABI::callFrom(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::init(tc); + GuestABI::allocateArguments(tc, position); GuestABI::callFrom(tc, position, target); } @@ -483,7 +534,7 @@ dumpSimcall(std::string name, ThreadContext *tc, auto position = GuestABI::PositionInitializer::init(tc); std::ostringstream ss; - GuestABI::ResultAllocator::allocate(tc, position); + GuestABI::allocateSignature(tc, position); ss << name; GuestABI::dumpArgsFrom(0, ss, tc, position); return ss.str(); diff --git a/src/sim/guest_abi.test.cc b/src/sim/guest_abi.test.cc index 439ad86e0..9b1fe6121 100644 --- a/src/sim/guest_abi.test.cc +++ b/src/sim/guest_abi.test.cc @@ -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 -struct Argument : public Argument {}; +// 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 +{ + static int + get(ThreadContext *tc, TestABI_Allocate::Position &position) + { + return tc->ints[--position]; + } + + static void + allocate(ThreadContext *tc, TestABI_Allocate::Position &position) + { + position++; + } +}; template -struct Result +struct Result { 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(&tc, testRetRegVoid); - invokeSimcall(&tc, testRetRegInt); + invokeSimcall(&tc, testAllocateVoid); + invokeSimcall(&tc, testAllocateInt); } TEST(GuestABI, ABI_2D_args) -- 2.30.2