From: Gabe Black Date: Thu, 28 Nov 2019 06:54:48 +0000 (-0800) Subject: sim: Add a unit test for the GuestABI mechanism. X-Git-Tag: v19.0.0.0~117 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=aabe7e1f697dc87e8ad0956c53a81dc1ecce2595;p=gem5.git sim: Add a unit test for the GuestABI mechanism. Jira Issue: https://gem5.atlassian.net/browse/GEM5-187 Change-Id: I76934d94b4c61570a4ca603388012c65280e2b7c Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23197 Reviewed-by: Jason Lowe-Power Maintainer: Jason Lowe-Power Tested-by: kokoro --- diff --git a/src/sim/SConscript b/src/sim/SConscript index aacdb341d..b27dc5139 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -78,6 +78,7 @@ Source('clocked_object.cc') Source('mathexpr.cc') GTest('byteswap.test', 'byteswap.test.cc', '../base/types.cc') +GTest('guest_abi.test', 'guest_abi.test.cc') if env['TARGET_ISA'] != 'null': SimObject('InstTracer.py') diff --git a/src/sim/guest_abi.test.cc b/src/sim/guest_abi.test.cc new file mode 100644 index 000000000..ee536aa13 --- /dev/null +++ b/src/sim/guest_abi.test.cc @@ -0,0 +1,277 @@ +/* + * Copyright 2019 Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include + +#include +#include + +#include "sim/guest_abi.hh" + +// Fake ThreadContext which holds data and captures results. +class ThreadContext +{ + public: + static const int ints[]; + static const double floats[]; + + static const int DefaultIntResult; + static const double DefaultFloatResult; + + int intResult = DefaultIntResult; + double floatResult = DefaultFloatResult; +}; + +const int ThreadContext::ints[] = { + 0, 1, 2, 3, 4, 5, 6, 7 +}; +const double ThreadContext::floats[] = { + 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0 +}; + +const int ThreadContext::DefaultIntResult = 0; +const double ThreadContext::DefaultFloatResult = 0.0; + +// ABI anchor for an ABI which has 1D progress. Conceptually, this could be +// because integer and floating point arguments are stored in the same +// registers. +struct TestABI_1D +{ + 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. +struct TestABI_2D +{ + using Position = std::pair; +}; + +namespace GuestABI +{ + +// Hooks for the 1D ABI arguments and return value. Add 1 or 1.0 to return +// values so we can tell they went through the right set of hooks. +template <> +struct Argument +{ + static int + get(ThreadContext *tc, TestABI_1D::Position &position) + { + return tc->ints[position++]; + } +}; + +template +struct Argument::value>::type> +{ + static Arg + get(ThreadContext *tc, TestABI_1D::Position &position) + { + return tc->floats[position++]; + } +}; + +template <> +struct Result +{ + static void + store(ThreadContext *tc, const int &ret) + { + tc->intResult = ret + 1; + } +}; + +template +struct Result::value>::type> +{ + static void + store(ThreadContext *tc, const Ret &ret) + { + tc->floatResult = ret + 1.0; + } +}; + +// 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. + +template <> +struct Argument +{ + static int + get(ThreadContext *tc, TestABI_2D::Position &position) + { + return tc->ints[position.first++]; + } +}; + +template +struct Argument::value>::type> +{ + static Arg + get(ThreadContext *tc, TestABI_2D::Position &position) + { + return tc->floats[position.second++]; + } +}; + +template <> +struct Result +{ + static void + store(ThreadContext *tc, const int &ret) + { + tc->intResult = ret + 2; + } +}; + +template +struct Result::value>::type> +{ + static void + store(ThreadContext *tc, const Ret &ret) + { + tc->floatResult = ret + 2.0; + } +}; + +} // namespace GuestABI + +// Test function which verifies that its arguments reflect the 1D ABI and +// which doesn't return anything. +void +testIntVoid(ThreadContext *tc, int a, float b, int c, double d, + GuestABI::VarArgs varargs) +{ + EXPECT_EQ(a, tc->ints[0]); + EXPECT_EQ(b, tc->floats[1]); + EXPECT_EQ(c, tc->ints[2]); + EXPECT_EQ(d, tc->floats[3]); + + EXPECT_EQ(varargs.get(), tc->ints[4]); + EXPECT_EQ(varargs.get(), tc->floats[5]); + EXPECT_EQ(varargs.get(), tc->floats[6]); +} + +// Test function which verifies that its arguments reflect the 2D ABI and +// which doesn't return anything. +void +test2DVoid(ThreadContext *tc, int a, float b, int c, double d, + GuestABI::VarArgs varargs) +{ + EXPECT_EQ(a, tc->ints[0]); + EXPECT_EQ(b, tc->floats[0]); + EXPECT_EQ(c, tc->ints[1]); + EXPECT_EQ(d, tc->floats[1]); + + EXPECT_EQ(varargs.get(), tc->ints[2]); + EXPECT_EQ(varargs.get(), tc->floats[2]); + EXPECT_EQ(varargs.get(), tc->floats[3]); +} + +// Test functions which returns various types of values. +const int IntRetValue = 50; +const float FloatRetValue = 3.14; +const double DoubleRetValue = 12.34; + +int testIntRet(ThreadContext *tc) { return IntRetValue; } +float testFloatRet(ThreadContext *tc) { return FloatRetValue; } +double testDoubleRet(ThreadContext *tc) { return DoubleRetValue; } + + +// The actual test bodies. +TEST(GuestABI, ABI_1D_args) +{ + ThreadContext tc; + invokeSimcall(&tc, testIntVoid); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult); +} + +TEST(GuestABI, ABI_2D_args) +{ + ThreadContext tc; + invokeSimcall(&tc, test2DVoid); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult); +} + +TEST(GuestABI, ABI_returns) +{ + // 1D returns. + { + ThreadContext tc; + int ret = invokeSimcall(&tc, testIntRet); + EXPECT_EQ(ret, IntRetValue); + EXPECT_EQ(tc.intResult, IntRetValue + 1); + EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult); + } + { + ThreadContext tc; + float ret = invokeSimcall(&tc, testFloatRet); + EXPECT_EQ(ret, FloatRetValue); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, FloatRetValue + 1.0); + } + { + ThreadContext tc; + double ret = invokeSimcall(&tc, testDoubleRet); + EXPECT_EQ(ret, DoubleRetValue); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, DoubleRetValue + 1.0); + } + + // 2D returns. + { + ThreadContext tc; + int ret = invokeSimcall(&tc, testIntRet); + EXPECT_EQ(ret, IntRetValue); + EXPECT_EQ(tc.intResult, IntRetValue + 2); + EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult); + } + { + ThreadContext tc; + float ret = invokeSimcall(&tc, testFloatRet); + EXPECT_EQ(ret, FloatRetValue); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, FloatRetValue + 2.0); + } + { + ThreadContext tc; + double ret = invokeSimcall(&tc, testDoubleRet); + EXPECT_EQ(ret, DoubleRetValue); + EXPECT_EQ(tc.intResult, tc.DefaultIntResult); + EXPECT_EQ(tc.floatResult, DoubleRetValue + 2.0); + } +}