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