1 // Copyright 2021 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/bigint/bigint-internal.h"
6 #include "src/bigint/digit-arithmetic.h"
7 #include "src/bigint/util.h"
8 #include "src/bigint/vector-arithmetic.h"
9
10 namespace v8 {
11 namespace bigint {
12
BitwiseAnd_PosPos(RWDigits Z,Digits X,Digits Y)13 void BitwiseAnd_PosPos(RWDigits Z, Digits X, Digits Y) {
14 int pairs = std::min(X.len(), Y.len());
15 DCHECK(Z.len() >= pairs);
16 int i = 0;
17 for (; i < pairs; i++) Z[i] = X[i] & Y[i];
18 for (; i < Z.len(); i++) Z[i] = 0;
19 }
20
BitwiseAnd_NegNeg(RWDigits Z,Digits X,Digits Y)21 void BitwiseAnd_NegNeg(RWDigits Z, Digits X, Digits Y) {
22 // (-x) & (-y) == ~(x-1) & ~(y-1)
23 // == ~((x-1) | (y-1))
24 // == -(((x-1) | (y-1)) + 1)
25 int pairs = std::min(X.len(), Y.len());
26 digit_t x_borrow = 1;
27 digit_t y_borrow = 1;
28 int i = 0;
29 for (; i < pairs; i++) {
30 Z[i] = digit_sub(X[i], x_borrow, &x_borrow) |
31 digit_sub(Y[i], y_borrow, &y_borrow);
32 }
33 // (At least) one of the next two loops will perform zero iterations:
34 for (; i < X.len(); i++) Z[i] = digit_sub(X[i], x_borrow, &x_borrow);
35 for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], y_borrow, &y_borrow);
36 DCHECK(x_borrow == 0);
37 DCHECK(y_borrow == 0);
38 for (; i < Z.len(); i++) Z[i] = 0;
39 Add(Z, 1);
40 }
41
BitwiseAnd_PosNeg(RWDigits Z,Digits X,Digits Y)42 void BitwiseAnd_PosNeg(RWDigits Z, Digits X, Digits Y) {
43 // x & (-y) == x & ~(y-1)
44 int pairs = std::min(X.len(), Y.len());
45 digit_t borrow = 1;
46 int i = 0;
47 for (; i < pairs; i++) Z[i] = X[i] & ~digit_sub(Y[i], borrow, &borrow);
48 for (; i < X.len(); i++) Z[i] = X[i];
49 for (; i < Z.len(); i++) Z[i] = 0;
50 }
51
BitwiseOr_PosPos(RWDigits Z,Digits X,Digits Y)52 void BitwiseOr_PosPos(RWDigits Z, Digits X, Digits Y) {
53 int pairs = std::min(X.len(), Y.len());
54 int i = 0;
55 for (; i < pairs; i++) Z[i] = X[i] | Y[i];
56 // (At least) one of the next two loops will perform zero iterations:
57 for (; i < X.len(); i++) Z[i] = X[i];
58 for (; i < Y.len(); i++) Z[i] = Y[i];
59 for (; i < Z.len(); i++) Z[i] = 0;
60 }
61
BitwiseOr_NegNeg(RWDigits Z,Digits X,Digits Y)62 void BitwiseOr_NegNeg(RWDigits Z, Digits X, Digits Y) {
63 // (-x) | (-y) == ~(x-1) | ~(y-1)
64 // == ~((x-1) & (y-1))
65 // == -(((x-1) & (y-1)) + 1)
66 int pairs = std::min(X.len(), Y.len());
67 digit_t x_borrow = 1;
68 digit_t y_borrow = 1;
69 int i = 0;
70 for (; i < pairs; i++) {
71 Z[i] = digit_sub(X[i], x_borrow, &x_borrow) &
72 digit_sub(Y[i], y_borrow, &y_borrow);
73 }
74 // Any leftover borrows don't matter, the '&' would drop them anyway.
75 for (; i < Z.len(); i++) Z[i] = 0;
76 Add(Z, 1);
77 }
78
BitwiseOr_PosNeg(RWDigits Z,Digits X,Digits Y)79 void BitwiseOr_PosNeg(RWDigits Z, Digits X, Digits Y) {
80 // x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
81 int pairs = std::min(X.len(), Y.len());
82 digit_t borrow = 1;
83 int i = 0;
84 for (; i < pairs; i++) Z[i] = digit_sub(Y[i], borrow, &borrow) & ~X[i];
85 for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], borrow, &borrow);
86 DCHECK(borrow == 0);
87 for (; i < Z.len(); i++) Z[i] = 0;
88 Add(Z, 1);
89 }
90
BitwiseXor_PosPos(RWDigits Z,Digits X,Digits Y)91 void BitwiseXor_PosPos(RWDigits Z, Digits X, Digits Y) {
92 int pairs = X.len();
93 if (Y.len() < X.len()) {
94 std::swap(X, Y);
95 pairs = X.len();
96 }
97 DCHECK(X.len() <= Y.len());
98 int i = 0;
99 for (; i < pairs; i++) Z[i] = X[i] ^ Y[i];
100 for (; i < Y.len(); i++) Z[i] = Y[i];
101 for (; i < Z.len(); i++) Z[i] = 0;
102 }
103
BitwiseXor_NegNeg(RWDigits Z,Digits X,Digits Y)104 void BitwiseXor_NegNeg(RWDigits Z, Digits X, Digits Y) {
105 // (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
106 int pairs = std::min(X.len(), Y.len());
107 digit_t x_borrow = 1;
108 digit_t y_borrow = 1;
109 int i = 0;
110 for (; i < pairs; i++) {
111 Z[i] = digit_sub(X[i], x_borrow, &x_borrow) ^
112 digit_sub(Y[i], y_borrow, &y_borrow);
113 }
114 // (At least) one of the next two loops will perform zero iterations:
115 for (; i < X.len(); i++) Z[i] = digit_sub(X[i], x_borrow, &x_borrow);
116 for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], y_borrow, &y_borrow);
117 DCHECK(x_borrow == 0);
118 DCHECK(y_borrow == 0);
119 for (; i < Z.len(); i++) Z[i] = 0;
120 }
121
BitwiseXor_PosNeg(RWDigits Z,Digits X,Digits Y)122 void BitwiseXor_PosNeg(RWDigits Z, Digits X, Digits Y) {
123 // x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
124 int pairs = std::min(X.len(), Y.len());
125 digit_t borrow = 1;
126 int i = 0;
127 for (; i < pairs; i++) Z[i] = X[i] ^ digit_sub(Y[i], borrow, &borrow);
128 // (At least) one of the next two loops will perform zero iterations:
129 for (; i < X.len(); i++) Z[i] = X[i];
130 for (; i < Y.len(); i++) Z[i] = digit_sub(Y[i], borrow, &borrow);
131 DCHECK(borrow == 0);
132 for (; i < Z.len(); i++) Z[i] = 0;
133 Add(Z, 1);
134 }
135
LeftShift(RWDigits Z,Digits X,digit_t shift)136 void LeftShift(RWDigits Z, Digits X, digit_t shift) {
137 int digit_shift = static_cast<int>(shift / kDigitBits);
138 int bits_shift = static_cast<int>(shift % kDigitBits);
139
140 int i = 0;
141 for (; i < digit_shift; ++i) Z[i] = 0;
142 if (bits_shift == 0) {
143 for (; i < X.len() + digit_shift; ++i) Z[i] = X[i - digit_shift];
144 for (; i < Z.len(); ++i) Z[i] = 0;
145 } else {
146 digit_t carry = 0;
147 for (; i < X.len() + digit_shift; ++i) {
148 digit_t d = X[i - digit_shift];
149 Z[i] = (d << bits_shift) | carry;
150 carry = d >> (kDigitBits - bits_shift);
151 }
152 if (carry != 0) Z[i++] = carry;
153 for (; i < Z.len(); ++i) Z[i] = 0;
154 }
155 }
156
RightShift_ResultLength(Digits X,bool x_sign,digit_t shift,RightShiftState * state)157 int RightShift_ResultLength(Digits X, bool x_sign, digit_t shift,
158 RightShiftState* state) {
159 int digit_shift = static_cast<int>(shift / kDigitBits);
160 int bits_shift = static_cast<int>(shift % kDigitBits);
161 int result_length = X.len() - digit_shift;
162 if (result_length <= 0) return 0;
163
164 // For negative numbers, round down if any bit was shifted out (so that e.g.
165 // -5n >> 1n == -3n and not -2n). Check now whether this will happen and
166 // whether it can cause overflow into a new digit.
167 bool must_round_down = false;
168 if (x_sign) {
169 const digit_t mask = (static_cast<digit_t>(1) << bits_shift) - 1;
170 if ((X[digit_shift] & mask) != 0) {
171 must_round_down = true;
172 } else {
173 for (int i = 0; i < digit_shift; i++) {
174 if (X[i] != 0) {
175 must_round_down = true;
176 break;
177 }
178 }
179 }
180 }
181 // If bits_shift is non-zero, it frees up bits, preventing overflow.
182 if (must_round_down && bits_shift == 0) {
183 // Overflow cannot happen if the most significant digit has unset bits.
184 const bool rounding_can_overflow = digit_ismax(X.msd());
185 if (rounding_can_overflow) ++result_length;
186 }
187
188 if (state) {
189 DCHECK(!must_round_down || x_sign);
190 state->must_round_down = must_round_down;
191 }
192 return result_length;
193 }
194
RightShift(RWDigits Z,Digits X,digit_t shift,const RightShiftState & state)195 void RightShift(RWDigits Z, Digits X, digit_t shift,
196 const RightShiftState& state) {
197 int digit_shift = static_cast<int>(shift / kDigitBits);
198 int bits_shift = static_cast<int>(shift % kDigitBits);
199
200 int i = 0;
201 if (bits_shift == 0) {
202 for (; i < X.len() - digit_shift; ++i) Z[i] = X[i + digit_shift];
203 } else {
204 digit_t carry = X[digit_shift] >> bits_shift;
205 for (; i < X.len() - digit_shift - 1; ++i) {
206 digit_t d = X[i + digit_shift + 1];
207 Z[i] = (d << (kDigitBits - bits_shift)) | carry;
208 carry = d >> bits_shift;
209 }
210 Z[i++] = carry;
211 }
212 for (; i < Z.len(); ++i) Z[i] = 0;
213
214 if (state.must_round_down) {
215 // Rounding down (a negative value) means adding one to
216 // its absolute value. This cannot overflow.
217 Add(Z, 1);
218 }
219 }
220
221 namespace {
222
223 // Z := (least significant n bits of X).
TruncateToNBits(RWDigits Z,Digits X,int n)224 void TruncateToNBits(RWDigits Z, Digits X, int n) {
225 int digits = DIV_CEIL(n, kDigitBits);
226 int bits = n % kDigitBits;
227 // Copy all digits except the MSD.
228 int last = digits - 1;
229 for (int i = 0; i < last; i++) {
230 Z[i] = X[i];
231 }
232 // The MSD might contain extra bits that we don't want.
233 digit_t msd = X[last];
234 if (bits != 0) {
235 int drop = kDigitBits - bits;
236 msd = (msd << drop) >> drop;
237 }
238 Z[last] = msd;
239 }
240
241 // Z := 2**n - (least significant n bits of X).
TruncateAndSubFromPowerOfTwo(RWDigits Z,Digits X,int n)242 void TruncateAndSubFromPowerOfTwo(RWDigits Z, Digits X, int n) {
243 int digits = DIV_CEIL(n, kDigitBits);
244 int bits = n % kDigitBits;
245 // Process all digits except the MSD. Take X's digits, then simulate leading
246 // zeroes.
247 int last = digits - 1;
248 int have_x = std::min(last, X.len());
249 digit_t borrow = 0;
250 int i = 0;
251 for (; i < have_x; i++) Z[i] = digit_sub2(0, X[i], borrow, &borrow);
252 for (; i < last; i++) Z[i] = digit_sub(0, borrow, &borrow);
253
254 // The MSD might contain extra bits that we don't want.
255 digit_t msd = last < X.len() ? X[last] : 0;
256 if (bits == 0) {
257 Z[last] = digit_sub2(0, msd, borrow, &borrow);
258 } else {
259 int drop = kDigitBits - bits;
260 msd = (msd << drop) >> drop;
261 digit_t minuend_msd = static_cast<digit_t>(1) << bits;
262 digit_t result_msd = digit_sub2(minuend_msd, msd, borrow, &borrow);
263 DCHECK(borrow == 0); // result < 2^n.
264 // If all subtracted bits were zero, we have to get rid of the
265 // materialized minuend_msd again.
266 Z[last] = result_msd & (minuend_msd - 1);
267 }
268 }
269
270 } // namespace
271
272 // Returns -1 when the operation would return X unchanged.
AsIntNResultLength(Digits X,bool x_negative,int n)273 int AsIntNResultLength(Digits X, bool x_negative, int n) {
274 int needed_digits = DIV_CEIL(n, kDigitBits);
275 // Generally: decide based on number of digits, and bits in the top digit.
276 if (X.len() < needed_digits) return -1;
277 if (X.len() > needed_digits) return needed_digits;
278 digit_t top_digit = X[needed_digits - 1];
279 digit_t compare_digit = digit_t{1} << ((n - 1) % kDigitBits);
280 if (top_digit < compare_digit) return -1;
281 if (top_digit > compare_digit) return needed_digits;
282 // Special case: if X == -2**(n-1), truncation is a no-op.
283 if (!x_negative) return needed_digits;
284 for (int i = needed_digits - 2; i >= 0; i--) {
285 if (X[i] != 0) return needed_digits;
286 }
287 return -1;
288 }
289
AsIntN(RWDigits Z,Digits X,bool x_negative,int n)290 bool AsIntN(RWDigits Z, Digits X, bool x_negative, int n) {
291 DCHECK(X.len() > 0);
292 DCHECK(n > 0);
293 DCHECK(AsIntNResultLength(X, x_negative, n) > 0);
294 int needed_digits = DIV_CEIL(n, kDigitBits);
295 digit_t top_digit = X[needed_digits - 1];
296 digit_t compare_digit = digit_t{1} << ((n - 1) % kDigitBits);
297 // The canonical algorithm would be: convert negative numbers to two's
298 // complement representation, truncate, convert back to sign+magnitude. To
299 // avoid the conversions, we predict what the result would be:
300 // When the (n-1)th bit is not set:
301 // - truncate the absolute value
302 // - preserve the sign.
303 // When the (n-1)th bit is set:
304 // - subtract the truncated absolute value from 2**n to simulate two's
305 // complement representation
306 // - flip the sign, unless it's the special case where the input is negative
307 // and the result is the minimum n-bit integer. E.g. asIntN(3, -12) => -4.
308 bool has_bit = (top_digit & compare_digit) == compare_digit;
309 if (!has_bit) {
310 TruncateToNBits(Z, X, n);
311 return x_negative;
312 }
313 TruncateAndSubFromPowerOfTwo(Z, X, n);
314 if (!x_negative) return true; // Result is negative.
315 // Scan for the special case (see above): if all bits below the (n-1)th
316 // digit are zero, the result is negative.
317 if ((top_digit & (compare_digit - 1)) != 0) return false;
318 for (int i = needed_digits - 2; i >= 0; i--) {
319 if (X[i] != 0) return false;
320 }
321 return true;
322 }
323
324 // Returns -1 when the operation would return X unchanged.
AsUintN_Pos_ResultLength(Digits X,int n)325 int AsUintN_Pos_ResultLength(Digits X, int n) {
326 int needed_digits = DIV_CEIL(n, kDigitBits);
327 if (X.len() < needed_digits) return -1;
328 if (X.len() > needed_digits) return needed_digits;
329 int bits_in_top_digit = n % kDigitBits;
330 if (bits_in_top_digit == 0) return -1;
331 digit_t top_digit = X[needed_digits - 1];
332 if ((top_digit >> bits_in_top_digit) == 0) return -1;
333 return needed_digits;
334 }
335
AsUintN_Pos(RWDigits Z,Digits X,int n)336 void AsUintN_Pos(RWDigits Z, Digits X, int n) {
337 DCHECK(AsUintN_Pos_ResultLength(X, n) > 0);
338 TruncateToNBits(Z, X, n);
339 }
340
AsUintN_Neg(RWDigits Z,Digits X,int n)341 void AsUintN_Neg(RWDigits Z, Digits X, int n) {
342 TruncateAndSubFromPowerOfTwo(Z, X, n);
343 }
344
345 } // namespace bigint
346 } // namespace v8
347