bug 1366287 - Part 2.0: Use GMP integers to represent BigInt values. r=jwalden
☠☠ backed out by f0cb25b7bdee ☠ ☠
authorRobin Templeton <robin@igalia.com>
Fri, 11 May 2018 19:42:48 -0700
changeset 472207 c834c0c12a7fcdbeec7b82eac2c9a3fd80a1e90f
parent 472206 dd19d38a2b1cf02eebf9ef33f1a9a0e44f844338
child 472208 f80aac2c702ab3b203632960a81b7e009ab7898d
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1366287
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1366287 - Part 2.0: Use GMP integers to represent BigInt values. r=jwalden Also link to libgmp and initialize it with custom memory allocation functions.
config/system-headers.mozbuild
js/src/build/moz.build
js/src/vm/BigIntType.cpp
js/src/vm/BigIntType.h
js/src/vm/Initialization.cpp
--- a/config/system-headers.mozbuild
+++ b/config/system-headers.mozbuild
@@ -1334,8 +1334,13 @@ if CONFIG['MOZ_SYSTEM_ICU']:
         'unicode/unistr.h',
         'unicode/unorm.h',
         'unicode/unum.h',
         'unicode/upluralrules.h',
         'unicode/ureldatefmt.h',
         'unicode/ustring.h',
         'unicode/utypes.h',
     ]
+
+if CONFIG['ENABLE_BIGINT']:
+    system_headers += [
+        'gmp.h'
+    ]
--- a/js/src/build/moz.build
+++ b/js/src/build/moz.build
@@ -29,16 +29,21 @@ else:
 FORCE_STATIC_LIB = True
 STATIC_LIBRARY_NAME = 'js_static'
 
 if CONFIG['ENABLE_INTL_API']:
     USE_LIBS += [
         'icu',
     ]
 
+if CONFIG['ENABLE_BIGINT']:
+    OS_LIBS += [
+        'gmp',
+    ]
+
 USE_LIBS += [
     'nspr',
     'zlib',
 ]
 
 if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'):
     OS_LIBS += [
         'm',
--- a/js/src/vm/BigIntType.cpp
+++ b/js/src/vm/BigIntType.cpp
@@ -4,80 +4,130 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/BigIntType.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/HashFunctions.h"
 
+#include <gmp.h>
+#include <math.h>
+
 #include "jsapi.h"
 
 #include "gc/Allocator.h"
 #include "gc/Tracer.h"
+#include "js/Utility.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 using namespace js;
 
+// The following functions are wrappers for use with
+// mp_set_memory_functions. GMP passes extra arguments to the realloc
+// and free functions not needed by the JS allocation interface.
+// js_malloc has the signature expected for GMP's malloc function, so no
+// wrapper is required.
+
+static void*
+js_mp_realloc(void* ptr, size_t old_size, size_t new_size)
+{
+    return js_realloc(ptr, new_size);
+}
+
+static void
+js_mp_free(void* ptr, size_t size)
+{
+    return js_free(ptr);
+}
+
+void
+BigInt::init()
+{
+    mp_set_memory_functions(js_malloc, js_mp_realloc, js_mp_free);
+}
+
 BigInt*
 BigInt::create(JSContext* cx)
 {
     BigInt* x = Allocate<BigInt>(cx);
     if (!x)
         return nullptr;
+    mpz_init(x->num_); // to zero
     return x;
 }
 
 BigInt*
 BigInt::copy(JSContext* cx, HandleBigInt x)
 {
-    BigInt* bi = create(cx);
+    BigInt* bi = Allocate<BigInt>(cx);
     if (!bi)
         return nullptr;
+    mpz_init_set(bi->num_, x->num_);
     return bi;
 }
 
 JSLinearString*
 BigInt::toString(JSContext* cx, BigInt* x)
 {
-    return nullptr;
+    // We need two extra chars for '\0' and potentially '-'.
+    size_t strSize = mpz_sizeinbase(x->num_, 10) + 2;
+    UniqueChars str(static_cast<char*>(js_malloc(strSize)));
+    if (!str) {
+        ReportOutOfMemory(cx);
+        return nullptr;
+    }
+    mpz_get_str(str.get(), 10, x->num_);
+
+    return NewStringCopyZ<CanGC>(cx, str.get());
 }
 
 void
 BigInt::finalize(js::FreeOp* fop)
 {
-    return;
+    mpz_clear(num_);
 }
 
 JSAtom*
 js::BigIntToAtom(JSContext* cx, BigInt* bi)
 {
     JSString* str = BigInt::toString(cx, bi);
     if (!str)
         return nullptr;
     return AtomizeString(cx, str);
 }
 
 bool
 BigInt::toBoolean()
 {
-    return false;
+    return mpz_sgn(num_) != 0;
 }
 
 js::HashNumber
 BigInt::hash()
 {
-    return 0;
+    const mp_limb_t* limbs = mpz_limbs_read(num_);
+    size_t limbCount = mpz_size(num_);
+    uint32_t hash = mozilla::HashBytes(limbs, limbCount * sizeof(mp_limb_t));
+    hash = mozilla::AddToHash(hash, mpz_sgn(num_));
+    return hash;
 }
 
 size_t
 BigInt::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    return 0;
+    // Use the total number of limbs allocated when calculating the size
+    // (_mp_alloc), not the number of limbs currently in use (_mp_size).
+    // See the Info node `(gmp)Integer Internals` for details.
+    mpz_srcptr n = static_cast<mpz_srcptr>(num_);
+    return sizeof(*n) + sizeof(mp_limb_t) * n->_mp_alloc;
 }
 
 JS::ubi::Node::Size
 JS::ubi::Concrete<BigInt>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    MOZ_ASSERT(get().isTenured());
