Version 19 (modified by bacek, 5 years ago)

--

The purpose of the PCC branch is to unify the internal implementation of the calling conventions into one code path. The current implementation of subroutine, method, and NCI invocation uses multiple different styles of passing and receiving arguments. A call to a subroutine or method from PIR saves a pointer to the location of the opcode where the arguments were passed, and later extracts the variables or constants from that opcode instruction. A call to a subroutine or method from C may pass around a varargs list and extract the arguments from it later or create a call state struct.

The new implementation standardizes argument passing for all calls (whether from PIR or C). Each call creates a call signature object right away, inserting arguments into the signature object from the opcode pointer or varargs list as soon as the call is made. Parameters are always extracted from a call signature object, and know nothing about the source of the call (they don't need to know what kind of environment invoked them, since all calls follow the same code path, and pass arguments the same way). NCI calls and what was previously known as PCCMETHOD calls also use the same argument passing strategy. Signature strings will be standardized on the PCC form (e.g. PI->S, for a call with a pmc and an integer argument, that returns a string result). The exception is NCI signatures, which will be left alone for now, but may be standardized later.

The following opcodes will be kept, but their implementation changed.

src/ops/core.ops:
  set_args
  get_params
  set_returns
  get_results

The following functions are deprecated and will be removed, as well as any functions only called by these functions.

src/call/pcc.c:
  parrot_pass_args
  parrot_pass_args_fromc
  Parrot_PCCINVOKE
  Parrot_init_ret_nci
  Parrot_init_arg_nci
  count_signature_elements (static)
  set_context_sig_returns_varargs (static)

src/call/ops.c:
  runops_args
  Parrot_run_meth_fromc
  Parrot_runops_fromc_args
  Parrot_runops_fromc_args_event
  Parrot_runops_fromc_args_reti
  Parrot_runops_fromc_args_retf
  Parrot_run_meth_fromc_args
  Parrot_run_meth_fromc_args_reti
  Parrot_run_meth_fromc_args_retf
  Parrot_runops_fromc_arglist
  Parrot_runops_fromc_arglist_reti
  Parrot_runops_fromc_arglist_retf
  Parrot_run_meth_fromc_arglist
  Parrot_run_meth_fromc_arglist_reti
  Parrot_run_meth_fromc_arglist_retf

src/extend.c (these functions are part of the defined public API, so will 
be modified to be backward compatible for now, and removed after 2.0):
  Parrot_call_sub
  Parrot_call_sub_ret_int
  Parrot_call_sub_ret_float
  Parrot_call_method
  Parrot_call_method_ret_int
  Parrot_call_method_ret_float

src/nci.c
  set_nci_I
  set_nci_N
  set_nci_S
  set_nci_P

The following code generators have been updated to produce new-style argument retrieval, instead of old-style argument retreival:

  tools/build/nativecall.pl
  lib/Parrot/Pmc2c/PCCMETHOD.pm

The following functions are added and act as replacements for the deprecated functions.

src/call/pcc.c:
  Parrot_pcc_invoke_sub_from_c_args
  Parrot_pcc_build_sig_object_from_op
  Parrot_pcc_build_sig_object_returns_from_op
  Parrot_pcc_build_sig_object_from_varargs (added in an earlier refactor, but for this purpose)
  Parrot_pcc_fill_params_from_op
  Parrot_pcc_fill_params_from_c_args
  Parrot_pcc_fill_returns_from_op
  Parrot_pcc_fill_returns_from_c_args

The following static functions are added, and support the new argument passing behavior:

src/call/pcc.c:
  dissect_aggregate_arg
  extract_named_arg_from_op
  parse_signature_string

src/call/callsignature.c
  Parrot_pcc_get_call_sig_raw_args
  Parrot_pcc_get_call_sig_raw_returns
  Parrot_pcc_set_call_sig_raw_args
  Parrot_pcc_set_call_sig_raw_returns

The signature object is stored in the current_sig element of the caller's context. This means that argument storing and return value extraction happens on current_sig in CURRENT_CONTEXT(interp), while parameter extraction and return value storing must retrieve the caller's context and use its current_sig element. The reason for this distinction is that the subroutine's local current_sig element will be set for every call it makes. (This will be simplified when contexts and call_signatures are collapsed into one.)

See also the earlier wishlist/tasklist CallingConventionsTasklist which mentions some of the motivations and reasoning for these changes.

Known issues

- The Parrot_call_sub_* and Parrot_call_method_* variants in src/extend.c don't all have the necessary changes to allow them to work with the new calling conventions (these are temporary implementations for backward compatibility before they're removed at the next deprecation point). Parrot_call_sub does have the right changes, and the others can be largely copied from it.

- NCI hasn't been fully updated on caller and receiver side to use the new argument passing style.

- Return argument processing (Parrot_pcc_fill_returns_from_op and Parrot_pcc_fill_returns_from_c_args) doesn't currently support :named, :slurpy, :flatten, :optional, :opt_flag etc. (t/compilers/imcc/syn/tail.t, t/pmc/coroutine.t)

