• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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