Author: Armin Rigo <[email protected]>
Branch: remove-raisingops
Changeset: r84365:7d31bc576cbc
Date: 2016-05-11 09:33 +0200
http://bitbucket.org/pypy/pypy/changeset/7d31bc576cbc/

Log:    Revert the whole change for int_{add,sub,mul}_ovf. It can be argued
        that the C backend should handle them directly, and more
        prosaically, it goes in the way of tests if they start seeing
        'cast_int_to_float' and other unexpected operations

diff --git a/rpython/jit/codewriter/jtransform.py 
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -333,6 +333,17 @@
     rewrite_op_float_gt  = _rewrite_symmetric
     rewrite_op_float_ge  = _rewrite_symmetric
 
+    def rewrite_op_int_add_ovf(self, op):
+        op0 = self._rewrite_symmetric(op)
+        op1 = SpaceOperation('-live-', [], None)
+        return [op1, op0]
+
+    rewrite_op_int_mul_ovf = rewrite_op_int_add_ovf
+
+    def rewrite_op_int_sub_ovf(self, op):
+        op1 = SpaceOperation('-live-', [], None)
+        return [op1, op]
+
     def _noop_rewrite(self, op):
         return op
 
@@ -426,7 +437,7 @@
         if oopspec_name.startswith('list.') or 
oopspec_name.startswith('newlist'):
             prepare = self._handle_list_call
         elif oopspec_name.startswith('int.'):
-            prepare = self._handle_int_ovf
+            prepare = self._handle_int_special
         elif oopspec_name.startswith('stroruni.'):
             prepare = self._handle_stroruni_call
         elif oopspec_name == 'str.str2unicode':
@@ -1479,6 +1490,7 @@
     for _old, _new in [('bool_not', 'int_is_zero'),
                        ('cast_bool_to_float', 'cast_int_to_float'),
 
+                       ('int_add_nonneg_ovf', 'int_add_ovf'),
                        ('keepalive', '-live-'),
 
                        ('char_lt', 'int_lt'),
@@ -1902,22 +1914,15 @@
                 llmemory.cast_ptr_to_adr(c_func.value))
         self.callcontrol.callinfocollection.add(oopspecindex, calldescr, func)
 
-    def _handle_int_ovf(self, op, oopspec_name, args):
-        opname = oopspec_name.replace('.', '_')
-        if oopspec_name in ('int.add_ovf', 'int.sub_ovf', 'int.mul_ovf'):
-            op0 = SpaceOperation(opname, args, op.result)
-            if oopspec_name in ('int.add_ovf', 'int.mul_ovf'):
-                op0 = self._rewrite_symmetric(op0)
-            oplist = [SpaceOperation('-live-', [], None), op0]
-            return oplist
-        elif oopspec_name == 'int.neg_ovf':
+    def _handle_int_special(self, op, oopspec_name, args):
+        if oopspec_name == 'int.neg_ovf':
             [v_x] = args
             op0 = SpaceOperation('int_sub_ovf',
                                  [Constant(0, lltype.Signed), v_x],
                                  op.result)
-            oplist = [SpaceOperation('-live-', [], None), op0]
-            return oplist
+            return self.rewrite_operation(op0)
         else:
+            opname = oopspec_name.replace('.', '_')
             os = getattr(EffectInfo, 'OS_' + opname.upper())
             return self._handle_oopspec_call(op, args, os,
                                     EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
diff --git a/rpython/jit/codewriter/test/test_flatten.py 
b/rpython/jit/codewriter/test/test_flatten.py
--- a/rpython/jit/codewriter/test/test_flatten.py
+++ b/rpython/jit/codewriter/test/test_flatten.py
@@ -71,9 +71,6 @@
     _descr_cannot_raise = FakeDescr()
     callinfocollection = FakeCallInfoCollection()
     def guess_call_kind(self, op):
-        if op.args[0].value._obj._name.startswith(
-                ('ll_int_add_ovf', 'll_int_sub_ovf', 'll_int_mul_ovf')):
-            return 'builtin'
         return 'residual'
     def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
                      extraeffect=None, extradescr=None):
diff --git a/rpython/jit/codewriter/test/test_jtransform.py 
b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -272,17 +272,17 @@
                     assert op1.result == v3
                     assert op1.opname == name2[0]
 