- Parrot_pcc_fill_params_from_op and Parrot_pcc_fill_params_from_c_args are monolithic functions (basically finite state machines iterating over the arguments), that contain a great deal of nearly repeated code (one or two things different each time). Parrot_pcc_fill_returns_from_op and Parrot_pcc_fill_returns_from_c_args will be just as bad once they support all the options. Not a requirement before the merge, but these should be refactored into smaller subroutines. Needs to be thought through carefully though, it was a similar plan that lead to the current mess.

- Flattening an argument doesn't alter the signature string stored, so multiple dispatch can't handle the resulting string. Need to modify the signature string while building the CallSignature object. (t/pmc/multisub.t and t/pmc/multidispatch.t)

- Edge cases on auto boxing/unboxing argument types, e.g. "Unable to set PMC value, the pointer is not a PMC" (t/oo/methods.t, t/oo/subclass.t, t/pmc/hashiteratorkey.t, t/pmc/object-meths.t, t/pmc/resizablestringarray.t, t/pmc/string.t)

- GC attempting to mark a bad variable (t/op/box.t) (Actually IMCC creates bad PackFile_Constants segment for box.t)

- Insufficient checking on positional arguments passed inside named arguments (t/op/calling.t)

- Insufficient checking on missing named arguments (t/op/cc_state.t)

- Insufficient checking on too many arguments passed (t/pmc/exporter.t)

- Some tests need to be updated to match the current error messages (t/op/calling.t, t/op/cc_params.t)

- Segfault in set_returns, possibly a null call object? (t/op/gc.t, t/pmc/exceptionhandler.t)

- Probably a problem with argument handling (t/op/lexicals.t, t/pmc/class.t, t/pmc/codestring.t, t/pmc/object-meths.t, t/pmc/sub.t)

- Two NCI edge cases (t/pmc/nci.t).

- Likely cases of code still calling into the old functions for invoking from C, instead of the new functions (t/pmc/sub.t, t/pmc/threads.t)

- Likely bug in Parrot_call_sub reimplementation (t/src/embed.t)

- miniparrot fails to compile if an installed parrot is not already present, because it has -lparrot in the compile options

Argument Processing Algorithms

Here is some documentation for ways that the argument processing algorithm (Parrot_pcc_fill_params_from_op and Parrot_pcc_fill_params_from_varargs) work. There are two possible ways, depending on whether we iterate over the list of parameters and pull arguments, or iterate over the list of arguments and push them into parameters. First, iterating over parameters (similar to what is done now):

Infinite loop
    get next positional arg
    get next positional parameter slot

    if parameter slot is NULL
        if positional arg is NULL # No more of either, done. Break
            break
        if error checking
            throw exception
    if positional arg is NULL
        if error checking
            throw exception

    if parameter slot is slurpy
        create new RPA PMC
        insert new RPA PMC into destination context parameter slot

    if we have a slurpy array pmc
        add positional arg to slurpy array pmc
    else
        Insert positional into destination context parameter slot
        if current parameter slot is optional
            set corresponding opt_flag TRUE

Infinite loop
    get next named arg name
    get next named arg value
    get next named parameter slot

    if arg name/value pair is NULL
        if parameter slot is NULL
            break # Even, we're done
        else if error checking
            throw exception
    if parameter slot is NULL
        throw exception

    if parmeter slot is slurpy
        create new Hash PMC
        insert new Hash PMC into destination context parameter slot

    if we have a slurpy hash PMC
        add name/value pair to slurpy hash PMC
    else
        Insert value into destination context parameter slot
        if current parameter slot is optional
            set corresponding opt_flag to TRUE

Loop over all remaining opt_flag parameters
    set to FALSE

Next, iterating over arguments. This requires iterator functionality installed into CallSignature, which does not currently exist:

Infinite loop
    get next positional arg
    get next parameter flag

    if no parameter flag
        if no positional arg # No more of either, done.
            end loop
        if error checking
            throw exception
    if no positional arg
        set all remaining positional opt_flags to FALSE
        if error checking
            throw exception

    if parameter flag is slurpy
        create new RPA PMC
        insert new RPA PMC into destination context parameter slot
        add positional arg to slurpy array pmc
        add all remaining positional args to slurpy array pmc
        end loop
 
    if parameter flag is named
        insert positional arg into next param slot
        mark name as used

    if parameter flag is lookahead
        if have named arg
            insert named arg into next param slot
            mark name as used
            fetch next param flag for positional
        else
            insert positional arg into next param slot

    Insert positional arg into destination parameter slot

    if current parameter slot is optional
        set corresponding opt_flag TRUE

Infinite loop
    get next named arg name
    get next named arg value
    get next parameter flag

    if no parameter flag
        if no arg name/value pair
            end loop # Even, we're done
        if error checking
            throw exception
    if no arg name/value pair
        set all remaining named opt_flags to FALSE
        if error checking
            throw exception

    if parameter flag is slurpy
        create new Hash PMC
        insert new Hash PMC into destination context parameter slot
        add name/value pair to slurpy hash PMC
        add all remaining name/value pairs to slurpy hash PMC

    Insert value into destination context parameter slot
    if current parameter slot is optional
        set corresponding opt_flag to TRUE