From d962f60c3dc6c195b683693d1d2f62165324b8ef Mon Sep 17 00:00:00 2001 From: Oldes Date: Tue, 24 Oct 2017 21:52:36 +0200 Subject: [PATCH] ATRONIX: Move integer arithmetic operations into their own functions Such that they can be used elsewhere. Also makes it possible to take advantages of builtin functions provided by compilers like clang. --- src/core/f-int.c | 169 ++++++++++++++++++++++++++++++++++++ src/core/f-random.c | 2 +- src/core/t-integer.c | 32 ++----- src/include/reb-c.h | 154 +++++++++++++++++++++++++++++--- src/include/sys-int-funcs.h | 133 ++++++++++++++++++++++++++++ src/tools/file-base.r | 1 + 6 files changed, 453 insertions(+), 38 deletions(-) create mode 100644 src/core/f-int.c create mode 100644 src/include/sys-int-funcs.h diff --git a/src/core/f-int.c b/src/core/f-int.c new file mode 100644 index 0000000000..3b7653ae19 --- /dev/null +++ b/src/core/f-int.c @@ -0,0 +1,169 @@ +/*********************************************************************** +** +** REBOL [R3] Language Interpreter and Run-time Environment +** +** Copyright 2014 Atronix Engineering, Inc +** REBOL is a trademark of REBOL Technologies +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +************************************************************************ +** +** Module: f-int.c +** Summary: integer arithmetic functions +** Section: functional +** Author: Shixin Zeng +** Notes: Based on original code in t-integer.c +** +***********************************************************************/ + +#include "reb-c.h" +#include "sys-int-funcs.h" + +REBOOL reb_i32_add_overflow(i32 x, i32 y, i32 *sum) +{ + i64 sum64 = (i64)x + (i64)y; + if (sum64 > MAX_I32 || sum64 < MIN_I32) return TRUE; + *sum = (i32)sum64; + return FALSE; +} + +REBOOL reb_u32_add_overflow(u32 x, u32 y, u32 *sum) +{ + u64 s = (u64)x + (u64)y; + if (s > MAX_I32) return TRUE; + *sum = (u32)s; + return FALSE; +} + +REBOOL reb_i64_add_overflow(i64 x, i64 y, i64 *sum) +{ + *sum = (REBU64)x + (REBU64)y; /* never overflow with unsigned integers*/ + if (((x < 0) == (y < 0)) + && ((x < 0) != (*sum < 0))) return TRUE; + return FALSE; +} + +REBOOL reb_u64_add_overflow(u64 x, u64 y, u64 *sum) +{ + *sum = x + y; + if (*sum < x || *sum < y) return TRUE; + return FALSE; +} + +REBOOL reb_i32_sub_overflow(i32 x, i32 y, i32 *diff) +{ + *diff = (i64)x - (i64)y; + if (((x < 0) != (y < 0)) && ((x < 0) != (*diff < 0))) return TRUE; + + return FALSE; +} + +REBOOL reb_i64_sub_overflow(i64 x, i64 y, i64 *diff) +{ + *diff = (REBU64)x - (REBU64)y; + if (((x < 0) != (y < 0)) && ((x < 0) != (*diff < 0))) return TRUE; + + return FALSE; +} + +REBOOL reb_i32_mul_overflow(i32 x, i32 y, i32 *prod) +{ + i64 p = (i64)x * (i64)y; + if (p > MAX_I32 || p < MIN_I32) return TRUE; + *prod = (i32)p; + return FALSE; +} + +REBOOL reb_u32_mul_overflow(u32 x, u32 y, u32 *prod) +{ + u64 p = (u64)x * (u64)y; + if (p > MAX_U32) return TRUE; + *prod = (u32)p; + return FALSE; +} + +REBOOL reb_i64_mul_overflow(i64 x, i64 y, i64 *prod) +{ + REBFLG sgn; + u64 p = 0; + + sgn = (x < 0); + if (sgn) { + if (x == MIN_I64) { + switch (y) { + case 0: + *prod = 0; + return 0; + case 1: + *prod = x; + return 0; + default: + return 1; + } + } + x = -x; /* undefined when x == MIN_I64 */ + } + if (y < 0) { + sgn = !sgn; + if (y == MIN_I64) { + switch (x) { + case 0: + *prod = 0; + return 0; + case 1: + if (!sgn) { + return 1; + } else { + *prod = y; + return 0; + } + default: + return 1; + } + } + y = -y; /* undefined when y == MIN_I64 */ + } + + if (REB_U64_MUL_OF(x, y, (u64 *)&p) + || (!sgn && p > MAX_I64) + || (sgn && p - 1 > MAX_I64)) return TRUE; /* assumes 2's complements */ + + if (sgn && p == (u64)MIN_I64) { + *prod = MIN_I64; + return FALSE; + } + + *prod = sgn? -(i64)p : p; + return FALSE; +} + +REBOOL reb_u64_mul_overflow(u64 x, u64 y, u64 *prod) +{ + u64 x0, y0, x1, y1; + u64 b = U64_C(1) << 32; + u64 tmp = 0; + x1 = x >> 32; + x0 = (u32)x; + y1 = y >> 32; + y0 = (u32)y; + + /* p = (x1 * y1) * b^2 + (x0 * y1 + x1 * y0) * b + x0 * y0 */ + + if (x1 && y1) return TRUE; /* (x1 * y1) * b^2 overflows */ + + tmp = (x0 * y1 + x1 * y0); /* never overflow, because x1 * y1 == 0 */ + if (tmp >= b) return TRUE; /*(x0 * y1 + x1 * y0) * b overflows */ + + return REB_U64_ADD_OF(tmp << 32, x0 * y0, prod); +} diff --git a/src/core/f-random.c b/src/core/f-random.c index 99cacc3f8b..471b39f115 100644 --- a/src/core/f-random.c +++ b/src/core/f-random.c @@ -149,7 +149,7 @@ static REBI64 ran_arr_cycle() return tmp; } -#define MAX_U64 ((REBU64)(REBI64)-1) +//#define MAX_U64 ((REBU64)(REBI64)-1) //defined in reb-c.h /*********************************************************************** ** */ REBI64 Random_Range(REBI64 r, REBFLG secure) diff --git a/src/core/t-integer.c b/src/core/t-integer.c index 2585633a2d..4288f0ac3b 100644 --- a/src/core/t-integer.c +++ b/src/core/t-integer.c @@ -29,6 +29,7 @@ #include "sys-core.h" #include "sys-deci-funcs.h" +#include "sys-int-funcs.h" /*********************************************************************** @@ -110,41 +111,18 @@ switch (action) { case A_ADD: - anum = (REBU64)num + (REBU64)arg; - if ( - ((num < 0) == (arg < 0)) && ((num < 0) != (anum < 0)) - ) Trap0(RE_OVERFLOW); + if (REB_I64_ADD_OF(num, arg, &anum)) Trap0(RE_OVERFLOW); num = anum; break; case A_SUBTRACT: - anum = (REBU64)num - (REBU64)arg; - if ( - ((num < 0) != (arg < 0)) && ((num < 0) != (anum < 0)) - ) Trap0(RE_OVERFLOW); + if (REB_I64_SUB_OF(num, arg, &anum)) Trap0(RE_OVERFLOW); num = anum; break; case A_MULTIPLY: - a = num; - sgn = (num < 0); - if (sgn) a = -a; - b = arg; - if (arg < 0) { - sgn = !sgn; - b = -b; - } - p = a * b; - a1 = a>>32; - a0 = a; - b1 = b>>32; - b0 = b; - if ( - (a1 && b1) - || ((REBU64)a0 * b1 + (REBU64)a1 * b0 > p >> 32) - || ((p > (REBU64)MAX_I64) && (!sgn || (p > -(REBU64)MIN_I64))) - ) Trap0(RE_OVERFLOW); - num = sgn ? -p : p; + if (REB_I64_MUL_OF(num, arg, &p)) Trap0(RE_OVERFLOW); + num = p; break; case A_DIVIDE: diff --git a/src/include/reb-c.h b/src/include/reb-c.h index 7b805d9d8d..052583d850 100644 --- a/src/include/reb-c.h +++ b/src/include/reb-c.h @@ -68,13 +68,31 @@ typedef uintptr_t REBUPT; // unsigned counterpart of void* #define MAX_I64 INT64_MAX #define MIN_I64 INT64_MIN +#define I8_C(c) INT8_C(c) +#define U8_C(c) UINT8_C(c) + +#define I16_C(c) INT16_C(c) +#define U16_C(c) UINT16_C(c) + +#define I32_C(c) INT32_C(c) +#define U32_C(c) UINT32_C(c) + +#define I64_C(c) INT64_C(c) +#define U64_C(c) UINT64_C(c) + #else /* C-code types: C99 definitions unavailable, do it ourselves */ typedef char i8; typedef unsigned char u8; +#define I8(c) c +#define U8(c) c + typedef short i16; typedef unsigned short u16; +#define I16(c) c +#define U16(c) c + #ifdef __LP64__ typedef int i32; typedef unsigned int u32; @@ -82,12 +100,19 @@ typedef unsigned int u32; typedef long i32; typedef unsigned long u32; #endif +#define I32_C(c) c +#define U32_C(c) c ## U + #ifdef ODD_INT_64 // Windows VC6 nonstandard typing for 64 bits typedef _int64 i64; typedef unsigned _int64 u64; +#define I64_C(c) c ## I64 +#define U64_C(c) c ## U64 #else typedef long long i64; typedef unsigned long long u64; +#define I64_C(c) c ## LL +#define U64_C(c) c ## ULL #endif #ifdef __LLP64__ typedef long long REBIPT; // integral counterpart of void* @@ -97,19 +122,17 @@ typedef long REBIPT; // integral counterpart of void* typedef unsigned long REBUPT; // unsigned counterpart of void* #endif -#define MAX_I32 ((i32)0x7fffffff) -#define MIN_I32 ((i32)0x80000000) -#ifdef HAS_LL_CONSTS -#define MAX_I64 ((i64)0x7fffffffffffffffLL) -#define MIN_I64 ((i64)0x8000000000000000LL) -#else -#define MAX_I64 ((i64)0x7fffffffffffffffI64) -#define MIN_I64 ((i64)0x8000000000000000I64) -#endif +#define MAX_I32 I32_C(0x7fffffff) +#define MIN_I32 ((i32)I32_C(0x80000000)) //compiler treats the hex literal as unsigned without casting +#define MAX_I64 I64_C(0x7fffffffffffffff) +#define MIN_I64 ((i64)I64_C(0x8000000000000000)) //compiler treats the hex literal as unsigned without casting #endif /* C-code types */ +#define MAX_U32 U32_C(0xffffffff) +#define MAX_U64 U64_C(0xffffffffffffffff) + #ifndef DEF_UINT // some systems define it, don't define it again typedef unsigned int uint; #endif @@ -180,7 +203,7 @@ enum { ** ***********************************************************************/ -#define MAX_INT_LEN 20 +#define MAX_INT_LEN 21 #define MAX_HEX_LEN 16 #ifdef ITOA64 // Integer to ascii conversion @@ -277,3 +300,114 @@ typedef void(*CFUNC)(void *); #define MAKE_STR(n) (REBCHR*)(malloc((n) * sizeof(REBCHR))) // OS chars! #define ROUND_TO_INT(d) (REBINT)(floor((d) + 0.5)) + +// +// CASTING MACROS +// +// The following code and explanation is from "Casts for the Masses (in C)": +// +// http://blog.hostilefork.com/c-casts-for-the-masses/ +// +// But debug builds don't inline functions--not even no-op ones whose sole +// purpose is static analysis. This means the cast macros add a headache when +// stepping through the debugger, and also they consume a measurable amount +// of runtime. Hence we sacrifice cast checking in the debug builds...and the +// release C++ builds on Travis are relied upon to do the proper optimizations +// as well as report any static analysis errors. +// + +#if !defined(__cplusplus) || !defined(NDEBUG) + /* These macros are easier-to-spot variants of the parentheses cast. + * The 'm_cast' is when getting [M]utablity on a const is okay (RARELY!) + * Plain 'cast' can do everything else (except remove volatile) + * The 'c_cast' helper ensures you're ONLY adding [C]onst to a value + */ + #define m_cast(t,v) ((t)(v)) + #define cast(t,v) ((t)(v)) + #define c_cast(t,v) ((t)(v)) + /* + * Q: Why divide roles? A: Frequently, input to cast is const but you + * "just forget" to include const in the result type, gaining mutable + * access. Stray writes to that can cause even time-traveling bugs, with + * effects *before* that write is made...due to "undefined behavior". + */ +#elif defined(__cplusplus) /* for gcc -Wundef */ && (__cplusplus < 201103L) + /* Well-intentioned macros aside, C has no way to enforce that you can't + * cast away a const without m_cast. C++98 builds can do that, at least: + */ + #define m_cast(t,v) const_cast(v) + #define cast(t,v) ((t)(v)) + #define c_cast(t,v) const_cast(v) +#else + /* __cplusplus >= 201103L has C++11's type_traits, where we get some + * actual power. cast becomes a reinterpret_cast for pointers and a + * static_cast otherwise. We ensure c_cast added a const and m_cast + * removed one, and that neither affected volatility. + */ + template + T m_cast_helper(V v) { + static_assert(!std::is_const::value, + "invalid m_cast() - requested a const type for output result"); + static_assert(std::is_volatile::value == std::is_volatile::value, + "invalid m_cast() - input and output have mismatched volatility"); + return const_cast(v); + } + /* reinterpret_cast for pointer to pointer casting (non-class source)*/ + template::value + && (std::is_pointer::value || std::is_pointer::value) + >::type* = nullptr> + T cast_helper(V v) { return reinterpret_cast(v); } + /* static_cast for non-pointer to non-pointer casting (non-class source) */ + template::value + && (!std::is_pointer::value && !std::is_pointer::value) + >::type* = nullptr> + T cast_helper(V v) { return static_cast(v); } + /* use static_cast on all classes, to go through their cast operators */ + template::value + >::type* = nullptr> + T cast_helper(V v) { return static_cast(v); } + template + T c_cast_helper(V v) { + static_assert(!std::is_const::value, + "invalid c_cast() - did not request const type for output result"); + static_assert(std::is_volatile::value == std::is_volatile::value, + "invalid c_cast() - input and output have mismatched volatility"); + return const_cast(v); + } + #define m_cast(t, v) m_cast_helper(v) + #define cast(t, v) cast_helper(v) + #define c_cast(t, v) c_cast_helper(v) +#endif +#if defined(NDEBUG) || !defined(REB_DEF) + /* These [S]tring and [B]inary casts are for "flips" between a 'char *' + * and 'unsigned char *' (or 'const char *' and 'const unsigned char *'). + * Being single-arity with no type passed in, they are succinct to use: + */ + #define s_cast(b) ((char *)(b)) + #define cs_cast(b) ((const char *)(b)) + #define b_cast(s) ((unsigned char *)(s)) + #define cb_cast(s) ((const unsigned char *)(s)) + /* + * In C++ (or C with '-Wpointer-sign') this is powerful. 'char *' can + * be used with string functions like strlen(). Then 'unsigned char *' + * can be saved for things you shouldn't _accidentally_ pass to functions + * like strlen(). (One GREAT example: encoded UTF-8 byte strings.) + */ +#else + /* We want to ensure the input type is what we thought we were flipping, + * particularly not the already-flipped type. Instead of type_traits, 4 + * functions check in both C and C++ (here only during Debug builds): + * (Definitions are in n-strings.c w/prototypes built by make-headers.r) + */ + #define s_cast(b) s_cast_(b) + #define cs_cast(b) cs_cast_(b) + #define b_cast(s) b_cast_(s) + #define cb_cast(s) cb_cast_(s) +#endif + diff --git a/src/include/sys-int-funcs.h b/src/include/sys-int-funcs.h new file mode 100644 index 0000000000..304e682ad4 --- /dev/null +++ b/src/include/sys-int-funcs.h @@ -0,0 +1,133 @@ +/*********************************************************************** +** +** REBOL [R3] Language Interpreter and Run-time Environment +** +** Copyright 2014 Atronix Engineering, Inc. +** REBOL is a trademark of REBOL Technologies +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +************************************************************************ +** +** Summary: Integer Datatype Functions +** Module: sys-int-funcs.h +** Notes: +*/ + +#ifndef __SYS_INT_FUNCS_H_ +#define __SYS_INT_FUNCS_H_ + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifdef __GNUC__ +#define GCC_VERSION_AT_LEAST(m, n) (__GNUC__ >= (m) && __GNUC_MINOR__ >= (n)) +#else +#define GCC_VERSION_AT_LEAST(m, n) 0 +#endif + +#if __has_builtin(__builtin_sadd_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#define REB_I32_ADD_OF(x, y, sum) __builtin_sadd_overflow((x), (y), (sum)) +#else +REBOOL reb_i32_add_overflow(i32 x, i32 y, i32 *sum); +#define REB_I32_ADD_OF(x, y, sum) reb_i32_add_overflow((x), (y), (sum)) +#endif + +#if __has_builtin(__builtin_uadd_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#define REB_U32_ADD_OF(x, y, sum) __builtin_uadd_overflow((x), (y), (sum)) +#else +REBOOL reb_u32_add_overflow(u32 x, u32 y, u32 *sum); +#define REB_U32_ADD_OF(x, y, sum) reb_u32_add_overflow((x), (y), (sum)) +#endif + +#if __has_builtin(__builtin_saddl_overflow) && __has_builtin(__builtin_saddll_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#ifdef __LP64__ +#define REB_I64_ADD_OF(x, y, sum) __builtin_saddl_overflow((x), (y), (sum)) +#else // presumably __LLP64__ or __LP32__ +#define REB_I64_ADD_OF(x, y, sum) __builtin_saddll_overflow((x), (y), (sum)) +#endif //__LP64__ +#else +REBOOL reb_i64_add_overflow(i64 x, i64 y, i64 *sum); +#define REB_I64_ADD_OF(x, y, sum) reb_i64_add_overflow((x), (y), (sum)) +#endif + +#if __has_builtin(__builtin_uaddl_overflow) && __has_builtin(__builtin_uaddll_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#ifdef __LP64__ +#define REB_U64_ADD_OF(x, y, sum) __builtin_uaddl_overflow((x), (y), (sum)) +#else // presumably __LLP64__ or __LP32__ +#define REB_U64_ADD_OF(x, y, sum) __builtin_uaddll_overflow((x), (y), (sum)) +#endif //__LP64__ +#else +REBOOL reb_u64_add_overflow(u64 x, u64 y, u64 *sum); +#define REB_U64_ADD_OF(x, y, sum) reb_u64_add_overflow((x), (y), (sum)) +#endif + +#if __has_builtin(__builtin_ssub_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#define REB_I32_SUB_OF(x, y, diff) __builtin_ssub_overflow((x), (y), (diff)) +#else +REBOOL reb_i32_sub_overflow(i32 x, i32 y, i32 *diff); +#define REB_I32_SUB_OF(x, y, diff) reb_i32_sub_overflow((x), (y), (diff)) +#endif + +#if __has_builtin(__builtin_ssubl_overflow) && __has_builtin(__builtin_ssubll_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#ifdef __LP64__ +#define REB_I64_SUB_OF(x, y, diff) __builtin_ssubl_overflow((x), (y), (diff)) +#else // presumably __LLP64__ or __LP32__ +#define REB_I64_SUB_OF(x, y, diff) __builtin_ssubll_overflow((x), (y), (diff)) +#endif //__LP64__ +#else +REBOOL reb_i64_sub_overflow(i64 x, i64 y, i64 *diff); +#define REB_I64_SUB_OF(x, y, diff) reb_i64_sub_overflow((x), (y), (diff)) +#endif + +#if __has_builtin(__builtin_smul_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#define REB_I32_MUL_OF(x, y, prod) __builtin_smul_overflow((x), (y), (prod)) +#else +REBOOL reb_i32_mul_overflow(i32 x, i32 y, i32 *prod); +#define REB_I32_MUL_OF(x, y, prod) reb_i32_mul_overflow((x), (y), (prod)) +#endif + +#if __has_builtin(__builtin_umul_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#define REB_U32_MUL_OF(x, y, prod) __builtin_umul_overflow((x), (y), (prod)) +#else +REBOOL reb_u32_mul_overflow(u32 x, u32 y, u32 *prod); +#define REB_U32_MUL_OF(x, y, prod) reb_u32_mul_overflow((x), (y), (prod)) +#endif + +#if __has_builtin(__builtin_smull_overflow) && __has_builtin(__builtin_smulll_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#ifdef __LP64__ +#define REB_I64_MUL_OF(x, y, prod) __builtin_smull_overflow((x), (y), (prod)) +#elif !defined(__clang__) //__builtin_smulll_overflow doesn't work on 32-bit systems yet, causing undefined reference to __mulodi4 +#define REB_I64_MUL_OF(x, y, prod) __builtin_smulll_overflow((x), (y), (prod)) +#else +REBOOL reb_i64_mul_overflow(i64 x, i64 y, i64 *prod); +#define REB_I64_MUL_OF(x, y, prod) reb_i64_mul_overflow((x), (y), (prod)) +#endif //__LP64__ +#else +REBOOL reb_i64_mul_overflow(i64 x, i64 y, i64 *prod); +#define REB_I64_MUL_OF(x, y, prod) reb_i64_mul_overflow((x), (y), (prod)) +#endif + +#if __has_builtin(__builtin_umull_overflow) && __has_builtin(__builtin_umulll_overflow) || GCC_VERSION_AT_LEAST(5, 1) +#ifdef __LP64__ +#define REB_U64_MUL_OF(x, y, prod) __builtin_umull_overflow((x), (y), (prod)) +#else // presumably __LLP64__ or __LP32__ +#define REB_U64_MUL_OF(x, y, prod) __builtin_umulll_overflow((x), (y), (prod)) +#endif //__LP64__ +#else +REBOOL reb_u64_mul_overflow(u64 x, u64 y, u64 *prod); +#define REB_U64_MUL_OF(x, y, prod) reb_u64_mul_overflow((x), (y), (prod)) +#endif + +#endif //__SYS_INT_FUNCS_H_ diff --git a/src/tools/file-base.r b/src/tools/file-base.r index 67d435cdac..62cce9758d 100644 --- a/src/tools/file-base.r +++ b/src/tools/file-base.r @@ -36,6 +36,7 @@ core: [ f-dtoa.c f-enbase.c f-extension.c + f-int.c f-math.c f-modify.c f-qsort.c