tests: arch-power: Add 64-bit hello binaries
[gem5.git] / src / sim / guest_abi.test.cc
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
28 #include <gtest/gtest.h>
29
30 #include <type_traits>
31 #include <utility>
32
33 #include "sim/guest_abi.hh"
34
35 // Fake ThreadContext which holds data and captures results.
36 class ThreadContext
37 {
38 public:
39 static const int ints[];
40 static const double floats[];
41
42 static const int DefaultIntResult;
43 static const double DefaultFloatResult;
44
45 int intResult = DefaultIntResult;
46 double floatResult = DefaultFloatResult;
47
48 int intOffset = 0;
49 };
50
51 const int ThreadContext::ints[] = {
52 0, 1, 2, 3, 4, 5, 6, 7
53 };
54 const double ThreadContext::floats[] = {
55 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0
56 };
57
58 const int ThreadContext::DefaultIntResult = 0;
59 const double ThreadContext::DefaultFloatResult = 0.0;
60
61 // ABI anchor for an ABI which has 1D progress. Conceptually, this could be
62 // because integer and floating point arguments are stored in the same
63 // registers.
64 struct TestABI_1D
65 {
66 using State = int;
67 };
68
69 // ABI anchor for an ABI which uses the prepare() hook.
70 struct TestABI_Prepare
71 {
72 using State = int;
73 };
74
75 // ABI anchor for an ABI which has 2D progress. Conceptually, this could be
76 // because integer and floating point arguments are stored in separate
77 // registers.
78 struct TestABI_2D
79 {
80 using State = std::pair<int, int>;
81 };
82
83 struct TestABI_TcInit
84 {
85 struct State
86 {
87 int pos;
88 State(const ThreadContext *tc) : pos(tc->intOffset) {}
89 };
90 };
91
92 namespace GuestABI
93 {
94
95 // Hooks for the 1D ABI arguments and return value. Add 1 or 1.0 to return
96 // values so we can tell they went through the right set of hooks.
97 template <>
98 struct Argument<TestABI_1D, int>
99 {
100 static int
101 get(ThreadContext *tc, TestABI_1D::State &state)
102 {
103 return tc->ints[state++];
104 }
105 };
106
107 template <typename Arg>
108 struct Argument<TestABI_1D, Arg,
109 typename std::enable_if_t<std::is_floating_point<Arg>::value>>
110 {
111 static Arg
112 get(ThreadContext *tc, TestABI_1D::State &state)
113 {
114 return tc->floats[state++];
115 }
116 };
117
118 template <>
119 struct Result<TestABI_1D, int>
120 {
121 static void
122 store(ThreadContext *tc, const int &ret)
123 {
124 tc->intResult = ret + 1;
125 }
126 };
127
128 template <typename Ret>
129 struct Result<TestABI_1D, Ret,
130 typename std::enable_if_t<std::is_floating_point<Ret>::value>>
131 {
132 static void
133 store(ThreadContext *tc, const Ret &ret)
134 {
135 tc->floatResult = ret + 1.0;
136 }
137 };
138
139 // Hooks for the ABI which uses prepare(). It uses the same rules as the
140 // 1D ABI for arguments, but allocates space for and discards return values
141 // and returns integer arguments in reverse order.
142 template <>
143 struct Argument<TestABI_Prepare, int>
144 {
145 static int
146 get(ThreadContext *tc, TestABI_Prepare::State &state)
147 {
148 return tc->ints[--state];
149 }
150
151 static void
152 prepare(ThreadContext *tc, TestABI_Prepare::State &state)
153 {
154 state++;
155 }
156 };
157
158 template <typename Ret>
159 struct Result<TestABI_Prepare, Ret>
160 {
161 static void store(ThreadContext *tc, const Ret &ret) {}
162 static void
163 prepare(ThreadContext *tc, TestABI_Prepare::State &state)
164 {
165 state++;
166 }
167 };
168
169 // Hooks for the 2D ABI arguments and return value. Add 2 or 2.0 to return
170 // values so we can tell they went through the right set of hooks.
171
172 template <>
173 struct Argument<TestABI_2D, int>
174 {
175 static int
176 get(ThreadContext *tc, TestABI_2D::State &state)
177 {
178 return tc->ints[state.first++];
179 }
180 };
181
182 template <typename Arg>
183 struct Argument<TestABI_2D, Arg,
184 typename std::enable_if_t<std::is_floating_point<Arg>::value>>
185 {
186 static Arg
187 get(ThreadContext *tc, TestABI_2D::State &state)
188 {
189 return tc->floats[state.second++];
190 }
191 };
192
193 template <>
194 struct Result<TestABI_2D, int>
195 {
196 static void
197 store(ThreadContext *tc, const int &ret)
198 {
199 tc->intResult = ret + 2;
200 }
201 };
202
203 template <typename Ret>
204 struct Result<TestABI_2D, Ret,
205 typename std::enable_if_t<std::is_floating_point<Ret>::value>>
206 {
207 static void
208 store(ThreadContext *tc, const Ret &ret)
209 {
210 tc->floatResult = ret + 2.0;
211 }
212 };
213
214 // Hooks for the TcInit ABI arguments.
215 template <>
216 struct Argument<TestABI_TcInit, int>
217 {
218 static int
219 get(ThreadContext *tc, TestABI_TcInit::State &state)
220 {
221 return tc->ints[state.pos++];
222 }
223 };
224
225 } // namespace GuestABI
226
227 // Test function which verifies that its arguments reflect the 1D ABI and
228 // which doesn't return anything.
229 void
230 testIntVoid(ThreadContext *tc, int a, float b, int c, double d,
231 GuestABI::VarArgs<int,float,double> varargs)
232 {
233 EXPECT_EQ(a, tc->ints[0]);
234 EXPECT_EQ(b, tc->floats[1]);
235 EXPECT_EQ(c, tc->ints[2]);
236 EXPECT_EQ(d, tc->floats[3]);
237
238 EXPECT_EQ(varargs.get<int>(), tc->ints[4]);
239 EXPECT_EQ(varargs.get<float>(), tc->floats[5]);
240 EXPECT_EQ(varargs.get<double>(), tc->floats[6]);
241 }
242
243 // Test functions which verify that the return allocating ABI allocates space
244 // for its return value successfully.
245 void
246 testPrepareVoid(ThreadContext *tc, int a, int b)
247 {
248 EXPECT_EQ(a, tc->ints[1]);
249 EXPECT_EQ(b, tc->ints[0]);
250 }
251
252 int
253 testPrepareInt(ThreadContext *tc, int a, int b)
254 {
255 EXPECT_EQ(a, tc->ints[2]);
256 EXPECT_EQ(b, tc->ints[1]);
257 return 0;
258 }
259
260 // Test function which verifies that its arguments reflect the 2D ABI and
261 // which doesn't return anything.
262 void
263 test2DVoid(ThreadContext *tc, int a, float b, int c, double d,
264 GuestABI::VarArgs<int,float,double> varargs)
265 {
266 EXPECT_EQ(a, tc->ints[0]);
267 EXPECT_EQ(b, tc->floats[0]);
268 EXPECT_EQ(c, tc->ints[1]);
269 EXPECT_EQ(d, tc->floats[1]);
270
271 EXPECT_EQ(varargs.get<int>(), tc->ints[2]);
272 EXPECT_EQ(varargs.get<float>(), tc->floats[2]);
273 EXPECT_EQ(varargs.get<double>(), tc->floats[3]);
274 }
275
276 void
277 testTcInit(ThreadContext *tc, int a)
278 {
279 EXPECT_EQ(tc->intOffset, 2);
280 EXPECT_EQ(a, tc->ints[2]);
281 }
282
283 // Test functions which returns various types of values.
284 const int IntRetValue = 50;
285 const float FloatRetValue = 3.14;
286 const double DoubleRetValue = 12.34;
287
288 int testIntRet(ThreadContext *tc) { return IntRetValue; }
289 float testFloatRet(ThreadContext *tc) { return FloatRetValue; }
290 double testDoubleRet(ThreadContext *tc) { return DoubleRetValue; }
291
292
293 // The actual test bodies.
294 TEST(GuestABI, ABI_1D_args)
295 {
296 ThreadContext tc;
297 invokeSimcall<TestABI_1D>(&tc, testIntVoid);
298 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
299 EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
300 }
301
302 TEST(GuestABI, ABI_Prepare)
303 {
304 ThreadContext tc;
305 invokeSimcall<TestABI_Prepare>(&tc, testPrepareVoid);
306 invokeSimcall<TestABI_Prepare>(&tc, testPrepareInt);
307 }
308
309 TEST(GuestABI, ABI_2D_args)
310 {
311 ThreadContext tc;
312 invokeSimcall<TestABI_2D>(&tc, test2DVoid);
313 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
314 EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
315 }
316
317 TEST(GuestABI, ABI_TC_init)
318 {
319 ThreadContext tc;
320 tc.intOffset = 2;
321 invokeSimcall<TestABI_TcInit>(&tc, testTcInit);
322 }
323
324 TEST(GuestABI, ABI_returns)
325 {
326 // 1D returns.
327 {
328 ThreadContext tc;
329 int ret = invokeSimcall<TestABI_1D>(&tc, testIntRet);
330 EXPECT_EQ(ret, IntRetValue);
331 EXPECT_EQ(tc.intResult, IntRetValue + 1);
332 EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
333 }
334 {
335 ThreadContext tc;
336 float ret = invokeSimcall<TestABI_1D>(&tc, testFloatRet);
337 EXPECT_EQ(ret, FloatRetValue);
338 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
339 EXPECT_EQ(tc.floatResult, FloatRetValue + 1.0);
340 }
341 {
342 ThreadContext tc;
343 double ret = invokeSimcall<TestABI_1D>(&tc, testDoubleRet);
344 EXPECT_EQ(ret, DoubleRetValue);
345 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
346 EXPECT_EQ(tc.floatResult, DoubleRetValue + 1.0);
347 }
348 {
349 // Disable storing the return value in the ThreadContext.
350 ThreadContext tc;
351 int ret = invokeSimcall<TestABI_1D, false>(&tc, testIntRet);
352 EXPECT_EQ(ret, IntRetValue);
353 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
354 EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
355 }
356
357
358 // 2D returns.
359 {
360 ThreadContext tc;
361 int ret = invokeSimcall<TestABI_2D>(&tc, testIntRet);
362 EXPECT_EQ(ret, IntRetValue);
363 EXPECT_EQ(tc.intResult, IntRetValue + 2);
364 EXPECT_EQ(tc.floatResult, tc.DefaultFloatResult);
365 }
366 {
367 ThreadContext tc;
368 float ret = invokeSimcall<TestABI_2D>(&tc, testFloatRet);
369 EXPECT_EQ(ret, FloatRetValue);
370 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
371 EXPECT_EQ(tc.floatResult, FloatRetValue + 2.0);
372 }
373 {
374 ThreadContext tc;
375 double ret = invokeSimcall<TestABI_2D>(&tc, testDoubleRet);
376 EXPECT_EQ(ret, DoubleRetValue);
377 EXPECT_EQ(tc.intResult, tc.DefaultIntResult);
378 EXPECT_EQ(tc.floatResult, DoubleRetValue + 2.0);
379 }
380 }
381
382 TEST(GuestABI, dumpSimcall)
383 {
384 ThreadContext tc;
385 std::string dump = dumpSimcall<TestABI_1D>("test", &tc, testIntVoid);
386 EXPECT_EQ(dump, "test(0, 11, 2, 13, ...)");
387 }
388
389 TEST(GuestABI, isVarArgs)
390 {
391 EXPECT_TRUE(GuestABI::IsVarArgs<GuestABI::VarArgs<int>>::value);
392 EXPECT_FALSE(GuestABI::IsVarArgs<int>::value);
393 EXPECT_FALSE(GuestABI::IsVarArgs<double>::value);
394 struct FooStruct {};
395 EXPECT_FALSE(GuestABI::IsVarArgs<FooStruct>::value);
396 union FooUnion {};
397 EXPECT_FALSE(GuestABI::IsVarArgs<FooUnion>::value);
398 }