[pypy-commit] pypy default: (fijal, agaynor, tons of other people) merge numpy-exp.

fijal noreply at buildbot.pypy.org
Tue May 17 13:28:04 CEST 2011


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: 
Changeset: r44237:335d1f0d4d8f
Date: 2011-05-17 13:37 +0200
http://bitbucket.org/pypy/pypy/changeset/335d1f0d4d8f/

Log:	(fijal, agaynor, tons of other people) merge numpy-exp.

	This brings a start to numpy module being developed on trunk (and
	enabled by default) with JIT support.

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -33,7 +33,7 @@
      "struct", "_hashlib", "_md5", "_sha", "_minimal_curses", "cStringIO",
      "thread", "itertools", "pyexpat", "_ssl", "cpyext", "array",
      "_bisect", "binascii", "_multiprocessing", '_warnings',
-     "_collections", "_multibytecodec"]
+     "_collections", "_multibytecodec", 'micronumpy']
 ))
 
 translation_modules = default_modules.copy()
diff --git a/pypy/conftest.py b/pypy/conftest.py
--- a/pypy/conftest.py
+++ b/pypy/conftest.py
@@ -46,6 +46,10 @@
     group.addoption('-P', '--platform', action="callback", type="string",
            default="host", callback=_set_platform,
            help="set up tests to use specified platform as compile/run target")
+    group = parser.getgroup("JIT options")
+    group.addoption('--viewloops', action="store_true",
+           default=False, dest="viewloops",
+           help="show only the compiled loops")
 
 def pytest_sessionstart():
     # have python subprocesses avoid startup customizations by default
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -209,7 +209,7 @@
                         llimpl.compile_add_fail_arg(c, var2index[box])
                     else:
                         llimpl.compile_add_fail_arg(c, -1)
-                        
+
             x = op.result
             if x is not None:
                 if isinstance(x, history.BoxInt):
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -330,7 +330,7 @@
         if not we_are_translated():
             self.assembler.dump('%s <- %s(%s)' % (result_loc, op, arglocs))
         self.assembler.regalloc_perform_llong(op, arglocs, result_loc)
-        
+
     def PerformMath(self, op, arglocs, result_loc):
         if not we_are_translated():
             self.assembler.dump('%s <- %s(%s)' % (result_loc, op, arglocs))
@@ -676,7 +676,7 @@
         loc0 = self.xrm.force_result_in_reg(op.result, op.getarg(0))
         self.Perform(op, [loc0], loc0)
         self.xrm.possibly_free_var(op.getarg(0))
-        
+
     consider_float_neg = _consider_float_unary_op
     consider_float_abs = _consider_float_unary_op
 
@@ -764,7 +764,7 @@
         loc1 = self.rm.make_sure_var_in_reg(op.getarg(1))
         self.PerformLLong(op, [loc1], loc0)
         self.rm.possibly_free_vars_for_op(op)
-        
+
     def _consider_math_sqrt(self, op):
         loc0 = self.xrm.force_result_in_reg(op.result, op.getarg(1))
         self.PerformMath(op, [loc0], loc0)
@@ -1271,12 +1271,12 @@
         xmmtmploc = self.xrm.force_allocate_reg(box1, selected_reg=xmmtmp)
         # Part about non-floats
         # XXX we don't need a copy, we only just the original list