[email protected]('opname', ['add_ovf', 'mul_ovf'])
-def test_symmetric_op_ovf(opname):
[email protected]('opname', ['add_ovf', 'sub_ovf', 'mul_ovf'])
+def test_int_op_ovf(opname):
     v3 = varoftype(lltype.Signed)
     for v1 in [varoftype(lltype.Signed), const(42)]:
         for v2 in [varoftype(lltype.Signed), const(43)]:
-            op = SpaceOperation('direct_call', [Constant(opname), v1, v2], v3)
-            oplist = Transformer(FakeCPU())._handle_int_ovf(op, 'int.'+opname,
-                                                            [v1, v2])
+            op = SpaceOperation('int_' + opname, [v1, v2], v3)
+            oplist = Transformer(FakeCPU()).rewrite_operation(op)
             op1, op0 = oplist
-            assert op0.opname == 'int_'+opname
-            if isinstance(v1, Constant) and isinstance(v2, Variable):
+            assert op0.opname == 'int_' + opname
+            if (isinstance(v1, Constant) and isinstance(v2, Variable)
+                    and opname != 'sub_ovf'):
                 assert op0.args == [v2, v1]
                 assert op0.result == v3
             else:
@@ -292,27 +292,12 @@
             assert op1.args == []
             assert op1.result is None
 
[email protected]('opname', ['sub_ovf'])
-def test_asymmetric_op_ovf(opname):
-    v3 = varoftype(lltype.Signed)
-    for v1 in [varoftype(lltype.Signed), const(42)]:
-        for v2 in [varoftype(lltype.Signed), const(43)]:
-            op = SpaceOperation('direct_call', [Constant(opname), v1, v2], v3)
-            oplist = Transformer(FakeCPU())._handle_int_ovf(op, 'int.'+opname,
-                                                            [v1, v2])
-            op1, op0 = oplist
-            assert op0.opname == 'int_'+opname
-            assert op0.args == [v1, v2]
-            assert op0.result == v3
-            assert op1.opname == '-live-'
-            assert op1.args == []
-            assert op1.result is None
-
 def test_neg_ovf():
     v3 = varoftype(lltype.Signed)
     for v1 in [varoftype(lltype.Signed), const(42)]:
         op = SpaceOperation('direct_call', [Constant('neg_ovf'), v1], v3)
-        oplist = Transformer(FakeCPU())._handle_int_ovf(op, 'int.neg_ovf', 
[v1])
+        oplist = Transformer(FakeCPU())._handle_int_special(op, 'int.neg_ovf',
+                                                            [v1])
         op1, op0 = oplist
         assert op0.opname == 'int_sub_ovf'
         assert op0.args == [Constant(0), v1]
@@ -322,13 +307,13 @@
         assert op1.result is None
 
 @py.test.mark.parametrize('opname', ['py_div', 'udiv', 'py_mod', 'umod'])
-def test_asymmetric_op_residual(opname):
+def test_int_op_residual(opname):
     v3 = varoftype(lltype.Signed)
     tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
     for v1 in [varoftype(lltype.Signed), const(42)]:
         for v2 in [varoftype(lltype.Signed), const(43)]:
             op = SpaceOperation('direct_call', [Constant(opname), v1, v2], v3)
-            op0 = tr._handle_int_ovf(op, 'int.'+opname, [v1, v2])
+            op0 = tr._handle_int_special(op, 'int.'+opname, [v1, v2])
             assert op0.opname == 'residual_call_ir_i'
             assert op0.args[0].value == opname  # pseudo-function as str
             expected = ('int_' + opname).upper()
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -1073,6 +1073,38 @@
     def op_track_alloc_stop(self, addr):
         checkadr(addr)
 
