[pypy-svn] r21267 - in pypy/dist/pypy/jit: . test

arigo at codespeak.net arigo at codespeak.net
Sun Dec 18 13:28:18 CET 2005


Author: arigo
Date: Sun Dec 18 13:28:16 2005
New Revision: 21267

Modified:
   pypy/dist/pypy/jit/llabstractinterp.py
   pypy/dist/pypy/jit/test/test_jit_tl.py
   pypy/dist/pypy/jit/test/test_llabstractinterp.py
Log:
Reintroduce something like the const_propagate policy.  It doesn't look too
bad, and the tests so far pass, but there is a flaw in here...


Modified: pypy/dist/pypy/jit/llabstractinterp.py
==============================================================================
--- pypy/dist/pypy/jit/llabstractinterp.py	(original)
+++ pypy/dist/pypy/jit/llabstractinterp.py	Sun Dec 18 13:28:16 2005
@@ -57,8 +57,18 @@
 
 
 class LLRuntimeValue(LLAbstractValue):
-    origin = None
-    fixed = False
+
+    origin = None     # list of LLRuntimeValues attached to a saved state:
+                      # the sources that did or could allow 'self' to be
+                      # computed as a constant
+
+    becomes = "var"   # only meaningful on LLRuntimeValues attached to a
+                      # saved state.  Describes what this LLRuntimeValue will
+                      # become in the next block:
+                      #   "var"    - becomes a Variable in the next block
+                      #   "single" - only one Constant seen so far, so
+                      #                for now it can stay a Constant
+                      #   "fixed"  - forced by hint() to stay a Constant
 
     def __init__(self, orig_v):
         if isinstance(orig_v, Variable):
@@ -67,6 +77,8 @@
         elif isinstance(orig_v, Constant):
             # we can share the Constant()
             self.copy_v = orig_v
+            self.origin = []
+            self.becomes = "single"
         elif isinstance(orig_v, lltype.LowLevelType):
             # hackish interface :-(  we accept a type too
             self.copy_v = newvar(orig_v)
@@ -83,10 +95,17 @@
         return self.copy_v
 
     def getruntimevars(self, memo):
-        if memo.get_fixed_flags:
-            return [self.fixed]
-        else:
+        if memo.key is None:
             return [self.copy_v]
+        else:
+            c = memo.key.next()
+            if self.becomes == "var":
+                return [None]
+            else:
+                assert isinstance(c, Constant), (
+                    "unexpected Variable %r reaching a %r input arg" %
+                    (c, self.becomes))
+                return [c]
 
     def maybe_get_constant(self):
         if isinstance(self.copy_v, Constant):
@@ -96,16 +115,16 @@
 
     def with_fresh_variables(self, memo):
         # don't use memo.seen here: shared variables must become distinct
-        if memo.key is not None:
-            c = memo.key.next()
-            if c is not None:
-                return LLRuntimeValue(c)
-        result = LLRuntimeValue(self.getconcretetype())
+        c = memo.key and memo.key.next()
+        if c is not None:    # allowed to propagate as a Constant?
+            result = LLRuntimeValue(c)
+        else:
+            result = LLRuntimeValue(self.getconcretetype())
         result.origin = [self]
         return result
 
     def match(self, other, memo):
-        memo.dependencies.append((self, other, self.fixed))
+        memo.dependencies.append((self, other))
         return isinstance(other, LLRuntimeValue)
 
 ll_no_return_value = LLRuntimeValue(const(None, lltype.Void))
@@ -417,13 +436,14 @@
 # ____________________________________________________________
 
 class Policy(object):
