blob: 77892b76d8a0918d95e15dc092c2ea647a6e8a0f [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'
namespace bigint {
const kPositiveSign: uint32 = 0;
const kNegativeSign: uint32 = 1;
const kGreaterThan: intptr = 1;
const kLessThan: intptr = -1;
const kMustRoundDownBitShift: uint32 = 30;
extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteMulAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::CppAbsoluteDivAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::CppAbsoluteModAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): int32;
extern macro BigIntBuiltinsAssembler::CppBitwiseAndPosPosAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseAndNegNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseAndPosNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseOrPosPosAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseOrNegNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseOrPosNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseXorPosPosAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseXorNegNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppBitwiseXorPosNegAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppLeftShiftAndCanonicalize(
MutableBigInt, BigIntBase, intptr): void;
extern macro BigIntBuiltinsAssembler::CppRightShiftResultLength(
BigIntBase, uint32, intptr): uint32;
extern macro BigIntBuiltinsAssembler::CppRightShiftAndCanonicalize(
MutableBigInt, BigIntBase, intptr, uint32): 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::AllocateRawBigInt(intptr): MutableBigInt;
extern macro CodeStubAssembler::StoreBigIntDigit(
MutableBigInt, intptr, uintptr): void;
extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr;
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 = AllocateRawBigInt(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(MessageTemplate::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);
dcheck(MutableBigIntAbsoluteCompare(x, y) >= 0);
if (xlength == 0) {
dcheck(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) {
dcheck(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(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro BigIntSubtractImpl(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 == -(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 BigIntSubtractNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntSubtract(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro BigIntMultiplyImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntTooBig, TerminationRequested {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n * y
if (xlength == 0) {
return x;
}
// case: x * 0n
if (ylength == 0) {
return y;
}
// case: x * y
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
const resultSign = (xsign != ysign) ? kNegativeSign : kPositiveSign;
const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + ylength)
otherwise BigIntTooBig;
if (CppAbsoluteMulAndCanonicalize(result, x, y) == 1) {
goto TerminationRequested;
}
return Convert<BigInt>(result);
}
builtin BigIntMultiplyNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntMultiplyImpl(x, y) otherwise BigIntTooBig,
TerminationRequested;
} label BigIntTooBig {
// Smi sentinel 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
} label TerminationRequested {
// Smi sentinel 1 is used to signal TerminateExecution exception.
return Convert<Smi>(1);
}
}
builtin BigIntMultiply(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntMultiplyImpl(x, y) otherwise BigIntTooBig,
TerminationRequested;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
} label TerminationRequested {
TerminateExecution();
}
}
macro BigIntDivideImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntDivZero, TerminationRequested {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: x / 0n
if (ylength == 0) {
goto BigIntDivZero;
}
// case: x / y, where x < y
if (MutableBigIntAbsoluteCompare(x, y) < 0) {
const zero = AllocateEmptyBigInt(kPositiveSign, 0);
return Convert<BigInt>(zero);
}
// case: x / 1n
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
const resultSign = (xsign != ysign) ? kNegativeSign : kPositiveSign;
if (ylength == 1 && LoadBigIntDigit(y, 0) == 1) {
return resultSign == xsign ? x : BigIntUnaryMinus(x);
}
// case: x / y
let resultLength = xlength - ylength + 1;
// This implies a *very* conservative estimate that kBarrettThreshold > 10.
if (ylength > 10) resultLength++;
const result = AllocateEmptyBigIntNoThrow(resultSign, resultLength)
otherwise unreachable;
if (CppAbsoluteDivAndCanonicalize(result, x, y) == 1) {
goto TerminationRequested;
}
return Convert<BigInt>(result);
}
builtin BigIntDivideNoThrow(implicit context: Context)(x: BigInt,
y: BigInt): Numeric {
try {
return BigIntDivideImpl(x, y) otherwise BigIntDivZero, TerminationRequested;
} label BigIntDivZero {
// Smi sentinel 0 is used to signal BigIntDivZero exception.
return Convert<Smi>(0);
} label TerminationRequested {
// Smi sentinel 1 is used to signal TerminateExecution exception.
return Convert<Smi>(1);
}
}
builtin BigIntDivide(implicit context: Context)(xNum: Numeric,
yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntDivideImpl(x, y) otherwise BigIntDivZero, TerminationRequested;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntDivZero {
ThrowRangeError(MessageTemplate::kBigIntDivZero);
} label TerminationRequested {
TerminateExecution();
}
}
macro BigIntModulusImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntDivZero, TerminationRequested {
const ylength = ReadBigIntLength(y);
// case: x % 0n
if (ylength == 0) {
goto BigIntDivZero;
}
// case: x % y, where x < y
if (MutableBigIntAbsoluteCompare(x, y) < 0) {
return x;
}
// case: x % 1n or x % -1n
if (ylength == 1 && LoadBigIntDigit(y, 0) == 1) {
const zero = AllocateEmptyBigInt(kPositiveSign, 0);
return Convert<BigInt>(zero);
}
// case: x % y
const resultSign = ReadBigIntSign(x);
const resultLength = ylength;
const result = AllocateEmptyBigIntNoThrow(resultSign, resultLength)
otherwise unreachable;
if (CppAbsoluteModAndCanonicalize(result, x, y) == 1) {
goto TerminationRequested;
}
return Convert<BigInt>(result);
}
builtin BigIntModulusNoThrow(implicit context: Context)(x: BigInt,
y: BigInt): Numeric {
try {
return BigIntModulusImpl(x, y) otherwise BigIntDivZero,
TerminationRequested;
} label BigIntDivZero {
// Smi sentinel 0 is used to signal BigIntDivZero exception.
return Convert<Smi>(0);
} label TerminationRequested {
// Smi sentinel 1 is used to signal TerminateExecution exception.
return Convert<Smi>(1);
}
}
builtin BigIntModulus(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntModulusImpl(x, y) otherwise BigIntDivZero,
TerminationRequested;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntDivZero {
ThrowRangeError(MessageTemplate::kBigIntDivZero);
} label TerminationRequested {
TerminateExecution();
}
}
macro BigIntBitwiseAndImpl(implicit context: Context)(x: BigInt,
y: BigInt): BigInt labels BigIntTooBig {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n & y
if (xlength == 0) {
return x;
}
// case: x & 0n
if (ylength == 0) {
return y;
}
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign == kPositiveSign && ysign == kPositiveSign) {
const resultLength = (xlength < ylength) ? xlength : ylength;
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, resultLength)
otherwise unreachable;
CppBitwiseAndPosPosAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kNegativeSign && ysign == kNegativeSign) {
const resultLength = ((xlength > ylength) ? xlength : ylength) + 1;
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise BigIntTooBig;
CppBitwiseAndNegNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kPositiveSign && ysign == kNegativeSign) {
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, xlength)
otherwise unreachable;
CppBitwiseAndPosNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else {
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, ylength)
otherwise unreachable;
CppBitwiseAndPosNegAndCanonicalize(result, y, x);
return Convert<BigInt>(result);
}
}
builtin BigIntBitwiseAndNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntBitwiseAndImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinel 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntBitwiseAnd(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntBitwiseAndImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro BigIntBitwiseOrImpl(implicit context: Context)(x: BigInt,
y: BigInt): BigInt {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n | y
if (xlength == 0) {
return y;
}
// case: x | 0n
if (ylength == 0) {
return x;
}
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
const resultLength = (xlength > ylength) ? xlength : ylength;
if (xsign == kPositiveSign && ysign == kPositiveSign) {
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, resultLength)
otherwise unreachable;
CppBitwiseOrPosPosAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kNegativeSign && ysign == kNegativeSign) {
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise unreachable;
CppBitwiseOrNegNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kPositiveSign && ysign == kNegativeSign) {
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise unreachable;
CppBitwiseOrPosNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else {
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise unreachable;
CppBitwiseOrPosNegAndCanonicalize(result, y, x);
return Convert<BigInt>(result);
}
}
builtin BigIntBitwiseOrNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
return BigIntBitwiseOrImpl(x, y);
}
builtin BigIntBitwiseOr(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntBitwiseOrImpl(x, y);
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
}
macro BigIntBitwiseXorImpl(implicit context: Context)(x: BigInt,
y: BigInt): BigInt labels BigIntTooBig {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n ^ y
if (xlength == 0) {
return y;
}
// case: x ^ 0n
if (ylength == 0) {
return x;
}
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign == kPositiveSign && ysign == kPositiveSign) {
const resultLength = (xlength > ylength) ? xlength : ylength;
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, resultLength)
otherwise unreachable;
CppBitwiseXorPosPosAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kNegativeSign && ysign == kNegativeSign) {
const resultLength = (xlength > ylength) ? xlength : ylength;
const result = AllocateEmptyBigIntNoThrow(kPositiveSign, resultLength)
otherwise unreachable;
CppBitwiseXorNegNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else if (xsign == kPositiveSign && ysign == kNegativeSign) {
const resultLength = ((xlength > ylength) ? xlength : ylength) + 1;
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise BigIntTooBig;
CppBitwiseXorPosNegAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
} else {
const resultLength = ((xlength > ylength) ? xlength : ylength) + 1;
const result = AllocateEmptyBigIntNoThrow(kNegativeSign, resultLength)
otherwise BigIntTooBig;
CppBitwiseXorPosNegAndCanonicalize(result, y, x);
return Convert<BigInt>(result);
}
}
builtin BigIntBitwiseXorNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntBitwiseXorImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinel 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntBitwiseXor(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntBitwiseXorImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
macro MutableBigIntLeftShiftByAbsolute(
implicit context: Context)(x: BigInt,
y: BigInt): BigInt labels BigIntTooBig {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n << y
if (xlength == 0) {
return x;
}
// case: x << 0n
if (ylength == 0) {
return x;
}
if (ylength > 1) {
// Depends on kBigIntMaxLengthBits <= (1 << kBigIntDigitSize).
goto BigIntTooBig;
}
const shiftAbs = LoadBigIntDigit(y, 0);
if (shiftAbs > kBigIntMaxLengthBits) {
goto BigIntTooBig;
}
// {shift} is positive.
const shift = Convert<intptr>(shiftAbs);
let resultLength = xlength + shift / kBigIntDigitBits;
const bitsShift = shift % kBigIntDigitBits;
const xmsd = LoadBigIntDigit(x, xlength - 1);
if (bitsShift != 0 &&
xmsd >>> Convert<uintptr>(kBigIntDigitBits - bitsShift) != 0) {
resultLength++;
}
const result = AllocateEmptyBigIntNoThrow(ReadBigIntSign(x), resultLength)
otherwise BigIntTooBig;
CppLeftShiftAndCanonicalize(result, x, shift);
return Convert<BigInt>(result);
}
macro RightShiftByMaximum(implicit context: Context)(sign: uint32): BigInt {
if (sign == kNegativeSign) {
const minusOne = AllocateEmptyBigInt(kNegativeSign, 1);
StoreBigIntDigit(minusOne, 0, 1);
return Convert<BigInt>(minusOne);
} else {
return Convert<BigInt>(AllocateEmptyBigInt(kPositiveSign, 0));
}
}
macro MutableBigIntRightShiftByAbsolute(
implicit context: Context)(x: BigInt, y: BigInt): BigInt {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n >> y
if (xlength == 0) {
return x;
}
// case: x >> 0n
if (ylength == 0) {
return x;
}
const sign = ReadBigIntSign(x);
if (ylength > 1) {
// Depends on kBigIntMaxLengthBits <= (1 << kBigIntDigitSize).
return RightShiftByMaximum(sign);
}
const shiftAbs = LoadBigIntDigit(y, 0);
if (shiftAbs > kBigIntMaxLengthBits) {
return RightShiftByMaximum(sign);
}
// {shift} is positive.
const shift = Convert<intptr>(shiftAbs);
const returnVal = CppRightShiftResultLength(x, sign, shift);
const mustRoundDown = returnVal >>> kMustRoundDownBitShift;
const lengthMask = (1 << kMustRoundDownBitShift) - 1;
const resultLength = Convert<intptr>(returnVal & lengthMask);
if (resultLength == 0) {
return RightShiftByMaximum(sign);
}
const result = AllocateEmptyBigIntNoThrow(sign, resultLength)
otherwise unreachable;
CppRightShiftAndCanonicalize(result, x, shift, mustRoundDown);
return Convert<BigInt>(result);
}
macro BigIntShiftLeftImpl(implicit context: Context)(x: BigInt,
y: BigInt): BigInt labels BigIntTooBig {
if (ReadBigIntSign(y) == kNegativeSign) {
return MutableBigIntRightShiftByAbsolute(x, y);
} else {
return MutableBigIntLeftShiftByAbsolute(x, y) otherwise BigIntTooBig;
}
}
macro BigIntShiftRightImpl(implicit context: Context)(x: BigInt,
y: BigInt): BigInt labels BigIntTooBig {
if (ReadBigIntSign(y) == kNegativeSign) {
return MutableBigIntLeftShiftByAbsolute(x, y) otherwise BigIntTooBig;
} else {
return MutableBigIntRightShiftByAbsolute(x, y);
}
}
builtin BigIntShiftLeftNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntShiftLeftImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinel 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntShiftLeft(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntShiftLeftImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
builtin BigIntShiftRightNoThrow(
implicit context: Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntShiftRightImpl(x, y) otherwise BigIntTooBig;
} label BigIntTooBig {
// Smi sentinel 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntShiftRight(
implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntShiftRightImpl(x, y) otherwise BigIntTooBig;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
}
}
builtin BigIntEqual(implicit context: Context)(x: BigInt,
y: BigInt): Boolean {
if (ReadBigIntSign(x) != ReadBigIntSign(y)) {
return False;
}
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
if (xlength != ylength) {
return False;
}
for (let i: intptr = 0; i < xlength; ++i) {
if (LoadBigIntDigit(x, i) != LoadBigIntDigit(y, i)) {
return False;
}
}
return True;
}
// Returns r such that r < 0 if |x| < |y|; r > 0 if |x| > |y|;
// r == 0 if |x| == |y|.
macro BigIntCompareAbsolute(
implicit context: Context)(x: BigInt, y: BigInt): intptr {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
const diff = xlength - ylength;
if (diff != 0) {
return diff;
}
// case: {xlength} == {ylength}
for (let i: intptr = xlength - 1; i >= 0; --i) {
const xdigit = LoadBigIntDigit(x, i);
const ydigit = LoadBigIntDigit(y, i);
if (xdigit != ydigit) {
return (xdigit > ydigit) ? kGreaterThan : kLessThan;
}
}
return 0;
}
// Returns r such that r < 0 if x < y; r > 0 if x > y; r == 0 if x == y.
macro BigIntCompare(implicit context: Context)(x: BigInt,
y: BigInt): intptr {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign != ysign) {
return xsign == kPositiveSign ? kGreaterThan : kLessThan;
}
// case: {xsign} == {ysign}
const diff = BigIntCompareAbsolute(x, y);
return xsign == kPositiveSign ? diff : 0 - diff;
}
builtin BigIntLessThan(implicit context: Context)(x: BigInt,
y: BigInt): Boolean {
return BigIntCompare(x, y) < 0 ? True : False;
}
builtin BigIntGreaterThan(implicit context: Context)(x: BigInt,
y: BigInt): Boolean {
return BigIntCompare(x, y) > 0 ? True : False;
}
builtin BigIntLessThanOrEqual(
implicit context: Context)(x: BigInt, y: BigInt): Boolean {
return BigIntCompare(x, y) <= 0 ? True : False;
}
builtin BigIntGreaterThanOrEqual(
implicit context: Context)(x: BigInt, y: BigInt): Boolean {
return BigIntCompare(x, y) >= 0 ? True : False;
}
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