+    # ____________________________________________________________
+    # Overflow-detecting variants
+
+    def op_int_add_ovf(self, x, y):
+        assert isinstance(x, (int, long, llmemory.AddressOffset))
+        assert isinstance(y, (int, long, llmemory.AddressOffset))
+        try:
+            return ovfcheck(x + y)
+        except OverflowError:
+            self.make_llexception()
+
+    def op_int_add_nonneg_ovf(self, x, y):
+        if isinstance(y, int):
+            assert y >= 0
+        return self.op_int_add_ovf(x, y)
+
+    def op_int_sub_ovf(self, x, y):
+        assert isinstance(x, (int, long))
+        assert isinstance(y, (int, long))
+        try:
+            return ovfcheck(x - y)
+        except OverflowError:
+            self.make_llexception()
+
+    def op_int_mul_ovf(self, x, y):
+        assert isinstance(x, (int, long, llmemory.AddressOffset))
+        assert isinstance(y, (int, long, llmemory.AddressOffset))
+        try:
+            return ovfcheck(x * y)
+        except OverflowError:
+            self.make_llexception()
+
     def op_int_is_true(self, x):
         # special case
         if type(x) is CDefinedIntSymbolic:
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -212,6 +212,12 @@
     'int_between':          LLOp(canfold=True),   # a <= b < c
     'int_force_ge_zero':    LLOp(canfold=True),   # 0 if a < 0 else a
 
+    '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
+    'int_sub_ovf':          LLOp(canraise=(OverflowError,), tryfold=True),
+    'int_mul_ovf':          LLOp(canraise=(OverflowError,), tryfold=True),
+
     'uint_is_true':         LLOp(canfold=True),
     'uint_invert':          LLOp(canfold=True),
 
diff --git a/rpython/rtyper/rint.py b/rpython/rtyper/rint.py
--- a/rpython/rtyper/rint.py
+++ b/rpython/rtyper/rint.py
@@ -219,21 +219,21 @@
                 hop = hop.copy()
                 hop.swap_fst_snd_args()
                 func = 'add_nonneg_ovf'
-        return _rtype_call_helper(hop, func)
+        return _rtype_template(hop, func)
 
     def rtype_sub(_, hop):
         return _rtype_template(hop, 'sub')
     rtype_inplace_sub = rtype_sub
 
     def rtype_sub_ovf(_, hop):
-        return _rtype_call_helper(hop, 'sub_ovf')
+        return _rtype_template(hop, 'sub_ovf')
 
     def rtype_mul(_, hop):
         return _rtype_template(hop, 'mul')
     rtype_inplace_mul = rtype_mul
 
     def rtype_mul_ovf(_, hop):
-        return _rtype_call_helper(hop, 'mul_ovf')
+        return _rtype_template(hop, 'mul_ovf')
 
     def rtype_floordiv(_, hop):
         return _rtype_call_helper(hop, 'floordiv', [ZeroDivisionError])
@@ -307,9 +307,6 @@
     """Write a simple operation implementing the given 'func'.
     It must be an operation that cannot raise.
     """
-    if '_ovf' in func or func.startswith(('mod', 'floordiv')):
-        raise TyperError("%r should not be used here any more" % (func,))
-
     r_result = hop.r_result
     if r_result.lowleveltype == Bool:
         repr = signed_repr
@@ -320,9 +317,17 @@
     else:
         repr2 = repr
     vlist = hop.inputargs(repr, repr2)
-    hop.exception_cannot_occur()
+    prefix = repr.opprefix
 
-    prefix = repr.opprefix
+    if '_ovf' in func or func.startswith(('mod', 'floordiv')):
+        if prefix+func not in ('int_add_ovf', 'int_add_nonneg_ovf',
+                               'int_sub_ovf', 'int_mul_ovf'):
+            raise TyperError("%r should not be used here any more" % (func,))
+        hop.has_implicit_exception(OverflowError)
+        hop.exception_is_here()
+    else:
+        hop.exception_cannot_occur()
+
     v_res = hop.genop(prefix+func, vlist, resulttype=repr)
     v_res = hop.llops.convertvar(v_res, repr, r_result)
     return v_res
@@ -533,59 +538,6 @@
     return ll_lllong_mod(x, y)
 
 
