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/vector-arithmetic.h"
8
9 namespace v8 {
10 namespace bigint {
11
12 // Z := X * y, where y is a single digit.
MultiplySingle(RWDigits Z,Digits X,digit_t y)13 void ProcessorImpl::MultiplySingle(RWDigits Z, Digits X, digit_t y) {
14 DCHECK(y != 0);
15 digit_t carry = 0;
16 digit_t high = 0;
17 for (int i = 0; i < X.len(); i++) {
18 digit_t new_high;
19 digit_t low = digit_mul(X[i], y, &new_high);
20 Z[i] = digit_add3(low, high, carry, &carry);
21 high = new_high;
22 }
23 AddWorkEstimate(X.len());
24 Z[X.len()] = carry + high;
25 for (int i = X.len() + 1; i < Z.len(); i++) Z[i] = 0;
26 }
27
28 #define BODY(min, max) \
29 for (int j = min; j <= max; j++) { \
30 digit_t high; \
31 digit_t low = digit_mul(X[j], Y[i - j], &high); \
32 digit_t carrybit; \
33 zi = digit_add2(zi, low, &carrybit); \
34 carry += carrybit; \
35 next = digit_add2(next, high, &carrybit); \
36 next_carry += carrybit; \
37 } \
38 Z[i] = zi
39
40 // Z := X * Y.
41 // O(n²) "schoolbook" multiplication algorithm. Optimized to minimize
42 // bounds and overflow checks: rather than looping over X for every digit
43 // of Y (or vice versa), we loop over Z. The {BODY} macro above is what
44 // computes one of Z's digits as a sum of the products of relevant digits
45 // of X and Y. This yields a nearly 2x improvement compared to more obvious
46 // implementations.
47 // This method is *highly* performance sensitive even for the advanced
48 // algorithms, which use this as the base case of their recursive calls.
MultiplySchoolbook(RWDigits Z,Digits X,Digits Y)49 void ProcessorImpl::MultiplySchoolbook(RWDigits Z, Digits X, Digits Y) {
50 DCHECK(IsDigitNormalized(X));
51 DCHECK(IsDigitNormalized(Y));
52 DCHECK(X.len() >= Y.len());
53 DCHECK(Z.len() >= X.len() + Y.len());
54 if (X.len() == 0 || Y.len() == 0) return Z.Clear();
55 digit_t next, next_carry = 0, carry = 0;
56 // Unrolled first iteration: it's trivial.
57 Z[0] = digit_mul(X[0], Y[0], &next);
58 int i = 1;
59 // Unrolled second iteration: a little less setup.
60 if (i < Y.len()) {
61 digit_t zi = next;
62 next = 0;
63 BODY(0, 1);
64 i++;
65 }
66 // Main part: since X.len() >= Y.len() > i, no bounds checks are needed.
67 for (; i < Y.len(); i++) {
68 digit_t zi = digit_add2(next, carry, &carry);
69 next = next_carry + carry;
70 carry = 0;
71 next_carry = 0;
72 BODY(0, i);
73 AddWorkEstimate(i);
74 }
75 // Last part: i exceeds Y now, we have to be careful about bounds.
76 int loop_end = X.len() + Y.len() - 2;
77 for (; i <= loop_end; i++) {
78 int max_x_index = std::min(i, X.len() - 1);
79 int max_y_index = Y.len() - 1;
80 int min_x_index = i - max_y_index;
81 digit_t zi = digit_add2(next, carry, &carry);
82 next = next_carry + carry;
83 carry = 0;
84 next_carry = 0;
85 BODY(min_x_index, max_x_index);
86 AddWorkEstimate(max_x_index - min_x_index);
87 }
88 // Write the last digit, and zero out any extra space in Z.
89 Z[i++] = digit_add2(next, carry, &carry);
90 DCHECK(carry == 0);
91 for (; i < Z.len(); i++) Z[i] = 0;
92 }
93
94 #undef BODY
95
96 } // namespace bigint
97 } // namespace v8
98