From a675dcb0ae3563b57df0c97f2f9e3b4fb502cee6 Mon Sep 17 00:00:00 2001 From: Denis Shelomovskij Date: Tue, 22 Sep 2015 19:21:55 +0300 Subject: [PATCH 1/2] Rewrite `core.checkedint` functions. Use fast and simple sign comparison instead of long, slow and complicated value comparisons. Also remove reference as it doesn't apply anymore. --- src/core/checkedint.d | 70 +++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/core/checkedint.d b/src/core/checkedint.d index 91f2a07191..69c90bdb21 100644 --- a/src/core/checkedint.d +++ b/src/core/checkedint.d @@ -22,7 +22,7 @@ * References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks) * Copyright: Copyright (c) Walter Bright 2014. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Authors: Walter Bright + * Authors: Walter Bright, Denis Shelomovskij * Source: $(DRUNTIMESRC core/_checkedint.d) */ @@ -49,10 +49,11 @@ pure: pragma(inline, true) int adds(int x, int y, ref bool overflow) { - long r = cast(long)x + cast(long)y; - if (r < int.min || r > int.max) + const r = x + y; + if (~x._differentSign(y) & // x and y has the same sign + x._differentSign(r)) // result has different sign overflow = true; - return cast(int)r; + return r; } unittest @@ -77,9 +78,9 @@ unittest pragma(inline, true) long adds(long x, long y, ref bool overflow) { - long r = cast(ulong)x + cast(ulong)y; - if (x < 0 && y < 0 && r >= 0 || - x >= 0 && y >= 0 && r < 0) + const r = x + y; + if (~x._differentSign(y) & // x and y has the same sign + x._differentSign(r)) // result has different sign overflow = true; return r; } @@ -119,8 +120,8 @@ unittest pragma(inline, true) uint addu(uint x, uint y, ref bool overflow) { - uint r = x + y; - if (r < x || r < y) + const r = x + y; + if (r < x) overflow = true; return r; } @@ -147,8 +148,8 @@ unittest pragma(inline, true) ulong addu(ulong x, ulong y, ref bool overflow) { - ulong r = x + y; - if (r < x || r < y) + const r = x + y; + if (r < x) overflow = true; return r; } @@ -188,10 +189,11 @@ unittest pragma(inline, true) int subs(int x, int y, ref bool overflow) { - long r = cast(long)x - cast(long)y; - if (r < int.min || r > int.max) + const r = x - y; + if (x._differentSign(y) & // x and y has different sign + x._differentSign(r)) // result and x has different sign overflow = true; - return cast(int)r; + return r; } unittest @@ -216,9 +218,9 @@ unittest pragma(inline, true) long subs(long x, long y, ref bool overflow) { - long r = cast(ulong)x - cast(ulong)y; - if (x < 0 && y >= 0 && r >= 0 || - x >= 0 && y < 0 && (r < 0 || y == long.min)) + const r = x - y; + if (x._differentSign(y) & // x and y has different sign + x._differentSign(r)) // result and x has different sign overflow = true; return r; } @@ -385,10 +387,10 @@ unittest pragma(inline, true) int muls(int x, int y, ref bool overflow) { - long r = cast(long)x * cast(long)y; + const r = cast(long) x * y; if (r < int.min || r > int.max) overflow = true; - return cast(int)r; + return cast(int) r; } unittest @@ -415,9 +417,10 @@ unittest pragma(inline, true) long muls(long x, long y, ref bool overflow) { - long r = cast(ulong)x * cast(ulong)y; - enum not0or1 = ~1L; - if((x & not0or1) && ((r == y)? r : (r / x) != y)) + const r = x * y; + if (~x._differentSign(y) & // x and y has the same sign + r._signBits || // and result is negative (covers min * -1) + x && r / x != y) // or perform simple check for x != 0 overflow = true; return r; } @@ -462,10 +465,10 @@ unittest pragma(inline, true) uint mulu(uint x, uint y, ref bool overflow) { - ulong r = ulong(x) * ulong(y); + const r = cast(ulong) x * y; if (r > uint.max) overflow = true; - return cast(uint)r; + return cast(uint) r; } unittest @@ -492,7 +495,7 @@ unittest pragma(inline, true) ulong mulu(ulong x, ulong y, ref bool overflow) { - ulong r = x * y; + const r = x * y; if (x && (r / x) != y) overflow = true; return r; @@ -517,3 +520,20 @@ unittest assert(mulu(0UL, 0UL, overflow) == 0); assert(overflow); // sticky } + + +private: + +pragma(inline, true) +@property size_t _signBits(T)(in T n) +if(is(T == int) || is(T == long)) +{ + return cast(size_t) (n >> T.sizeof * 8 - 1); +} + +pragma(inline, true) +size_t _differentSign(T)(in T a, in T b) +if(is(T == int) || is(T == long)) +{ + return (a ^ b)._signBits; +} From f57723c36b395c50353710f7e39c9fd4c4df7e84 Mon Sep 17 00:00:00 2001 From: Denis Shelomovskij Date: Sun, 27 Sep 2015 14:29:53 +0300 Subject: [PATCH 2/2] Use logical operations in and let compiler to optimize. At least Clang knows `(a < 0) != (b < 0)` pattern. The only possible shortcoming of this change is that use of logical operators currently causes compilers to emit two conditional jumps instead of one for bitwise operations. --- src/core/checkedint.d | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/core/checkedint.d b/src/core/checkedint.d index 69c90bdb21..5bbfdc06d9 100644 --- a/src/core/checkedint.d +++ b/src/core/checkedint.d @@ -50,8 +50,8 @@ pragma(inline, true) int adds(int x, int y, ref bool overflow) { const r = x + y; - if (~x._differentSign(y) & // x and y has the same sign - x._differentSign(r)) // result has different sign + if (!x._differentSign(y) && // x and y has the same sign + x._differentSign(r)) // result has different sign overflow = true; return r; } @@ -79,8 +79,8 @@ pragma(inline, true) long adds(long x, long y, ref bool overflow) { const r = x + y; - if (~x._differentSign(y) & // x and y has the same sign - x._differentSign(r)) // result has different sign + if (!x._differentSign(y) && // x and y has the same sign + x._differentSign(r)) // result has different sign overflow = true; return r; } @@ -190,8 +190,8 @@ pragma(inline, true) int subs(int x, int y, ref bool overflow) { const r = x - y; - if (x._differentSign(y) & // x and y has different sign - x._differentSign(r)) // result and x has different sign + if (x._differentSign(y) && // x and y has different sign + x._differentSign(r)) // result and x has different sign overflow = true; return r; } @@ -219,8 +219,8 @@ pragma(inline, true) long subs(long x, long y, ref bool overflow) { const r = x - y; - if (x._differentSign(y) & // x and y has different sign - x._differentSign(r)) // result and x has different sign + if (x._differentSign(y) && // x and y has different sign + x._differentSign(r)) // result and x has different sign overflow = true; return r; } @@ -418,9 +418,9 @@ pragma(inline, true) long muls(long x, long y, ref bool overflow) { const r = x * y; - if (~x._differentSign(y) & // x and y has the same sign - r._signBits || // and result is negative (covers min * -1) - x && r / x != y) // or perform simple check for x != 0 + if (!x._differentSign(y) && // x and y has the same sign + r < 0 || // and result is negative (covers min * -1) + x && r / x != y) // or perform simple check for x != 0 overflow = true; return r; } @@ -525,15 +525,8 @@ unittest private: pragma(inline, true) -@property size_t _signBits(T)(in T n) +bool _differentSign(T)(in T a, in T b) if(is(T == int) || is(T == long)) { - return cast(size_t) (n >> T.sizeof * 8 - 1); -} - -pragma(inline, true) -size_t _differentSign(T)(in T a, in T b) -if(is(T == int) || is(T == long)) -{ - return (a ^ b)._signBits; + return (a < 0) != (b < 0); }