From: Robert Bradshaw Date: Tue, 31 Jul 2012 07:14:56 +0000 (-0700) Subject: First pass at int overflow checking. X-Git-Tag: 0.18b1~191^2~8 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5e89023e2f5925d954b3e425a35a75f436ae3210;p=platform%2Fupstream%2Fpython-cython.git First pass at int overflow checking. --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c476ac2..6cfc19e 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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, diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 74f53f1..ef6db92 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -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 diff --git a/Cython/Utility/Overflow.c b/Cython/Utility/Overflow.c new file mode 100644 index 0000000..00d0ff3 --- /dev/null +++ b/Cython/Utility/Overflow.c @@ -0,0 +1,262 @@ +/* +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 + } + } +}