[pypy-svn] r75320 - in pypy/trunk/pypy: jit/codewriter/test jit/metainterp jit/metainterp/test rpython rpython/lltypesystem translator/c translator/c/src

arigo at codespeak.net arigo at codespeak.net
Sat Jun 12 12:22:26 CEST 2010


Author: arigo
Date: Sat Jun 12 12:22:24 2010
New Revision: 75320

Modified:
   pypy/trunk/pypy/jit/codewriter/test/test_flatten.py
   pypy/trunk/pypy/jit/metainterp/blackhole.py
   pypy/trunk/pypy/jit/metainterp/pyjitpl.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
   pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
   pypy/trunk/pypy/rpython/lltypesystem/rclass.py
   pypy/trunk/pypy/rpython/normalizecalls.py
   pypy/trunk/pypy/translator/c/funcgen.py
   pypy/trunk/pypy/translator/c/src/int.h
Log:
Merge branch/int-between.

It adds an 'int_between' operation produced by lltypesystem.rclass.
This operation has a slightly more efficient form in the C backend.
More importantly, in the JIT it becomes a single True/False test;
previously, it was done with a double test "a <= b <= c", which
can fail for two distinct reasons: b < a or b > c.  If we ever see
that case, it would create two equal traces from that point.


Modified: pypy/trunk/pypy/jit/codewriter/test/test_flatten.py
==============================================================================
--- pypy/trunk/pypy/jit/codewriter/test/test_flatten.py	(original)
+++ pypy/trunk/pypy/jit/codewriter/test/test_flatten.py	Sat Jun 12 12:22:24 2010
@@ -715,3 +715,12 @@
             uint_le %i2, $456L -> %i3
             int_return %i3
         """, transform=True)
+
+    def test_int_between(self):
+        from pypy.rpython.lltypesystem.lloperation import llop
+        def f(n, m, p):
+            return llop.int_between(lltype.Bool, n, m, p)
+        self.encoding_test(f, [5, 6, 7], """
+            int_between %i0, %i1, %i2 -> %i3
+            int_return %i3
+        """, transform=True)

Modified: pypy/trunk/pypy/jit/metainterp/blackhole.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/blackhole.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/blackhole.py	Sat Jun 12 12:22:24 2010
@@ -475,6 +475,9 @@
     @arguments("i", returns="i")
     def bhimpl_int_is_true(a):
         return bool(a)
+    @arguments("i", "i", "i", returns="i")
+    def bhimpl_int_between(a, b, c):
+        return a <= b < c
 
     @arguments("i", "i", returns="i")
     def bhimpl_uint_lt(a, b):

Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py	Sat Jun 12 12:22:24 2010
@@ -315,6 +315,16 @@
         if value:
             self.pc = target
 
+    @arguments("box", "box", "box")
+    def opimpl_int_between(self, b1, b2, b3):
+        b5 = self.execute(rop.INT_SUB, b3, b1)
+        if isinstance(b5, ConstInt) and b5.getint() == 1:
+            # the common case of int_between(a, b, a+1) turns into just INT_EQ
+            return self.execute(rop.INT_EQ, b2, b1)
+        else:
+            b4 = self.execute(rop.INT_SUB, b2, b1)
+            return self.execute(rop.UINT_LT, b4, b5)
+
     @arguments("box", "descr", "orgpc")
     def opimpl_switch(self, valuebox, switchdict, orgpc):
         box = self.implement_guard_value(orgpc, valuebox)

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Sat Jun 12 12:22:24 2010
@@ -145,6 +145,9 @@
 
     def check_operations_history(self, expected=None, **isns):
         # this can be used after interp_operations
+        if expected is not None:
+            expected = dict(expected)
+            expected['jump'] = 1
         self.metainterp.staticdata.stats.check_history(expected, **isns)
 
 
@@ -561,6 +564,53 @@
                                       int_le=0, uint_le=1,
                                       int_sub=1)
 
+    def test_int_between(self):
+        #
+        def check(arg1, arg2, arg3, expect_result, **expect_operations):
+            from pypy.rpython.lltypesystem import lltype
+            from pypy.rpython.lltypesystem.lloperation import llop
+            loc = locals().copy()
+            exec py.code.Source("""
+                def f(n, m, p):
+                    arg1 = %(arg1)s
+                    arg2 = %(arg2)s
+                    arg3 = %(arg3)s
+                    return llop.int_between(lltype.Bool, arg1, arg2, arg3)
+            """ % locals()).compile() in loc
+            res = self.interp_operations(loc['f'], [5, 6, 7])
+            assert res == expect_result
+            self.check_operations_history(expect_operations)
+        #
+        check('n', 'm', 'p', True,  int_sub=2, uint_lt=1)
+        check('n', 'p', 'm', False, int_sub=2, uint_lt=1)
+        #
+        check('n', 'm', 6, False, int_sub=2, uint_lt=1)
+        #
+        check('n', 4, 'p', False, int_sub=2, uint_lt=1)
+        check('n', 5, 'p', True,  int_sub=2, uint_lt=1)
+        check('n', 8, 'p', False, int_sub=2, uint_lt=1)
+        #
+        check('n', 6, 7, True, int_sub=2, uint_lt=1)
+        #
+        check(-2, 'n', 'p', True,  int_sub=2, uint_lt=1)
+        check(-2, 'm', 'p', True,  int_sub=2, uint_lt=1)
+        check(-2, 'p', 'm', False, int_sub=2, uint_lt=1)
+        #check(0, 'n', 'p', True,  uint_lt=1)   xxx implement me
+        #check(0, 'm', 'p', True,  uint_lt=1)
+        #check(0, 'p', 'm', False, uint_lt=1)
+        #
+        check(2, 'n', 6, True,  int_sub=1, uint_lt=1)
+        check(2, 'm', 6, False, int_sub=1, uint_lt=1)
+        check(2, 'p', 6, False, int_sub=1, uint_lt=1)
+        check(5, 'n', 6, True,  int_eq=1)    # 6 == 5+1
+        check(5, 'm', 6, False, int_eq=1)    # 6 == 5+1
+        #
+        check(2, 6, 'm', False, int_sub=1, uint_lt=1)
+        check(2, 6, 'p', True,  int_sub=1, uint_lt=1)
+        #
+        check(2, 40, 6,  False)
+        check(2, 40, 60, True)
+
     def test_getfield(self):
         class A:
             pass

Modified: pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	Sat Jun 12 12:22:24 2010
@@ -227,6 +227,8 @@
     'int_rshift':           LLOp(canfold=True),
     'int_xor':              LLOp(canfold=True),
 
+    'int_between':          LLOp(canfold=True),   # a <= b < c
+
     'int_add_ovf':          LLOp(canraise=(OverflowError,), tryfold=True),
     'int_add_nonneg_ovf':   LLOp(canraise=(OverflowError,), tryfold=True),
               # ^^^ more efficient version when 2nd arg is nonneg

Modified: pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/opimpl.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/opimpl.py	Sat Jun 12 12:22:24 2010
@@ -197,6 +197,12 @@
     assert isinstance(y, int)
     return intmask(x - y)
 
+def op_int_between(a, b, c):
+    assert lltype.typeOf(a) is lltype.Signed
+    assert lltype.typeOf(b) is lltype.Signed
+    assert lltype.typeOf(c) is lltype.Signed
+    return a <= b < c
+
 def op_int_and(x, y):
     if not isinstance(x, int):
         from pypy.rpython.lltypesystem import llgroup

Modified: pypy/trunk/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rclass.py	Sat Jun 12 12:22:24 2010
@@ -22,6 +22,7 @@
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib import objectmodel
 from pypy.lib.identity_dict import identity_dict
+from pypy.rpython.lltypesystem.lloperation import llop
 
 #
 #  There is one "vtable" per user class, with the following structure:
@@ -645,10 +646,12 @@
     return cast_pointer(OBJECTPTR, obj).typeptr
 
 def ll_issubclass(subcls, cls):
-    return cls.subclassrange_min <= subcls.subclassrange_min <= cls.subclassrange_max
+    return llop.int_between(Bool, cls.subclassrange_min,
+                                  subcls.subclassrange_min,
+                                  cls.subclassrange_max)
 
 def ll_issubclass_const(subcls, minid, maxid):
-    return minid <= subcls.subclassrange_min <= maxid
+    return llop.int_between(Bool, minid, subcls.subclassrange_min, maxid)
 
 
 def ll_isinstance(obj, cls): # obj should be cast to OBJECT or NONGCOBJECT

Modified: pypy/trunk/pypy/rpython/normalizecalls.py
==============================================================================
--- pypy/trunk/pypy/rpython/normalizecalls.py	(original)
+++ pypy/trunk/pypy/rpython/normalizecalls.py	Sat Jun 12 12:22:24 2010
@@ -292,6 +292,13 @@
         else:
             return cmp(self.orderwitness, other.orderwitness)
 
+    # support for implementing int_between: (a<=b<c) with (b-a<c-a)
+    # see pypy.jit.metainterp.pyjitpl.opimpl_int_between
+    def __sub__(self, other):
+        return self.compute_fn() - other
+    def __rsub__(self, other):
+        return other - self.compute_fn()
+
     def compute_fn(self):
         if self.value is None:
             self.peers.sort()

Modified: pypy/trunk/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/trunk/pypy/translator/c/funcgen.py	(original)
+++ pypy/trunk/pypy/translator/c/funcgen.py	Sat Jun 12 12:22:24 2010
@@ -318,6 +318,7 @@
 
     def gen_op(self, op):
         macro = 'OP_%s' % op.opname.upper()
+        line = None
         if op.opname.startswith('gc_'):
             meth = getattr(self.gcpolicy, macro, None)
             if meth:
@@ -326,7 +327,7 @@
             meth = getattr(self, macro, None)
             if meth:
                 line = meth(op)
-        if meth is None:
+        if line is None:
             lst = [self.expr(v) for v in op.args]
             lst.append(self.expr(op.result))
             line = '%s(%s);' % (macro, ', '.join(lst))
@@ -849,5 +850,16 @@
         return 'PYPY_DEBUG_CATCH_EXCEPTION("%s", %s, %s);' % (
             self.getdebugfunctionname(), gottype, ' || '.join(exprs))
 
+    def OP_INT_BETWEEN(self, op):
+        if (isinstance(op.args[0], Constant) and
+            isinstance(op.args[2], Constant) and
+            op.args[2].value - op.args[0].value == 1):
+            # (a <= b < a+1) ----> (b == a)
+            return '%s = (%s == %s);  /* was INT_BETWEEN */' % (
+                self.expr(op.result),
+                self.expr(op.args[1]),
+                self.expr(op.args[0]))
+        else:
+            return None    # use the default
 
 assert not USESLOTS or '__dict__' not in dir(FunctionCodeGenerator)

Modified: pypy/trunk/pypy/translator/c/src/int.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/int.h	(original)
+++ pypy/trunk/pypy/translator/c/src/int.h	Sat Jun 12 12:22:24 2010
@@ -47,6 +47,14 @@
 #define OP_INT_LT(x,y,r)	  r = ((x) <  (y))
 #define OP_INT_GE(x,y,r)	  r = ((x) >= (y))
 
+/* Implement INT_BETWEEN by optimizing for the common case where a and c
+   are constants (the 2nd subtraction below is then constant-folded), or
+   for the case of a == 0 (both subtractions are then constant-folded).
+   Note that the following line only works if a <= c in the first place,
+   which we assume is true. */
+#define OP_INT_BETWEEN(a,b,c,r)   r = (((unsigned long)b - (unsigned long)a) \
+                                     < ((unsigned long)c - (unsigned long)a))
+
 /* addition, subtraction */
 
 #define OP_INT_ADD(x,y,r)     r = (x) + (y)



More information about the Pypy-commit mailing list