diff options
Diffstat (limited to 'include/ruby/internal/memory.h')
| -rw-r--r-- | include/ruby/internal/memory.h | 147 |
1 files changed, 124 insertions, 23 deletions
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h index 6884db195d..cd099f85db 100644 --- a/include/ruby/internal/memory.h +++ b/include/ruby/internal/memory.h @@ -40,7 +40,12 @@ #if defined(_MSC_VER) && defined(_WIN64) # include <intrin.h> +# if defined(_M_AMD64) # pragma intrinsic(_umul128) +# endif +# if defined(_M_ARM64) +# pragma intrinsic(__umulh) +# endif #endif #include "ruby/internal/attr/alloc_size.h" @@ -56,13 +61,14 @@ #include "ruby/internal/has/builtin.h" #include "ruby/internal/stdalign.h" #include "ruby/internal/stdbool.h" +#include "ruby/internal/stdckdint.h" #include "ruby/internal/xmalloc.h" #include "ruby/backward/2/limits.h" #include "ruby/backward/2/long_long.h" #include "ruby/backward/2/assume.h" #include "ruby/defines.h" -/** @cond INTENAL_MACRO */ +/** @cond INTERNAL_MACRO */ /* Make alloca work the best possible way. */ #if defined(alloca) @@ -402,7 +408,8 @@ typedef uint128_t DSIZE_T; /** * @private * - * This is an implementation detail of rbimpl_size_mul_overflow(). + * This is an implementation detail of rbimpl_size_mul_overflow() and + * rbimpl_size_add_overflow(). * * @internal * @@ -410,9 +417,9 @@ typedef uint128_t DSIZE_T; * nothing more than std::variant<std::size_t> if we could use recent C++, but * reality is we cannot. */ -struct rbimpl_size_mul_overflow_tag { - bool left; /**< Whether overflow happened or not. */ - size_t right; /**< Multiplication result. */ +struct rbimpl_size_overflow_tag { + bool overflowed; /**< Whether overflow happened or not. */ + size_t result; /**< Calculation result. */ }; RBIMPL_SYMBOL_EXPORT_BEGIN() @@ -482,6 +489,18 @@ RBIMPL_ATTR_NORETURN() */ void ruby_malloc_size_overflow(size_t x, size_t y); +RBIMPL_ATTR_NORETURN() +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @exception rb_eArgError `x` + `y` would integer overflow. + */ +void ruby_malloc_add_size_overflow(size_t x, size_t y); + #ifdef HAVE_RB_GC_GUARDED_PTR_VAL volatile VALUE *rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val); #endif @@ -554,39 +573,46 @@ RBIMPL_ATTR_CONST() * * @param[in] x Arbitrary value. * @param[in] y Arbitrary value. - * @return `{ left, right }`, where `left` is whether there is an integer - * overflow or not, and `right` is a (possibly overflowed) result - * of `x` * `y`. + * @return `{ overflowed, result }`, where `overflowed` is whether there is + * an integer overflow or not, and `result` is a (possibly + * overflowed) result of `x` * `y`. * * @internal * * This is in fact also an implementation detail of ruby_xmalloc2() etc. */ -static inline struct rbimpl_size_mul_overflow_tag +static inline struct rbimpl_size_overflow_tag rbimpl_size_mul_overflow(size_t x, size_t y) { - struct rbimpl_size_mul_overflow_tag ret = { false, 0, }; + struct rbimpl_size_overflow_tag ret = { false, 0, }; + +#if defined(ckd_mul) + ret.overflowed = ckd_mul(&ret.result, x, y); -#if RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) - ret.left = __builtin_mul_overflow(x, y, &ret.right); +#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) + ret.overflowed = __builtin_mul_overflow(x, y, &ret.result); #elif defined(DSIZE_T) RB_GNUC_EXTENSION DSIZE_T dx = x; RB_GNUC_EXTENSION DSIZE_T dy = y; RB_GNUC_EXTENSION DSIZE_T dz = dx * dy; - ret.left = dz > SIZE_MAX; - ret.right = RBIMPL_CAST((size_t)dz); + ret.overflowed = dz > SIZE_MAX; + ret.result = RBIMPL_CAST((size_t)dz); -#elif defined(_MSC_VER) && defined(_WIN64) +#elif defined(_MSC_VER) && defined(_M_AMD64) unsigned __int64 dp = 0; unsigned __int64 dz = _umul128(x, y, &dp); - ret.left = RBIMPL_CAST((bool)dp); - ret.right = RBIMPL_CAST((size_t)dz); + ret.overflowed = RBIMPL_CAST((bool)dp); + ret.result = RBIMPL_CAST((size_t)dz); + +#elif defined(_MSC_VER) && defined(_M_ARM64) + ret.overflowed = __umulh(x, y) != 0; + ret.result = x * y; #else /* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap */ - ret.left = (y != 0) && (x > SIZE_MAX / y); - ret.right = x * y; + ret.overflowed = (y != 0) && (x > SIZE_MAX / y); + ret.result = x * y; #endif return ret; @@ -610,11 +636,11 @@ rbimpl_size_mul_overflow(size_t x, size_t y) static inline size_t rbimpl_size_mul_or_raise(size_t x, size_t y) { - struct rbimpl_size_mul_overflow_tag size = + struct rbimpl_size_overflow_tag size = rbimpl_size_mul_overflow(x, y); - if (RB_LIKELY(! size.left)) { - return size.right; + if (RB_LIKELY(! size.overflowed)) { + return size.result; } else { ruby_malloc_size_overflow(x, y); @@ -622,6 +648,81 @@ rbimpl_size_mul_or_raise(size_t x, size_t y) } } +#if defined(__DOXYGEN__) +RBIMPL_ATTR_CONSTEXPR(CXX14) +#elif RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0) +RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70507 */ +#elif RBIMPL_COMPILER_SINCE(Clang, 7, 0, 0) +RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://bugs.llvm.org/show_bug.cgi?id=37633 */ +#endif +RBIMPL_ATTR_CONST() +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @return `{ overflowed, result }`, where `overflowed` is whether there is + * an integer overflow or not, and `result` is a (possibly + * overflowed) result of `x` + `y`. + * + * @internal + */ +static inline struct rbimpl_size_overflow_tag +rbimpl_size_add_overflow(size_t x, size_t y) +{ + struct rbimpl_size_overflow_tag ret = { false, 0, }; + +#if defined(ckd_add) + ret.overflowed = ckd_add(&ret.result, x, y); + +#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow) + ret.overflowed = __builtin_add_overflow(x, y, &ret.result); + +#elif defined(DSIZE_T) + RB_GNUC_EXTENSION DSIZE_T dx = x; + RB_GNUC_EXTENSION DSIZE_T dy = y; + RB_GNUC_EXTENSION DSIZE_T dz = dx + dy; + ret.overflowed = dz > SIZE_MAX; + ret.result = (size_t)dz; + +#else + ret.result = x + y; + ret.overflowed = ret.result < y; + +#endif + + return ret; +} + +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @exception rb_eArgError Multiplication could integer overflow. + * @return `x` + `y`. + * + * @internal + */ +static inline size_t +rbimpl_size_add_or_raise(size_t x, size_t y) +{ + struct rbimpl_size_overflow_tag size = + rbimpl_size_add_overflow(x, y); + + if (RB_LIKELY(!size.overflowed)) { + return size.result; + } + else { + ruby_malloc_add_size_overflow(x, y); + RBIMPL_UNREACHABLE_RETURN(0); + } +} + /** * This is an implementation detail of #RB_ALLOCV_N(). People don't use this * directly. @@ -639,7 +740,7 @@ rbimpl_size_mul_or_raise(size_t x, size_t y) static inline void * rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize) { - const size_t total_size = rbimpl_size_mul_or_raise(count, elsize); + const size_t total_size = rbimpl_size_mul_or_raise(RBIMPL_CAST((size_t)count), elsize); const size_t cnt = (total_size + sizeof(VALUE) - 1) / sizeof(VALUE); return rb_alloc_tmp_buffer_with_count(store, total_size, cnt); } |
