[pypy-commit] pypy guard-compatible: Write down the plan

arigo pypy.commits at gmail.com
Fri May 20 16:12:08 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: guard-compatible
Changeset: r84543:2588b05d7184
Date: 2016-05-20 22:12 +0200
http://bitbucket.org/pypy/pypy/changeset/2588b05d7184/

Log:	Write down the plan

diff --git a/rpython/jit/backend/x86/guard_compat.py b/rpython/jit/backend/x86/guard_compat.py
--- a/rpython/jit/backend/x86/guard_compat.py
+++ b/rpython/jit/backend/x86/guard_compat.py
@@ -9,6 +9,130 @@
 from rpython.jit.metainterp.history import BasicFailDescr
 
 
+#
+# GUARD_COMPATIBLE(reg, const-ptr) produces the following assembler.
+# It uses a special version of the failure recovery stub code written
+# by generate_quick_failure(), which saves a few things at different
+# locations and then jumps to the tree-searching algo, as described
+# later.  We also have the normal failure code at <failure_recovery>,
+# see below.  In the following code, ofs(x) means the offset in the GC
+# table of the constant pointer 'x':
+#
+#     MOV reg2, [RIP + ofs(_backend_choices)]
+#     CMP reg, [reg2 + bc_most_recent]
+#     JNE <failure_and_search_tree>
+#     JMP *[reg2 + bc_most_recent + 8]
+#    sequel:
+#
+# The faildescr for this guard is a GuardCompatibleDescr.  The
+# '_backend_choices' (which is added as a field to
+# GuardCompatibleDescr only when not translated) has the following
+# structure:
+#
+#     - bc_gcmap: a copy of the gcmap at this point
+#     - bc_faildescr: a copy of the faildescr of that guard
+#     - bc_most_recent: 1 pair (gcref, asmaddr)
+#     - bc_tree: N pairs (gcref, asmaddr)
+#
+# The tree contains all items for which find_compatible() was called and
+# returned non-zero.  It caches the non-zero result in 'asmaddr'.  The
+# separate most_recent entry caches the last value seen, along with
+# the result of find_compatible().  If this find_compatible() returned
+# zero, then the cache entry contains the 'fail_guard' label below
+# as the 'asmaddr' value (such a value is never found inside the tree,
+# only in the most_recent entry).
+#
+# The tree is a binary-search tree with a value at every node and leaf.
+# The length N of the array is equal to '2**D - 1', where D is the depth
+# of the tree.  There are '2**(D-1) - 1' nodes and '2**(D-1)' leaves.
+# Trees start at D=1 and grows by one every time they need to be
+# reallocated.  A tree of depth D has always all its nodes used, but
+# some leaves may be unused; such leaves store a pair (NULL, zero).
+# For now we assume that NULL is never received by guard_compatible().
+#
+# Tree organization: the root is at index 0.  Starting at a node at
+# index i, the left child is at i+1, and the right child is at i+w,
+# where 'w' is computed as follows: start from the length of the whole
+# array; divide it by two and round the (non-integer) result upwards
+# for every level you see; you get 'w'.  When you reach 'w=1', you are
+# at the level of leaves.
+#
+# The special value 'asmaddr=-1' is replaced with the actual address
+# of the 'sequel' label above inside the tree, so that we don't have
+# to special-case it here.  The special value 'asmaddr=0' in
+# 'most_recent' is replaced with the <failure_recovery> address
+# introduced above.
+#
+# Here is the modified <failure_and_search_tree> code:
+#
+#     PUSH RAX        # save
+#     PUSH RDX        # save
+#     MOV RAX, reg    # the value to search for
+#     MOV RDX, reg2   # _backend_choices object
+#     JMP search_tree
+#
+# Here is the x86-64 runtime code to walk the tree:
+#
+#   search_tree:
+#     MOV [RSP+16], RDX                    # save
+#     MOV R11, [RDX + bc_tree.length]
+#     LEA RDX, [RDX + bc_tree.items]
+#     JMP entry
+#   right:
+#     LEA RDX, [RDX + 8*R11 + 8]
+#   loop:
+#     SHR R11, 1
+#     JZ not_found
+#   entry:
+#     CMP RAX, [RDX]
+#     JA right
+#     JE found
+#     ADD RDX, 16
+#     JMP loop
+#
+#   found:
+#     MOV R11, [RDX + 8]
+#     MOV RDX, [RSP+16]
+#     MOV [RDX + bc_most_recent], RAX
+#     MOV [RDX + bc_most_recent + 8], R11
+#     POP RAX
+#     POP RDX
+#     JMP *R11
+#
+#   not_found:
+#     MOV RDX, [RSP+16]
+#     MOV R11, [RDX + bc_gcmap]
+#     MOV [RBP + jf_gcmap], R11
+#     <save all registers to the jitframe RBP,
+#         reading and popping RAX and RDX off the stack>
+#     <call invoke_find_compatible(_backend_choices=RDX, value=RAX)>
+#     <_reload_frame_if_necessary>
+#     MOV R11, RAX
+#     <restore the non-saved registers>
+#     JMP *R11
+#
+#
+# invoke_find_compatible(_backend_choices, value):
+#     try:
+#         descr = _backend_choices.bc_faildescr
+#         result = descr.find_compatible(cpu, value)
+#         if result == 0:
+#             result = <failure_recovery>
+#         else:
+#             if result == -1:
+#                 result = <sequel>
+#             _backend_choices = add_in_tree(_backend_choices, value, result)
+#         _backend_choices.bc_most_recent.gcref = value
+#         _backend_choices.bc_most_recent.asmaddr = result
+#         return result
+#     except:             # oops!
+#         return <failure_recovery>
+#
+# ____________________________________________________________
+
+
+
+
 # uses the raw structure COMPATINFO, which is informally defined like this:
 # it starts with a negative 'small_ofs' value (see in the code)
 # then there is an array containing all the expected values that should pass
diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -478,8 +478,6 @@
         y = self.loc(op.getarg(1))
         self.perform_guard(op, [x, y], None)
 
-    consider_guard_compatible = consider_guard_value
-
     def consider_guard_class(self, op):
         assert not isinstance(op.getarg(0), Const)
         x = self.rm.make_sure_var_in_reg(op.getarg(0))
@@ -488,6 +486,7 @@
 
     consider_guard_nonnull_class = consider_guard_class
     consider_guard_gc_type = consider_guard_class
+    consider_guard_compatible = consider_guard_class
 
     def consider_guard_is_object(self, op):
         x = self.make_sure_var_in_reg(op.getarg(0))


More information about the pypy-commit mailing list