aboutsummaryrefslogtreecommitdiffstats
path: root/internal.h
diff options
context:
space:
mode:
authornaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-02-02 15:54:51 +0000
committernaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-02-02 15:54:51 +0000
commit14b58d799bcc0c4457b6ff0010dcce2e4669a4fd (patch)
tree1a1526b8206577a2b172836d875fc904d3b33f7f /internal.h
parent9ae47fb45bcc531bcc289411a7a48281011176f1 (diff)
downloadruby-14b58d799bcc0c4457b6ff0010dcce2e4669a4fd.tar.gz
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.h47
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