-
Notifications
You must be signed in to change notification settings - Fork 50
Thoughts about engine.md (Guido)
(This is just personal notes notes -- I am trying something new and using the GitHub Wiki.)
How can the copy-and-patch (JIT) compiler be considered an "execution engine"? It's a compiler, so by definition (unlike an interpreter) it doesn't execute anything. Its output is executable though. Maybe we need more terminology so the distinction is clearer? As written this leaves me just confused.
Entry to and exit from superblocks -- is this from Tier 1?
"Performance of jumping [...] is also important. Memory consumption is also important." Yeah, yeah. :-)
Fine. As clarified off-line, both may exist in the binary, but we decide very early on which one to use, and stick to that one.
I am guessing we might also decide to use neither. Or only one or the other may exist in the binary (e.g. the JIT is not always present).
"An executor is the runtime object that is the executable representation of a superblock." -- how does this relate to execution engines? Is the executor passed to the engine for execution? Or how otherwise is the engine invoked or the executor executed?
"Creating executors from superblocks is the first job of the execution engine." -- This leaves me more confused about the relationship between superblocks, executors, and engines. It seems the mapping between artifacts in the code (like functions) and concepts in this document is a bit fuzzy; I'd like it to be as crisp as possible (with instructions for how to split things up if the code conflates concepts).
Maybe we could start out by not worrying too much about this (either about the speed of hot exits or the space of cold ones) and iterate after we've got other aspects in place? Or is this of such importance that the entire architecture needs to be aware of this?
Similar -- we can iterate on this. Also, the process is somewhat convoluted, since before we can create the executor we have to have the superblock.
Another possible way to avoid this problem would be to make exits where the executor hasn't done any work yet always go to Tier 1. We can statically know (during the translation from superblock to executor) whether an exit hasn't done any work yet: the exit can only be preceded by pure guards (which we can mark in bytecodes.c). On the surface, at least, this is not the same as (1), since it's not about which executors are special, but about which exits; and it's definitely not the same as (2). Reasoning about this seems straightforward.
"Some superblocks can be quite short, but form a larger region of hot code" -- this confused me. Maybe "form" should be "combine into"? So several short superblocks combine into a larger region, right? This feels quite advanced right now. Maybe we can punt until we're farther along the project?
That doesn't sound very helpful -- how do we do this?
Since this is just one possible implementation, it still leaves room for a lot of misunderstandings about how various pieces fit together.
To be clear, "IP" and "first instruction" here refer to the Tier 2 interpreter's IP, right? So at the C level, or to the hardware CPU, this is still several instructions. And this transfer is responsible for the INCREF/DECREF of the old/new executors.
And in the JIT case, I'd like to understand how a JIT executor and its function pointer relate (presumably the executor is still an object, so I guess it has to contain a function pointer as a piece of data), and who is responsible for the executor object INCREF/DECREF.
(Here I was interrupted and had to save my work. More later.)