NAME ^

src/mmd.c - Multimethod dispatch for binary opcode functions

SYNOPSIS ^

This system is set up to handle type-based dispatching for binary (i.e. two-arg) functions. This includes, though isn't necessarily limited to, binary operators such as addition or subtraction.

DESCRIPTION ^

The MMD system is straightforward, and currently must be explicitly invoked, for example by a vtable function. (We may reserve the right to use MMD in all circumstances, but currently do not).

API ^

For the purposes of the API, each MMD-able function is assigned a unique number which is used to find the correct function table. This is the func_num parameter in the following functions. While Parrot isn't restricted to a predefined set of functions, it does set things up so that all the binary vtable functions have a MMD table preinstalled for them, with default behaviour.

Remarks ^

binop_mmd_funcs->x and ->y are table sizes not highest type in table.

Functions ^

static PMC *mmd_deref(Interp *interp, INTVAL function, PMC *value)

If value is a reference-like PMC, dereference it so we can make an MMD call on the 'real' value.

static void mmd_ensure_writable(Interp *, INTVAL function, PMC *pmc)

Make sure pmc is writable enough for function.

PMC *mmd_dispatch_p_ppp(Interp *, PMC *left, PMC *right, PMC *dest, INTVAL function)

Dispatch to a multimethod that returns a PMC. left, right, and dest are all PMC pointers, while func_num is the MMD table that should be used to do the dispatching. If the dest pointer is NULL, it dispatches two a two-argument function that returns a new dest always.

The MMD system will figure out which function should be called based on the types of left and right and call it, passing in left, right, and possibly dest like any other binary vtable function.

PMC *mmd_dispatch_p_pip(Interp *, PMC *left, INTVAL right, PMC *dest, INTVAL function)

Like above, right argument is a native INTVAL.

PMC *mmd_dispatch_p_pnp(Interp *, PMC *left, FLOATVAL right, PMC *dest, INTVAL function)

Like above, right argument is a native FLOATVAL.

PMC *mmd_dispatch_p_psp(Interp *, PMC *left, STRING *right, PMC *dest, INTVAL function)

Like above, right argument is a native STRING *.

void mmd_dispatch_v_pp(Interp *, PMC *left, PMC *right, INTVAL function)

void mmd_dispatch_v_pi(Interp *, PMC *left, INTVAL right, INTVAL function)

void mmd_dispatch_v_pn(Interp *, PMC *left, FLOATVAL right, INTVAL function)

void mmd_dispatch_v_ps(Interp *, PMC *left, STRING *right, INTVAL function)

Inplace dispatch functions for left <op=> right.

*/

