If you have a Sub PMC (or subclass), you can invoke that PMC from C code using Parrot_call_sub() or a similar function. Of course, if you want to pass arguments to that PMC, something needs to translate those arguments from C's calling conventions to Parrot's calling conventions.
That something is a function something like runops_args() in src/inter_run.c. Here's the fun part. This function calls: dest = VTABLE_invoke(interp, sub, NULL); That is, it invokes the PMC. A Sub PMC returns the address of the next opcode to run -- the first opcode of the sub. runops_args() can check that opcode to see if it fetches arguments. A subroutine that takes arguments will start with an opcode to fetch the arguments. The important thing is that this doesn't really execute the subroutine. It just gets the address of the next opcode to run. Parrot can run that later, after it sets up the arguments appropriately. Of course, an NCI PMC (a subclass of the Sub PMC) doesn't really have an opcodes to execute, as it's a function pointer to execute. Its invoke() vtable override actually calls the function. Note that this happens *before* Parrot translates the arguments from the C conventions into Parrot's conventions, so in the NCI thunk that translate arguments from Parrot's conventions back into C's conventions, the arguments Just Aren't There. I'm not sure what the right solution is, but Liskov cries a little bit. Clearly, invoke() behaves very differently for Sub and NCI subs. I haven't looked at how Multi behaves, but I have my guesses. -- c