Currently there are quite a few local variables in PyEval_EvalDefault().
Each of these is:
- A candidate for register allocation, confusing the compiler.
- Potentially adds extra state to the interpreter
- Adds complexity to automatically generated interpreters and compilers
The local variables break down into three classes:
- The state of the interpreter
- Transient values
- Values for gathering stats, debugging, etc.
The last category are not present in release builds, so we can ignore those.
The transient values, like opcode and oparg are an artifact of the dispatching mechanism and will be absent for compiled code, or different for a different interpreter.
It is the remaining values that matter.
Each of these will consume a register in compiled code, and need to kept in sync where there is redundancy.
The current values are:
next_instr -- The VM instruction pointer
frame -- Pointer to the current frame
stackpointer -- Pointer to the top of stack
tstate -- Pointer to the current thread
eval_break -- Pointer to the eval-breaker
kwnames -- Pointer to the keyword names, if any for the next call
next_instr and frame are fundamental to VM operation.
Hypothetically stackpointer can be eliminated by a compiler, and tstate can be eliminated by some clever stack arrangement, or using thread-local storage. However, these approaches are complex and may not be worthwhile.
eval_breaker can easily be eliminated, as it is redundant. eval_breaker == tstate->interp->eval_breaker
kwnames is logically transient, in that it is only valid between the KW_NAMES instruction and a CALL. However from the very local view of a single instruction it is effectively an interpreter-wide value.
It can removed by pushing the kwnames to the stack, but that is likely to cause a slowdown. We need to experiment.
Linked PRs
Currently there are quite a few local variables in
PyEval_EvalDefault().Each of these is:
The local variables break down into three classes:
The last category are not present in release builds, so we can ignore those.
The transient values, like
opcodeandopargare an artifact of the dispatching mechanism and will be absent for compiled code, or different for a different interpreter.It is the remaining values that matter.
Each of these will consume a register in compiled code, and need to kept in sync where there is redundancy.
The current values are:
next_instr-- The VM instruction pointerframe-- Pointer to the current framestackpointer-- Pointer to the top of stacktstate-- Pointer to the current threadeval_break-- Pointer to the eval-breakerkwnames-- Pointer to the keyword names, if any for the next callnext_instrandframeare fundamental to VM operation.Hypothetically
stackpointercan be eliminated by a compiler, andtstatecan be eliminated by some clever stack arrangement, or using thread-local storage. However, these approaches are complex and may not be worthwhile.eval_breakercan easily be eliminated, as it is redundant.eval_breaker == tstate->interp->eval_breakerkwnamesis logically transient, in that it is only valid between theKW_NAMESinstruction and aCALL. However from the very local view of a single instruction it is effectively an interpreter-wide value.It can removed by pushing the kwnames to the stack, but that is likely to cause a slowdown. We need to experiment.
Linked PRs
eval_breakerback at the start of the interpreter state. #107383