@@ -60,8 +60,12 @@ When tier 2 is enabled but the JIT is not (python was configured with
6060[ ` --enable-experimental-jit=interpreter ` ] ( https://docs.python.org/dev/using/configure.html#cmdoption-enable-experimental-jit ) ),
6161the executor jumps to ` tier2_dispatch: ` in
6262[ ` Python/ceval.c ` ] ( ../Python/ceval.c ) , where there is a loop that
63- executes the micro-ops which are defined in
64- [ ` Python/executor_cases.c.h ` ] ( ../Python/executor_cases.c.h ) .
63+ executes the micro-ops. The micro-ops are are defined in
64+ [ ` Python/executor_cases.c.h ` ] ( ../Python/executor_cases.c.h ) ,
65+ which is generated by the build script
66+ [ ` Tools/cases_generator/tier2_generator.py ` ] ( ../Tools/cases_generator/tier2_generator.py )
67+ from the bytecode definitions in
68+ [ ` Python/bytecodes.c ` ] ( ../Python/bytecodes.c ) .
6569This loop exits when an ` _EXIT_TRACE ` or ` _DEOPT ` uop is reached,
6670and execution returns to teh tier 1 interpreter.
6771
@@ -72,3 +76,49 @@ inserted into a list of all executors which is stored in the interpreter
7276state's ` executor_list_head ` field. This list is used when it is necessary
7377to invalidate executors because values that their construction depended
7478on may have changed.
79+
80+ ## The JIT
81+
82+ When the jit is enabled (python was configured with
83+ [ ` --enable-experimental-jit ` ] ( https://docs.python.org/dev/using/configure.html#cmdoption-enable-experimental-jit ) ,
84+ the uop executor's ` jit_code ` field is populated with a pointer to a compiled
85+ C function that implement the executor logic. This function's signature is
86+ defined by ` jit_func ` in [ ` pycore_jit.h ` ] ( Include/internal/pycore_jit.h ) .
87+ When the executor is invoked by ` ENTER_EXECUTOR ` , instead of jumping to
88+ the uop interpreter at ` tier2_dispatch ` , the executor runs the function
89+ that ` jit_code ` points to. This function returns the instruction pointer
90+ of the next Tier 1 instruction that needs to execute.
91+
92+ The generation of the jitted fuctions uses the copy-and-patch technique
93+ which is described in
94+ [ Haoran Xu's article] ( https://sillycross.github.io/2023/05/12/2023-05-12/ ) .
95+ At its core are statically generated ` stencils ` for the implementation
96+ of the micro ops, which are completed with runtime information while
97+ the jitted code is constructed for an executor by
98+ [ ` _PyJIT_Compile ` ] ( ../Python/jit.c ) .
99+
100+ The stencils are generated under the build target ` regen-jit ` by the scripts
101+ in [ ` /Tools/jit ` ] ( /Tools/jit ) . This script reads
102+ [ ` Python/executor_cases.c.h ` ] ( ../Python/executor_cases.c.h ) (which is
103+ generated from [ ` Python/bytecodes.c ` ] ( ../Python/bytecodes.c ) ). For
104+ each opcode, it constructs a ` .c ` file that contains a function for
105+ implementing this opcode, with some runtime information injected.
106+ This is done by replacing ` CASE ` by the bytecode definition in the
107+ template file [ ` Tools/jit/template.c ` ] ( ../Tools/jit/template.c ) .
108+
109+ Each of the ` .c ` file is compiled by LLVM, to produce an object file
110+ that contains a function that executes the opcode. These compiled
111+ functions are used to generate the file
112+ [ ` jit_stencils.h ` ] ( ../jit_stencils.h ) , which contains the functions
113+ that the JIT can use to emit code for each of the bytecodes.
114+
115+ For Python maintainers this means that changes to the bytecodes and
116+ their implementations do not require changes related to the JIT,
117+ because everything the JIT needs is automatically generated from
118+ [ ` Python/bytecodes.c ` ] ( ../Python/bytecodes.c ) at build time.
119+
120+ See Also:
121+
122+ * [ Copy-and-Patch Compilation: A fast compilation algorithm for high-level languages and bytecode] ( https://arxiv.org/abs/2011.13127 )
123+
124+ * [ PyCon 2024: Building a JIT compiler for CPython] ( https://www.youtube.com/watch?v=kMO3Ju0QCDo )
0 commit comments