Commits

MostAwesomeDude committed af8ad67

micronumpy: Bring in hodgestar's float support.

Had to be fairly heftily rewritten. Also, enable the tests, fix up slightly,
and make things more readable.

I have a tendency to comment and doc things. Hope this isn't a Bad Thing.

  • Participants
  • Parent commits e583fdf
  • Branches numpy-exp

Comments (0)

Files changed (3)

File pypy/module/micronumpy/numarray.py

 from pypy.interpreter.baseobjspace import ObjSpace, W_Root, Wrappable
-from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import operationerrfmt
 from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.rpython.lltypesystem import lltype
 from pypy.rlib import jit
 
                              virtualizables = ['frame'])
 
 class ComputationFrame(object):
-    _virtualizable2_ = ['valuestackdepth', 'valuestack[*]', 'local_pos',
-                        'locals[*]']
+    _virtualizable2_ = ['valuestackdepth', 'valuestack[*]',
+                        'array_pos', 'arrays[*]',
+                        'float_pos', 'floats[*]',
+                        ]
 
-    def __init__(self, input):
+    def __init__(self, arrays, floats):
         self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
         self.valuestackdepth = 0
-        self.valuestack = [0.0] * len(input)
-        self.locals = input[:]
-        self.local_pos = len(input)
+        self.arrays = arrays
+        self.array_pos = len(arrays)
+        self.floats = floats
+        self.float_pos = len(floats)
+        self.valuestack = [0.0] * (len(arrays) + len(floats))
 
-    def getlocal(self):
-        p = self.local_pos - 1
+    def reset(self):
+        self.valuestackdepth = 0
+        self.array_pos = len(self.arrays)
+        self.float_pos = len(self.floats)
+
+    def getarray(self):
+        p = self.array_pos - 1
         assert p >= 0
-        res = self.locals[p]
-        self.local_pos = p
+        res = self.arrays[p]
+        self.array_pos = p
+        return res
+
+    def getfloat(self):
+        p = self.float_pos - 1
+        assert p >= 0
+        res = self.floats[p]
+        self.float_pos = p
         return res
 
     def popvalue(self):
         self.valuestack[self.valuestackdepth] = v
         self.valuestackdepth += 1
 
-def compute(bytecode, input):
-    result_size = input[0].size
+class Code(object):
+    """
+    A chunk of bytecode.
+    """
+
+    def __init__(self, bytecode, arrays, floats):
+        self.bytecode = bytecode
+        self.arrays = arrays
+        self.floats = floats
+
+    def merge(self, code, other):
+        """
+        Merge this bytecode with the other bytecode, using ``code`` as the
+        bytecode instruction for performing the merge.
+        """
+
+        return Code(code + self.bytecode + other.bytecode,
+            self.arrays + other.arrays,
+            self.floats + other.floats)
+
+def compute(code):
+    """
+    Crunch a ``Code`` full of bytecode.
+    """
+
+    bytecode = code.bytecode
+    result_size = code.arrays[0].size
     result = SingleDimArray(result_size)
     bytecode_pos = len(bytecode) - 1
     i = 0
-    frame = ComputationFrame(input)
+    frame = ComputationFrame(code.arrays, code.floats)
     while i < result_size:
         numpy_driver.jit_merge_point(bytecode=bytecode, result=result,
                                      result_size=result_size,
                                      bytecode_pos=bytecode_pos)
         if bytecode_pos == -1:
             bytecode_pos = len(bytecode) - 1
-            frame.local_pos = len(frame.locals)
+            frame.reset()
             result.storage[i] = frame.valuestack[0]
