sim: Generalize the GuestABI Result::allocate() mechanism.
[gem5.git] / src / sim / guest_abi.hh
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 #ifndef __SIM_GUEST_ABI_HH__
29 #define __SIM_GUEST_ABI_HH__
30
31 #include <functional>
32 #include <memory>
33 #include <sstream>
34 #include <type_traits>
35
36 class ThreadContext;
37
38 namespace GuestABI
39 {
40
41 /*
42 * To implement an ABI, a subclass needs to implement a system of
43 * specializations of these two templates Result and Argument, and define a
44 * "Position" type.
45 *
46 * The Position type carries information about, for instance, how many
47 * integer registers have been consumed gathering earlier arguments. It
48 * may contain multiple elements if there are multiple dimensions to track,
49 * for instance the number of integer and floating point registers used so far.
50 *
51 * Result and Argument are class templates instead of function templates so
52 * that they can be partially specialized if necessary. C++ doesn't let you
53 * partially specialize function templates because that conflicts with
54 * template resolution using the function's arguments. Since we already know
55 * what type we want and we don't need argument based resolution, we can just
56 * wrap the desired functionality in classes and sidestep the problem.
57 *
58 * Also note that these templates have an "Enabled" parameter to support
59 * std::enable_if style conditional specializations.
60 */
61
62 /*
63 * Position may need to be initialized based on the ThreadContext, for instance
64 * to find out where the stack pointer is initially.
65 */
66 template <typename ABI, typename Enabled=void>
67 struct PositionInitializer
68 {
69 static typename ABI::Position
70 init(const ThreadContext *tc)
71 {
72 return typename ABI::Position();
73 }
74 };
75
76 template <typename ABI>
77 struct PositionInitializer<ABI, typename std::enable_if<
78 std::is_constructible<typename ABI::Position, const ThreadContext *>::value
79 >::type>
80 {
81 static typename ABI::Position
82 init(const ThreadContext *tc)
83 {
84 return typename ABI::Position(tc);
85 }
86 };
87
88 template <typename ABI, typename Ret, typename Enabled=void>
89 struct Result
90 {
91 private:
92 /*
93 * Store result "ret" into the state accessible through tc.
94 *
95 * Note that the declaration below is only to document the expected
96 * signature and is private so it won't be used by accident.
97 * Specializations of this Result class should define their own version
98 * of this method which actually does something and is public.
99 */
100 static void store(ThreadContext *tc, const Ret &ret);
101
102 /*
103 * Adjust the position of arguments based on the return type, if necessary.
104 *
105 * This method can be excluded if no adjustment is necessary.
106 */
107 static void allocate(ThreadContext *tc, typename ABI::Position &position);
108 };
109
110 /*
111 * This partial specialization prevents having to special case 'void' when
112 * working with return types.
113 */
114 template <typename ABI>
115 struct Result<ABI, void>
116 {};
117
118 template <typename ABI, typename Arg, typename Enabled=void>
119 struct Argument
120 {
121 /*
122 * Retrieve an argument of type Arg from the state accessible through tc,
123 * assuming the state represented by "position" has already been used.
124 * Also update position to account for this argument as well.
125 *
126 * Like Result::store above, the declaration below is only to document
127 * the expected method signature.
128 */
129 static Arg get(ThreadContext *tc, typename ABI::Position &position);
130
131 /*
132 * Adjust the position of arguments based on the argument type, if
133 * necessary.
134 *
135 * This method can be excluded if no adjustment is necessary.
136 */
137 static void allocate(ThreadContext *tc, typename ABI::Position &position);
138 };
139
140 /*
141 * This struct template provides a default allocate() method in case the
142 * Result or Argument template doesn't provide one. This is the default in
143 * cases where the return or argument type doesn't affect where things are
144 * stored.
145 */
146 template <typename ABI, template <class ...> class Role,
147 typename Type, typename Enabled=void>
148 struct Allocator
149 {
150 static void
151 allocate(ThreadContext *tc, typename ABI::Position &position)
152 {}
153 };
154
155 /*
156 * If the return type is void, don't allocate anything.
157 */
158 template <typename ABI, template <class ...> class Role>
159 struct Allocator<ABI, Role, void>
160 {
161 static void
162 allocate(ThreadContext *tc, typename ABI::Position &position)
163 {}
164 };
165
166 /*
167 * If the return or argument type isn't void and does affect where things
168 * are stored, the ABI can implement an allocate() method for the various
169 * argument and/or return types, and this specialization will call into it.
170 */
171 template <typename ABI, template <class ...> class Role, typename Type>
172 struct Allocator<ABI, Role, Type, decltype((void)&Role<ABI, Type>::allocate)>
173 {
174 static void
175 allocate(ThreadContext *tc, typename ABI::Position &position)
176 {
177 Role<ABI, Type>::allocate(tc, position);
178 }
179 };
180
181 template <typename ABI, typename Ret, typename Enabled=void>
182 static void
183 allocateResult(ThreadContext *tc, typename ABI::Position &position)
184 {
185 Allocator<ABI, Result, Ret>::allocate(tc, position);
186 }
187
188 template <typename ABI>
189 static void
190 allocateArguments(ThreadContext *tc, typename ABI::Position &position)
191 {
192 return;
193 }
194
195 template <typename ABI, typename NextArg, typename ...Args>
196 static void
197 allocateArguments(ThreadContext *tc, typename ABI::Position &position)
198 {
199 Allocator<ABI, Argument, NextArg>::allocate(tc, position);
200 allocateArguments<ABI, Args...>(tc, position);
201 }
202
203 template <typename ABI, typename Ret, typename ...Args>
204 static void
205 allocateSignature(ThreadContext *tc, typename ABI::Position &position)
206 {
207 allocateResult<ABI, Ret>(tc, position);
208 allocateArguments<ABI, Args...>(tc, position);
209 }
210
211
212 /*
213 * These templates implement a variadic argument mechanism for guest ABI
214 * functions. A function might be written like this:
215 *
216 * void
217 * func(ThreadContext *tc, VarArgs<Addr, int> varargs)
218 * {
219 * warn("Address = %#x, int = %d.",
220 * varargs.get<Addr>(), varargs.get<int>());
221 * }
222 *
223 * where an object of type VarArgs<...> is its last argument. The types given
224 * to the template specify what types the function might need to retrieve from
225 * varargs. The varargs object will then have get<> methods for each of those
226 * types.
227 *
228 * Note that each get<> will happen live. If you modify values through the
229 * ThreadContext *tc and then run get<>(), you may alter one of your arguments.
230 * If you're going to use tc to modify state, it would be a good idea to use
231 * get<>() as soon as possible to avoid corrupting the functions arguments.
232 */
233
234 // A recursive template which defines virtual functions to retrieve each of the
235 // requested types. This provides the ABI agnostic interface the function uses.
236 template <typename ...Types>
237 class VarArgsBase;
238
239 template <typename First, typename ...Types>
240 class VarArgsBase<First, Types...> : public VarArgsBase<Types...>
241 {
242 public:
243 // The virtual function takes a reference parameter so that the different
244 // _getImpl methods can co-exist through overloading.
245 virtual void _getImpl(First &) = 0;
246
247 // Make sure base class _getImpl-es aren't hidden by this one.
248 using VarArgsBase<Types...>::_getImpl;
249 };
250
251 // The base case of the recursion.
252 template <>
253 class VarArgsBase<>
254 {
255 protected:
256 // This just gives the "using" statement in the non base case something to
257 // refer to.
258 void _getImpl();
259 };
260
261
262 // A recursive template which defines the ABI specific implementation of the
263 // interface defined above.
264 //
265 // The types in Types are consumed one by one, and by
266 // the time we get down to the base case we'd have lost track of the complete
267 // set we need to know what interface to inherit. The Base parameter keeps
268 // track of that through the recursion.
269 template <typename ABI, typename Base, typename ...Types>
270 class VarArgsImpl;
271
272 template <typename ABI, typename Base, typename First, typename ...Types>
273 class VarArgsImpl<ABI, Base, First, Types...> :
274 public VarArgsImpl<ABI, Base, Types...>
275 {
276 protected:
277 // Bring forward the base class constructor.
278 using VarArgsImpl<ABI, Base, Types...>::VarArgsImpl;
279 // Make sure base class _getImpl-es don't get hidden by ours.
280 using VarArgsImpl<ABI, Base, Types...>::_getImpl;
281
282 // Implement a version of _getImple, using the ABI specialized version of
283 // the Argument class.
284 void
285 _getImpl(First &first) override
286 {
287 first = Argument<ABI, First>::get(this->tc, this->position);
288 }
289 };
290
291 // The base case of the recursion, which inherits from the interface class.
292 template <typename ABI, typename Base>
293 class VarArgsImpl<ABI, Base> : public Base
294 {
295 protected:
296 // Declare state to pass to the Argument<>::get methods.
297 ThreadContext *tc;
298 typename ABI::Position position;
299
300 // Give the "using" statement in our subclass something to refer to.
301 void _getImpl();
302
303 public:
304 VarArgsImpl(ThreadContext *_tc, const typename ABI::Position &_pos) :
305 tc(_tc), position(_pos)
306 {}
307 };
308
309 // A wrapper which provides a nice interface to the virtual functions, and a
310 // hook for the Argument template mechanism.
311 template <typename ...Types>
312 class VarArgs
313 {
314 private:
315 // This points to the implementation which knows how to read arguments
316 // based on the ABI being used.
317 std::shared_ptr<VarArgsBase<Types...>> _ptr;
318
319 public:
320 VarArgs(VarArgsBase<Types...> *ptr) : _ptr(ptr) {}
321
322 // This template is a friendlier wrapper around the virtual functions the
323 // raw interface provides. This version lets you pick a type which it then
324 // returns, instead of having to pre-declare a variable to pass in.
325 template <typename Arg>
326 Arg
327 get()
328 {
329 Arg arg;
330 _ptr->_getImpl(arg);
331 return arg;
332 }
333 };
334
335 template <typename T>
336 struct IsVarArgs : public std::false_type {};
337
338 template <typename ...Types>
339 struct IsVarArgs<VarArgs<Types...>> : public std::true_type {};
340
341 template <typename ...Types>
342 std::ostream &
343 operator << (std::ostream &os, const VarArgs<Types...> &va)
344 {
345 os << "...";
346 return os;
347 }
348
349 // The ABI independent hook which tells the GuestABI mechanism what to do with
350 // a VarArgs argument. It constructs the underlying implementation which knows
351 // about the ABI, and installs it in the VarArgs wrapper to give to the
352 // function.
353 template <typename ABI, typename ...Types>
354 struct Argument<ABI, VarArgs<Types...>>
355 {
356 static VarArgs<Types...>
357 get(ThreadContext *tc, typename ABI::Position &position)
358 {
359 using Base = VarArgsBase<Types...>;
360 using Impl = VarArgsImpl<ABI, Base, Types...>;
361 return VarArgs<Types...>(new Impl(tc, position));
362 }
363 };
364
365
366 /*
367 * These functions will likely be common among all ABIs and implement the
368 * mechanism of gathering arguments, calling the target function, and then
369 * storing the result. They might need to be overridden if, for instance,
370 * the location of arguments need to be determined in a different order.
371 * For example, there might be an ABI which gathers arguments starting
372 * from the last in the list instead of the first. This is unlikely but
373 * still possible to support by redefining these functions..
374 */
375
376 // With no arguments to gather, call the target function and store the
377 // result.
378 template <typename ABI, typename Ret>
379 static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
380 callFrom(ThreadContext *tc, typename ABI::Position &position,
381 std::function<Ret(ThreadContext *)> target)
382 {
383 Ret ret = target(tc);
384 Result<ABI, Ret>::store(tc, ret);
385 return ret;
386 }
387
388 // With no arguments to gather and nothing to return, call the target function.
389 template <typename ABI>
390 static void
391 callFrom(ThreadContext *tc, typename ABI::Position &position,
392 std::function<void(ThreadContext *)> target)
393 {
394 target(tc);
395 }
396
397 // Recursively gather arguments for target from tc until we get to the base
398 // case above.
399 template <typename ABI, typename Ret, typename NextArg, typename ...Args>
400 static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
401 callFrom(ThreadContext *tc, typename ABI::Position &position,
402 std::function<Ret(ThreadContext *, NextArg, Args...)> target)
403 {
404 // Extract the next argument from the thread context.
405 NextArg next = Argument<ABI, NextArg>::get(tc, position);
406
407 // Build a partial function which adds the next argument to the call.
408 std::function<Ret(ThreadContext *, Args...)> partial =
409 [target,next](ThreadContext *_tc, Args... args) {
410 return target(_tc, next, args...);
411 };
412
413 // Recursively handle any remaining arguments.
414 return callFrom<ABI, Ret, Args...>(tc, position, partial);
415 }
416
417 // Recursively gather arguments for target from tc until we get to the base
418 // case above. This version is for functions that don't return anything.
419 template <typename ABI, typename NextArg, typename ...Args>
420 static void
421 callFrom(ThreadContext *tc, typename ABI::Position &position,
422 std::function<void(ThreadContext *, NextArg, Args...)> target)
423 {
424 // Extract the next argument from the thread context.
425 NextArg next = Argument<ABI, NextArg>::get(tc, position);
426
427 // Build a partial function which adds the next argument to the call.
428 std::function<void(ThreadContext *, Args...)> partial =
429 [target,next](ThreadContext *_tc, Args... args) {
430 target(_tc, next, args...);
431 };
432
433 // Recursively handle any remaining arguments.
434 callFrom<ABI, Args...>(tc, position, partial);
435 }
436
437
438
439 /*
440 * These functions are like the ones above, except they print the arguments
441 * a target function would be called with instead of actually calling it.
442 */
443
444 // With no arguments to print, add the closing parenthesis and return.
445 template <typename ABI, typename Ret>
446 static void
447 dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
448 typename ABI::Position &position)
449 {
450 os << ")";
451 }
452
453 // Recursively gather arguments for target from tc until we get to the base
454 // case above, and append those arguments to the string stream being
455 // constructed.
456 template <typename ABI, typename Ret, typename NextArg, typename ...Args>
457 static void
458 dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
459 typename ABI::Position &position)
460 {
461 // Either open the parenthesis or add a comma, depending on where we are
462 // in the argument list.
463 os << (count ? ", " : "(");
464
465 // Extract the next argument from the thread context.
466 NextArg next = Argument<ABI, NextArg>::get(tc, position);
467
468 // Add this argument to the list.
469 os << next;
470
471 // Recursively handle any remaining arguments.
472 dumpArgsFrom<ABI, Ret, Args...>(count + 1, os, tc, position);
473 }
474
475 } // namespace GuestABI
476
477
478 // These functions wrap a simulator level function with the given signature.
479 // The wrapper takes one argument, a thread context to extract arguments from
480 // and write a result (if any) back to. For convenience, the wrapper also
481 // returns the result of the wrapped function.
482
483 template <typename ABI, typename Ret, typename ...Args>
484 Ret
485 invokeSimcall(ThreadContext *tc,
486 std::function<Ret(ThreadContext *, Args...)> target)
487 {
488 // Default construct a Position to track consumed resources. Built in
489 // types will be zero initialized.
490 auto position = GuestABI::PositionInitializer<ABI>::init(tc);
491 GuestABI::allocateSignature<ABI, Ret, Args...>(tc, position);
492 return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
493 }
494
495 template <typename ABI, typename Ret, typename ...Args>
496 Ret
497 invokeSimcall(ThreadContext *tc, Ret (*target)(ThreadContext *, Args...))
498 {
499 return invokeSimcall<ABI>(
500 tc, std::function<Ret(ThreadContext *, Args...)>(target));
501 }
502
503 template <typename ABI, typename ...Args>
504 void
505 invokeSimcall(ThreadContext *tc,
506 std::function<void(ThreadContext *, Args...)> target)
507 {
508 // Default construct a Position to track consumed resources. Built in
509 // types will be zero initialized.
510 auto position = GuestABI::PositionInitializer<ABI>::init(tc);
511 GuestABI::allocateArguments<ABI, Args...>(tc, position);
512 GuestABI::callFrom<ABI, Args...>(tc, position, target);
513 }
514
515 template <typename ABI, typename ...Args>
516 void
517 invokeSimcall(ThreadContext *tc, void (*target)(ThreadContext *, Args...))
518 {
519 invokeSimcall<ABI>(
520 tc, std::function<void(ThreadContext *, Args...)>(target));
521 }
522
523
524 // These functions also wrap a simulator level function. Instead of running the
525 // function, they return a string which shows what arguments the function would
526 // be invoked with if it were called from the given context.
527
528 template <typename ABI, typename Ret, typename ...Args>
529 std::string
530 dumpSimcall(std::string name, ThreadContext *tc,
531 std::function<Ret(ThreadContext *, Args...)> target=
532 std::function<Ret(ThreadContext *, Args...)>())
533 {
534 auto position = GuestABI::PositionInitializer<ABI>::init(tc);
535 std::ostringstream ss;
536
537 GuestABI::allocateSignature<ABI, Ret, Args...>(tc, position);
538 ss << name;
539 GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
540 return ss.str();
541 }
542
543 template <typename ABI, typename Ret, typename ...Args>
544 std::string
545 dumpSimcall(std::string name, ThreadContext *tc,
546 Ret (*target)(ThreadContext *, Args...))
547 {
548 return dumpSimcall<ABI>(
549 name, tc, std::function<Ret(ThreadContext *, Args...)>(target));
550 }
551
552 #endif // __SIM_GUEST_ABI_HH__