-    def __init__(self, inlining=False,
+    def __init__(self, inlining=False, const_propagate=False,
                        concrete_propagate=True, concrete_args=True):
         self.inlining = inlining
+        self.const_propagate = const_propagate
         self.concrete_propagate = concrete_propagate
         self.concrete_args = concrete_args
 
-best_policy = Policy(inlining=True, concrete_args=False)
+best_policy = Policy(inlining=True, const_propagate=True)
 
 
 class LLAbstractInterp(object):
@@ -484,15 +504,26 @@
             if state.match(inputstate, memo):
                 # already matched
                 must_restart = False
-                for statevar, inputvar, fixed in memo.dependencies:
-                    if fixed:
+                for statevar, inputvar in memo.dependencies:
+                    if statevar.becomes == "single":
+                        # the saved state only records one possible Constant
+                        # incoming value so far.  Are we seeing a different
+                        # Constant, or even a Variable?
+                        if inputvar.copy_v != statevar.copy_v:
+                            statevar.becomes = "var"
+                            must_restart = True
+                    elif statevar.becomes == "fixed":
+                        # the saved state says that this new incoming
+                        # variable must be forced to a constant
                         must_restart |= self.hint_needs_constant(inputvar)
                 if must_restart:
                     raise RestartCompleting
-                for statevar, inputvar, fixed in memo.dependencies:
-                    if statevar.origin is None:
-                        statevar.origin = []
-                    statevar.origin.append(inputvar)
+                # The new inputstate is merged into the existing saved state.
+                # Record this inputstate's variables in the possible origins
+                # of the saved state's variables.
+                for statevar, inputvar in memo.dependencies:
+                    if statevar.origin is not None:
+                        statevar.origin.append(inputvar)
                 return state
         else:
             # cache and return this new state
@@ -500,23 +531,28 @@
             return inputstate
 
     def hint_needs_constant(self, a):
-        if a.maybe_get_constant() is not None:
-            return False
+        # Force the given LLRuntimeValue to be a fixed constant.
+        must_restart = False
         fix_me = [a]
         while fix_me:
             a = fix_me.pop()
-            if not a.origin:
+            assert isinstance(a, LLRuntimeValue)
+            if a.becomes == "fixed":
+                continue    # already fixed
+            print 'fixing:', a
+            if a.becomes == "var":
+                must_restart = True    # this Var is now fixed
+                # (no need to restart if a.becomes was "single")
+            a.becomes = "fixed"
+            if a.origin:
+                fix_me.extend(a.origin)
+            elif a.maybe_get_constant() is None:
+                # a Variable with no recorded origin
                 raise Exception("hint() failed: cannot trace the variable %r "
                                 "back to a link where it was a constant" % (a,))
-            for a_origin in a.origin:
-                # 'a_origin' is a LLRuntimeValue attached to a saved state
-                assert isinstance(a_origin, LLRuntimeValue)
-                if not a_origin.fixed:
-                    print 'fixing:', a_origin
-                    a_origin.fixed = True
-                    if a_origin.maybe_get_constant() is None:
-                        fix_me.append(a_origin)
-        return True
+        assert self.policy.const_propagate, (
+            "hint() can only be used with a policy of const_propagate=True")
+        return must_restart
 
 
 class GraphState(object):
@@ -569,17 +605,10 @@
         while pending:
             next = pending.pop()
             state = interp.pendingstates[next]
-            fixed_flags = state.getruntimevars(VarMemo(get_fixed_flags=True))
-            key = []
-            for fixed, c in zip(fixed_flags, next.args):
-                if fixed:
-                    assert isinstance(c, Constant), (
-                        "unexpected Variable %r reaching a fixed input arg" %
-                        (c,))
-                    key.append(c)
-                else:
-                    key.append(None)
-            key = tuple(key)
+            if interp.policy.const_propagate:
+                key = tuple(state.getruntimevars(VarMemo(next.args)))
+            else:
+                key = None
             if key not in state.copyblocks:
                 self.flowin(state, key)
             block = state.copyblocks[key]
@@ -626,14 +655,15 @@
         newexitswitch = None
         # debugging print
         arglist = []
-        for v1, v2, k in zip(state.getruntimevars(VarMemo()),
-                             builder.runningstate.getruntimevars(VarMemo()),
-                             key):
-            if k is None:
-                assert isinstance(v2, Variable)
-            else:
-                assert v2 == k
-            arglist.append('%s => %s' % (v1, v2))
+        if key:
+            for v1, v2, k in zip(state.getruntimevars(VarMemo()),
+                                 builder.runningstate.getruntimevars(VarMemo()),
+                                 key):
+                if k is None:
+                    assert isinstance(v2, Variable)
+                else:
+                    assert v2 == k
+                arglist.append('%s => %s' % (v1, v2))
         print
         print '--> %s [%s]' % (origblock, ', '.join(arglist))
         for op in origblock.operations:
@@ -718,7 +748,7 @@
 
     def __init__(self, interp, initialstate, key):
         self.interp = interp
-        memo = VarMemo(iter(key))
+        memo = VarMemo(key)
         self.runningstate = initialstate.with_fresh_variables(memo)
         self.newinputargs = self.runningstate.getruntimevars(VarMemo())
         # {Variables-of-origblock: a_value}
@@ -767,7 +797,9 @@
         if any_concrete and self.interp.policy.concrete_propagate:
             return LLConcreteValue(concreteresult)
         else:
-            return LLRuntimeValue(const(concreteresult))
+            a_result = LLRuntimeValue(const(concreteresult))
+            self.record_origin(a_result, args_a)
+            return a_result
 
     def residual(self, opname, args_a, a_result):
         v_result = a_result.forcevarorconst(self)
@@ -796,7 +828,7 @@
     def record_origin(self, a_result, args_a):
         origin = []
         for a in args_a:
-            if a.maybe_get_constant() is not None:
+            if isinstance(a, LLConcreteValue):
                 continue
             if not isinstance(a, LLRuntimeValue) or a.origin is None:
                 return
@@ -1044,10 +1076,12 @@
         self.other_alias = {}
 
 class VarMemo(object):
-    def __init__(self, key=None, get_fixed_flags=False):
+    def __init__(self, key=None):
         self.seen = {}
-        self.key = key
-        self.get_fixed_flags = get_fixed_flags
+        if key is not None:
+            self.key = iter(key)
+        else:
+            self.key = None
 
 
 def live_variables(block, position):

Modified: pypy/dist/pypy/jit/test/test_jit_tl.py
==============================================================================
--- pypy/dist/pypy/jit/test/test_jit_tl.py	(original)
+++ pypy/dist/pypy/jit/test/test_jit_tl.py	Sun Dec 18 13:28:16 2005
@@ -32,7 +32,7 @@
 
     assert result1 == result2
 
-    interp.graphs[0].show()
+    #interp.graphs[0].show()
 
 
 def run_jit(code):

Modified: pypy/dist/pypy/jit/test/test_llabstractinterp.py
==============================================================================
--- pypy/dist/pypy/jit/test/test_llabstractinterp.py	(original)
+++ pypy/dist/pypy/jit/test/test_llabstractinterp.py	Sun Dec 18 13:28:16 2005
@@ -62,7 +62,8 @@
     return graph2, insns
 
 P_INLINE = Policy(inlining=True)
-P_HINT_DRIVEN = Policy(inlining=True, concrete_args=False)
+P_CONST_INLINE = Policy(inlining=True, const_propagate=True)
+P_HINT_DRIVEN = Policy(inlining=True, const_propagate=True, concrete_args=False)
 
 
 def test_simple():
@@ -315,13 +316,13 @@
     graph2, insns = abstrinterp(ll1, [3, 4, 5], [1, 2], policy=P_INLINE)
     assert insns == {'int_add': 1}
 
-##def test_const_propagate():
-##    def ll_add(x, y):
-##        return x + y
-##    def ll1(x):
-##        return ll_add(x, 42)
-##    graph2, insns = abstrinterp(ll1, [3], [0], policy=P_CONST_INLINE)
-##    assert insns == {}
+def test_const_propagate():
+    def ll_add(x, y):
+        return x + y
+    def ll1(x):
+        return ll_add(x, 42)
+    graph2, insns = abstrinterp(ll1, [3], [0], policy=P_CONST_INLINE)
+    assert insns == {}
 
 def test_dont_unroll_loop():
     def ll_factorial(n):
@@ -331,7 +332,7 @@
             i += 1
             result *= i
         return result
-    graph2, insns = abstrinterp(ll_factorial, [7], [], policy=P_INLINE)
+    graph2, insns = abstrinterp(ll_factorial, [7], [], policy=P_CONST_INLINE)
     assert insns == {'int_lt': 1, 'int_add': 1, 'int_mul': 1}
 
 def test_hint():
@@ -358,3 +359,32 @@
     graph2, insns = abstrinterp(ll_interp, [bytecode], [0],
                                 policy=P_HINT_DRIVEN)
     assert insns == {'int_add': 4, 'int_lt': 1}
+
+def test_hint_across_call():
+    from pypy.rpython.objectmodel import hint
+    A = lltype.GcArray(lltype.Char, hints={'immutable': True})
+    def ll_length(a):
+        return len(a)
+    def ll_getitem(a, i):
+        return a[i]
+    def ll_interp(code):
+        accum = 0
+        pc = 0
+        while pc < ll_length(code):
+            opcode = hint(ll_getitem(code, pc), concrete=True)
+            pc += 1
+            if opcode == 'A':
+                accum += 6
+            elif opcode == 'B':
+                if accum < 20:
+                    pc = 0
+        return accum
+    bytecode = lltype.malloc(A, 5)
+    bytecode[0] = 'A'
+    bytecode[1] = 'A'
+    bytecode[2] = 'A'
+    bytecode[3] = 'B'
+    bytecode[4] = 'A'
+    graph2, insns = abstrinterp(ll_interp, [bytecode], [0],
+                                policy=P_HINT_DRIVEN)
+    assert insns == {'int_add': 4, 'int_lt': 1}



More information about the Pypy-commit mailing list