-            frame.valuestackdepth = 0
             i += 1
             numpy_driver.can_enter_jit(bytecode=bytecode, result=result,
                                        result_size=result_size,
         else:
             opcode = bytecode[bytecode_pos]
             if opcode == 'l':
-                val = frame.getlocal().storage[i]
-                frame.valuestack[frame.valuestackdepth] = val
-                frame.valuestackdepth += 1
+                # Load array.
+                val = frame.getarray().storage[i]
+                frame.pushvalue(val)
+            elif opcode == 'f':
+                # Load float.
+                val = frame.getfloat()
+                frame.pushvalue(val)
             elif opcode == 'a':
+                # Add.
                 b = frame.popvalue()
                 a = frame.popvalue()
                 frame.pushvalue(a + b)
             else:
-                raise NotImplementedError
+                raise NotImplementedError(
+                    "Can't handle bytecode instruction %s" % opcode)
             bytecode_pos -= 1
     return result
 
 
 class BaseArray(Wrappable):
     def force(self):
-        bytecode, stack = self.compile()
+        code = self.compile()
         try:
-            bytecode = JITCODES[bytecode]
+            code.bytecode = JITCODES[code.bytecode]
         except KeyError:
-            JITCODES[bytecode] = bytecode
+            JITCODES[code.bytecode] = code.bytecode
         # the point of above hacks is to intern the bytecode string
         # otherwise we have to compile new assembler each time, which sucks
         # (we still have to compile new bytecode, but too bad)
-        return compute(bytecode, stack)
+        return compute(code)
 
     def descr_add(self, space, w_other):
-        return space.wrap(Add(self, w_other))
+        if isinstance(w_other, BaseArray):
+            return space.wrap(Add(self, w_other))
+        else:
+            return space.wrap(FloatAdd(self, space.float_w(w_other)))
 
     def compile(self):
         raise NotImplementedError("abstract base class")
 
 class Add(BaseArray):
+    """
+    Intermediate class for adding arrays.
+    """
+
     def __init__(self, left, right):
-        assert isinstance(left, BaseArray)
-        assert isinstance(right, BaseArray)
         self.left = left
         self.right = right
 
     def compile(self):
-        left_bc, left_stack = self.left.compile()
-        right_bc, right_stack = self.right.compile()
-        return 'a' + left_bc + right_bc, left_stack + right_stack
+        left_code = self.left.compile()
+        right_code = self.right.compile()
+        return left_code.merge('a', right_code)
+
+class FloatAdd(BaseArray):
+    """
+    Intermediate class for adding an array and a float.
+    """
+
+    def __init__(self, left, right):
+        self.left = left
+        self.right = right
+
+    def compile(self):
+        left_code = self.left.compile()
+        right_code = Code('f', [], [self.right])
+        return left_code.merge('a', right_code)
 
 BaseArray.typedef = TypeDef(
     'Operation',
-    force=interp2app(BaseArray.force),
+    force = interp2app(BaseArray.force),
     __add__ = interp2app(BaseArray.descr_add),
 )
 
         # XXX find out why test_jit explodes with trackign of allocations
 
     def compile(self):
-        return "l", [self]
+        return Code('l', [self], [])
 
     @unwrap_spec(item=int)
     def descr_getitem(self, space, item):
     __add__ = interp2app(BaseArray.descr_add),
     force = interp2app(SingleDimArray.force),
 )
-

File pypy/module/micronumpy/test/test_jit.py

-
-from pypy.module.micronumpy.numarray import SingleDimArray, Add
-from pypy.conftest import gettestobjspace
+from pypy.module.micronumpy.numarray import SingleDimArray, Add, FloatAdd
 from pypy.jit.metainterp.test.test_basic import LLJitMixin
 
 class FakeSpace(object):
                 v = ar
             return v.force().storage[3]
 
-        self.meta_interp(f, [5], listops=True, backendopt=True)
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
         self.check_loops({'getarrayitem_raw': 2, 'float_add': 1,
                           'setarrayitem_raw': 1, 'int_add': 1,
                           'int_lt': 1, 'guard_true': 1, 'jump': 1})
-        
+        assert result == f(5)
+
+    def test_floatadd(self):
+        space = self.space
+
+        def f(i):
+            ar = SingleDimArray(i)
+            if i:
+                v = FloatAdd(ar, 4.5)
+            else:
+                v = ar
+            return v.force().storage[3]
+
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
+        self.check_loops({"getarrayitem_gc": 2, "float_add": 2,
+                          "setarrayitem_gc": 1, "int_add": 1,
+                          "int_lt": 1, "guard_true": 1, "jump": 1})
+        assert result == f(5)

File pypy/module/micronumpy/test/test_numpy.py

         b = b.force()
         assert b[2] == 2 + 2
 
+    def test_floatadd(self):
+        from numpy import array
+        a = array(range(5))
+        b = a + 5
+        b = b.force()
+        for i in range(5):
+            assert b[i] == i + 5
+
+
 class AppTestNumpy(object):
     def setup_class(cls):
-        py.test.skip("skip")
         cls.space = gettestobjspace(usemodules=('micronumpy',))
     
     def test_zeroes(self):
 
 class AppTestMultiDim(object):
     def setup_class(cls):
-        py.test.skip("skip")
         cls.space = gettestobjspace(usemodules=('micronumpy',))
 
     def test_multidim(self):