https://github.com/python/cpython/issues/131798
https://github.com/python/cpython/issues/130415
- Tracing JIT
- Shifting 0 and 1 on to 16 bit number for every `if` to figure out which way is the happy path
- Dicts, types, code objects can have watchers which gets called upon any modification (e.g. to get notified when the globals dict changes to invalidate a constant load optimization)
- There is an interpreter for µop to make it easy to test/debug the JIT
- There's an environment variable to emit tracing details
- µop -> [[#`Tools/jit/template.c`]] -> assembly | [[#`Python/optimizer.c`]] -> machine code (as JSON) | [[#`Tools/jit/_targets.py`]] -> stencil
# Docs
`Tools/jit/README.md`
`InternalDocs/jit.md`
# `Python/bytecodes.c`
- DSL for generating the interpreter loop
- `inst(<name>, (<pop from stack, ...> -- <push from stack, ...>))`
- Names used for popping and pushing automatically get assigned/assignable
- `pure` means no side-effects
- `replicate` means very common and low opargs
- Body is free-form C code
- For stack manipulation, `<name>[<oparg>]` gathers an array
- `tier1` is interpreter only; `tier2` is for the JIT
- `op(...)`
- Fraction of an instruction; µop
- Effectively break an instructions into its discrete steps for the JIT to optimize (e.g. leave out a µop)
- `DEOPT_IF` and `EXIT_IF` guards when to bail out in the JIT
- Stack argument`<name>/<size>` is a cached value for the op
- `size` is measure in 16 bits / 2 bytes
- `specializing` decides whether to specialize or to fall-through in the macro using this µop
- `macro(<name>) = ...`
- How to change together µops
- Have either a macro or instruction, but not both
- `family(<instruction>, ...) = <instructions or macros, >`
- If an `EXIT_IF` fails, fall back to the parent instruction
# `Python/specialize.c`
- Where to keep logic for choosing to specialize
# `Python/optimizer.c`
- Should probably be named `tracing.c`
- Executor is the Python object with the pointer to some JIT code that manages it
# `Python/optimizer_analysis.c`
- Analyzes and optimizes
- Acts as the peephole optimizer for the µops
# `Python/optimizer_bytecodes.c`
- DSL for generating the abstract interpreter
- Same DSL format as [[#`Python/bytecodes.c`]]
- Used to evaluate situations where either the type is already known or you gain knowledge about the type, .e.g.
- `bool(int)` is always an int
- Look at the arguments to the µop and then calculate what the type is
- Narrow down to constants (e.g. not just an int, but 42)
- `REPLACE_OPCODE_IF_EVALUATES_PURE()` means the types are known-safe types where we know the semantics (i.e. built-in types in a situation where there isn't an issue such as potentially triggering a warning)
- Constants are a `const *`, not a `* const`, i.e. the object is _constantly_ the same, not that the object is immutable
# `Python/jit.c`
- Walks the linear list of µops, copies the stencil value for each µop, and then patches the machine code
- `patch_<arch>_<size><details>()` functions do the patching
- Naming follows what linkers use (e.g. `lld`)
# `Tools/jit/template.c`
- File used to fill with the implementation of a single µop to compile it for use as a stencil
# `Tools/jit/_optimizers.py`
- Optimize the assembly from the C code that gets put into [[#`Tools/jit/template.c`]]
# `Tools/jit/_targets.py`
- Extracts data from JSON dump of the object file from LLVM
# `Tools/jit/_stencils.py`
- Working with stencils