gem5  v19.0.0.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
guest_abi.test.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2019 Google, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met: redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer;
8  * redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution;
11  * neither the name of the copyright holders nor the names of its
12  * contributors may be used to endorse or promote products derived from
13  * this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * Authors: Gabe Black
28  */
29 
30 #include <gtest/gtest.h>
31 
32 #include <type_traits>
33 #include <utility>
34 
35 #include "sim/guest_abi.hh"
36 
37 // Fake ThreadContext which holds data and captures results.
38 class ThreadContext
39 {
40  public:
41  static const int ints[];
42  static const double floats[];
43 
44  static const int DefaultIntResult;
45  static const double DefaultFloatResult;
46 
49 
50  int intOffset = 0;
51 };
52 
53 const int ThreadContext::ints[] = {
54  0, 1, 2, 3, 4, 5, 6, 7
55 };
56 const double ThreadContext::floats[] = {
57  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0
58 };
59 
61 const double ThreadContext::DefaultFloatResult = 0.0;
62 
63 // ABI anchor for an ABI which has 1D progress. Conceptually, this could be
64 // because integer and floating point arguments are stored in the same
65 // registers.
66 struct TestABI_1D
67 {
68  using Position = int;
69 };
70 
71 // ABI anchor for an ABI which allocates a register for non-void return types.
73 {
74  using Position = int;
75 };
76 
77 // ABI anchor for an ABI which has 2D progress. Conceptually, this could be
78 // because integer and floating point arguments are stored in separate
79 // registers.
80 struct TestABI_2D
81 {
83 };
84 
86 {
87  struct Position
88  {
89  int pos;
90  Position(const ThreadContext *tc) : pos(tc->intOffset) {}
91  };
92 };
93 
94 namespace GuestABI
95 {
96 
97 // Hooks for the 1D ABI arguments and return value. Add 1 or 1.0 to return
98 // values so we can tell they went through the right set of hooks.
99 template <>
100 struct Argument<TestABI_1D, int>
101 {
102  static int
104  {
105  return tc->ints[position++];
106  }
107 };
108 
109 template <typename Arg>
110 struct Argument<TestABI_1D, Arg,
111  typename std::enable_if<std::is_floating_point<Arg>::value>::type>
112 {
113  static Arg
115  {
116  return tc->floats[position++];
117  }
118 };
119 
120 template <>
121 struct Result<TestABI_1D, int>
122 {
123  static void
124  store(ThreadContext *tc, const int &ret)
125  {
126  tc->intResult = ret + 1;
127  }
128 };
129 
130 template <typename Ret>
131 struct Result<TestABI_1D, Ret,
132  typename std::enable_if<std::is_floating_point<Ret>::value>::type>
133 {
134  static void
135  store(ThreadContext *tc, const Ret &ret)
136  {
137  tc->floatResult = ret + 1.0;
138  }
139 };
140 
141 // Hooks for the return value allocating ABI. It uses the same rules as the
142 // 1D ABI for arguments, but allocates space for and discards return values.
143 template <typename Arg>
144 struct Argument<TestABI_RetReg, Arg> : public Argument<TestABI_1D, Arg> {};
145 
146 template <typename Ret>
147 struct Result<TestABI_RetReg, Ret>
148 {
149  static void store(ThreadContext *tc, const Ret &ret) {}
150  static void
152  {
153  position++;
154  }
155 };
156 
157 // Hooks for the 2D ABI arguments and return value. Add 2 or 2.0 to return
158 // values so we can tell they went through the right set of hooks.
159 
160 template <>
161 struct Argument<TestABI_2D, int>
162 {
163  static int
165  {
166  return tc->ints[position.first++];
167  }
168 };
169 
170 template <typename Arg>
171 struct Argument<TestABI_2D, Arg,
172  typename std::enable_if<std::is_floating_point<Arg>::value>::type>
173 {
174  static Arg
176  {
177  return tc->floats[position.second++];
178  }
179 };
180 
181 template <>
182 struct Result<TestABI_2D, int>
183 {
184  static void
185  store(ThreadContext *tc, const int &ret)
186  {
187  tc->intResult = ret + 2;
188  }
189 };
190 
191 template <typename Ret>
192 struct Result<TestABI_2D, Ret,
193  typename std::enable_if<std::is_floating_point<Ret>::value>::type>
194 {
195  static void
196  store(ThreadContext *tc, const Ret &ret)
197  {
198  tc->floatResult = ret + 2.0;
199  }
200 };
201 
202 // Hooks for the TcInit ABI arguments.
203 template <>
205 {
206  static int
208  {
209  return tc->ints[position.pos++];
210  }
211 };
212 
213 } // namespace GuestABI
214 
215 // Test function which verifies that its arguments reflect the 1D ABI and
216 // which doesn't return anything.
217 void
218 testIntVoid(ThreadContext *tc, int a, float b, int c, double d,
220 {
221  EXPECT_EQ(a, tc->ints[0]);
222  EXPECT_EQ(b, tc->floats[1]);
223  EXPECT_EQ(c, tc->ints[2]);
224  EXPECT_EQ(d, tc->floats[3]);
225 
226  EXPECT_EQ(varargs.get<int>(), tc->ints[4]);
227  EXPECT_EQ(varargs.get<float>(), tc->floats[5]);
228  EXPECT_EQ(varargs.get<double>(), tc->floats[6]);
229 }
230 
231 // Test functions which verify that the return allocating ABI allocates space
232 // for its return value successfully.
233 void
235 {
236  EXPECT_EQ(a, tc->ints[0]);
237 }
238 
239 int
241 {
242  EXPECT_EQ(a, tc->ints[1]);
243  return 0;
244 }
245 
246 // Test function which verifies that its arguments reflect the 2D ABI and
247 // which doesn't return anything.
248 void
249 test2DVoid(ThreadContext *tc, int a, float b, int c, double d,
251 {
252  EXPECT_EQ(a, tc->ints[0]);
253  EXPECT_EQ(b, tc->floats[0]);
254  EXPECT_EQ(c, tc->ints[1]);
255  EXPECT_EQ(d, tc->floats[1]);
256 
257  EXPECT_EQ(varargs.get<int>(), tc->ints[2]);
258  EXPECT_EQ(varargs.get<float>(), tc->floats[2]);
259  EXPECT_EQ(varargs.get<double>(), tc->floats[3]);
260 }
261 
262 void
264 {
265  EXPECT_EQ(tc->intOffset, 2);
266  EXPECT_EQ(a, tc->ints[2]);
267 }
268 
269 // Test functions which returns various types of values.
270 const int IntRetValue = 50;
271 const float FloatRetValue = 3.14;
272 const double DoubleRetValue = 12.34;
273 
277 
278 
279 // The actual test bodies.
280 TEST(GuestABI, ABI_1D_args)
281 {
282  ThreadContext tc;
283  invokeSimcall<TestABI_1D>(&tc, testIntVoid);
284  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
285  EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
286 }
287 
288 TEST(GuestABI, ABI_RetReg)
289 {
290  ThreadContext tc;
291  invokeSimcall<TestABI_RetReg>(&tc, testRetRegVoid);
292  invokeSimcall<TestABI_RetReg>(&tc, testRetRegInt);
293 }
294 
295 TEST(GuestABI, ABI_2D_args)
296 {
297  ThreadContext tc;
298  invokeSimcall<TestABI_2D>(&tc, test2DVoid);
299  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
300  EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
301 }
302 
303 TEST(GuestABI, ABI_TC_init)
304 {
305  ThreadContext tc;
306  tc.intOffset = 2;
307  invokeSimcall<TestABI_TcInit>(&tc, testTcInit);
308 }
309 
310 TEST(GuestABI, ABI_returns)
311 {
312  // 1D returns.
313  {
314  ThreadContext tc;
315  int ret = invokeSimcall<TestABI_1D>(&tc, testIntRet);
316  EXPECT_EQ(ret, IntRetValue);
317  EXPECT_EQ(tc.intResult, IntRetValue + 1);
318  EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
319  }
320  {
321  ThreadContext tc;
322  float ret = invokeSimcall<TestABI_1D>(&tc, testFloatRet);
323  EXPECT_EQ(ret, FloatRetValue);
324  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
325  EXPECT_EQ(tc.floatResult, FloatRetValue + 1.0);
326  }
327  {
328  ThreadContext tc;
329  double ret = invokeSimcall<TestABI_1D>(&tc, testDoubleRet);
331  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
332  EXPECT_EQ(tc.floatResult, DoubleRetValue + 1.0);
333  }
334 
335  // 2D returns.
336  {
337  ThreadContext tc;
338  int ret = invokeSimcall<TestABI_2D>(&tc, testIntRet);
339  EXPECT_EQ(ret, IntRetValue);
340  EXPECT_EQ(tc.intResult, IntRetValue + 2);
341  EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
342  }
343  {
344  ThreadContext tc;
345  float ret = invokeSimcall<TestABI_2D>(&tc, testFloatRet);
346  EXPECT_EQ(ret, FloatRetValue);
347  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
348  EXPECT_EQ(tc.floatResult, FloatRetValue + 2.0);
349  }
350  {
351  ThreadContext tc;
352  double ret = invokeSimcall<TestABI_2D>(&tc, testDoubleRet);
354  EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
355  EXPECT_EQ(tc.floatResult, DoubleRetValue + 2.0);
356  }
357 }
358 
360 {
361  ThreadContext tc;
362  std::string dump = dumpSimcall<TestABI_1D>("test", &tc, testIntVoid);
363  EXPECT_EQ(dump, "test(0, 11, 2, 13, ...)");
364 }
365 
366 TEST(GuestABI, isVarArgs)
367 {
371  struct FooStruct {};
373  union FooUnion {};
375 }
std::string dumpSimcall(std::string name, ThreadContext *tc, std::function< Ret(ThreadContext *, Args...)> target=std::function< Ret(ThreadContext *, Args...)>())
Definition: guest_abi.hh:481
void testTcInit(ThreadContext *tc, int a)
const float FloatRetValue
const int IntRetValue
STL pair class.
Definition: stl.hh:61
static const double floats[]
void test2DVoid(ThreadContext *tc, int a, float b, int c, double d, GuestABI::VarArgs< int, float, double > varargs)
Bitfield< 8 > a
static const double DefaultFloatResult
Overload hash function for BasicBlockRange type.
Definition: vec_reg.hh:586
ThreadContext is the external interface to all thread state for anything outside of the CPU...
static const int DefaultIntResult
static void store(ThreadContext *tc, const int &ret)
TEST(GuestABI, ABI_1D_args)
#define EXPECT_TRUE(expr)
A macro which verifies that expr evaluates to true.
Definition: unittest.hh:105
Bitfield< 7 > b
uint8_t type
Definition: inet.hh:333
void testRetRegVoid(ThreadContext *tc, int a)
Position(const ThreadContext *tc)
void testIntVoid(ThreadContext *tc, int a, float b, int c, double d, GuestABI::VarArgs< int, float, double > varargs)
Bitfield< 9 > d
static void store(ThreadContext *tc, const Ret &ret)
#define EXPECT_FALSE(expr)
A macro which verifies that expr evaluates to false.
Definition: unittest.hh:108
static void allocate(ThreadContext *tc, TestABI_RetReg::Position &position)
static const int ints[]
Bitfield< 29 > c
const double DoubleRetValue
int testRetRegInt(ThreadContext *tc, int a)
float testFloatRet(ThreadContext *tc)
void dump()
Dump all statistics data to the registered outputs.
Definition: statistics.cc:561
static void store(ThreadContext *tc, const int &ret)
double testDoubleRet(ThreadContext *tc)
int testIntRet(ThreadContext *tc)
#define EXPECT_EQ(lhs, rhs)
A macro which verifies that lhs and rhs are equal to each other.
Definition: unittest.hh:112

Generated on Fri Feb 28 2020 16:27:02 for gem5 by doxygen 1.8.13