From 038ccbd112d26d15b7433b86bf48b1be0fea6e2f Mon Sep 17 00:00:00 2001 From: naruse Date: Thu, 2 Feb 2017 15:54:51 +0000 Subject: Use carry flag to reduce instructions NOTE: (1) Fixnum's LSB is always 1. It means you can always run `x - 1` without overflow. (2) Of course `z = x + (y-1)` may overflow. Now z's LSB is always 1, and the MSB of true result is also 1. You can get true result in long as `(1<<63)|(z>>1)`, and it equals to `(z<<63)|(z>>1)` == `ror(z)`. GCC and Clang have __builtin_add_ovewflow: * https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html * https://clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57506 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- internal.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'internal.h') diff --git a/internal.h b/internal.h index 3e5550f4ca..4ed1e41440 100644 --- a/internal.h +++ b/internal.h @@ -348,6 +348,53 @@ VALUE rb_int128t2big(int128_t n); #define ST2FIX(h) LONG2FIX((long)(h)) +static inline unsigned long +rb_ulong_rotate_right(unsigned long x) +{ + return (x >> 1) | (x << (SIZEOF_LONG * CHAR_BIT - 1)); +} + +static inline VALUE +rb_fix_plus_fix(VALUE x, VALUE y) +{ +#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW + long lz; + /* NOTE + * (1) Fixnum's LSB is always 1. + * It means you can always run `x - 1` without overflow. + * (2) Of course `z = x + (y-1)` may overflow. + * Now z's LSB is always 1, and the MSB of true result is also 1. + * You can get true result in long as `(1<<63)|(z>>1)`, + * and it equals to `(z<<63)|(z>>1)` == `ror(z)`. + */ + if (__builtin_add_overflow((long)x, (long)y-1, &lz)) { + return rb_int2big(rb_ulong_rotate_right((unsigned long)lz)); + } + else { + return (VALUE)lz; + } +#else + long lz = FIX2LONG(x) + FIX2LONG(y); + return LONG2NUM(lz); +#endif +} + +static inline VALUE +rb_fix_minus_fix(VALUE x, VALUE y) +{ +#ifdef HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW + long lz; + if (__builtin_sub_overflow((long)x, (long)y-1, &lz)) { + return rb_int2big(rb_ulong_rotate_right((unsigned long)lz)); + } + else { + return (VALUE)lz; + } +#else + long lz = FIX2LONG(x) - FIX2LONG(y); + return LONG2NUM(lz); +#endif +} /* arguments must be Fixnum */ static inline VALUE -- cgit v1.2.3