This week, the project is converting Parrot's context structures, which at the moment are just normal C structures, into garbage-collectable "Context" PMCs. TT #596 contains some information about the work, although it's currently very sparse. This is not a new idea by any stretch: I've been thinking about it since last summer when I was doing my GC work, and my ambitions were so high I was convinced that all memory allocations in Parrot could be done through the GC—I still think that, but I'm more humbled by the sheer amount of effort required to make that happen. Not only does this project make it easier to manage contexts without all sorts of manual reference counting, there is also going to be a lot of potential for streamlining and optimizing parts of the codebase.
Parrot_Context structures form a linked list of sorts that represent the current calling context and all parent contexts. The context structure contains the array of I, N, S, and P registers too. It also contains a few data fields to help manage the CPS calling system. In short, Contexts are pretty central to Parrot and are very important.
The calling conventions system as it currently stands is pretty inefficient and messy. This isn't a new sentiment, I've blogged about it before (although I can't find a link right now). There are about half a dozen different ways to execute a PIR subroutine, many of those have multiple special-purpose interfaces. Allison is doing some great work in getting things to be more unified, and once she gets her work merged into trunk we will be able to start optimizing the new unified pipeline. A major part of those optimizations is going to be converting Contexts into PMCs.
For an idea of why, consider what happens in the unified calling path when we make a call (note that these aren't exactly in order):
- Arguments come in as an array, either as a va_list variadic argument array or as an opcode_t* serialization in bytecode. We also get an invocant object (if any) and a return address. The reason for the difference here is because we can be invoking a subroutine from PIR (via the invoke opcode) or via C (using Parrot_pcc_invoke_from_sig_object)
- Arguments and the invocant are added into a CallSignature PMC, which needs to be allocated and initialized.
- A new context is created, initialized, and a reference is made to it.
- The arguments from the CallSignature PMC are added into the registers of the Context
- The invocant, if any, is extracted from the CallSignature PMC and put into the interpreter structure as the current "self".
- A new RetContinuation PMC is created to handle a return call and stored in the Context.
- Using a series of Call_State structures, and a long loop, extract arguments from the context registers and associate them with parameters in the called sub.
- Arguments come in as an array, either as a va_list variadic argument array or as an opcode_t* serialization in bytecode. We also get an invocant object (if any) and a return address.
- The arguments, the invocant, and the return address are added to the Context PMC
- A custom iterator is created for the Context PMC and the subroutine parameters are extracted from it directly.
Some of my recent work on the GC API refactor has really exposed the current Context system API, and has made it easier to pull individual functions out into the new Context PMC VTABLEs. This is going to be a big project, but it's not unmanageably big. I'm probably going to get started on this work sometime after the next release (which is going to be on May 19th I think). I'll definitely be planning and prototyping before that, however.