Source code
Revision control
Copy as Markdown
Other Tools
// |jit-test| skip-if: !getBuildConfiguration("moz-memory")
// Run this test only if we're using jemalloc. Other malloc implementations
// exhibit surprising behaviors. For example, 32-bit Fedora builds have
// non-deterministic allocation sizes.
// Check JS::ubi::Node::size results for BigInts.
// We actually hard-code specific sizes into this test, even though they're
// implementation details, because in practice there are only two architecture
// variants to consider (32-bit and 64-bit), and if these sizes change, that's
// something SpiderMonkey hackers really want to know; they're supposed to be
// stable.
const pointerByteSize = getBuildConfiguration("pointer-byte-size");
assertEq(pointerByteSize === 4 || pointerByteSize === 8, true);
const m32 = pointerByteSize === 4;
// 32-bit: sizeof(CellWithLengthAndFlags) + 2 * sizeof(BigInt::Digit) = 8 + 2 * 4 = 16
// 64-bit: sizeof(CellWithLengthAndFlags) + sizeof(BigInt::Digit) = 8 + 8 = 16
const SIZE_OF_BIGINT = 16;
// sizeof(BigInt::Digit)
const SIZE_OF_DIGIT = pointerByteSize;
// sizeof(JS::Value)
const SIZE_OF_VALUE = 8;
// See Nursery::bigIntHeaderSize().
const SIZE_OF_BIGINT_HEADER = 8;
const SIZE_OF_TENURED_BIGINT = SIZE_OF_BIGINT;
const SIZE_OF_NURSERY_BIGINT = SIZE_OF_BIGINT + SIZE_OF_BIGINT_HEADER;
function nurseryDigitSize(length) {
// overallocate on 32-bit.
if (m32) {
length += (length & 1);
}
return length * SIZE_OF_DIGIT;
}
function mallocDigitSize(length) {
// overallocate on 32-bit.
if (m32) {
length += (length & 1);
}
// Malloc buffer sizes are always a power of two.
return 1 << Math.ceil(Math.log2(length * SIZE_OF_DIGIT));
}
// Constant BigInts (tenured, inline digits).
assertEq(byteSize(10n), SIZE_OF_TENURED_BIGINT);
assertEq(byteSize(0xffff_ffff_ffff_ffffn), SIZE_OF_TENURED_BIGINT);
// Constant BigInt (tenured, heap digits).
assertEq(byteSize(0x1_0000_0000_0000_0000n),
SIZE_OF_TENURED_BIGINT + mallocDigitSize(m32 ? 3 : 2));
assertEq(byteSize(0x1_0000_0000_0000_0000_0000_0000n),
SIZE_OF_TENURED_BIGINT + mallocDigitSize(m32 ? 4 : 2));
assertEq(byteSize(0x1_0000_0000_0000_0000_0000_0000_0000_0000n),
SIZE_OF_TENURED_BIGINT + mallocDigitSize(m32 ? 5 : 3));
///////////////////////////////////////////////////////////////////////////////
// Nursery BigInt tests below this point. //
///////////////////////////////////////////////////////////////////////////////
// Hack to skip this test if BigInts are not allocated in the nursery.
{
const sample_nursery = BigInt(123456789);
const before = byteSize(sample_nursery);
gc();
const after = byteSize(sample_nursery);
let nursery_disabled = before == after;
if (nursery_disabled) {
printErr("nursery BigInts appear to be disabled");
quit(0);
}
}
// Convert an input BigInt, which is probably tenured because it's a literal in
// the source text, to a nursery-allocated BigInt with the same contents.
function copyBigInt(bi) {
var plusOne = bi + 1n;
return plusOne - 1n;
}
// Return the nursery byte size of |bi|.
function nByteSize(bi) {
// BigInts that appear in the source will always be tenured.
return byteSize(copyBigInt(bi));
}
// BigInts (nursery, inline digits).
assertEq(nByteSize(10n), SIZE_OF_NURSERY_BIGINT);
assertEq(nByteSize(0xffff_ffff_ffff_ffffn), SIZE_OF_NURSERY_BIGINT);
// BigInt (nursery, nursery heap digits).
//
// This assumes small nursery buffer allocations always succeed.
assertEq(nByteSize(0x1_0000_0000_0000_0000n),
SIZE_OF_NURSERY_BIGINT + nurseryDigitSize(m32 ? 3 : 2));
assertEq(nByteSize(0x1_0000_0000_0000_0000_0000_0000n),
SIZE_OF_NURSERY_BIGINT + nurseryDigitSize(m32 ? 4 : 2));
assertEq(nByteSize(0x1_0000_0000_0000_0000_0000_0000_0000_0000n),
SIZE_OF_NURSERY_BIGINT + nurseryDigitSize(m32 ? 5 : 3));
// BigInt (nursery, malloc heap digits).
//
// |Nursery::MaxNurseryBufferSize| is 1024, so when
// |BigInt::digitLength * sizeof(BigInt::Digit)| exceeds 1024, the digits buffer
// should be malloc'ed. Pick a larger number to be future-proof.
assertEq(nByteSize(2n ** (64n * 1000n)),
SIZE_OF_NURSERY_BIGINT + mallocDigitSize(m32 ? 2002 : 1001));