// 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' { } type BigInt extends BigIntBase; @noVerifier @hasSameInstanceTypeAsParent extern class MutableBigInt extends BigIntBase generates 'TNode' { } Convert(i: MutableBigInt): BigInt { assert(bigint::IsCanonicalized(i)); return %RawDownCast(Convert(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(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(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(0); } } builtin BigIntAdd(implicit context: Context)(xNum: Numeric, yNum: Numeric): BigInt { try { const x = Cast(xNum) otherwise MixedTypes; const y = Cast(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(result); } } // namespace bigint