summaryrefslogtreecommitdiff
path: root/src/sim/guest_abi.hh
blob: 2dd27c424efb50e977b91acbe52ab42c0b4f5984 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*
 * 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
 */

#ifndef __SIM_GUEST_ABI_HH__
#define __SIM_GUEST_ABI_HH__

#include <functional>
#include <memory>
#include <sstream>
#include <type_traits>

class ThreadContext;

namespace GuestABI
{

/*
 * To implement an ABI, a subclass needs to implement a system of
 * specializations of these two templates Result and Argument, and define a
 * "Position" type.
 *
 * The Position type carries information about, for instance, how many
 * integer registers have been consumed gathering earlier arguments. It
 * may contain multiple elements if there are multiple dimensions to track,
 * for instance the number of integer and floating point registers used so far.
 *
 * Result and Argument are class templates instead of function templates so
 * that they can be partially specialized if necessary. C++ doesn't let you
 * partially specialize function templates because that conflicts with
 * template resolution using the function's arguments. Since we already know
 * what type we want and we don't need argument based resolution, we can just
 * wrap the desired functionality in classes and sidestep the problem.
 *
 * Also note that these templates have an "Enabled" parameter to support
 * std::enable_if style conditional specializations.
 */

template <typename ABI, typename Ret, typename Enabled=void>
struct Result
{
  private:
    /*
     * Store result "ret" into the state accessible through tc.
     *
     * Note that the declaration below is only to document the expected
     * signature and is private so it won't be used by accident.
     * Specializations of this Result class should define their own version
     * of this method which actually does something and is public.
     */
    static void store(ThreadContext *tc, const Ret &ret);

    /*
     * Adjust the position of arguments based on the return type, if necessary.
     *
     * This method can be excluded if no adjustment is necessary.
     */
    static void allocate(ThreadContext *tc, typename ABI::Position &position);
};

/*
 * This partial specialization prevents having to special case 'void' when
 * working with return types.
 */
template <typename ABI>
struct Result<ABI, void>
{};

template <typename ABI, typename Arg, typename Enabled=void>
struct Argument
{
    /*
     * Retrieve an argument of type Arg from the state accessible through tc,
     * assuming the state represented by "position" has already been used.
     * Also update position to account for this argument as well.
     *
     * Like Result::store above, the declaration below is only to document
     * the expected method signature.
     */
    static Arg get(ThreadContext *tc, typename ABI::Position &position);
};


/*
 * This struct template provides a default allocate() method in case the
 * Result template doesn't provide one. This is the default in cases where the
 * return type doesn't affect how arguments are laid out.
 */
template <typename ABI, typename Ret, typename Enabled=void>
struct ResultAllocator
{
    static void
    allocate(ThreadContext *tc, typename ABI::Position &position)
    {}
};

/*
 * If the return type *does* affect how the arguments are laid out, the ABI
 * can implement an allocate() method for the various return types, and this
 * specialization will call into it.
 */
template <typename ABI, typename Ret>
struct ResultAllocator<ABI, Ret, decltype((void)&Result<ABI, Ret>::allocate)>
{
    static void
    allocate(ThreadContext *tc, typename ABI::Position &position)
    {
        Result<ABI, Ret>::allocate(tc, position);
    }
};


/*
 * These templates implement a variadic argument mechanism for guest ABI
 * functions. A function might be written like this:
 *
 * void
 * func(ThreadContext *tc, VarArgs<Addr, int> varargs)
 * {
 *     warn("Address = %#x, int = %d.",
 *          varargs.get<Addr>(), varargs.get<int>());
 * }
 *
 * where an object of type VarArgs<...> is its last argument. The types given
 * to the template specify what types the function might need to retrieve from
 * varargs. The varargs object will then have get<> methods for each of those
 * types.
 *
 * Note that each get<> will happen live. If you modify values through the
 * ThreadContext *tc and then run get<>(), you may alter one of your arguments.
 * If you're going to use tc to modify state, it would be a good idea to use
 * get<>() as soon as possible to avoid corrupting the functions arguments.
 */

// A recursive template which defines virtual functions to retrieve each of the
// requested types. This provides the ABI agnostic interface the function uses.
template <typename ...Types>
class VarArgsBase;

template <typename First, typename ...Types>
class VarArgsBase<First, Types...> : public VarArgsBase<Types...>
{
  public:
    // The virtual function takes a reference parameter so that the different
    // _getImpl methods can co-exist through overloading.
    virtual void _getImpl(First &) = 0;