-# ---------- add, sub, mul ----------
-
[email protected]("int.add_ovf(x, y)")
-def ll_int_add_ovf(x, y):
-    r = intmask(r_uint(x) + r_uint(y))
-    if r^x < 0 and r^y < 0:
-        raise OverflowError("integer addition")
-    return r
-
[email protected]("int.add_ovf(x, y)")
-def ll_int_add_nonneg_ovf(x, y):     # y can be assumed >= 0
-    r = intmask(r_uint(x) + r_uint(y))
-    if r < x:
-        raise OverflowError("integer addition")
-    return r
-
[email protected]("int.sub_ovf(x, y)")
-def ll_int_sub_ovf(x, y):
-    r = intmask(r_uint(x) - r_uint(y))
-    if r^x < 0 and r^~y < 0:
-        raise OverflowError("integer subtraction")
-    return r
-
[email protected]("int.mul_ovf(a, b)")
-def ll_int_mul_ovf(a, b):
-    if INT_BITS_1 < LLONG_BITS_1:
-        rr = r_longlong(a) * r_longlong(b)
-        r = intmask(rr)
-        if r_longlong(r) != rr:
-            raise OverflowError("integer multiplication")
-        return r
-    else:
-        longprod = intmask(a * b)
-        doubleprod = float(a) * float(b)
-        doubled_longprod = float(longprod)
-
-        # Fast path for normal case:  small multiplicands, and no info
-        # is lost in either method.
-        if doubled_longprod == doubleprod:
-            return longprod
-
-        # Somebody somewhere lost info.  Close enough, or way off?  Note
-        # that a != 0 and b != 0 (else doubled_longprod == doubleprod == 0).
-        # The difference either is or isn't significant compared to the
-        # true value (of which doubleprod is a good approximation).
-        # absdiff/absprod <= 1/32 iff 32 * absdiff <= absprod -- 5 good
-        # bits is "close enough"
-        if 32.0 * abs(doubled_longprod - doubleprod) <= abs(doubleprod):
-            return longprod
-
-        raise OverflowError("integer multiplication")
-
-
 # ---------- lshift, neg, abs ----------
 
 def ll_int_lshift_ovf(x, y):
diff --git a/rpython/rtyper/test/test_rint.py b/rpython/rtyper/test/test_rint.py
--- a/rpython/rtyper/test/test_rint.py
+++ b/rpython/rtyper/test/test_rint.py
@@ -6,6 +6,7 @@
 from rpython.rlib.rarithmetic import ovfcheck, r_int64, intmask, int_between
 from rpython.rlib import objectmodel
 from rpython.rtyper.test.tool import BaseRtypingTest
+from rpython.flowspace.model import summary
 
 
 class TestSnippet(object):
@@ -380,6 +381,8 @@
             except OverflowError:
                 return 1
             return a
+        t, rtyper, graph = self.gengraph(f, [int])
+        assert summary(graph).get('int_add_nonneg_ovf') == 2
         res = self.interpret(f, [-3])
         assert res == 144
         res = self.interpret(f, [sys.maxint-50])
diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py
--- a/rpython/translator/c/genc.py
+++ b/rpython/translator/c/genc.py
@@ -800,6 +800,7 @@
         srcdir / 'debug_traceback.c',  # ifdef HAVE_RTYPER
         srcdir / 'asm.c',
         srcdir / 'instrument.c',
+        srcdir / 'int.c',
         srcdir / 'stack.c',
         srcdir / 'threadlocal.c',
     ]
diff --git a/rpython/translator/c/src/exception.c 
b/rpython/translator/c/src/exception.c
--- a/rpython/translator/c/src/exception.c
+++ b/rpython/translator/c/src/exception.c
@@ -32,6 +32,13 @@
                RPyClearException();                                    \
        } while (0)
 
+/* implementations */
+
+void _RPyRaiseSimpleException(RPYTHON_EXCEPTION rexc)
+{
+       RPyRaiseException(RPYTHON_TYPE_OF_EXC_INST(rexc), rexc);
+}
+
 
 /******************************************************************/
 #endif                                             /* HAVE_RTYPER */
diff --git a/rpython/translator/c/src/exception.h 
b/rpython/translator/c/src/exception.h
--- a/rpython/translator/c/src/exception.h
+++ b/rpython/translator/c/src/exception.h
@@ -35,4 +35,9 @@
                RPyClearException();                                    \
        } while (0)
 
+/* prototypes */
+
+RPY_EXTERN
+void _RPyRaiseSimpleException(RPYTHON_EXCEPTION rexc);
+
 #endif
