blob: a1b1cb67809d84a623c9c52acf38b225cd5fdc95 [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-bigint-gen.h'
// TODO(nicohartmann): Discuss whether types used by multiple builtins should be
// in global namespace
@noVerifier
extern class BigIntBase extends HeapObject generates 'TNode<BigInt>' {
}
type BigInt extends BigIntBase;
@noVerifier
@hasSameInstanceTypeAsParent
extern class MutableBigInt extends BigIntBase generates 'TNode<BigInt>' {
}
Convert<BigInt, MutableBigInt>(i: MutableBigInt): BigInt {
assert(bigint::IsCanonicalized(i));
return %RawDownCast<BigInt>(Convert<BigIntBase>(i));
}
namespace bigint {
const kPositiveSign: uint32 = 0;
const kNegativeSign: uint32 = 1;
extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare(
BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32;
extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr;
extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength(
MutableBigInt, uint32, intptr): void;
extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt;
extern macro CodeStubAssembler::StoreBigIntDigit(
MutableBigInt, intptr, uintptr): void;
extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr;
@export // Silence unused warning.
// TODO(szuend): Remove @export once macros that are only used in
// asserts are no longer detected as unused.
macro IsCanonicalized(bigint: BigIntBase): bool {
const length = ReadBigIntLength(bigint);
if (length == 0) {
return ReadBigIntSign(bigint) == kPositiveSign;
}
return LoadBigIntDigit(bigint, length - 1) != 0;
}
macro InvertSign(sign: uint32): uint32 {
return sign == kPositiveSign ? kNegativeSign : kPositiveSign;
}
macro AllocateEmptyBigIntNoThrow(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig {
if (length > kBigIntMaxLength) {
goto BigIntTooBig;
}
const result: MutableBigInt = AllocateBigInt(length);
WriteBigIntSignAndLength(result, sign, length);
return result;
}
macro AllocateEmptyBigInt(implicit context: Context)(
sign: uint32, length: intptr): MutableBigInt {
try {
return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig;
}
label BigIntTooBig {
ThrowRangeError(kBigIntTooBig);
}
}
macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 {
return CppAbsoluteCompare(x, y);
}
macro MutableBigIntAbsoluteSub(implicit context: Context)(
x: BigInt, y: BigInt, resultSign: uint32): BigInt {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
const xsign = ReadBigIntSign(x);
assert(MutableBigIntAbsoluteCompare(x, y) >= 0);
if (xlength == 0) {
assert(ylength == 0);
return x;
}
if (ylength == 0) {
return resultSign == xsign ? x : BigIntUnaryMinus(x);
}
const result = AllocateEmptyBigInt(resultSign, xlength);
CppAbsoluteSubAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
}
macro MutableBigIntAbsoluteAdd(implicit context: Context)(
xBigint: BigInt, yBigint: BigInt,
resultSign: uint32): BigInt labels BigIntTooBig {
let xlength = ReadBigIntLength(xBigint);
let ylength = ReadBigIntLength(yBigint);
let x = xBigint;
let y = yBigint;
if (xlength < ylength) {
// Swap x and y so that x is longer.
x = yBigint;
y = xBigint;
const tempLength = xlength;
xlength = ylength;
ylength = tempLength;
}
// case: 0n + 0n
if (xlength == 0) {
assert(ylength == 0);
return x;
}
// case: x + 0n
if (ylength == 0) {
return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x);
}
// case: x + y
const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1)
otherwise BigIntTooBig;
CppAbsoluteAddAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
}
macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt
labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign == ysign) {
// x + y == x + y
// -x + -y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
// x + -y == x - y == -(y - x)
// -x + y == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
}
builtin BigIntAddNoThrow(implicit context: Context)(x: BigInt, y: BigInt):
Numeric {
try {
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
}
label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntAdd(implicit context: Context)(xNum: Numeric, yNum: Numeric):
BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntAddImpl(x, y) otherwise BigIntTooBig;
}
label MixedTypes {
ThrowTypeError(kBigIntMixedTypes);
}
label BigIntTooBig {
ThrowRangeError(kBigIntTooBig);
}
}
builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
const length = ReadBigIntLength(bigint);
// There is no -0n.
if (length == 0) {
return bigint;
}
const result =
AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length);
for (let i: intptr = 0; i < length; ++i) {
StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i));
}
return Convert<BigInt>(result);
}
} // namespace bigint