Changing layout of f_localsplus in frame objects

I first worked on a register-based virtual machine in the 1.5.2 timeframe. That was before PEP 227 (closures) landed. Prior to that, local variables and the stack were contiguous in the f_localsplus array. Based on a comment at the time from Tim Peters (which I recall but can no longer find), the number of required registers won’t be any greater than the maximum extent of the stack. (It makes sense, and worked for me at the time.) That allowed me to repurpose the stack space as my registers and treat the full f_localsplus array as a single large “register file,” eliminating LOAD_FAST_REG and STORE_FAST_REG instructions completely, which I believe was a major win.

I want to rearrange the current f_localsplus to make locals and stack neighbors again. My first look at the code suggested there is no good reason it can’t be done, but figured Jeremy Hylton must have had a good reason to prefer the current layout which places cells and frees in the middle.

While the necessary changes didn’t look extensive, they did look a bit tedious to get right, which I have confirmed. My first attempt to reorganize f_localsplus from locals/cells/frees/stack to locals/stack/cells/frees has been an abysmal failure. I’ve found a couple mistakes and corrected them. Implementing those corrections caused the types of failures to change (convincing me they were necessary), but did not eliminate them entirely (so, necessary, but not sufficient). I’ve clearly missed something. I’m also fairly ignorant about recent changes to the language (big understatement), so thought that before going any further, I would see if anyone with better current knowledge of frame objects knew of a reason why my desired layout change wouldn’t work.

I’m pretty ignorant myself about recent changes in this area, but I don’t think there’s any particular magic here. The main problem is that the position of everything is dynamic – the first cell starts after the last local, and so on. The reason for picking the order locals/cells/frees/stack is probably that the original code had just locals/stack, and putting cells and frees in between preserved two important properties:

  • Local variable i is located at f_localsplus[i]
  • There’s nothing beyond the stack

It looks like there are less than 20 references to f_localsplus[1]; it shouldn’t be too bad to review all those. Maybe my second property above is used implicitly somewhere? That would suggest that maybe you could rearrange things to cells/frees/locals/stack.

[1] However, note that GETLOCAL(i) is a macro meaning fastlocals[i], and appears to be used for more than just locals. (Ditto for SETLOCAL(i, value).)

Thanks, Guido. That’s about what I was thinking. While the stack can
grow and shrink though, the compiler computers the max size. No matter
what you do with the stack, you typically run from f_valuestack to
f_stacktop. As I was tracking down all the references to f_localsplus,
I noticed a number of places where offsets for f_nlocals were added to
get to the start of the cells/frees. I was thinking it might be
worthwhile to add a slow to the _frame struct and just compute that
location once at creation. That would reduce the number of places
where these calculations need to be made at the cost of an extra
pointer per _frame.

I’ll keep messing with it. If I get to the point where I’m ready to
throw my laptop out the window I’ll just commit and push to my fork,
then ask for more eyeballs.

Skip

2 Likes

Found and fixed the last holdout (a SETLOCAL call in ceval.c). For the curious, I’ve pushed the change to my fork:

As I expected, almost all the changes were in frameobject.c. The other changes were mostly just to remove no longer needed pointer arithmetic.

Skip