From ccaf2a12e39a951595efbbfdf8e00bba636ca694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 2 Dec 2019 17:37:06 +0000 Subject: [PATCH] Bug 1599465 - Part 5: Add fast path for BigInt multiplication with uint64 magnitude. r=jwalden Similar to the previous part, also add a fast-path when multiplying two Bigints which fit into uint64_t. When the result also fits into uint64_t, this approach avoids allocating unused malloc memory. Differential Revision: https://phabricator.services.mozilla.com/D54761 --HG-- extra : moz-landing-system : lando --- js/src/util/CheckedArithmetic.h | 26 ++++++++++++++++++++++ js/src/vm/BigIntType.cpp | 38 ++++++++++++++++++++++++++++++++- js/src/vm/BigIntType.h | 2 ++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/js/src/util/CheckedArithmetic.h b/js/src/util/CheckedArithmetic.h index 1a9666805fdd..691b85bf4187 100644 --- a/js/src/util/CheckedArithmetic.h +++ b/js/src/util/CheckedArithmetic.h @@ -9,6 +9,7 @@ #include "mozilla/Attributes.h" #include "mozilla/Compiler.h" +#include "mozilla/MathAlgorithms.h" #include @@ -62,6 +63,31 @@ MOZ_MUST_USE inline bool SafeMul(int32_t one, int32_t two, int32_t* res) { #endif } +MOZ_MUST_USE inline bool SafeMul(uint64_t one, uint64_t two, uint64_t* res) { +#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_mul_overflow) + return !__builtin_mul_overflow(one, two, res); +#else + // Hacker's Delight, 2nd edition, 2-13 Overflow detection, Fig. 2-2. + int zeroes = + mozilla::CountLeadingZeroes64(one) + mozilla::CountLeadingZeroes64(two); + if (zeroes <= 62) { + return false; + } + uint64_t half = one * (two >> 1); + if (int64_t(half) < 0) { + return false; + } + *res = half * 2; + if (two & 1) { + *res += one; + if (*res < one) { + return false; + } + } + return true; +#endif +} + } /* namespace js */ #endif /* util_CheckedArithmetic_h */ diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp index 58c383bd1f93..e2a6d11dc317 100644 --- a/js/src/vm/BigIntType.cpp +++ b/js/src/vm/BigIntType.cpp @@ -101,6 +101,7 @@ #include "js/Initialization.h" #include "js/StableStringChars.h" #include "js/Utility.h" +#include "util/CheckedArithmetic.h" #include "vm/JSContext.h" #include "vm/SelfHosting.h" @@ -212,6 +213,28 @@ BigInt* BigInt::negativeOne(JSContext* cx) { return createFromDigit(cx, 1, true); } +BigInt* BigInt::createFromNonZeroRawUint64(JSContext* cx, uint64_t n, + bool isNegative) { + MOZ_ASSERT(n != 0); + + size_t resultLength = 1; + if (DigitBits == 32 && (n >> 32) != 0) { + resultLength = 2; + } + + BigInt* result = createUninitialized(cx, resultLength, isNegative); + if (!result) { + return nullptr; + } + result->setDigit(0, n); + if (DigitBits == 32 && resultLength > 1) { + result->setDigit(1, n >> 32); + } + + MOZ_ASSERT(!HasLeadingZeroes(result)); + return result; +} + BigInt* BigInt::neg(JSContext* cx, HandleBigInt x) { if (x->isZero()) { return x; @@ -1810,8 +1833,21 @@ BigInt* BigInt::mul(JSContext* cx, HandleBigInt x, HandleBigInt y) { return y; } - unsigned resultLength = x->digitLength() + y->digitLength(); bool resultNegative = x->isNegative() != y->isNegative(); + + // Fast path for the likely-common case of up to a uint64_t of magnitude. + if (x->absFitsInUint64() && y->absFitsInUint64()) { + uint64_t lhs = x->uint64FromAbsNonZero(); + uint64_t rhs = y->uint64FromAbsNonZero(); + + uint64_t res; + if (js::SafeMul(lhs, rhs, &res)) { + MOZ_ASSERT(res != 0); + return createFromNonZeroRawUint64(cx, res, resultNegative); + } + } + + unsigned resultLength = x->digitLength() + y->digitLength(); RootedBigInt result(cx, createUninitialized(cx, resultLength, resultNegative)); if (!result) { diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index acdbb4d293f8..d4a0f5db8f63 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -104,6 +104,8 @@ class BigInt final static BigInt* createFromUint64(JSContext* cx, uint64_t n); static BigInt* createFromInt64(JSContext* cx, int64_t n); static BigInt* createFromDigit(JSContext* cx, Digit d, bool isNegative); + static BigInt* createFromNonZeroRawUint64(JSContext* cx, uint64_t n, + bool isNegative); // FIXME: Cache these values. static BigInt* zero(JSContext* cx); static BigInt* one(JSContext* cx);