[pypy-commit] pypy numpy-exp: (alex, arigato, michaelh) Replace hacky bytecode with less-hacky signature objects.

alex_gaynor noreply at buildbot.pypy.org
Mon May 16 22:42:07 CEST 2011


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: numpy-exp
Changeset: r44218:3944edac9c2d
Date: 2011-05-16 15:51 -0500
http://bitbucket.org/pypy/pypy/changeset/3944edac9c2d/

Log:	(alex, arigato, michaelh) Replace hacky bytecode with less-hacky
	signature objects.

diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -17,9 +17,28 @@
 
 TP = lltype.Array(lltype.Float, hints={'nolength': True})
 
-numpy_driver = jit.JitDriver(greens = ['bytecode'],
+numpy_driver = jit.JitDriver(greens = ['signature'],
                              reds = ['result_size', 'i', 'self', 'result'])
 
+class Signature(object):
+    def __init__(self):
+        self.transitions = {}
+
+    def transition(self, target):
+        if target in self.transitions:
+            return self.transitions[target]
+        self.transitions[target] = new = Signature()
+        return new
+
+def add(v1, v2):
+    return v1 + v2
+def sub(v1, v2):
+    return v1 - v2
+def mul(v1, v2):
+    return v1 * v2
+def div(v1, v2):
+    return v1 / v2
+
 class BaseArray(Wrappable):
     def __init__(self):
         self.invalidates = []
@@ -29,25 +48,34 @@
             arr.force_if_needed()
         self.invalidates = []
 
-    def _binop_impl(bytecode):
+    def _binop_impl(function):
+        signature = Signature()
         def impl(self, space, w_other):
+            new_sig = self.signature.transition(signature)
             if isinstance(w_other, BaseArray):
-                res = space.wrap(BinOp(bytecode, self, w_other))
+                res = space.wrap(Call2(
+                    function,
+                    self,
+                    w_other,
+                    new_sig.transition(w_other.signature)
+                ))
                 w_other.invalidates.append(res)
             else:
-                res = space.wrap(BinOp(
-                    bytecode,
+                w_other = FloatWrapper(space.float_w(w_other))
+                res = space.wrap(Call2(
+                    function,
                     self,
-                    FloatWrapper(space.float_w(w_other))
+                    w_other,
+                    new_sig.transition(w_other.signature)
                 ))
             self.invalidates.append(res)
             return res
-        return func_with_new_name(impl, "binop_%s_impl" % bytecode)
+        return func_with_new_name(impl, "binop_%s_impl" % function.__name__)
 
-    descr_add = _binop_impl("a")
-    descr_sub = _binop_impl("s")
-    descr_mul = _binop_impl("m")
-    descr_div = _binop_impl("d")
+    descr_add = _binop_impl(add)
+    descr_sub = _binop_impl(sub)
+    descr_mul = _binop_impl(mul)
+    descr_div = _binop_impl(div)
 
     def get_concrete(self):
         raise NotImplementedError
@@ -70,14 +98,12 @@
     Intermediate class representing a float literal.
     """
     _immutable_fields_ = ["float_value"]
+    signature = Signature()
 
     def __init__(self, float_value):
         BaseArray.__init__(self)
         self.float_value = float_value
 
-    def bytecode(self):
-        return "f"
-
     def find_size(self):
         raise ValueError
 
@@ -88,17 +114,18 @@
     """
     Class for representing virtual arrays, such as binary ops or ufuncs
     """
-    def __init__(self):
+    def __init__(self, signature):
         BaseArray.__init__(self)
         self.forced_result = None
+        self.signature = signature
 
     def compute(self):
         i = 0
-        bytecode = self.bytecode()
+        signature = self.signature
         result_size = self.find_size()
         result = SingleDimArray(result_size)
         while i < result_size:
-            numpy_driver.jit_merge_point(bytecode=bytecode,
+            numpy_driver.jit_merge_point(signature=signature,
                                          result_size=result_size, i=i,
                                          self=self, result=result)
             result.storage[i] = self.eval(i)
@@ -118,24 +145,31 @@
             return self.forced_result.eval(i)
         return self._eval(i)
 
+class Call1(VirtualArray):
+    _immutable_fields_ = ["function", "values"]
 
-class BinOp(VirtualArray):
+    def __init__(self, function, values, signature):
+        VirtualArray.__init__(self, signature)
+        self.function = function
+        self.values = values
+
+    def find_size(self):
+        return self.values.find_size()
+
+    def _eval(self, i):
+        return self.function(self.values.eval(i))
+
+class Call2(VirtualArray):
     """
     Intermediate class for performing binary operations.
     """
-    _immutable_fields_ = ["opcode", "left", "right"]
-    # Hack for test_zjit so the annotator doesn't see the bytecode as constant
-    opcode = "?"
-
-    def __init__(self, opcode, left, right):
-        VirtualArray.__init__(self)
-        self.opcode = opcode
+    _immutable_fields_ = ["function", "left", "right"]
+    def __init__(self, function, left, right, signature):
+        VirtualArray.__init__(self, signature)
+        self.function = function
         self.left = left
         self.right = right
 
-    def bytecode(self):
-        return self.opcode + self.left.bytecode() + self.right.bytecode()
-
     def find_size(self):
         try:
             return self.left.find_size()
@@ -145,36 +179,12 @@
 
     def _eval(self, i):
         lhs, rhs = self.left.eval(i), self.right.eval(i)
-        if self.opcode == "a":
-            return lhs + rhs
-        elif self.opcode == "s":
-            return lhs - rhs
-        elif self.opcode == "m":
-            return lhs * rhs
-        elif self.opcode == "d":
-            return lhs / rhs
-        else:
-            raise NotImplementedError("Don't know opcode %s" % self.opcode)
-
-class Call(VirtualArray):
-    _immutable_fields_ = ["function", "values"]
-
-    def __init__(self, function, values):
-        VirtualArray.__init__(self)
-        self.function = function
-        self.values = values
-
-    def bytecode(self):
-        return "c" + self.values.bytecode()
-
-    def find_size(self):
-        return self.values.find_size()
-
-    def _eval(self, i):
-        return self.function(self.values.eval(i))
+        return self.function(lhs, rhs)
 
 
 class SingleDimArray(BaseArray):
+    signature = Signature()
+
     def __init__(self, size):
         BaseArray.__init__(self)
         self.size = size
@@ -185,9 +195,6 @@
     def get_concrete(self):
         return self
 
-    def bytecode(self):
-        return "l"
-
     def find_size(self):
         return self.size
 
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -1,12 +1,13 @@
 from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.micronumpy.interp_numarray import BaseArray, Call
+from pypy.module.micronumpy.interp_numarray import BaseArray, Call1, Signature
 from pypy.tool.sourcetools import func_with_new_name
 
 
 def ufunc(func):
+    signature = Signature()
     @unwrap_spec(array=BaseArray)
     def impl(space, array):
-        w_res = Call(func, array)
+        w_res = Call1(func, array, array.signature.transition(signature))
         array.invalidates.append(w_res)
         return w_res
     return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -1,6 +1,6 @@
 from pypy.jit.metainterp.test.support import LLJitMixin
-from pypy.module.micronumpy.interp_numarray import (SingleDimArray, BinOp,
-    FloatWrapper, Call)
+from pypy.module.micronumpy.interp_numarray import (SingleDimArray, Signature,
+    FloatWrapper, Call1, Call2, add, mul)
 from pypy.module.micronumpy.interp_ufuncs import negative
 
 
@@ -16,10 +16,7 @@
 
         def f(i):
             ar = SingleDimArray(i)
-            if i:
-                v = BinOp('a', ar, ar)
-            else:
-                v = ar
+            v = Call2(add, ar, ar, Signature())
             return v.get_concrete().storage[3]
 
         result = self.meta_interp(f, [5], listops=True, backendopt=True)
@@ -33,10 +30,7 @@
 
         def f(i):
             ar = SingleDimArray(i)
-            if i:
-                v = BinOp('a', ar, FloatWrapper(4.5))
-            else:
-                v = ar
+            v = Call2(add, ar, FloatWrapper(4.5), Signature())
             return v.get_concrete().storage[3]
 
         result = self.meta_interp(f, [5], listops=True, backendopt=True)
@@ -50,8 +44,8 @@
 
         def f(i):
             ar = SingleDimArray(i)
-            v1 = BinOp('a', ar, FloatWrapper(4.5))
-            v2 = BinOp('m', v1, FloatWrapper(4.5))
+            v1 = Call2(add, ar, FloatWrapper(4.5), Signature())
+            v2 = Call2(mul, v1, FloatWrapper(4.5), Signature())
             v1.force_if_needed()
             return v2.get_concrete().storage[3]
 
@@ -68,7 +62,7 @@
         space = self.space
         def f(i):
             ar = SingleDimArray(i)
-            v1 = BinOp('a', ar, ar)
+            v1 = Call2(add, ar, ar, Signature())
             v2 = negative(space, v1)
             return v2.get_concrete().storage[3]
 
@@ -77,4 +71,24 @@
                           "setarrayitem_raw": 1, "int_add": 1,
                           "int_lt": 1, "guard_true": 1, "jump": 1,
         })
-        assert result == f(5)
\ No newline at end of file
+        assert result == f(5)
+
+    def test_appropriate_specialization(self):
+        space = self.space
+        def f(i):
+            add_sig = Signature()
+            mul_sig = Signature()
+            ar = SingleDimArray(i)
+
+            v1 = Call2(add, ar, ar, ar.signature.transition(add_sig))
+            v2 = negative(space, v1)
+            v2.get_concrete()
+
+            for i in xrange(5):
+                v1 = Call2(mul, ar, ar, ar.signature.transition(mul_sig))
+                v2 = negative(space, v1)
+                v2.get_concrete()
+
+        self.meta_interp(f, [5], listops=True, backendopt=True)
+        # This is 3, not 2 because there is a bridge for the exit.
+        self.check_loop_count(3)
\ No newline at end of file


More information about the pypy-commit mailing list