-        src_locations1 = [self.loc(op.getarg(i)) for i in range(op.numargs()) 
+        src_locations1 = [self.loc(op.getarg(i)) for i in range(op.numargs())
                          if op.getarg(i).type != FLOAT]
         assert tmploc not in nonfloatlocs
         dst_locations1 = [loc for loc in nonfloatlocs if loc is not None]
         # Part about floats
-        src_locations2 = [self.loc(op.getarg(i)) for i in range(op.numargs()) 
+        src_locations2 = [self.loc(op.getarg(i)) for i in range(op.numargs())
                          if op.getarg(i).type == FLOAT]
         dst_locations2 = [loc for loc in floatlocs if loc is not None]
         remap_frame_layout_mixed(assembler,
diff --git a/pypy/jit/backend/x86/regloc.py b/pypy/jit/backend/x86/regloc.py
--- a/pypy/jit/backend/x86/regloc.py
+++ b/pypy/jit/backend/x86/regloc.py
@@ -508,7 +508,9 @@
     LEA = _binaryop('LEA')
 
     MOVSD = _binaryop('MOVSD')
+    MOVAPD = _binaryop('MOVAPD')
     ADDSD = _binaryop('ADDSD')
+    ADDPD = _binaryop('ADDPD')
     SUBSD = _binaryop('SUBSD')
     MULSD = _binaryop('MULSD')
     DIVSD = _binaryop('DIVSD')
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -690,12 +690,17 @@
 
 define_modrm_modes('MOVSD_x*', ['\xF2', rex_nw, '\x0F\x10', register(1,8)], regtype='XMM')
 define_modrm_modes('MOVSD_*x', ['\xF2', rex_nw, '\x0F\x11', register(2,8)], regtype='XMM')
+define_modrm_modes('MOVAPD_x*', ['\x66', rex_nw, '\x0F\x28', register(1,8)],
+                   regtype='XMM')
+define_modrm_modes('MOVAPD_*x', ['\x66', rex_nw, '\x0F\x29', register(2,8)],
+                   regtype='XMM')
 
 define_modrm_modes('SQRTSD_x*', ['\xF2', rex_nw, '\x0F\x51', register(1,8)], regtype='XMM')
 
 #define_modrm_modes('XCHG_r*', [rex_w, '\x87', register(1, 8)])
 
 define_modrm_modes('ADDSD_x*', ['\xF2', rex_nw, '\x0F\x58', register(1, 8)], regtype='XMM')
+define_modrm_modes('ADDPD_x*', ['\x66', rex_nw, '\x0F\x58', register(1, 8)], regtype='XMM')
 define_modrm_modes('SUBSD_x*', ['\xF2', rex_nw, '\x0F\x5C', register(1, 8)], regtype='XMM')
 define_modrm_modes('MULSD_x*', ['\xF2', rex_nw, '\x0F\x59', register(1, 8)], regtype='XMM')
 define_modrm_modes('DIVSD_x*', ['\xF2', rex_nw, '\x0F\x5E', register(1, 8)], regtype='XMM')
diff --git a/pypy/jit/conftest.py b/pypy/jit/conftest.py
--- a/pypy/jit/conftest.py
+++ b/pypy/jit/conftest.py
@@ -5,7 +5,4 @@
     group.addoption('--slow', action="store_true",
            default=False, dest="run_slow_tests",
            help="run all the compiled tests (instead of just a few)")
-    group.addoption('--viewloops', action="store_true",
-           default=False, dest="viewloops",
-           help="show only the compiled loops")
 
diff --git a/pypy/jit/metainterp/test/test_ztranslation.py b/pypy/jit/metainterp/test/test_ztranslation.py
--- a/pypy/jit/metainterp/test/test_ztranslation.py
+++ b/pypy/jit/metainterp/test/test_ztranslation.py
@@ -2,7 +2,7 @@
 from pypy.jit.metainterp.warmspot import rpython_ll_meta_interp, ll_meta_interp
 from pypy.jit.backend.llgraph import runner
 from pypy.rlib.jit import JitDriver, unroll_parameters
-from pypy.rlib.jit import PARAMETERS, dont_look_inside
+from pypy.rlib.jit import PARAMETERS, dont_look_inside, hint
 from pypy.jit.metainterp.jitprof import Profiler
 from pypy.rpython.lltypesystem import lltype, llmemory
 
@@ -24,10 +24,21 @@
         # - string concatenation, slicing and comparison
 
         class Frame(object):
-            _virtualizable2_ = ['i']
+            _virtualizable2_ = ['l[*]']
 
             def __init__(self, i):
+                self = hint(self, fresh_virtualizable=True,
+                            access_directly=True)
+                self.l = [i]
+
+        class OtherFrame(object):
+            _virtualizable2_ = ['i', 'l[*]']
+
+            def __init__(self, i):
+                self = hint(self, fresh_virtualizable=True,
+                            access_directly=True)
                 self.i = i
+                self.l = [float(i)]
 
         class OtherFrame(object):
             _virtualizable2_ = ['i']
@@ -57,13 +68,13 @@
             jitdriver.set_param("trace_eagerness", 2)
             total = 0
             frame = Frame(i)
-            while frame.i > 3:
+            while frame.l[0] > 3:
                 jitdriver.can_enter_jit(frame=frame, total=total)
                 jitdriver.jit_merge_point(frame=frame, total=total)
-                total += frame.i
-                if frame.i >= 20:
-                    frame.i -= 2
-                frame.i -= 1
+                total += frame.l[0]
+                if frame.l[0] >= 20:
+                    frame.l[0] -= 2
+                frame.l[0] -= 1
             return total * 10
         #
         myjitdriver2 = JitDriver(greens = ['g'], reds = ['m', 's', 'f'],
@@ -71,25 +82,30 @@
         def f2(g, m, x):
             s = ""
             f = OtherFrame(x)
+            float_s = 0.0
             while m > 0:
-                myjitdriver2.can_enter_jit(g=g, m=m, f=f, s=s)
-                myjitdriver2.jit_merge_point(g=g, m=m, f=f, s=s)
+                myjitdriver2.can_enter_jit(g=g, m=m, f=f, s=s, float_s=float_s)
+                myjitdriver2.jit_merge_point(g=g, m=m, f=f, s=s,
+                                             float_s=float_s)
                 s += 'xy'
                 if s[:2] == 'yz':
                     return -666
                 m -= 1
                 f.i += 3
+                float_s += f.l[0]
             return f.i
         #
         def main(i, j):
             return f(i) - f2(i+j, i, j)
         res = ll_meta_interp(main, [40, 5], CPUClass=self.CPUClass,
-                             type_system=self.type_system)
+                             type_system=self.type_system,
+                             listops=True)
         assert res == main(40, 5)
         res = rpython_ll_meta_interp(main, [40, 5],
                                      CPUClass=self.CPUClass,
                                      type_system=self.type_system,
-                                     ProfilerClass=Profiler)
+                                     ProfilerClass=Profiler,
+                                     listops=True)
         assert res == main(40, 5)
 
     def test_external_exception_handling_translates(self):
diff --git a/pypy/jit/metainterp/typesystem.py b/pypy/jit/metainterp/typesystem.py
--- a/pypy/jit/metainterp/typesystem.py
+++ b/pypy/jit/metainterp/typesystem.py
@@ -5,7 +5,7 @@
 from pypy.rpython.annlowlevel import cast_instance_to_base_obj
 from pypy.jit.metainterp import history
 from pypy.jit.codewriter import heaptracker
-from pypy.rlib.objectmodel import r_dict
+from pypy.rlib.objectmodel import r_dict, specialize
 
 def deref(T):
     if isinstance(T, lltype.Ptr):
@@ -97,12 +97,15 @@
     def cast_to_baseclass(self, value):
         return lltype.cast_opaque_ptr(lltype.Ptr(rclass.OBJECT), value)
 
+    @specialize.ll()
     def getlength(self, array):
         return len(array)
 
+    @specialize.ll()
     def getarrayitem(self, array, i):
         return array[i]
 
+    @specialize.ll()
     def setarrayitem(self, array, i, newvalue):
         array[i] = newvalue
 
@@ -201,12 +204,15 @@
     def cast_to_baseclass(self, value):
         return ootype.cast_from_object(ootype.ROOT, value)
 
+    @specialize.ll()
     def getlength(self, array):
         return array.ll_length()
 
+    @specialize.ll()
     def getarrayitem(self, array, i):
         return array.ll_getitem_fast(i)
 
+    @specialize.ll()
     def setarrayitem(self, array, i, newvalue):
         array.ll_setitem_fast(i, newvalue)
 
diff --git a/pypy/jit/tl/pypyjit.py b/pypy/jit/tl/pypyjit.py
--- a/pypy/jit/tl/pypyjit.py
+++ b/pypy/jit/tl/pypyjit.py
@@ -42,6 +42,7 @@
 config.objspace.usemodules._lsprof = True
 #
 config.objspace.usemodules._ffi = True
+config.objspace.usemodules.micronumpy = True
 #
 set_pypy_opt_level(config, level='jit')
 
diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py
--- a/pypy/jit/tl/pypyjit_demo.py
+++ b/pypy/jit/tl/pypyjit_demo.py
@@ -1,11 +1,10 @@
 
 try:
-    def f(x):
-        i = 0
-        while i < x:
-            range(i)
-            i += 1
-    f(10000)
+    import numpy
+    a = numpy.array(range(10))
+    b = a + a + a
+    print b[3]
+
 except Exception, e:
     print "Exception: ", type(e)
     print e
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -1,13 +1,23 @@
 
-from pypy.interpreter.mixedmodule import MixedModule 
+from pypy.interpreter.mixedmodule import MixedModule
 
 class Module(MixedModule):
 
     applevel_name = 'numpy'
-    
+
     interpleveldefs = {
-        'zeros'    : 'numarray.zeros',
-        'minimum'  : 'ufunc.minimum',
-        }
+        'array': 'interp_numarray.SingleDimArray',
+        'zeros': 'interp_numarray.zeros',
+
+        # ufuncs
+        'absolute': 'interp_ufuncs.absolute',
+        'copysign': 'interp_ufuncs.copysign',
+        'exp': 'interp_ufuncs.exp',
+        'maximum': 'interp_ufuncs.maximum',
+        'minimum': 'interp_ufuncs.minimum',
+        'negative': 'interp_ufuncs.negative',
+        'reciprocal': 'interp_ufuncs.reciprocal',
+        'sign': 'interp_ufuncs.sign',
+    }
 
     appleveldefs = {}
diff --git a/pypy/module/micronumpy/bench/add.py b/pypy/module/micronumpy/bench/add.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/bench/add.py
@@ -0,0 +1,10 @@
+
+import numpy
+
+def f():
+    a = numpy.zeros(10000000)
+    a = a + a + a + a + a
+    # To ensure that the computation isn't totally optimized away.
+    a[0] = 3.0
+
+f()
diff --git a/pypy/module/micronumpy/bench/iterate.py b/pypy/module/micronumpy/bench/iterate.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/bench/iterate.py
@@ -0,0 +1,11 @@
+
+import numpy
+
+def f():
+    sum = 0
+    a = numpy.zeros(10000000)
+    for i in range(10000000):
+        sum += a[i]
+    return sum
+
+f()
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -0,0 +1,257 @@
+from pypy.interpreter.baseobjspace import ObjSpace, W_Root, Wrappable
+from pypy.interpreter.error import operationerrfmt
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
+from pypy.rlib import jit
+from pypy.rpython.lltypesystem import lltype
+from pypy.tool.sourcetools import func_with_new_name
+
+
+def dummy1(v):
+    assert isinstance(v, float)
+    return v
+
+def dummy2(v):
+    assert isinstance(v, float)
+    return v
+
+TP = lltype.Array(lltype.Float, hints={'nolength': True})
+
+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 = []
+
+    def invalidated(self):
+        for arr in self.invalidates:
+            arr.force_if_needed()
+        self.invalidates = []
+
+    def _binop_impl(function):
+        signature = Signature()
+        def impl(self, space, w_other):
+            new_sig = self.signature.transition(signature)
+            if isinstance(w_other, BaseArray):
+                res = Call2(
+                    function,
+                    self,
+                    w_other,
+                    new_sig.transition(w_other.signature)
+                )
+                w_other.invalidates.append(res)
+            else:
+                w_other = FloatWrapper(space.float_w(w_other))
+                res = Call2(
+                    function,
+                    self,
+                    w_other,
+                    new_sig.transition(w_other.signature)
+                )
+            self.invalidates.append(res)
+            return space.wrap(res)
+        return func_with_new_name(impl, "binop_%s_impl" % function.__name__)
+
+    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
+
+    def descr_len(self, space):
+        return self.get_concrete().descr_len(space)
+
+    @unwrap_spec(item=int)
+    def descr_getitem(self, space, item):
+        return self.get_concrete().descr_getitem(space, item)
+
+    @unwrap_spec(item=int, value=float)
+    def descr_setitem(self, space, item, value):
+        self.invalidated()
+        return self.get_concrete().descr_setitem(space, item, value)
+
+
+class FloatWrapper(BaseArray):
+    """
+    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 find_size(self):
+        raise ValueError
+
+    def eval(self, i):
+        return self.float_value
+
+class VirtualArray(BaseArray):
+    """
+    Class for representing virtual arrays, such as binary ops or ufuncs
+    """
+    def __init__(self, signature):
+        BaseArray.__init__(self)
+        self.forced_result = None
+        self.signature = signature
+
+    def compute(self):
+        i = 0
+        signature = self.signature
+        result_size = self.find_size()
+        result = SingleDimArray(result_size)
+        while i < result_size:
+            numpy_driver.jit_merge_point(signature=signature,
+                                         result_size=result_size, i=i,
+                                         self=self, result=result)
+            result.storage[i] = self.eval(i)
+            i += 1
+        return result
+
+    def force_if_needed(self):
+        if self.forced_result is None:
+            self.forced_result = self.compute()
+
+    def get_concrete(self):
+        self.force_if_needed()
+        return self.forced_result
+
+    def eval(self, i):
+        if self.forced_result is not None:
+            return self.forced_result.eval(i)
+        return self._eval(i)
+
+class Call1(VirtualArray):
+    _immutable_fields_ = ["function", "values"]
+
+    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_ = ["function", "left", "right"]
+    def __init__(self, function, left, right, signature):
+        VirtualArray.__init__(self, signature)
+        self.function = function
+        self.left = left
+        self.right = right
+
+    def find_size(self):
+        try:
+            return self.left.find_size()
+        except ValueError:
+            pass
+        return self.right.find_size()
+
+    def _eval(self, i):
+        lhs, rhs = self.left.eval(i), self.right.eval(i)
+        return self.function(lhs, rhs)
+
+
+class SingleDimArray(BaseArray):
+    signature = Signature()
+
+    def __init__(self, size):
+        BaseArray.__init__(self)
+        self.size = size
+        self.storage = lltype.malloc(TP, size, zero=True,
+                                     flavor='raw', track_allocation=False)
+        # XXX find out why test_zjit explodes with trackign of allocations
+
+    def get_concrete(self):
+        return self
+
+    def find_size(self):
+        return self.size
+
+    def eval(self, i):
+        return self.storage[i]
+
+    def getindex(self, space, item):
+        if item >= self.size:
+            raise operationerrfmt(space.w_IndexError,
+              '%d above array size', item)
+        if item < 0:
+            item += self.size
+        if item < 0:
+            raise operationerrfmt(space.w_IndexError,
+              '%d below zero', item)
+        return item
+
+    def descr_len(self, space):
+        return space.wrap(self.size)
+
+    @unwrap_spec(item=int)
+    def descr_getitem(self, space, item):
+        item = self.getindex(space, item)
+        return space.wrap(self.storage[item])
+
+    @unwrap_spec(item=int, value=float)
+    def descr_setitem(self, space, item, value):
+        item = self.getindex(space, item)
+        self.invalidated()
+        self.storage[item] = value
+
+    def __del__(self):
+        lltype.free(self.storage, flavor='raw')
+
+def descr_new_numarray(space, w_type, w_size_or_iterable):
+    l = space.listview(w_size_or_iterable)
+    arr = SingleDimArray(len(l))
+    i = 0
+    for w_elem in l:
+        arr.storage[i] = space.float_w(space.float(w_elem))
+        i += 1
+    return space.wrap(arr)
+
+ at unwrap_spec(ObjSpace, int)
+def zeros(space, size):
+    return space.wrap(SingleDimArray(size))
+
+
+BaseArray.typedef = TypeDef(
+    'numarray',
+    __new__ = interp2app(descr_new_numarray),
+    __len__ = interp2app(BaseArray.descr_len),
+    __getitem__ = interp2app(BaseArray.descr_getitem),
+    __setitem__ = interp2app(BaseArray.descr_setitem),
+
+    __add__ = interp2app(BaseArray.descr_add),
+    __sub__ = interp2app(BaseArray.descr_sub),
+    __mul__ = interp2app(BaseArray.descr_mul),
+    __div__ = interp2app(BaseArray.descr_div),
+)
\ No newline at end of file
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -0,0 +1,63 @@
+import math
+
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.module.micronumpy.interp_numarray import BaseArray, Call1, Call2, Signature
+from pypy.rlib import rfloat
+from pypy.tool.sourcetools import func_with_new_name
+
+
+def ufunc(func):
+    signature = Signature()
+    @unwrap_spec(array=BaseArray)
+    def impl(space, 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__)
+
+def ufunc2(func):
+    signature = Signature()
+    @unwrap_spec(larray=BaseArray, rarray=BaseArray)
+    def impl(space, larray, rarray):
+        new_sig = larray.signature.transition(signature).transition(rarray.signature)
+        w_res = Call2(func, larray, rarray, new_sig)
+        larray.invalidates.append(w_res)
+        rarray.invalidates.append(w_res)
+        return w_res
+    return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
+
+ at ufunc
+def absolute(value):
+    return abs(value)
+
+ at ufunc2
+def copysign(lvalue, rvalue):
+    return rfloat.copysign(lvalue, rvalue)
+
+ at ufunc
+def exp(value):
+    return math.exp(value)
+
+ at ufunc2
+def maximum(lvalue, rvalue):
+    return max(lvalue, rvalue)
+
+ at ufunc2
+def minimum(lvalue, rvalue):
+    return min(lvalue, rvalue)
+
+ at ufunc
+def negative(value):
+    return -value
+
+ at ufunc
+def reciprocal(value):
+    if value == 0.0:
+        return rfloat.copysign(rfloat.INFINITY, value)
+    return 1.0 / value
+
+ at ufunc
+def sign(value):
+    if value == 0.0:
+        return 0.0
+    return rfloat.copysign(1.0, value)
\ No newline at end of file
diff --git a/pypy/module/micronumpy/numarray.py b/pypy/module/micronumpy/numarray.py
deleted file mode 100644
--- a/pypy/module/micronumpy/numarray.py
+++ /dev/null
@@ -1,123 +0,0 @@
-
-from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.error import OperationError
-from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
-from pypy.rlib.debug import make_sure_not_resized
-
-class BaseNumArray(Wrappable):
-    pass
-
-class NumArray(BaseNumArray):
-    def __init__(self, space, dim, dtype):
-        self.dim = dim
-        self.space = space
-        # ignore dtype for now
-        self.storage = [0] * dim
-        make_sure_not_resized(self.storage)
-
-    @unwrap_spec(index=int)
-    def descr_getitem(self, index):
-        space = self.space
-        try:
-            return space.wrap(self.storage[index])
-        except IndexError:
-            raise OperationError(space.w_IndexError,
-                                 space.wrap("list index out of range"))
-
-    @unwrap_spec(index=int, value=int)
-    def descr_setitem(self, index, value):
-        space = self.space
-        try:
-            self.storage[index] = value
-        except IndexError:
-            raise OperationError(space.w_IndexError,
-                                 space.wrap("list index out of range"))
-        return space.w_None
-
-    def descr_len(self):
-        return self.space.wrap(len(self.storage))
-
-NumArray.typedef = TypeDef(
-    'NumArray',
-    __getitem__ = interp2app(NumArray.descr_getitem),
-    __setitem__ = interp2app(NumArray.descr_setitem),
-    __len__     = interp2app(NumArray.descr_len),
-)
-
-def compute_pos(space, indexes, dim):
-    current = 1
-    pos = 0
-    for i in range(len(indexes)):
-        index = indexes[i]
-        d = dim[i]
-        if index >= d or index <= -d - 1:
-            raise OperationError(space.w_IndexError,
-                                 space.wrap("invalid index"))
-        if index < 0:
-            index = d + index
-        pos += index * current
-        current *= d
-    return pos
-
-class MultiDimArray(BaseNumArray):
-    def __init__(self, space, dim, dtype):
-        self.dim = dim
-        self.space = space
-        # ignore dtype for now
-        size = 1
-        for el in dim:
-            size *= el
-        self.storage = [0] * size
-        make_sure_not_resized(self.storage)
-
-    def _unpack_indexes(self, space, w_index):
-        indexes = [space.int_w(w_i) for w_i in space.fixedview(w_index)]
-        if len(indexes) != len(self.dim):
-            raise OperationError(space.w_IndexError, space.wrap(
-                'Wrong index'))
-        return indexes
-
-    def descr_getitem(self, w_index):
-        space = self.space
-        indexes = self._unpack_indexes(space, w_index)
-        pos = compute_pos(space, indexes, self.dim)
-        return space.wrap(self.storage[pos])
-
-    @unwrap_spec(value=int)
-    def descr_setitem(self, w_index, value):
-        space = self.space
-        indexes = self._unpack_indexes(space, w_index)
-        pos = compute_pos(space, indexes, self.dim)
-        self.storage[pos] = value
-        return space.w_None
-
-    def descr_len(self):
-        return self.space.wrap(self.dim[0])
-
-MultiDimArray.typedef = TypeDef(
-    'NumArray',
-    __getitem__ = interp2app(MultiDimArray.descr_getitem),
-    __setitem__ = interp2app(MultiDimArray.descr_setitem),
-    __len__     = interp2app(MultiDimArray.descr_len),
-)
-
-def unpack_dim(space, w_dim):
-    if space.is_true(space.isinstance(w_dim, space.w_int)):
-        return [space.int_w(w_dim)]
-    dim_w = space.fixedview(w_dim)
-    return [space.int_w(w_i) for w_i in dim_w]
-
-def unpack_dtype(space, w_dtype):
-    if space.is_w(w_dtype, space.w_int):
-        return 'i'
-    else:
-        raise NotImplementedError
-
-def zeros(space, w_dim, w_dtype):
-    dim = unpack_dim(space, w_dim)
-    dtype = unpack_dtype(space, w_dtype)
-    if len(dim) == 1:
-        return space.wrap(NumArray(space, dim[0], dtype))
-    else:
-        return space.wrap(MultiDimArray(space, dim, dtype))
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -0,0 +1,19 @@
+from pypy.conftest import gettestobjspace
+from pypy.module.micronumpy.interp_numarray import SingleDimArray, FloatWrapper
+
+
+class BaseNumpyAppTest(object):
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=('micronumpy',))
+
+
+class TestSignature(object):
+    def test_binop_signature(self, space):
+        ar = SingleDimArray(10)
+        v1 = ar.descr_add(space, ar)
+        v2 = ar.descr_add(space, FloatWrapper(2.0))
+        assert v1.signature is not v2.signature
+        v3 = ar.descr_add(space, FloatWrapper(1.0))
+        assert v2.signature is v3.signature
+        v4 = ar.descr_add(space, ar)
+        assert v1.signature is v4.signature
\ No newline at end of file
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -0,0 +1,141 @@
+import py
+
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+
+class AppTestNumArray(BaseNumpyAppTest):
+    def test_type(self):
+        from numpy import array
+        ar = array(range(5))
+        assert type(ar) is type(ar + ar)
+
+    def test_init(self):
+        from numpy import zeros
+        a = zeros(15)
+        # Check that storage was actually zero'd.
+        assert a[10] == 0.0
+        # And check that changes stick.
+        a[13] = 5.3
+        assert a[13] == 5.3
+
+    def test_iterator_init(self):
+        from numpy import array
+        a = array(range(5))
+        assert a[3] == 3
+
+    def test_getitem(self):
+        from numpy import array
+        a = array(range(5))
+        raises(IndexError, "a[5]")
+        a = a + a
+        raises(IndexError, "a[5]")
+        assert a[-1] == 8
+        raises(IndexError, "a[-6]")
+
+    def test_setitem(self):
+        from numpy import array
+        a = array(range(5))
+        a[-1] = 5.0
+        assert a[4] == 5.0
+        raises(IndexError, "a[5] = 0.0")
+        raises(IndexError, "a[-6] = 3.0")
+
+    def test_len(self):
+        from numpy import array
+        a = array(range(5))
+        assert len(a) == 5
+        assert len(a + a) == 5
+
+    def test_add(self):
+        from numpy import array
+        a = array(range(5))
+        b = a + a
+        for i in range(5):
+            assert b[i] == i + i
+
+    def test_add_other(self):
+        from numpy import array
+        a = array(range(5))
+        b = array(reversed(range(5)))
+        c = a + b
+        for i in range(5):
+            assert c[i] == 4
+
+    def test_add_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a + 5
+        for i in range(5):
+            assert b[i] == i + 5
+
+    def test_subtract(self):
+        from numpy import array
+        a = array(range(5))
+        b = a - a
+        for i in range(5):
+            assert b[i] == 0
+
+    def test_subtract_other(self):
+        from numpy import array
+        a = array(range(5))
+        b = array([1, 1, 1, 1, 1])
+        c = a - b
+        for i in range(5):
+            assert c[i] == i - 1
+
+    def test_subtract_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a - 5
+        for i in range(5):
+            assert b[i] == i - 5
+
+    def test_mul(self):
+        from numpy import array
+        a = array(range(5))
+        b = a * a
+        for i in range(5):
+            assert b[i] == i * i
+
+    def test_mul_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a * 5
+        for i in range(5):
+            assert b[i] == i * 5
+
+    def test_div(self):
+        from numpy import array
+        a = array(range(1, 6))
+        b = a / a
+        for i in range(5):
+            assert b[i] == 1
+
+    def test_div_other(self):
+        from numpy import array
+        a = array(range(5))
+        b = array([2, 2, 2, 2, 2])
+        c = a / b
+        for i in range(5):
+            assert c[i] == i / 2.0
+
+    def test_div_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a / 5.0
+        for i in range(5):
+            assert b[i] == i / 5.0
+
+    def test_auto_force(self):
+        from numpy import array
+        a = array(range(5))
+        b = a - 1
+        a[2] = 3
+        for i in range(5):
+            assert b[i] == i - 1
+
+        a = array(range(5))
+        b = a + a
+        c = b + b
+        b[1] = 5
+        assert c[1] == 4
\ No newline at end of file
diff --git a/pypy/module/micronumpy/test/test_numpy.py b/pypy/module/micronumpy/test/test_numpy.py
deleted file mode 100644
--- a/pypy/module/micronumpy/test/test_numpy.py
+++ /dev/null
@@ -1,65 +0,0 @@
-
-from pypy.conftest import gettestobjspace
-
-class AppTestNumpy(object):
-    def setup_class(cls):
-        cls.space = gettestobjspace(usemodules=('micronumpy',))
-    
-    def test_zeroes(self):
-        from numpy import zeros
-        ar = zeros(3, dtype=int)
-        assert ar[0] == 0
-    
-    def test_setitem_getitem(self):
-        from numpy import zeros
-        ar = zeros(8, dtype=int)
-        assert ar[0] == 0
-        ar[1] = 3
-        assert ar[1] == 3
-        raises((TypeError, ValueError), ar.__getitem__, 'xyz')
-        raises(IndexError, ar.__getitem__, 38)
-        assert ar[-2] == 0
-        assert ar[-7] == 3
-        assert len(ar) == 8
-
-    def test_minimum(self):
-        from numpy import zeros, minimum
-        ar = zeros(5, dtype=int)
-        ar2 = zeros(5, dtype=int)
-        ar[0] = 3
-        ar[1] = -3
-        ar[2] = 8
-        ar2[3] = -1
-        ar2[4] = 8
-        x = minimum(ar, ar2)
-        assert x[0] == 0
-        assert x[1] == -3
-        assert x[2] == 0
-        assert x[3] == -1
-        assert x[4] == 0
-        assert len(x) == 5
-        raises(ValueError, minimum, ar, zeros(3, dtype=int))
-
-class AppTestMultiDim(object):
-    def setup_class(cls):
-        cls.space = gettestobjspace(usemodules=('micronumpy',))
-
-    def test_multidim(self):
-        from numpy import zeros
-        ar = zeros((3, 3), dtype=int)
-        assert ar[0, 2] == 0
-        raises(IndexError, ar.__getitem__, (3, 0))
-        assert ar[-2, 1] == 0
-
-    def test_multidim_getset(self):
-        from numpy import zeros
-        ar = zeros((3, 3, 3), dtype=int)
-        ar[1, 2, 1] = 3
-        assert ar[1, 2, 1] == 3
-        assert ar[-2, 2, 1] == 3
-        assert ar[2, 2, 1] == 0
-        assert ar[-2, 2, -2] == 3
-
-    def test_len(self):
-        from numpy import zeros
-        assert len(zeros((3, 2, 1), dtype=int)) == 3
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -0,0 +1,79 @@
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+
+class AppTestUfuncs(BaseNumpyAppTest):
+    def test_negative(self):
+        from numpy import array, negative
+
+        a = array([-5.0, 0.0, 1.0])
+        b = negative(a)
+        for i in range(3):
+            assert b[i] == -a[i]
+
+        a = array([-5.0, 1.0])
+        b = negative(a)
+        a[0] = 5.0
+        assert b[0] == 5.0
+
+    def test_abs(self):
+        from numpy import array, absolute
+
+        a = array([-5.0, -0.0, 1.0])
+        b = absolute(a)
+        for i in range(3):
+            assert b[i] == abs(a[i])
+
+    def test_minimum(self):
+        from numpy import array, minimum
+
+        a = array([-5.0, -0.0, 1.0])
+        b = array([ 3.0, -2.0,-3.0])
+        c = minimum(a, b)
+        for i in range(3):
+            assert c[i] == min(a[i], b[i])
+
+    def test_maximum(self):
+        from numpy import array, maximum
+
+        a = array([-5.0, -0.0, 1.0])
+        b = array([ 3.0, -2.0,-3.0])
+        c = maximum(a, b)
+        for i in range(3):
+            assert c[i] == max(a[i], b[i])
+
+    def test_sign(self):
+        from numpy import array, sign
+
+        reference = [-1.0, 0.0, 0.0, 1.0]
+        a = array([-5.0, -0.0, 0.0, 6.0])
+        b = sign(a)
+        for i in range(4):
+            assert b[i] == reference[i]
+
+    def test_reciporocal(self):
+        from numpy import array, reciprocal
+
+        reference = [-0.2, float("inf"), float("-inf"), 2.0]
+        a = array([-5.0, 0.0, -0.0, 0.5])
+        b = reciprocal(a)
+        for i in range(4):
+            assert b[i] == reference[i]
+
+    def test_copysign(self):
+        from numpy import array, copysign
+
+        reference = [5.0, -0.0, 0.0, -6.0]
+        a = array([-5.0, 0.0, 0.0, 6.0])
+        b = array([5.0, -0.0, 3.0, -6.0])
+        c = copysign(a, b)
+        for i in range(4):
+            assert c[i] == reference[i]
+
+    def test_exp(self):
+        import math
+        from numpy import array, exp
+
+        a = array([-5.0, -0.0, 0.0, float("inf")])
+        b = exp(a)
+        for i in range(4):
+            assert b[i] == math.exp(a[i])
\ No newline at end of file
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -0,0 +1,94 @@
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.module.micronumpy.interp_numarray import (SingleDimArray, Signature,
+    FloatWrapper, Call1, Call2, add, mul)
+from pypy.module.micronumpy.interp_ufuncs import negative
+
+
+class FakeSpace(object):
+    pass
+
+class TestNumpyJIt(LLJitMixin):
+    def setup_class(cls):
+        cls.space = FakeSpace()
+
+    def test_add(self):
+        space = self.space
+
+        def f(i):
+            ar = SingleDimArray(i)
+            v = Call2(add, ar, ar, Signature())
+            return v.get_concrete().storage[3]
+
+        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)
+            v = Call2(add, ar, FloatWrapper(4.5), Signature())
+            return v.get_concrete().storage[3]
+
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
+        self.check_loops({"getarrayitem_raw": 1, "float_add": 1,
+                          "setarrayitem_raw": 1, "int_add": 1,
+                          "int_lt": 1, "guard_true": 1, "jump": 1})
+        assert result == f(5)
+
+    def test_already_forecd(self):
+        space = self.space
+
+        def f(i):
+            ar = SingleDimArray(i)
+            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]
+
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
+        # This is the sum of the ops for both loops, however if you remove the
+        # optimization then you end up with 2 float_adds, so we can still be
+        # sure it was optimized correctly.
+        self.check_loops({"getarrayitem_raw": 2, "float_mul": 1, "float_add": 1,
+                           "setarrayitem_raw": 2, "int_add": 2,
+                           "int_lt": 2, "guard_true": 2, "jump": 2})
+        assert result == f(5)
+
+    def test_ufunc(self):
+        space = self.space
+        def f(i):
+            ar = SingleDimArray(i)
+            v1 = Call2(add, ar, ar, Signature())
+            v2 = negative(space, v1)
+            return v2.get_concrete().storage[3]
+
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
+        self.check_loops({"getarrayitem_raw": 2, "float_add": 1, "float_neg": 1,
+                          "setarrayitem_raw": 1, "int_add": 1,
+                          "int_lt": 1, "guard_true": 1, "jump": 1,
+        })
+        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
diff --git a/pypy/module/micronumpy/ufunc.py b/pypy/module/micronumpy/ufunc.py
deleted file mode 100644
--- a/pypy/module/micronumpy/ufunc.py
+++ /dev/null
@@ -1,21 +0,0 @@
-
-from numarray import NumArray
-from pypy.interpreter.baseobjspace import ObjSpace, W_Root
-from pypy.interpreter.error import OperationError
-
-def minimum(space, w_a, w_b):
-    if not isinstance(w_a, NumArray) or not isinstance(w_b, NumArray):
-        raise OperationError(space.w_TypeError,
-                             space.wrap("expecting NumArrat object"))
-    if w_a.dim != w_b.dim:
-        raise OperationError(space.w_ValueError,
-                             space.wrap("minimum of arrays of different length"))
-    res = NumArray(space, w_a.dim, 'i')
-    for i in range(len(w_a.storage)):
-        one = w_a.storage[i]
-        two = w_b.storage[i]
-        if one < two:
-            res.storage[i] = one
-        else:
-            res.storage[i] = two
-    return space.wrap(res)
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -113,7 +113,7 @@
                         flags['fresh_virtualizable'] = True
                     s_x = annmodel.SomeInstance(s_x.classdef,
                                                 s_x.can_be_None,
-                                                flags)        
+                                                flags)
         return s_x
 
     def specialize_call(self, hop, **kwds_i):
@@ -201,7 +201,7 @@
 # VRefs
 
 def virtual_ref(x):
-    
+
     """Creates a 'vref' object that contains a reference to 'x'.  Calls
     to virtual_ref/virtual_ref_finish must be properly nested.  The idea
     is that the object 'x' is supposed to be JITted as a virtual between
@@ -275,7 +275,7 @@
 
 # ____________________________________________________________
 
-class JitDriver(object):    
+class JitDriver(object):
     """Base class to declare fine-grained user control on the JIT.  So
     far, there must be a singleton instance of JitDriver.  This style
     will allow us (later) to support a single RPython program with
@@ -557,7 +557,7 @@
     def specialize_call(self, hop):
         from pypy.rpython.lltypesystem import lltype
         from pypy.rpython.lltypesystem.rstr import string_repr
-        
+
         hop.exception_cannot_occur()
         driver = self.instance.im_self
         name = hop.args_s[0].const
diff --git a/pypy/rlib/nonconst.py b/pypy/rlib/nonconst.py
--- a/pypy/rlib/nonconst.py
+++ b/pypy/rlib/nonconst.py
@@ -18,6 +18,12 @@
     def __nonzero__(self):
         return bool(self.__dict__['constant'])
 
+    def __eq__(self, other):
+        return self.__dict__['constant'] == other
+
+    def __add__(self, other):
+        return self.__dict__['constant'] + other
+
 class EntryNonConstant(ExtRegistryEntry):
     _about_ = NonConstant
 
diff --git a/pypy/translator/c/src/float.h b/pypy/translator/c/src/float.h
--- a/pypy/translator/c/src/float.h
+++ b/pypy/translator/c/src/float.h
@@ -43,3 +43,4 @@
 #define OP_CAST_FLOAT_TO_LONGLONG(x,r) r = (long long)(x)
 #define OP_CAST_FLOAT_TO_ULONGLONG(x,r) r = (unsigned long long)(x)
 #endif
+
diff --git a/pypy/translator/goal/targetnumpystandalone.py b/pypy/translator/goal/targetnumpystandalone.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/goal/targetnumpystandalone.py
@@ -0,0 +1,57 @@
+
+""" Usage:
+
+./targetnumpystandalone-c <bytecode> array_size
+
+Will execute a give numpy bytecode. Arrays will be ranges (in float) modulo 10,
+constants would be consecutive starting from one.
+
+Bytecode should contain letters 'a' 'l' and 'f' so far and be correct
+"""
+
+import time
+from pypy.module.micronumpy.numarray import SingleDimArray, Code, compute
+from pypy.jit.codewriter.policy import JitPolicy
+
+def create_array(size):
+    a = SingleDimArray(size)
+    for i in range(size):
+        a.storage[i] = float(i % 10)
+    return a
+
+def entry_point(argv):
+    if len(argv) != 3:
+        print __doc__
+        return 1
+    bytecode = argv[1]
+    for b in bytecode:
+        if b not in 'alf':
+            print "WRONG BYTECODE"
+            print __doc__
+            return 2
+    try:
+        size = int(argv[2])
+    except ValueError:
+        print "INVALID LITERAL FOR INT:", argv[2]
+        print __doc__
+        return 3
+    no_arrays = bytecode.count('l')
+    no_floats = bytecode.count('f')
+    arrays = []
+    floats = []
+    for i in range(no_arrays):
+        arrays.append(create_array(size))
+    for i in range(no_floats):
+        floats.append(float(i + 1))
+    code = Code(bytecode, arrays, floats)
+    t0 = time.time()
+    compute(code)
+    print "bytecode:", bytecode, "size:", size
+    print "took:", time.time() - t0
+    return 0
+
+def target(*args):
+    return entry_point, None
+
+def jitpolicy(driver):
+    return JitPolicy()


More information about the pypy-commit mailing list