-    return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+    BigInt& bi = get();
+    MOZ_ASSERT(bi.isTenured());
+    size_t size = js::gc::Arena::thingSize(bi.asTenured().getAllocKind());
+    size += bi.sizeOfExcludingThis(mallocSizeOf);
+    return size;
 }
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -2,33 +2,38 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_BigIntType_h
 #define vm_BigIntType_h
 
+#include <gmp.h>
+
 #include "gc/Barrier.h"
 #include "gc/GC.h"
 #include "gc/Heap.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "vm/StringType.h"
 
 namespace JS {
 
 class BigInt final : public js::gc::TenuredCell
 {
   private:
     // The minimum allocation size is currently 16 bytes (see
     // SortedArenaList in gc/ArenaList.h).
-    uint8_t unused_[js::gc::MinCellSize];
+    union {
+        mpz_t num_;
+        uint8_t unused_[js::gc::MinCellSize];
+    };
 
   public:
     // Allocate and initialize a BigInt value
     static BigInt* create(JSContext* cx);
 
     static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
 
     void traceChildren(JSTracer* trc);
@@ -37,16 +42,18 @@ class BigInt final : public js::gc::Tenu
 
     js::HashNumber hash();
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     static JSLinearString* toString(JSContext* cx, BigInt* x);
     bool toBoolean();
 
+    static void init();
+
     static BigInt* copy(JSContext* cx, Handle<BigInt*> x);
 };
 
 static_assert(sizeof(BigInt) >= js::gc::MinCellSize,
               "sizeof(BigInt) must be greater than the minimum allocation size");
 
 } // namespace JS
 
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -20,16 +20,19 @@
 #include "jit/ExecutableAllocator.h"
 #include "jit/Ion.h"
 #include "jit/JitCommon.h"
 #include "js/Utility.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/DateTime.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vtune/VTuneWrapper.h"
 #include "wasm/WasmProcess.h"
 
@@ -129,16 +132,20 @@ JS::detail::InitWithFailureDiagnostic(bo
     RETURN_IF_FAIL(js::CreateHelperThreadsState());
     RETURN_IF_FAIL(FutexThread::initialize());
     RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
 
 #ifdef JS_SIMULATOR
     RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
 #endif
 
+#ifdef ENABLE_BIGINT
+    JS::BigInt::init();
+#endif
+
     libraryInitState = InitState::Running;
     return nullptr;
 }
 
 #undef RETURN_IF_FAIL
 
 JS_PUBLIC_API(void)
 JS_ShutDown(void)