1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include 'src/builtins/builtins-bigint-gen.h'
6
7namespace bigint {
8
9const kPositiveSign: uint32 = 0;
10const kNegativeSign: uint32 = 1;
11
12extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
13    MutableBigInt, BigIntBase, BigIntBase): void;
14extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
15    MutableBigInt, BigIntBase, BigIntBase): void;
16extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare(
17    BigIntBase, BigIntBase): int32;
18
19extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32;
20extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr;
21extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength(
22    MutableBigInt, uint32, intptr): void;
23
24extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt;
25extern macro CodeStubAssembler::StoreBigIntDigit(
26    MutableBigInt, intptr, uintptr): void;
27extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr;
28
29macro IsCanonicalized(bigint: BigIntBase): bool {
30  const length = ReadBigIntLength(bigint);
31
32  if (length == 0) {
33    return ReadBigIntSign(bigint) == kPositiveSign;
34  }
35
36  return LoadBigIntDigit(bigint, length - 1) != 0;
37}
38
39macro InvertSign(sign: uint32): uint32 {
40  return sign == kPositiveSign ? kNegativeSign : kPositiveSign;
41}
42
43macro AllocateEmptyBigIntNoThrow(implicit context: Context)(
44    sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig {
45  if (length > kBigIntMaxLength) {
46    goto BigIntTooBig;
47  }
48  const result: MutableBigInt = AllocateBigInt(length);
49
50  WriteBigIntSignAndLength(result, sign, length);
51  return result;
52}
53
54macro AllocateEmptyBigInt(implicit context: Context)(
55    sign: uint32, length: intptr): MutableBigInt {
56  try {
57    return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig;
58  } label BigIntTooBig {
59    ThrowRangeError(MessageTemplate::kBigIntTooBig);
60  }
61}
62
63macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 {
64  return CppAbsoluteCompare(x, y);
65}
66
67macro MutableBigIntAbsoluteSub(implicit context: Context)(
68    x: BigInt, y: BigInt, resultSign: uint32): BigInt {
69  const xlength = ReadBigIntLength(x);
70  const ylength = ReadBigIntLength(y);
71  const xsign = ReadBigIntSign(x);
72
73  assert(MutableBigIntAbsoluteCompare(x, y) >= 0);
74  if (xlength == 0) {
75    assert(ylength == 0);
76    return x;
77  }
78
79  if (ylength == 0) {
80    return resultSign == xsign ? x : BigIntUnaryMinus(x);
81  }
82
83  const result = AllocateEmptyBigInt(resultSign, xlength);
84  CppAbsoluteSubAndCanonicalize(result, x, y);
85  return Convert<BigInt>(result);
86}
87
88macro MutableBigIntAbsoluteAdd(implicit context: Context)(
89    xBigint: BigInt, yBigint: BigInt,
90    resultSign: uint32): BigInt labels BigIntTooBig {
91  let xlength = ReadBigIntLength(xBigint);
92  let ylength = ReadBigIntLength(yBigint);
93
94  let x = xBigint;
95  let y = yBigint;
96  if (xlength < ylength) {
97    // Swap x and y so that x is longer.
98    x = yBigint;
99    y = xBigint;
100    const tempLength = xlength;
101    xlength = ylength;
102    ylength = tempLength;
103  }
104
105  // case: 0n + 0n
106  if (xlength == 0) {
107    assert(ylength == 0);
108    return x;
109  }
110
111  // case: x + 0n
112  if (ylength == 0) {
113    return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x);
114  }
115
116  // case: x + y
117  const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1)
118      otherwise BigIntTooBig;
119  CppAbsoluteAddAndCanonicalize(result, x, y);
120  return Convert<BigInt>(result);
121}
122
123macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt
124    labels BigIntTooBig {
125  const xsign = ReadBigIntSign(x);
126  const ysign = ReadBigIntSign(y);
127  if (xsign == ysign) {
128    // x + y == x + y
129    // -x + -y == -(x + y)
130    return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
131  }
132
133  // x + -y == x - y == -(y - x)
134  // -x + y == y - x == -(x - y)
135  if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
136    return MutableBigIntAbsoluteSub(x, y, xsign);
137  }
138  return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
139}
140
141builtin BigIntAddNoThrow(implicit context: Context)(
142    x: BigInt, y: BigInt): Numeric {
143  try {
144    return BigIntAddImpl(x, y) otherwise BigIntTooBig;
145  } label BigIntTooBig {
146    // Smi sentinal is used to signal BigIntTooBig exception.
147    return Convert<Smi>(0);
148  }
149}
150
151builtin BigIntAdd(implicit context: Context)(
152    xNum: Numeric, yNum: Numeric): BigInt {
153  try {
154    const x = Cast<BigInt>(xNum) otherwise MixedTypes;
155    const y = Cast<BigInt>(yNum) otherwise MixedTypes;
156
157    return BigIntAddImpl(x, y) otherwise BigIntTooBig;
158  } label MixedTypes {
159    ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
160  } label BigIntTooBig {
161    ThrowRangeError(MessageTemplate::kBigIntTooBig);
162  }
163}
164
165macro BigIntSubtractImpl(implicit context: Context)(
166    x: BigInt, y: BigInt): BigInt labels BigIntTooBig {
167  const xsign = ReadBigIntSign(x);
168  const ysign = ReadBigIntSign(y);
169  if (xsign != ysign) {
170    // x - (-y) == x + y
171    // (-x) - y == -(x + y)
172    return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
173  }
174
175  // x - y == -(y - x)
176  // (-x) - (-y) == y - x == -(x - y)
177  if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
178    return MutableBigIntAbsoluteSub(x, y, xsign);
179  }
180  return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
181}
182
183builtin BigIntSubtractNoThrow(implicit context: Context)(
184    x: BigInt, y: BigInt): Numeric {
185  try {
186    return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
187  } label BigIntTooBig {
188    // Smi sentinal is used to signal BigIntTooBig exception.
189    return Convert<Smi>(0);
190  }
191}
192
193builtin BigIntSubtract(implicit context: Context)(
194    xNum: Numeric, yNum: Numeric): BigInt {
195  try {
196    const x = Cast<BigInt>(xNum) otherwise MixedTypes;
197    const y = Cast<BigInt>(yNum) otherwise MixedTypes;
198
199    return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
200  } label MixedTypes {
201    ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
202  } label BigIntTooBig {
203    ThrowRangeError(MessageTemplate::kBigIntTooBig);
204  }
205}
206
207builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
208  const length = ReadBigIntLength(bigint);
209
210  // There is no -0n.
211  if (length == 0) {
212    return bigint;
213  }
214
215  const result =
216      AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length);
217  for (let i: intptr = 0; i < length; ++i) {
218    StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i));
219  }
220  return Convert<BigInt>(result);
221}
222
223}  // namespace bigint
224