Description
The instruction pointer (IP) and the stack pointer (SP) are critical to the operation of the VM.
Because they are so performance critical, removing the need to constantly read and write them to memory is critical for performance.
In the tier 1 interpreter, we write the IP every instruction, but avoid reads by caching it in a (C) local variable. We go further with the stack pointer and only store it when pushing or popping a stack frame.
In the tier 2 interpreter, we fully virtualize the IP, so that we don't even need to have it cached in a (C) local variable.
We expect that in the copy-and-patch compiler we will fully virtualize the SP as well, allowing to keep some values on the stack in registers.
One optimization that is important for PyPy and was also valuable for HotPy is eliding frames for inlined funtions. To do that we also need to virtualize the frame pointer.
If we explicitly modify, load or store either the IP or the SP, we end up with ugly #ifdef
s and hard to analyze code.
Obviously, we need to way to express modifications, loads and stores of the IP and SP.
Micro-ops seems the obvious way to do this, although we should try to group operations for convenience and efficiency.
The operations we need are:
- Save the IP (at the start of every instruction)
- Add a constant to the IP (at the end of every instruction)
- Add/subtract the
oparg
to the IP, for jumps. Although it is probably simpler to just special case the jumps. - Save both the IP and SP (before calls and sends)
- Load both the IP and SP (after calls and sends)
So, how do we define these micro-ops?
In short, we define them in English, not C.
This means that all tools will need recognize and handle these special micro-ops, but as long as there aren't too many, that shouldn't be a problem.