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