[pypy-svn] r24582 - pypy/dist/pypy/doc

arigo at codespeak.net arigo at codespeak.net
Mon Mar 20 13:57:34 CET 2006


Author: arigo
Date: Mon Mar 20 13:57:20 2006
New Revision: 24582

Modified:
   pypy/dist/pypy/doc/jit.txt
Log:
JIT Doc: some notes about the merging/bookkeeping done by the
compilers produced by the hint-rtyper.  Wrote down a possible
approach, to be reviewed :-)


Modified: pypy/dist/pypy/doc/jit.txt
==============================================================================
--- pypy/dist/pypy/doc/jit.txt	(original)
+++ pypy/dist/pypy/doc/jit.txt	Mon Mar 20 13:57:20 2006
@@ -106,4 +106,132 @@
 helpers; when the graph of the now-compiler is running, these helpers
 will produce new ``int_add`` and ``int_sub`` operations.
 
-(Extra merging/bookkeeping blocks are actually inserted; XXX explain...)
+Merging and bookkeeping
+-----------------------
+
+XXX the following is not the way it works currently, but rather a
+proposal for how it might work -- although it's all open to changes.  It
+is a mix of how Psyco and the Flow Object Space work.
+
+Unlike ll_plus_minus() above, any realistic interpreter needs to handle
+two complications:
+
+1. Jumping or looping opcodes.  When looping back to a previous bytecode
+   position, the now-compiler must not continue to compile again and again;
+   it must generate a loop in the residual graph as well, and stop
+   compiling.
+
+2. Conditional branches.  Unless the condition is known at compile time,
+   the compiler must generate a branch in the residual graph and compile
+   both paths.
+
+Keep in mind that this is all about how the interpreter is transformed
+to become a compiler.  Unless explicitly specified, I don't speak about
+the interpreted user program here.
+
+As a reminder, this is handled in the Flow Space as follows:
+
+1. When a residual operation is about to be generated, we check if the
+   bytecode position closed back to an already-seen position.  To do so,
+   for each bytecode position we save a "state".  The state remembers
+   the bytecode interpreter's frame state, as a pattern of Variables and
+   Constants; in addition, the state points to the residual block that
+   corresponded to that position.
+
+2. A branch forces the current block to fork in two "EggBlocks".  This
+   makes a tree with a "SpamBlock" at the root and two EggBlock children,
+   which themselves might again have two EggBlock children, and so on.
+   The Flow Space resumes its analysis from the start of the root
+   SpamBlock and explores each branch of this tree in turn.  Unexplored
+   branches are remembered for later.
+
+In Psyco:
+
+1. A state is saved at the beginning of each bytecode, as in the Flow
+   Space.  (There are actually memory-saving tricks to avoid saving a
+   state for each and every bytecode.)  We close a loop in the residual
+   graph as soon as we reach an already-seen bytecode position, but
+   only if the states are "compatible enough".  Indeed, as in the PyPy
+   JIT, Psyco states store more than just Variable/Constant: they store
+   "blue" containers detailing individual field's content.  Blue
+   containers of too-different shapes are not "compatible enough".  In
+   addition, some Constants can be marked as "fixed" to prevent them
+   from being merged with different Constants and becoming Variables.
+
+2. Branching in Psyco work as in the Flow Space, with the exception that
+   each condition has got a "likely" and a "less likely" outcome.  The
+   compilation follows the "likely" path only.  The "unlikely" paths
+   are only explored if at run-time the execution actually reaches them.
+   (When it does, the same trick as in the Flow Space is used:
+   compilation restarts from the root SpamBlock and follows the complete
+   branch of the EggBlock tree.)
+
+3. A different kind of branching that doesn't occur in the Flow Space:
+   promoting a value from Variable to Constant.  This is used e.g. when
+   an indirect function call is about to be performed.  A typical
+   example is to call a PyTypeObject's slot based on the type of a
+   PyObject instance.  In this case, Psyco considers the PyObject's
+   ob_type field as a Variable, which it turns into a Constant.
+   Conceptually, the current residual block is ended with a "switch"
+   and every time a different run-time value reaches this point, a new
+   case is compiled and added to the switch.  (As in 2., the compilation
+   is restarted from the root SpamBlock until it reaches that point
+   again.)
+
+The "tree of EggBlocks" approach doesn't work too well in general.  For
+example, it unrolls loops infinitely if they are not loops in the
+bytecode but loops in the implementation of a single opcode (we had this
+problem working on the annotator in Vilnius).
+
+The current preliminary work on the hint-rtyper turns the interpreter
+into a compiler that saves its state all the time everywhere.  This
+makes sure that loops are not unexpectedly unrolled, and that the code
+that follows an if/else is not duplicated (as it would be in the
+tree-of-EggBlocks approach).  It is also extremely inefficient and
+perfect to explode the memory usage.
+
+I think we could try to target the following model -- which also has the
+advantage that simple calls in the interpreter are still simple calls in
+the compiler, as in Psyco:
+
+1. We only save the state at one point.  We probably need a hint to
+   specify where this point is -- at the beginning of the bytecode
+   interpreter loop.  It is easy to know in advance which information
+   needs to be stored in each state: the tuple of green variables is used
+   as a key in a global dict; the content of the red variables is stored
+   as a state under this key.  Several states can be stored under the same
+   key if they are not "compatible enough".
+
+2. Branching: a possible approach would be to try to have the compiler
+   produce a residual graph that has the same shape as the original
+   graph in the interpreter, at least as far as the conditions are
+   unknown at compile-time.  (This would remove the branches whose
+   conditions are known at compile time, and unroll all-green loops
+   like the bytecode interpreter loop itself.)
+
+3. Promoting a Variable to a Constant, or, in colored terms, a red
+   variable to a green one (using a hint which we have not implemented
+   so far): this is the case where we have no choice but suspend the
+   compilation, and wait for execution to provide real values.  We will
+   implement this case later, but I mention it now because it seems that
+   there are solutions compatible with this model, including Psyco's
+   (see 3. above).
+
+The motivation to do point 2. differently than in Psyco is that it is
+both more powerful (no extra unrolling/duplication of code) and closer
+to what we have already now: the bookkeeping code inserted by the
+hint-rtyper in the compiler's graphs.  In Psyco it would have been a
+mess to write that bookkeeping code everywhere by hand, not to mention
+changing it to experiment with other ideas.
+
+Random notes
+-----------------
+
+An idea to consider: red variables in the compiler could come with
+a concrete value attached too, which represents a real execution-time
+value.  The compiler would perform concrete operations on it in addition
+to generating residual operations.  In other words, the compiler would
+also perform directly some interpretation as it goes along.  In this
+way, we can avoid some of the recompilation by using this attached value
+e.g. as the first switch case in the red-to-green promotions, or as a
+hint about which outcome of a run-time condition is more likely.



More information about the Pypy-commit mailing list