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
This commit is contained in:
André Bargull 2019-12-02 17:37:06 +00:00
Родитель 0903f6d258
Коммит ccaf2a12e3
3 изменённых файлов: 65 добавлений и 1 удалений

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/Compiler.h"
#include "mozilla/MathAlgorithms.h"
#include <stdint.h>
@ -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 */

Просмотреть файл

@ -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) {

Просмотреть файл

@ -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);