diff options
author | naruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-02-02 15:54:51 +0000 |
---|---|---|
committer | naruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-02-02 15:54:51 +0000 |
commit | 038ccbd112d26d15b7433b86bf48b1be0fea6e2f (patch) | |
tree | 1a1526b8206577a2b172836d875fc904d3b33f7f /internal.h | |
parent | b36f5ba197ebf5b871044c037bebfffc2675780c (diff) |
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
Diffstat (limited to 'internal.h')
-rw-r--r-- | internal.h | 47 |
1 files changed, 47 insertions, 0 deletions
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 |