    // Make sure base class _getImpl-es aren't hidden by this one.
    using VarArgsBase<Types...>::_getImpl;
};

// The base case of the recursion.
template <>
class VarArgsBase<>
{
  protected:
    // This just gives the "using" statement in the non base case something to
    // refer to.
    void _getImpl();
};


// A recursive template which defines the ABI specific implementation of the
// interface defined above.
//
// The types in Types are consumed one by one, and by
// the time we get down to the base case we'd have lost track of the complete
// set we need to know what interface to inherit. The Base parameter keeps
// track of that through the recursion.
template <typename ABI, typename Base, typename ...Types>
class VarArgsImpl;

template <typename ABI, typename Base, typename First, typename ...Types>
class VarArgsImpl<ABI, Base, First, Types...> :
    public VarArgsImpl<ABI, Base, Types...>
{
  protected:
    // Bring forward the base class constructor.
    using VarArgsImpl<ABI, Base, Types...>::VarArgsImpl;
    // Make sure base class _getImpl-es don't get hidden by ours.
    using VarArgsImpl<ABI, Base, Types...>::_getImpl;

    // Implement a version of _getImple, using the ABI specialized version of
    // the Argument class.
    void
    _getImpl(First &first) override
    {
        first = Argument<ABI, First>::get(this->tc, this->position);
    }
};

// The base case of the recursion, which inherits from the interface class.
template <typename ABI, typename Base>
class VarArgsImpl<ABI, Base> : public Base
{
  protected:
    // Declare state to pass to the Argument<>::get methods.
    ThreadContext *tc;
    typename ABI::Position position;

    // Give the "using" statement in our subclass something to refer to.
    void _getImpl();

  public:
    VarArgsImpl(ThreadContext *_tc, const typename ABI::Position &_pos) :
        tc(_tc), position(_pos)
    {}
};

// A wrapper which provides a nice interface to the virtual functions, and a
// hook for the Argument template mechanism.
template <typename ...Types>
class VarArgs
{
  private:
    // This points to the implementation which knows how to read arguments
    // based on the ABI being used.
    std::shared_ptr<VarArgsBase<Types...>> _ptr;

  public:
    VarArgs(VarArgsBase<Types...> *ptr) : _ptr(ptr) {}

    // This template is a friendlier wrapper around the virtual functions the
    // raw interface provides. This version lets you pick a type which it then
    // returns, instead of having to pre-declare a variable to pass in.
    template <typename Arg>
    Arg
    get()
    {
        Arg arg;
        _ptr->_getImpl(arg);
        return arg;
    }
};

template <typename ...Types>
std::ostream &
operator << (std::ostream &os, const VarArgs<Types...> &va)
{
    os << "...";
    return os;
}

// The ABI independent hook which tells the GuestABI mechanism what to do with
// a VarArgs argument. It constructs the underlying implementation which knows
// about the ABI, and installs it in the VarArgs wrapper to give to the
// function.
template <typename ABI, typename ...Types>
struct Argument<ABI, VarArgs<Types...>>
{
    static VarArgs<Types...>
    get(ThreadContext *tc, typename ABI::Position &position)
    {
        using Base = VarArgsBase<Types...>;
        using Impl = VarArgsImpl<ABI, Base, Types...>;
        return VarArgs<Types...>(new Impl(tc, position));
    }
};


/*
 * These functions will likely be common among all ABIs and implement the
 * mechanism of gathering arguments, calling the target function, and then
 * storing the result. They might need to be overridden if, for instance,
 * the location of arguments need to be determined in a different order.
 * For example, there might be an ABI which gathers arguments starting
 * from the last in the list instead of the first. This is unlikely but
 * still possible to support by redefining these functions..
 */

// With no arguments to gather, call the target function and store the
// result.
template <typename ABI, typename Ret>
static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
callFrom(ThreadContext *tc, typename ABI::Position &position,
        std::function<Ret(ThreadContext *)> target)
{
    Ret ret = target(tc);
    Result<ABI, Ret>::store(tc, ret);
    return ret;
}

// With no arguments to gather and nothing to return, call the target function.
template <typename ABI>
static void
callFrom(ThreadContext *tc, typename ABI::Position &position,
        std::function<void(ThreadContext *)> target)
{
    target(tc);
}

// Recursively gather arguments for target from tc until we get to the base
// case above.
template <typename ABI, typename Ret, typename NextArg, typename ...Args>
static typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
callFrom(ThreadContext *tc, typename ABI::Position &position,
        std::function<Ret(ThreadContext *, NextArg, Args...)> target)
{
    // Extract the next argument from the thread context.
    NextArg next = Argument<ABI, NextArg>::get(tc, position);

    // Build a partial function which adds the next argument to the call.
    std::function<Ret(ThreadContext *, Args...)> partial =
        [target,next](ThreadContext *_tc, Args... args) {
            return target(_tc, next, args...);
        };

    // Recursively handle any remaining arguments.
    return callFrom<ABI, Ret, Args...>(tc, position, partial);
}

// Recursively gather arguments for target from tc until we get to the base
// case above. This version is for functions that don't return anything.
template <typename ABI, typename NextArg, typename ...Args>
static void
callFrom(ThreadContext *tc, typename ABI::Position &position,
        std::function<void(ThreadContext *, NextArg, Args...)> target)
{
    // Extract the next argument from the thread context.
    NextArg next = Argument<ABI, NextArg>::get(tc, position);

    // Build a partial function which adds the next argument to the call.
    std::function<void(ThreadContext *, Args...)> partial =
        [target,next](ThreadContext *_tc, Args... args) {
            target(_tc, next, args...);
        };

    // Recursively handle any remaining arguments.
    callFrom<ABI, Args...>(tc, position, partial);
}



/*
 * These functions are like the ones above, except they print the arguments
 * a target function would be called with instead of actually calling it.
 */

// With no arguments to print, add the closing parenthesis and return.
template <typename ABI, typename Ret>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
             typename ABI::Position &position)
{
    os << ")";
}

