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