From ad204d9de0a19d64f0ab50a9290ebd04750a8ff7 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Thu, 18 Feb 2021 06:28:26 -0800 Subject: [PATCH] sim: Simplify some code in the guest ABI mechanism. Instead of using recursively applied templates to accumulate a series of wrapper lambdas which dispatch to a call, use pure parameter pack expansion. This has two benefits. One, it makes the code simpler(ish) and easier to understand. The parameter pack machinery is still intrinsically fairly tricky, but there's less of it and it's a fairly straightforward application of that mechanism. Also, a nice side benefit is that the template for simcall dispatch will expand to a small fixed number of functions which do all their work locally, instead of having a new function for each layer of the onion, one per parameter, and no calls through lambdas. That should hopefully make debugging easier, and produce less bookkeeping overhead as far as really long names, lots of functions, etc. This code, specifically the code in dispatch.hh, can be simplified even further in the future once we start using c++17 which is if constexpr, and std::apply which explodes a tuple and uses its components as arguments to a function, something I'm doing manually here. Change-Id: If7c9234cc1014101211474c2ec20362702cf78c2 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/41600 Reviewed-by: Gabe Black Maintainer: Gabe Black Tested-by: kokoro --- src/sim/guest_abi.hh | 6 +- src/sim/guest_abi/dispatch.hh | 127 ++++++++++------------------------ src/sim/guest_abi/layout.hh | 27 ++------ 3 files changed, 48 insertions(+), 112 deletions(-) diff --git a/src/sim/guest_abi.hh b/src/sim/guest_abi.hh index ea3325f6a2..75c4e00268 100644 --- a/src/sim/guest_abi.hh +++ b/src/sim/guest_abi.hh @@ -51,7 +51,7 @@ invokeSimcall(ThreadContext *tc, // types will be zero initialized. auto state = GuestABI::initializeState(tc); GuestABI::prepareForFunction(tc, state); - return GuestABI::callFrom(tc, state, target); + return GuestABI::callFrom(tc, state, target); } template @@ -86,7 +86,7 @@ invokeSimcall(ThreadContext *tc, // types will be zero initialized. auto state = GuestABI::initializeState(tc); GuestABI::prepareForArguments(tc, state); - GuestABI::callFrom(tc, state, target); + GuestABI::callFrom(tc, state, target); } template @@ -113,7 +113,7 @@ dumpSimcall(std::string name, ThreadContext *tc, GuestABI::prepareForFunction(tc, state); ss << name; - GuestABI::dumpArgsFrom(0, ss, tc, state); + GuestABI::dumpArgsFrom(ss, tc, state); return ss.str(); } diff --git a/src/sim/guest_abi/dispatch.hh b/src/sim/guest_abi/dispatch.hh index bc365b9796..8f3a4ac036 100644 --- a/src/sim/guest_abi/dispatch.hh +++ b/src/sim/guest_abi/dispatch.hh @@ -30,8 +30,11 @@ #include #include +#include #include +#include +#include "base/compiler.hh" #include "sim/guest_abi/definition.hh" #include "sim/guest_abi/layout.hh" @@ -50,114 +53,60 @@ namespace GuestABI * still possible to support by redefining these functions.. */ -// With no arguments to gather, call the target function and store the -// result. -template -static typename std::enable_if_t::value && store_ret, Ret> -callFrom(ThreadContext *tc, typename ABI::State &state, - std::function target) +template +static inline typename std::enable_if_t +callFromHelper(Target &target, ThreadContext *tc, State &state, Args &&args, + std::index_sequence) { - Ret ret = target(tc); + return target(tc, std::get(args)...); +} + +template +static inline typename std::enable_if_t +callFromHelper(Target &target, ThreadContext *tc, State &state, Args &&args, + std::index_sequence) +{ + Ret ret = target(tc, std::get(args)...); storeResult(tc, ret, state); return ret; } -template -static typename std::enable_if_t::value && !store_ret, Ret> +template +static inline Ret callFrom(ThreadContext *tc, typename ABI::State &state, - std::function target) + std::function target) { - return target(tc); + // Extract all the arguments from the thread context. Braced initializers + // are evaluated from left to right. + auto args = std::tuple{getArgument(tc, state)...}; + + // Call the wrapper which will call target. + return callFromHelper( + target, tc, state, std::move(args), + std::make_index_sequence{}); } -// With no arguments to gather and nothing to return, call the target function. -template -static void -callFrom(ThreadContext *tc, typename ABI::State &state, - std::function target) -{ - target(tc); -} - -// Recursively gather arguments for target from tc until we get to the base -// case above. -template -static typename std::enable_if_t::value, Ret> -callFrom(ThreadContext *tc, typename ABI::State &state, - std::function target) -{ - // Extract the next argument from the thread context. - NextArg next = getArgument(tc, state); - - // Build a partial function which adds the next argument to the call. - std::function partial = - [target,next](ThreadContext *_tc, Args... args) { - return target(_tc, next, args...); - }; - - // Recursively handle any remaining arguments. - return callFrom(tc, state, partial); -} - -// Recursively gather arguments for target from tc until we get to the base -// case above. This version is for functions that don't return anything. -template -static void -callFrom(ThreadContext *tc, typename ABI::State &state, - std::function target) -{ - // Extract the next argument from the thread context. - NextArg next = getArgument(tc, state); - - // Build a partial function which adds the next argument to the call. - std::function partial = - [target,next](ThreadContext *_tc, Args... args) { - target(_tc, next, args...); - }; - - // Recursively handle any remaining arguments. - callFrom(tc, state, partial); -} - - /* - * These functions are like the ones above, except they print the arguments + * This function is like the ones above, except it prints the arguments * a target function would be called with instead of actually calling it. */ -// With no arguments to print, add the closing parenthesis and return. -template +template static void -dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc, - typename ABI::State &state) +dumpArgsFrom(std::ostream &os, M5_VAR_USED ThreadContext *tc, + typename ABI::State &state) { + int count = 0; + // Extract all the arguments from the thread context and print them, + // prefixed with either a ( or a , as appropriate. + M5_FOR_EACH_IN_PACK(os << (count++ ? ", " : "("), + os << getArgument(tc, state)); os << ")"; } -// Recursively gather arguments for target from tc until we get to the base -// case above, and append those arguments to the string stream being -// constructed. -template -static void -dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc, - typename ABI::State &state) -{ - // Either open the parenthesis or add a comma, depending on where we are - // in the argument list. - os << (count ? ", " : "("); - - // Extract the next argument from the thread context. - NextArg next = getArgument(tc, state); - - // Add this argument to the list. - os << next; - - // Recursively handle any remaining arguments. - dumpArgsFrom(count + 1, os, tc, state); -} - } // namespace GuestABI #endif // __SIM_GUEST_ABI_DISPATCH_HH__ diff --git a/src/sim/guest_abi/layout.hh b/src/sim/guest_abi/layout.hh index bb46d62070..6de04703e8 100644 --- a/src/sim/guest_abi/layout.hh +++ b/src/sim/guest_abi/layout.hh @@ -30,6 +30,7 @@ #include +#include "base/compiler.hh" #include "sim/guest_abi/definition.hh" class ThreadContext; @@ -100,29 +101,21 @@ struct Preparer::prepare)> }; template -static void +static inline void prepareForResult(ThreadContext *tc, typename ABI::State &state) { Preparer::prepare(tc, state); } -template -static void -prepareForArguments(ThreadContext *tc, typename ABI::State &state) +template +static inline void +prepareForArguments(M5_VAR_USED ThreadContext *tc, typename ABI::State &state) { - return; -} - -template -static void -prepareForArguments(ThreadContext *tc, typename ABI::State &state) -{ - Preparer::prepare(tc, state); - prepareForArguments(tc, state); + M5_FOR_EACH_IN_PACK(Preparer::prepare(tc, state)); } template -static void +static inline void prepareForFunction(ThreadContext *tc, typename ABI::State &state) { prepareForResult(tc, state); @@ -144,12 +137,6 @@ struct ResultStorer } }; -template -std::true_type foo(void (*)(ThreadContext *, const Ret &ret, State &state)); - -template -std::false_type foo(void (*)(ThreadContext *, const Ret &ret)); - template struct ResultStorer