// Recursively gather arguments for target from tc until we get to the base
// case above, and append those arguments to the string stream being
// constructed.
template <typename ABI, typename Ret, typename NextArg, typename ...Args>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
             typename ABI::Position &position)
{
    // Either open the parenthesis or add a comma, depending on where we are
    // in the argument list.
    os << (count ? ", " : "(");

    // Extract the next argument from the thread context.
    NextArg next = Argument<ABI, NextArg>::get(tc, position);

    // Add this argument to the list.
    os << next;

    // Recursively handle any remaining arguments.
    dumpArgsFrom<ABI, Ret, Args...>(count + 1, os, tc, position);
}

} // namespace GuestABI


// These functions wrap a simulator level function with the given signature.
// The wrapper takes one argument, a thread context to extract arguments from
// and write a result (if any) back to. For convenience, the wrapper also
// returns the result of the wrapped function.

template <typename ABI, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc,
              std::function<Ret(ThreadContext *, Args...)> target)
{
    // Default construct a Position to track consumed resources. Built in
    // types will be zero initialized.
    auto position = typename ABI::Position();
    GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
    return GuestABI::callFrom<ABI, Ret, Args...>(tc, position, target);
}

template <typename ABI, typename Ret, typename ...Args>
Ret
invokeSimcall(ThreadContext *tc, Ret (*target)(ThreadContext *, Args...))
{
    return invokeSimcall<ABI>(
            tc, std::function<Ret(ThreadContext *, Args...)>(target));
}

template <typename ABI, typename ...Args>
void
invokeSimcall(ThreadContext *tc,
              std::function<void(ThreadContext *, Args...)> target)
{
    // Default construct a Position to track consumed resources. Built in
    // types will be zero initialized.
    auto position = typename ABI::Position();
    GuestABI::callFrom<ABI, Args...>(tc, position, target);
}

template <typename ABI, typename ...Args>
void
invokeSimcall(ThreadContext *tc, void (*target)(ThreadContext *, Args...))
{
    invokeSimcall<ABI>(
            tc, std::function<void(ThreadContext *, Args...)>(target));
}


// These functions also wrap a simulator level function. Instead of running the
// function, they return a string which shows what arguments the function would
// be invoked with if it were called from the given context.

template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
            std::function<Ret(ThreadContext *, Args...)> target=
            std::function<Ret(ThreadContext *, Args...)>())
{
    auto position = typename ABI::Position();
    std::ostringstream ss;

    GuestABI::ResultAllocator<ABI, Ret>::allocate(tc, position);
    ss << name;
    GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
    return ss.str();
}

template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
            Ret (*target)(ThreadContext *, Args...))
{
    return dumpSimcall<ABI>(
            name, tc, std::function<Ret(ThreadContext *, Args...)>(target));
}

#endif // __SIM_GUEST_ABI_HH__