Skip to content
Prev Previous commit
Next Next commit
add content to tier2
  • Loading branch information
iritkatriel committed Dec 5, 2024
commit 24148714e28c8d1750f56ba4faa4f045e2755e84
2 changes: 1 addition & 1 deletion InternalDocs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Program Execution

- [The Specializing Interpreter](adaptive.md)

- [The Tier 2 Interpreter (coming soon)](tier2.md)
- [The Tier 2 Interpreter](tier2.md)

- [Garbage Collector Design](garbage_collector.md)

Expand Down
66 changes: 65 additions & 1 deletion InternalDocs/tier2.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,67 @@
# The Tier 2 Interpreter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is potentially confusing. The tier 2 interpreter is, in my mind, only used to debug tier 2 code. It is the jit-compiled machine code that executes tier 2 normally.

Also, I think we want to move away from "tier 2" and use "JIT". See faster-cpython/ideas#614 (comment)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm making this change, but the code does refer to tier 2 in various places so I think it needs to be mentioned, otherwise future devs will be confused.


Coming soon.
The [basic interpreter](interpreter.md), also referred to as the `tier 1`
interpreter, consists of a main loop that executes the bytecode instructions
generated by the [bytecode compiler](compiler.md) and their
[specializations](adaptive.md). Runtime optimization in tier 1 can only be
done for one instruction at a time. The `tier 2` interpreter is based on a
mechanism to replace an entire sequence of bytecode instructions, and this
enables optimizations that span multiple instructions.

## The Optimizer and Executors

The program begins running in tier 1, until a `JUMP_BACKWARD` instruction
determines that it is `hot` because the counter in its
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
[inline cache](interpreter.md#inline-cache-entries) indicates that is
executed more than some threshold number of times. It then calls the
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
function `_PyOptimizer_Optimize()` in
[`Python/optimizer.c`](../Python/optimizer.c), passing it the current
[frame](frames.md) and instruction pointer. `_PyOptimizer_Optimize()`
constructs an object of type
[`_PyExecutorObject`](Include/internal/pycore_optimizer.h) which implements
an optimized version of the instruction trace beginning at this jump.

The optimizer determines where the trace ends, and the executor is set up
to either return to `tier 1` and resume execution, or transfer control
to another executor (see `_PyExitData` in Include/internal/pycore_optimizer.h).

The executor is stored on the [`code object`](code_objects.md) of the frame,
in the `co_executors` field which is an array of executors. The start
instruction of the trace (the `JUMP_BACKWARD`) is replaced by an
`ENTER_EXECUTOR` instruction whose `oparg` is equal to the index of the
executor in `co_executors`.

## The uop optimizer
Comment thread
iritkatriel marked this conversation as resolved.
Outdated

The optimizer that `_PyOptimizer_Optimize()` runs is configurable
via the `_Py_SetTier2Optimizer()` function (this is used in test
via `_testinternalcapi.set_optimizer()`.)
Comment thread
iritkatriel marked this conversation as resolved.
Outdated

The tier 2 optimizer, `_PyUOpOptimizer_Type`, is defined in
[`Python/optimizer.c`](../Python/optimizer.c). It translates
an instruction trace into a sequence of micro-ops by replacing
each bytecode by an equivalent sequence of micro-ops
(see `_PyOpcode_macro_expansion` in
[pycore_opcode_metadata.h](../Include/internal/pycore_opcode_metadata.h)
which is generated from [`Python/bytecodes.c`](../Python/bytecodes.c)).
The micro-op sequence is then optimized by
`_Py_uop_analyze_and_optimize` in
[`Python/optimizer_analysis.c`](../Python/optimizer_analysis.c).

## Running a uop executor

After a tier 1 `JUMP_BACKWARD` instruction invokes the uop optimizer
to create a tier 2 uop executor, it transfers control to this executor
via the `GOTO_TIER_TWO` macro, which jumps to `tier2_dispatch:` in
[`Python/ceval.c`](../Python/ceval.c), where there is a loops that
Comment thread
iritkatriel marked this conversation as resolved.
Outdated
executes the micro-ops which are defined in
[`Python/executor_cases.c.h`](../Python/executor_cases.c.h).
This loop exits when an `_EXIT_TRACE` or `_DEOPT` uop is reached.
Comment thread
iritkatriel marked this conversation as resolved.
Outdated

## Invalidating Executors

In addition to being stored on the code object, each executor is also
inserted into a list of all executors which is stored in the interpreter
state's `executor_list_head` field. This list is used when it is necessary
to invalidate executors because values that their construction depended
on may have changed.