Commit 5e89023e authored by Robert Bradshaw's avatar Robert Bradshaw

First pass at int overflow checking.

parent 498b9db0
......@@ -7997,6 +7997,7 @@ class NumBinopNode(BinopNode):
# Binary operation taking numeric arguments.
infix = True
overflow_check = False
def analyse_c_operation(self, env):
type1 = self.operand1.type
......@@ -8007,6 +8008,11 @@ class NumBinopNode(BinopNode):
return
if self.type.is_complex:
self.infix = False
if self.type.is_int and self.operator in ('+', '-', '*', '/'):
self.overflow_check = True
binop = {'+': 'add', '-': 'sub', '*': 'mul', '/': 'div'}[self.operator]
self.func = self.type.overflow_check_binop(binop, env)
self.is_temp = True
if not self.infix or (type1.is_numeric and type2.is_numeric):
self.operand1 = self.operand1.coerce_to(self.type, env)
self.operand2 = self.operand2.coerce_to(self.type, env)
......@@ -8048,8 +8054,26 @@ class NumBinopNode(BinopNode):
return (type1.is_numeric or type1.is_enum) \
and (type2.is_numeric or type2.is_enum)
def generate_result_code(self, code):
super(NumBinopNode, self).generate_result_code(code)
if self.overflow_check:
self.overflow_bit = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
code.putln("%s = 0;" % self.overflow_bit);
code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
code.putln("if (unlikely(%s)) {" % self.overflow_bit)
code.putln('PyErr_Format(PyExc_OverflowError, "value too large");')
code.putln(code.error_goto(self.pos))
code.putln("}")
code.funcstate.release_temp(self.overflow_bit)
def calculate_result_code(self):
if self.infix:
if self.overflow_check:
return "%s(%s, %s, &%s)" % (
self.func,
self.operand1.result(),
self.operand2.result(),
self.overflow_bit)
elif self.infix:
return "(%s %s %s)" % (
self.operand1.result(),
self.operator,
......
......@@ -25,7 +25,7 @@ class BaseType(object):
# This is not entirely robust.
safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
all = []
for c in self.declaration_code("").replace(" ", "__"):
for c in self.declaration_code("").replace("unsigned ", "unsigned_").replace("long long", "long_long").replace(" ", "__"):
if c in safe:
all.append(c)
else:
......@@ -1548,6 +1548,30 @@ class CIntType(CNumericType):
# be negative for signed ints, which is good.
return "0xbad0bad0";
def overflow_check_binop(self, binop, env):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
type = self.declaration_code("")
name = self.specialization_name()
if type in ('int', 'long', 'long long'):
env.use_utility_code(TempitaUtilityCode.load("BaseCaseSigned", "Overflow.c", context={'INT': type, 'NAME': name}))
elif type in ('unsigned int', 'unsigned long', 'unsigned long long'):
env.use_utility_code(TempitaUtilityCode.load("BaseCaseUnsigned", "Overflow.c", context={'UINT': type, 'NAME': name}))
elif self.rank <= 1:
# sizeof(short) < sizeof(int)
return "__Pyx_%s_%s_no_overflow" % (binop, name)
else:
_load_overflow_base(env)
env.use_utility_code(TempitaUtilityCode.load("Binop", "Overflow.c", context={'TYPE': type, 'NAME': name, 'BINOP': binop}))
return "__Pyx_%s_%s_checking_overflow" % (binop, name)
def _load_overflow_base(env):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
for type in ('int', 'long', 'long long'):
env.use_utility_code(TempitaUtilityCode.load("BaseCaseSigned", "Overflow.c", context={'INT': type, 'NAME': type.replace(' ', '_')}))
for type in ('unsigned int', 'unsigned long', 'unsigned long long'):
env.use_utility_code(TempitaUtilityCode.load("BaseCaseUnsigned", "Overflow.c", context={'UINT': type, 'NAME': type.replace(' ', '_')}))
class CAnonEnumType(CIntType):
is_enum = 1
......
/*
These functions provide integer arithmetic with integer checking. They do not
actually raise an exception when an overflow is detected, but rather set a bit
in the overflow parameter. (This parameter may be re-used accross several
arithmetic operations, so should be or-ed rather than assigned to.)
The implementation is divided into two parts, the signed and unsigned basecases,
which is where the magic happens, and a generic template matching a specific
type to an implementation based on its (c-compile-time) size and signedness.
When possible, branching is avoided, and preference is given to speed over
accuracy (a low rate of falsely "detected" overflows are acceptable,
undetected overflows are not).
TODO: Hook up checking.
TODO: Conditionally support 128-bit with intmax_t?
*/
/////////////// Common.proto ///////////////
static int __Pyx_check_twos_complement() {
if (-1 != ~0) {
PyErr_SetString(PyExc_RuntimeError, "Two's complement required for overflow checks.");
return 1;
} else if (sizeof(short) == sizeof(int)) {
PyErr_SetString(PyExc_RuntimeError, "sizeof(short) < sizeof(int) required for overflow checks.");
return 1;
} else {
return 0;
}
}
#define __PYX_IS_UNSIGNED(type) ((type) -1 > 0)
#define __PYX_SIGN_BIT(type) ((unsigned type) 1 << (sizeof(type) * 8 - 1))
#define __PYX_HALF_MAX(type) (((type) 1) << (sizeof(type) * 8 - 2))
#define __PYX_MIN(type) (__PYX_IS_UNSIGNED(type) ? (type) 0 : 0 - __PYX_HALF_MAX(type) - __PYX_HALF_MAX(type))
#define __PYX_MAX(type) (~__PYX_MIN(type))
#define __Pyx_add_no_overflow(a, b, overflow) ((a) + (b))
#define __Pyx_add_const_no_overflow(a, b, overflow) ((a) + (b))
#define __Pyx_sub_no_overflow(a, b, overflow) ((a) - (b))
#define __Pyx_sub_no_const_overflow(a, b, overflow) ((a) - (b))
#define __Pyx_mul_no_overflow(a, b, overflow) ((a) * (b))
#define __Pyx_mul_const_no_overflow(a, b, overflow) ((a) * (b))
#define __Pyx_div_no_overflow(a, b, overflow) ((a) / (b))
#define __Pyx_div_const_no_overflow(a, b, overflow) ((a) / (b))
/////////////// BaseCaseUnsigned.proto ///////////////
static CYTHON_INLINE {{UINT}} __Pyx_add_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
static CYTHON_INLINE {{UINT}} __Pyx_sub_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
static CYTHON_INLINE {{UINT}} __Pyx_div_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow);
// Use these when b is known at compile time.
#define __Pyx_add_const_{{NAME}}_checking_overflow __Pyx_add_{{NAME}}_checking_overflow
#define __Pyx_sub_const_{{NAME}}_checking_overflow __Pyx_sub_{{NAME}}_checking_overflow
static CYTHON_INLINE {{UINT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} constant, int *overflow);
#define __Pyx_div_const_{{NAME}}_checking_overflow __Pyx_div_{{NAME}}_checking_overflow
/////////////// BaseCaseUnsigned ///////////////
static CYTHON_INLINE {{UINT}} __Pyx_add_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
{{UINT}} r = a + b;
*overflow |= r < a;
return r;
}
static CYTHON_INLINE {{UINT}} __Pyx_sub_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
{{UINT}} r = a - b;
*overflow |= r > a;
return r;
}
static CYTHON_INLINE {{UINT}} __Pyx_mul_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
if (sizeof({{UINT}}) < sizeof(long)) {
unsigned long big_r = ((unsigned long) a) * ((unsigned long) b);
{{UINT}} r = ({{UINT}}) big_r;
*overflow |= big_r != r;
return ({{UINT}}) r;
} else if (sizeof({{UINT}}) < sizeof(long long)) {
unsigned long long big_r = ((unsigned long long) a) * ((unsigned long long) b);
{{UINT}} r = ({{UINT}}) big_r;
*overflow |= big_r != r;
return ({{UINT}}) r;
} else {
{{UINT}} prod = a * b;
double dprod = ((double) a) * ((double) b);
// False positives. Yes, the equality is required to avoid false negatives.
*overflow |= dprod >= (double) __PYX_MAX({{UINT}});
return prod;
}
}
static CYTHON_INLINE {{UINT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
if (b > 1) {
*overflow |= a > __PYX_MAX({{UINT}}) / b;
}
return a * b;
}
static CYTHON_INLINE {{UINT}} __Pyx_div_{{NAME}}_checking_overflow({{UINT}} a, {{UINT}} b, int *overflow) {
if (b == 0) {
*overflow |= 1;
return 0;
}
return a / b;
}
/////////////// BaseCaseSigned.proto ///////////////
static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_sub_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_div_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
// Use when b is known at compile time.
static CYTHON_INLINE {{INT}} __Pyx_add_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_sub_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow);
static CYTHON_INLINE {{INT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} constant, int *overflow);
#define __Pyx_div_const_{{NAME}}_checking_overflow __Pyx_div_{{NAME}}_checking_overflow
/////////////// BaseCaseSigned ///////////////
#define TOP_TWO_BITS(value, type) (value & ((unsigned type)3 << (sizeof(type) * 8 - 2)))
static CYTHON_INLINE {{INT}} __Pyx_add_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (sizeof({{INT}}) < sizeof(long)) {
long big_r = ((long) a) + ((long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
} else if (sizeof({{INT}}) < sizeof(long long)) {
long long big_r = ((long long) a) + ((long long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
} else {
// Signed overflow undefined, but unsigned is well defined.
{{INT}} r = ({{INT}}) ((unsigned {{INT}}) a + (unsigned {{INT}}) b);
// sign(a) == sign(b) != sign(r)
{{INT}} sign_a = __PYX_SIGN_BIT({{INT}}) & a;
{{INT}} sign_b = __PYX_SIGN_BIT({{INT}}) & b;
{{INT}} sign_r = __PYX_SIGN_BIT({{INT}}) & r;
*overflow |= (sign_a == sign_b) & (sign_a != sign_r);
return r;
}
}
static CYTHON_INLINE {{INT}} __Pyx_add_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (b > 0) {
*overflow |= a > __PYX_MAX({{INT}}) - b;
} else if (b < 0) {
*overflow |= a < __PYX_MIN({{INT}}) - b;
}
return a + b;
}
static CYTHON_INLINE {{INT}} __Pyx_sub_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
*overflow |= b == __PYX_MIN({{INT}});
return __Pyx_add_{{NAME}}_checking_overflow(a, -b, overflow);
}
static CYTHON_INLINE {{INT}} __Pyx_sub_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
*overflow |= b == __PYX_MIN({{INT}});
return __Pyx_add_const_{{NAME}}_checking_overflow(a, -b, overflow);
}
static CYTHON_INLINE {{INT}} __Pyx_mul_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (sizeof({{INT}}) < sizeof(long)) {
long big_r = ((long) a) * ((long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
} else if (sizeof({{INT}}) < sizeof(long long)) {
long long big_r = ((long long) a) * ((long long) b);
{{INT}} r = ({{INT}}) big_r;
*overflow |= big_r != r;
return ({{INT}}) r;
} else {
{{INT}} prod = a * b;
double dprod = ((double) a) * ((double) b);
// False positives.
*overflow |= fabs(dprod) > (double) __PYX_MAX({{INT}});
return prod;
}
}
static CYTHON_INLINE {{INT}} __Pyx_mul_const_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (b > 1) {
*overflow |= a > __PYX_MAX({{INT}}) / b;
*overflow |= a < __PYX_MIN({{INT}}) / b;
} else if (b == -1) {
*overflow |= a == __PYX_MIN({{INT}});
} else if (b < -1) {
*overflow |= a > __PYX_MIN({{INT}}) / b;
*overflow |= a < __PYX_MAX({{INT}}) / b;
}
return a * b;
}
static CYTHON_INLINE {{INT}} __Pyx_div_{{NAME}}_checking_overflow({{INT}} a, {{INT}} b, int *overflow) {
if (b == 0) {
*overflow |= 1;
return 0;
}
*overflow |= (a == __PYX_MIN({{INT}})) & (b == -1);
return a / b;
}
/////////////// SizeCheck.proto ///////////////
static int __Pyx_check_sane_{{TYPE}}() {
if (sizeof({{TYPE}}) == sizeof(int) ||
sizeof({{TYPE}}) == sizeof(long) ||
sizeof({{TYPE}}) == sizeof(long long)) {
return 0;
} else {
PyErr_Format(PyExc_RuntimeError, "Bad size for int type %s: %d", "{{TYPE}}", sizeof({{TYPE}}));
return 1;
}
}
/////////////// Binop.proto ///////////////
static CYTHON_INLINE {{TYPE}} __Pyx_{{BINOP}}_{{NAME}}_checking_overflow({{TYPE}} a, {{TYPE}} b, int *overflow);
/////////////// Binop ///////////////
static CYTHON_INLINE {{TYPE}} __Pyx_{{BINOP}}_{{NAME}}_checking_overflow({{TYPE}} a, {{TYPE}} b, int *overflow) {
if (sizeof({{TYPE}}) < sizeof(int)) {
return __Pyx_{{BINOP}}_no_overflow(a, b, overflow);
} else if (__PYX_IS_UNSIGNED({{TYPE}})) {
if (sizeof({{TYPE}}) == sizeof(int)) {
return __Pyx_{{BINOP}}_unsigned_int_checking_overflow(a, b, overflow);
} else if (sizeof({{TYPE}}) == sizeof(long)) {
return __Pyx_{{BINOP}}_unsigned_long_checking_overflow(a, b, overflow);
} else if (sizeof({{TYPE}}) == sizeof(long long)) {
return __Pyx_{{BINOP}}_unsigned_long_long_checking_overflow(a, b, overflow);
} else {
abort(); return 0; // handled elsewhere
}
} else {
if (sizeof({{TYPE}}) == sizeof(int)) {
return __Pyx_{{BINOP}}_int_checking_overflow(a, b, overflow);
} else if (sizeof({{TYPE}}) == sizeof(long)) {
return __Pyx_{{BINOP}}_long_checking_overflow(a, b, overflow);
} else if (sizeof({{TYPE}}) == sizeof(long long)) {
return __Pyx_{{BINOP}}_long_long_checking_overflow(a, b, overflow);
} else {
abort(); return 0; // handled elsewhere
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment