/*
 * 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);
};

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);
};


/*
 * 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();
    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;

    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__