blob: 27a3a243117e0cbd8cf3f6ff9b31c2490b5fb607 [file] [log] [blame] [edit]
// Copyright 2021 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/bigint/bigint-internal.h"
#include "src/bigint/digit-arithmetic.h"
#include "src/bigint/vector-arithmetic.h"
namespace v8 {
namespace bigint {
// Z := X * y, where y is a single digit.
void ProcessorImpl::MultiplySingle(RWDigits Z, Digits X, digit_t y) {
DCHECK(y != 0);
digit_t carry = 0;
digit_t high = 0;
for (int i = 0; i < X.len(); i++) {
digit_t new_high;
digit_t low = digit_mul(X[i], y, &new_high);
Z[i] = digit_add3(low, high, carry, &carry);
high = new_high;
}
AddWorkEstimate(X.len());
Z[X.len()] = carry + high;
for (int i = X.len() + 1; i < Z.len(); i++) Z[i] = 0;
}
#define BODY(min, max) \
for (int j = min; j <= max; j++) { \
digit_t high; \
digit_t low = digit_mul(X[j], Y[i - j], &high); \
digit_t carrybit; \
zi = digit_add2(zi, low, &carrybit); \
carry += carrybit; \
next = digit_add2(next, high, &carrybit); \
next_carry += carrybit; \
} \
Z[i] = zi
// Z := X * Y.
// O(n²) "schoolbook" multiplication algorithm. Optimized to minimize
// bounds and overflow checks: rather than looping over X for every digit
// of Y (or vice versa), we loop over Z. The {BODY} macro above is what
// computes one of Z's digits as a sum of the products of relevant digits
// of X and Y. This yields a nearly 2x improvement compared to more obvious
// implementations.
// This method is *highly* performance sensitive even for the advanced
// algorithms, which use this as the base case of their recursive calls.
void ProcessorImpl::MultiplySchoolbook(RWDigits Z, Digits X, Digits Y) {
DCHECK(IsDigitNormalized(X));
DCHECK(IsDigitNormalized(Y));
DCHECK(X.len() >= Y.len());
DCHECK(Z.len() >= X.len() + Y.len());
if (X.len() == 0 || Y.len() == 0) return Z.Clear();
digit_t next, next_carry = 0, carry = 0;
// Unrolled first iteration: it's trivial.
Z[0] = digit_mul(X[0], Y[0], &next);
int i = 1;
// Unrolled second iteration: a little less setup.
if (i < Y.len()) {
digit_t zi = next;
next = 0;
BODY(0, 1);
i++;
}
// Main part: since X.len() >= Y.len() > i, no bounds checks are needed.
for (; i < Y.len(); i++) {
digit_t zi = digit_add2(next, carry, &carry);
next = next_carry + carry;
carry = 0;
next_carry = 0;
BODY(0, i);
AddWorkEstimate(i);
}
// Last part: i exceeds Y now, we have to be careful about bounds.
int loop_end = X.len() + Y.len() - 2;
for (; i <= loop_end; i++) {
int max_x_index = std::min(i, X.len() - 1);
int max_y_index = Y.len() - 1;
int min_x_index = i - max_y_index;
digit_t zi = digit_add2(next, carry, &carry);
next = next_carry + carry;
carry = 0;
next_carry = 0;
BODY(min_x_index, max_x_index);
AddWorkEstimate(max_x_index - min_x_index);
}
// Write the last digit, and zero out any extra space in Z.
Z[i++] = digit_add2(next, carry, &carry);
DCHECK(carry == 0);
for (; i < Z.len(); i++) Z[i] = 0;
}
#undef BODY
} // namespace bigint
} // namespace v8