PMC* mmd_dispatch_p_ppp(Interp *interp, PMC *left, PMC *right, PMC *dest, INTVAL func_nr) { mmd_f_p_ppp real_function; PMC *sub; int is_pmc;

    left = mmd_deref(interp, func_nr, left);
    right = mmd_deref(interp, func_nr, right);

    real_function = (mmd_f_p_ppp)get_mmd_dispatcher(interp,
            left, right, func_nr, &is_pmc);

    if (is_pmc) {
        sub = (PMC*)real_function;
        if (dest)
            return Parrot_runops_fromc_args(interp, sub, "PPPP",
                    left, right, dest);
        else
            return Parrot_runops_fromc_args(interp, sub, "PPP",
                    left, right);
    }
    else {
        return (*real_function)(interp, left, right, dest);
    }
}
PMC* mmd_dispatch_p_pip(Interp *interp, PMC *left, INTVAL right, PMC *dest, INTVAL func_nr) { int is_pmc;

    UINTVAL left_type;
    mmd_f_p_pip real_function;

    left = mmd_deref(interp, func_nr, left);

    left_type = left->vtable->base_type;

    real_function =
        (mmd_f_p_pip)get_mmd_dispatch_type(interp, func_nr,
                                           left_type, enum_type_INTVAL,
                                           &is_pmc);

    if (is_pmc) {
        PMC * const sub = (PMC*)real_function;
        if (dest)
            return Parrot_runops_fromc_args(interp, sub, "PPIP",
                    left, right, dest);
        else
            return Parrot_runops_fromc_args(interp, sub, "PPI",
                    left, right);
    }
    else {
        return (*real_function)(interp, left, right, dest);
    }
}
PMC* mmd_dispatch_p_pnp(Interp *interp, PMC *left, FLOATVAL right, PMC *dest, INTVAL func_nr) { mmd_f_p_pnp real_function; PMC *sub; int is_pmc; UINTVAL left_type;

    left = mmd_deref(interp, func_nr, left);

    left_type = left->vtable->base_type;
    real_function = (mmd_f_p_pnp)get_mmd_dispatch_type(interp,
            func_nr, left_type, enum_type_FLOATVAL, &is_pmc);
    if (is_pmc) {
        sub = (PMC*)real_function;
        if (dest)
            return Parrot_runops_fromc_args(interp, sub, "PPNP",
                    left, right, dest);
        else
            return Parrot_runops_fromc_args(interp, sub, "PPN",
                    left, right);
    }
    else {
        return (*real_function)(interp, left, right, dest);
    }
}
PMC* mmd_dispatch_p_psp(Interp *interp, PMC *left, STRING *right, PMC *dest, INTVAL func_nr) { mmd_f_p_psp real_function; PMC *sub; int is_pmc; UINTVAL left_type;

    left_type = left->vtable->base_type;
    real_function = (mmd_f_p_psp)get_mmd_dispatch_type(interp,
            func_nr, left_type, enum_type_STRING, &is_pmc);
    if (is_pmc) {
        sub = (PMC*)real_function;
        if (dest)
            return Parrot_runops_fromc_args(interp, sub, "PPSP",
                    left, right, dest);
        else
            return Parrot_runops_fromc_args(interp, sub, "PPS",
                    left, right);
    }
    else {
        return (*real_function)(interp, left, right, dest);
    }
}
/* * inplace variants */ void mmd_dispatch_v_pp(Interp *interp, PMC *left, PMC *right, INTVAL func_nr) { mmd_f_v_pp real_function; PMC *sub; int is_pmc;

    left = mmd_deref(interp, func_nr, left);
    right = mmd_deref(interp, func_nr, right);

    mmd_ensure_writable(interp, func_nr, left);

    real_function = (mmd_f_v_pp)get_mmd_dispatcher(interp,
            left, right, func_nr, &is_pmc);

    if (is_pmc) {
        sub = (PMC*)real_function;
        Parrot_runops_fromc_args(interp, sub, "vPP", left, right);
    }
    else {
        (*real_function)(interp, left, right);
    }
}
void mmd_dispatch_v_pi(Interp *interp, PMC *left, INTVAL right, INTVAL func_nr) { mmd_f_v_pi real_function; PMC *sub; int is_pmc; UINTVAL left_type;

    left = mmd_deref(interp, func_nr, left);
    mmd_ensure_writable(interp, func_nr, left);

    left_type = left->vtable->base_type;
    real_function = (mmd_f_v_pi)get_mmd_dispatch_type(interp,
            func_nr, left_type, enum_type_INTVAL, &is_pmc);
    if (is_pmc) {
        sub = (PMC*)real_function;
        Parrot_runops_fromc_args(interp, sub, "vPI", left, right);
    }
    else {
        (*real_function)(interp, left, right);
    }
}
void mmd_dispatch_v_pn(Interp *interp, PMC *left, FLOATVAL right, INTVAL func_nr) { mmd_f_v_pn real_function; PMC *sub; int is_pmc; UINTVAL left_type;

    left = mmd_deref(interp, func_nr, left);
    mmd_ensure_writable(interp, func_nr, left);

    left_type = left->vtable->base_type;
    real_function = (mmd_f_v_pn)get_mmd_dispatch_type(interp,
            func_nr, left_type, enum_type_FLOATVAL, &is_pmc);
    if (is_pmc) {
        sub = (PMC*)real_function;
        Parrot_runops_fromc_args(interp, sub, "vPN", left, right);
    }
    else {
        (*real_function)(interp, left, right);
    }
}
void mmd_dispatch_v_ps(Interp *interp, PMC *left, STRING *right, INTVAL func_nr) { mmd_f_v_ps real_function; PMC *sub; int is_pmc; UINTVAL left_type;

    left = mmd_deref(interp, func_nr, left);
    mmd_ensure_writable(interp, func_nr, left);

    left_type = VTABLE_type(interp, left);
    real_function = (mmd_f_v_ps)get_mmd_dispatch_type(interp,
            func_nr, left_type, enum_type_STRING, &is_pmc);
    if (is_pmc) {
        sub = (PMC*)real_function;
        Parrot_runops_fromc_args(interp, sub, "vPS", left, right);
    }
    else {
        (*real_function)(interp, left, right);
    }
}
/*

INTVAL mmd_dispatch_i_pp(Interp *interp, PMC *left, PMC *right, INTVAL func_nr)

Like mmd_dispatch_p_ppp(), only it returns an INTVAL. This is used by MMD compare functions.

void mmd_add_function(Interp *interp, INTVAL funcnum, funcptr_t function)

Add a new binary MMD function to the list of functions the MMD system knows of. func_num is the number of the new function. function is ignored.

TODO change this to a MMD register interface that takes a function *name*.

static void mmd_expand_x(Interp *interp, INTVAL func_nr, INTVAL new_x)

Expands the function table in the X dimension to include new_x.

static void mmd_expand_y(Interp *interp, INTVAL func_nr, INTVAL new_y)

Expands the function table in the Y direction.

void mmd_add_by_class(Interp *interp, INTVAL functype, STRING *left_class, STRING *right_class, funcptr_t funcptr)

Add a function to the MMD table by class name, rather than class number. Handles the case where the named class isn't loaded yet.

Adds a new MMD function funcptr to the func_num function table that will be invoked when the left parameter is of class left_class and the right parameter is of class right_class. Both classes are STRING *s that hold the PMC class names for the left and right sides. If either class isn't yet loaded, Parrot will cache the information such that the function will be installed if at some point in the future both classes are available.

Currently this is done by just assigning class numbers to the classes, which the classes will pick up and use if they're later loaded, but we may later put the functions into a deferred table that we scan when PMC classes are loaded. Either way, the function will be guaranteed to be installed when it's needed.

The function table must exist, but if it is too small, it will automatically be expanded.

void mmd_register(Interp *interp, INTVAL func_num, INTVAL left_type, INTVAL right_type, funcptr_t funcptr)

Register a function funcptr for MMD function table func_num for classes left_type and right_type. The left and right types are INTVALs that represent the class ID numbers.

The function table must exist, but if it is too small, it will automatically be expanded.

Adding a new function to the table can be interestingly non-trivial, so we get to be tricky.

If the left or right types are larger than anything we've seen so far, it means that we have to expand the table. Making Y larger is simple -- just realloc with some more rows. Making X larger is less simple. In either case, we punt to other functions.

TODO - Currently the MMD system doesn't handle inheritance and best match searching, as it assumes that all PMC types have no parent type. This can be considered a bug, and will be resolved at some point in the future.

void mmd_destroy(Parrot_Interp interp)

Frees all the memory allocated used the MMD subsystem.

PMC *mmd_vtfind(Parrot_Interp interp, INTVAL type, INTVAL left, INTVAL right)

Return an MMD PMC function for the given data types. The return result is either a Sub PMC (for PASM MMD functions) or a NCI PMC holding the C function pointer in PMC_struct_val.

PMC *Parrot_mmd_sort_candidate_list(Interp *interp, PMC *candidates)

Given an array PMC (usually a MultiSub) sort the mmd candidates by their manhatten distance to the current args.

static PMC *mmd_arg_tuple_inline(Interp *, STRING *signature, va_list args)

Return a list of argument types. PMC arguments are specified as function arguments.

static PMC *mmd_arg_tuple_func(Interp *)

Return a list of argument types. PMC arguments are take from registers according to calling conventions.

static PMC *mmd_search_default(Interp *, STRING *meth, PMC *arg_tuple)

Default implementation of MMD search. Search scopes for candidates, walk the class hierarchy, sort all candidates by their Manhattan distance, and return result

static void mmd_search_classes(Interp *, STRING *meth, PMC *arg_tuple, PMC *cl, INTVAL start_at_parent)

Search all the classes in all MultiSubs of the candidates cl and return a list of all candidates. start_at_parent is 0 to start at the class itself or 1 to search from the first parent class.

static UINTVAL mmd_distance(Interp *, PMC *pmc, PMC *arg_tuple)

Create Manhattan Distance of sub pmc against given argument types. 0xffff is the maximum distance

static void mmd_sort_candidates(Interp *, PMC *arg_tuple, PMC *cl)

Sort the candidate list cl by Manhattan Distance

static PMC *mmd_search_scopes(Interp *, STRING *meth, PMC *arg_tuple)

Search all scopes for MMD candidates matching the arguments given in arg_tuple.

static int mmd_is_hidden(Interp *, PMC *multi, PMC *cl)

Check if the given multi sub is hidden by any inner multi sub (already in the candidate list cl.

static int mmd_maybe_candidate(Interp *, PMC *pmc, PMC *arg_tuple, PMC *cl)

If the candidate pmc is a Sub PMC, push it on the candidate list and return TRUE to stop further search.

If the candidate is a MultiSub remember all matching Subs and return FALSE to continue searching outer scopes.

static int mmd_search_cur_namespace(Interp *, STRING *meth, PMC *arg_tuple, PMC *cl)

Search the current package namespace for matching candidates. Return TRUE if the MMD search should stop.

static void mmd_search_builtin(Interp *, STRING *meth, PMC *arg_tuple, PMC *cl)

Search the builtin namespace for matching candidates. This is the last search in all the namespaces.

void Parrot_mmd_register_table(Interp*, INTVAL type, MMD_init *, INTVAL)

Register MMD functions for this PMC type.

void Parrot_mmd_rebuild_table(Interp*, INTVAL type, INTVAL func_nr)

Rebuild the static MMD_table for the given class type and MMD function number. If type is negative all classes are rebuilt. If func_nr is negative all MMD functions are rebuilt.

SEE ALSO ^

include/parrot/mmd.h, http://svn.perl.org/perl6/doc/trunk/design/apo/A12.pod, http://svn.perl.org/perl6/doc/trunk/design/syn/S12.pod


parrot