|
1 | 1 | # The Tier 2 Interpreter |
2 | 2 |
|
3 | | -Coming soon. |
| 3 | +The [basic interpreter](interpreter.md), also referred to as the `tier 1` |
| 4 | +interpreter, consists of a main loop that executes the bytecode instructions |
| 5 | +generated by the [bytecode compiler](compiler.md) and their |
| 6 | +[specializations](adaptive.md). Runtime optimization in tier 1 can only be |
| 7 | +done for one instruction at a time. The `tier 2` interpreter is based on a |
| 8 | +mechanism to replace an entire sequence of bytecode instructions, and this |
| 9 | +enables optimizations that span multiple instructions. |
| 10 | + |
| 11 | +## The Optimizer and Executors |
| 12 | + |
| 13 | +The program begins running in tier 1, until a `JUMP_BACKWARD` instruction |
| 14 | +determines that it is `hot` because the counter in its |
| 15 | +[inline cache](interpreter.md#inline-cache-entries) indicates that is |
| 16 | +executed more than some threshold number of times. It then calls the |
| 17 | +function `_PyOptimizer_Optimize()` in |
| 18 | +[`Python/optimizer.c`](../Python/optimizer.c), passing it the current |
| 19 | +[frame](frames.md) and instruction pointer. `_PyOptimizer_Optimize()` |
| 20 | +constructs an object of type |
| 21 | +[`_PyExecutorObject`](Include/internal/pycore_optimizer.h) which implements |
| 22 | +an optimized version of the instruction trace beginning at this jump. |
| 23 | + |
| 24 | +The optimizer determines where the trace ends, and the executor is set up |
| 25 | +to either return to `tier 1` and resume execution, or transfer control |
| 26 | +to another executor (see `_PyExitData` in Include/internal/pycore_optimizer.h). |
| 27 | + |
| 28 | +The executor is stored on the [`code object`](code_objects.md) of the frame, |
| 29 | +in the `co_executors` field which is an array of executors. The start |
| 30 | +instruction of the trace (the `JUMP_BACKWARD`) is replaced by an |
| 31 | +`ENTER_EXECUTOR` instruction whose `oparg` is equal to the index of the |
| 32 | +executor in `co_executors`. |
| 33 | + |
| 34 | +## The uop optimizer |
| 35 | + |
| 36 | +The optimizer that `_PyOptimizer_Optimize()` runs is configurable |
| 37 | +via the `_Py_SetTier2Optimizer()` function (this is used in test |
| 38 | +via `_testinternalcapi.set_optimizer()`.) |
| 39 | + |
| 40 | +The tier 2 optimizer, `_PyUOpOptimizer_Type`, is defined in |
| 41 | +[`Python/optimizer.c`](../Python/optimizer.c). It translates |
| 42 | +an instruction trace into a sequence of micro-ops by replacing |
| 43 | +each bytecode by an equivalent sequence of micro-ops |
| 44 | +(see `_PyOpcode_macro_expansion` in |
| 45 | +[pycore_opcode_metadata.h](../Include/internal/pycore_opcode_metadata.h) |
| 46 | +which is generated from [`Python/bytecodes.c`](../Python/bytecodes.c)). |
| 47 | +The micro-op sequence is then optimized by |
| 48 | +`_Py_uop_analyze_and_optimize` in |
| 49 | +[`Python/optimizer_analysis.c`](../Python/optimizer_analysis.c). |
| 50 | + |
| 51 | +## Running a uop executor |
| 52 | + |
| 53 | +After a tier 1 `JUMP_BACKWARD` instruction invokes the uop optimizer |
| 54 | +to create a tier 2 uop executor, it transfers control to this executor |
| 55 | +via the `GOTO_TIER_TWO` macro, which jumps to `tier2_dispatch:` in |
| 56 | +[`Python/ceval.c`](../Python/ceval.c), where there is a loops that |
| 57 | +executes the micro-ops which are defined in |
| 58 | +[`Python/executor_cases.c.h`](../Python/executor_cases.c.h). |
| 59 | +This loop exits when an `_EXIT_TRACE` or `_DEOPT` uop is reached. |
| 60 | + |
| 61 | +## Invalidating Executors |
| 62 | + |
| 63 | +In addition to being stored on the code object, each executor is also |
| 64 | +inserted into a list of all executors which is stored in the interpreter |
| 65 | +state's `executor_list_head` field. This list is used when it is necessary |
| 66 | +to invalidate executors because values that their construction depended |
| 67 | +on may have changed. |
0 commit comments