diff --git a/rpython/translator/c/src/int.c b/rpython/translator/c/src/int.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/c/src/int.c
@@ -0,0 +1,45 @@
+#include "common_header.h"
+#include "structdef.h"
+#include "forwarddecl.h"
+#include "preimpl.h"
+#include <src/int.h>
+#include <src/support.h>
+#include <src/exception.h>
+
+/* adjusted from intobject.c, Python 2.3.3 */
+
+long long op_llong_mul_ovf(long long a, long long b)
+{
+    double doubled_longprod;   /* (double)longprod */
+    double doubleprod;         /* (double)a * (double)b */
+    long long longprod;
+
+    longprod = a * b;
+    doubleprod = (double)a * (double)b;
+    doubled_longprod = (double)longprod;
+
+    /* Fast path for normal case:  small multiplicands, and no info
+       is lost in either method. */
+    if (doubled_longprod == doubleprod)
+       return longprod;
+
+    /* Somebody somewhere lost info.  Close enough, or way off?  Note
+       that a != 0 and b != 0 (else doubled_longprod == doubleprod == 0).
+       The difference either is or isn't significant compared to the
+       true value (of which doubleprod is a good approximation).
+    */
+    {
+       const double diff = doubled_longprod - doubleprod;
+       const double absdiff = diff >= 0.0 ? diff : -diff;
+       const double absprod = doubleprod >= 0.0 ? doubleprod :
+           -doubleprod;
+       /* absdiff/absprod <= 1/32 iff
+          32 * absdiff <= absprod -- 5 good bits is "close enough" */
+       if (32.0 * absdiff <= absprod)
+           return longprod;
+
+       FAIL_OVF("integer multiplication");
+       return -1;
+    }
+}
+
diff --git a/rpython/translator/c/src/int.h b/rpython/translator/c/src/int.h
--- a/rpython/translator/c/src/int.h
+++ b/rpython/translator/c/src/int.h
@@ -45,9 +45,36 @@
 /* addition, subtraction */
 
 #define OP_INT_ADD(x,y,r)     r = (x) + (y)
+
+/* cast to avoid undefined behaviour on overflow */
+#define OP_INT_ADD_OVF(x,y,r) \
+        r = (Signed)((Unsigned)x + y); \
+        if ((r^x) < 0 && (r^y) < 0) FAIL_OVF("integer addition")
+
+#define OP_INT_ADD_NONNEG_OVF(x,y,r)  /* y can be assumed >= 0 */ \
+        r = (Signed)((Unsigned)x + y); \
+        if ((r&~x) < 0) FAIL_OVF("integer addition")
+
 #define OP_INT_SUB(x,y,r)     r = (x) - (y)
+
+#define OP_INT_SUB_OVF(x,y,r) \
+        r = (Signed)((Unsigned)x - y); \
+        if ((r^x) < 0 && (r^~y) < 0) FAIL_OVF("integer subtraction")
+
 #define OP_INT_MUL(x,y,r)     r = (x) * (y)
 
+#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG
+#define OP_INT_MUL_OVF(x,y,r) \
+       { \
+               long long _lr = (long long)x * y; \
+               r = (long)_lr; \
+               if (_lr != (long long)r) FAIL_OVF("integer multiplication"); \
+       }
+#else
+#define OP_INT_MUL_OVF(x,y,r) \
+       r = op_llong_mul_ovf(x, y)   /* long == long long */
+#endif
+
 /* shifting */
 
 /* NB. shifting has same limitations as C: the shift count must be
diff --git a/rpython/translator/c/src/support.h 
b/rpython/translator/c/src/support.h
--- a/rpython/translator/c/src/support.h
+++ b/rpython/translator/c/src/support.h
@@ -8,6 +8,8 @@
 #define RUNNING_ON_LLINTERP    0
 #define OP_JIT_RECORD_EXACT_CLASS(i, c, r)  /* nothing */
 
+#define FAIL_OVF(msg) _RPyRaiseSimpleException(RPyExc_OverflowError)
+
 /* Extra checks can be enabled with the RPY_ASSERT or RPY_LL_ASSERT
  * macros.  They differ in the level at which the tests are made.
  * Remember that RPython lists, for example, are implemented as a
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to