a/math/numbers/mul.rs
1//! Multiplication of [`Number`]s: `*` and `*=`, with either operand owned or borrowed.
2//!
3//! The magnitude of a product is the product of the magnitudes, and it is negative when exactly
4//! one operand is and neither is zero. A zero magnitude is zero whatever `negative` says, and
5//! zero bytes above the most significant one count for nothing.
6//!
7//! Operands whose vectors both hold at most 16 bytes take a short path in registers: their
8//! magnitudes are read into `u128`s as the addition reads them, multiplied into four limbs, and
9//! the product, at most 32 bytes, is written in the byte order of the left operand. Up to 32 bytes
10//! each, the general path does the same with four limbs an operand and a product of eight, and up
11//! to 48 with six and a product of twelve, in registers too.
12//!
13//! Longer operands are read into limbs of eight bytes, native `u64`s, the least significant first,
14//! in working memory: a buffer in the operator's own frame when the operands have at most
15//! [`MEDIUM_LIMBS`] limbs together, which the schoolbook kernels multiply directly; otherwise a
16//! buffer on the stack of [`STACK_LIMBS`] limbs, or one on the heap past it, for the operands, the
17//! product and the scratch of Karatsuba's and Toom-Cook's methods (see [`limbs`]), each starting on
18//! 16 bytes. The kernels are inline assembly on 64-bit little-endian `aarch64` and, with BMI2, on
19//! `x86_64`, and portable Rust elsewhere and under Miri. A borrowed operand multiplied by itself
20//! (`&a * &a`) is squared, which needs about half the products of limbs.
21//!
22//! The product's length in the shortest form is then known, and a vector is chosen for it as for
23//! a sum: the vector of an operand taken by value, or of the target of `*=`, when it has room for
24//! the product (the left operand's first), and otherwise one new vector of exactly its length.
25//! The product's bytes are written there, in the byte order of the left operand, the bytes above
26//! the whole limbs as the eight that end at the top, over the limb below.
27
28// The kernels of the schoolbook method, one set per target under the name `kernels`, chosen as
29// those of the addition are: assembly on 64-bit little-endian aarch64 where the system allows
30// unaligned accesses and on x86_64, and portable Rust elsewhere and under Miri.
31pub(super) mod fft;
32#[cfg(all(
33 target_arch = "aarch64",
34 target_endian = "little",
35 target_pointer_width = "64",
36 any(
37 target_vendor = "apple",
38 target_os = "linux",
39 target_os = "android",
40 target_os = "windows",
41 target_os = "freebsd",
42 target_os = "netbsd",
43 target_os = "openbsd",
44 target_os = "fuchsia"
45 ),
46 not(miri)
47))]
48#[path = "mul/aarch64.rs"]
49pub(super) mod kernels;
50#[cfg(all(target_arch = "x86_64", not(miri)))]
51#[path = "mul/x86_64.rs"]
52mod kernels;
53#[cfg(not(any(
54 all(
55 target_arch = "aarch64",
56 target_endian = "little",
57 target_pointer_width = "64",
58 any(
59 target_vendor = "apple",
60 target_os = "linux",
61 target_os = "android",
62 target_os = "windows",
63 target_os = "freebsd",
64 target_os = "netbsd",
65 target_os = "openbsd",
66 target_os = "fuchsia"
67 ),
68 not(miri)
69 ),
70 all(target_arch = "x86_64", not(miri))
71)))]
72#[path = "mul/portable.rs"]
73mod kernels;
74pub(super) mod limbs;
75
76/// The schoolbook kernels, for the products of fixed length of the curves' fields.
77pub(crate) mod basecase {
78 use super::kernels;
79
80 /// Sets the `2n` limbs at `r` to the product of the `n` limbs at `x` and at `y`.
81 ///
82 /// # Safety
83 ///
84 /// `n >= 1`; the limbs of `x` and `y` must be valid for reads and those of `r` for writes, and
85 /// `r` apart from both.
86 #[inline(always)]
87 pub(crate) unsafe fn mul(r: *mut u64, x: *const u64, y: *const u64, n: usize) {
88 // SAFETY: as the caller guarantees.
89 unsafe { kernels::mul_basecase(r, x, n, y, n) }
90 }
91
92 /// Sets the `2n` limbs at `r` to the square of the `n` limbs at `x`.
93 ///
94 /// # Safety
95 ///
96 /// `n >= 1`; the limbs of `x` must be valid for reads and those of `r` for writes, and apart
97 /// from them.
98 #[inline(always)]
99 pub(crate) unsafe fn sqr(r: *mut u64, x: *const u64, n: usize) {
100 // SAFETY: as the caller guarantees.
101 unsafe { kernels::sqr_basecase(r, x, n) }
102 }
103}
104mod toom;
105
106pub(super) use kernels::mul_1_bytes;
107
108use super::add::{SMALL, read, read_small, significant_len, small, write, write_small};
109use super::div::limb_of;
110use super::prim::{copy_times_limb, times_limb};
111use super::{ByteOrder, Number};
112use core::mem::{self, MaybeUninit};
113use core::ops::{Mul, MulAssign};
114use core::ptr;
115
116impl Mul<&Number> for &Number {
117 type Output = Number;
118
119 /// Returns `self * rhs`, in the byte order of `self`, in the shortest form.
120 ///
121 /// The product is built in one new vector of exactly its length (one byte for zero); both
122 /// operands are left as they were. A number multiplied by itself (`&a * &a`) is squared, each
123 /// product of two different limbs computed once. The operands may have any signs, byte orders
124 /// and lengths, and need not be in the shortest form.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use a::math::numbers::{ByteOrder, Number};
130 ///
131 /// // 0x01_00 * -0x01_01 = -0x01_01_00, in the byte order of the left operand.
132 /// let a = Number::new_from_i32(0x0100, ByteOrder::BigEndian);
133 /// let b = Number::new_from_i32(-0x0101, ByteOrder::LittleEndian);
134 /// let product = &a * &b;
135 /// assert_eq!(product.value, [0x01, 0x01, 0x00]);
136 /// assert!(product.is_negative());
137 /// assert!(product.byte_order() == ByteOrder::BigEndian);
138 ///
139 /// // Anything times zero is zero, one zero byte and not negative.
140 /// let zero = &b * &Number::new_from_u8(0, ByteOrder::BigEndian);
141 /// assert_eq!(zero.value, [0]);
142 /// assert!(!zero.is_negative());
143 /// ```
144 fn mul(self, rhs: &Number) -> Number {
145 if small(self, rhs) {
146 // SAFETY: both vectors hold at most 16 bytes.
147 let p = unsafe { small_product(self, rhs) };
148 return small_number(self.byte_order, p);
149 }
150 ref_times_ref(self, rhs)
151 }
152}
153
154impl Mul<&Number> for Number {
155 type Output = Number;
156
157 /// Returns `self * rhs`, in the byte order of `self`, in the shortest form, as `self *= rhs`
158 /// does.
159 ///
160 /// The product is built in the vector of `self` when its capacity has room for it, and
161 /// otherwise in one new vector of exactly its length. `rhs` is left as it was.
162 ///
163 /// # Examples
164 ///
165 /// ```
166 /// use a::math::numbers::{ByteOrder, Number};
167 ///
168 /// let mut value = Vec::with_capacity(4);
169 /// value.extend([0xFF, 0xFF]);
170 /// let a = Number::new(value, ByteOrder::BigEndian);
171 /// let pointer = a.value.as_ptr();
172 ///
173 /// // 0xFF_FF * 0xFF_FF = 0xFF_FE_00_01, in the vector of `a`.
174 /// let product = a * &Number::new_from_u16(0xFFFF, ByteOrder::LittleEndian);
175 /// assert_eq!(product.value, [0xFF, 0xFE, 0x00, 0x01]);
176 /// assert_eq!(product.value.as_ptr(), pointer);
177 /// ```
178 fn mul(mut self, rhs: &Number) -> Number {
179 self *= rhs;
180 self
181 }
182}
183
184impl Mul<Number> for &Number {
185 type Output = Number;
186
187 /// Returns `self * rhs`, in the byte order of `self`, in the shortest form.
188 ///
189 /// The product is built in the vector of `rhs` when its capacity has room for it, also when
190 /// `rhs` is in the other byte order; otherwise it gets one new vector of exactly its length.
191 /// `self` is left as it was.
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// use a::math::numbers::{ByteOrder, Number};
197 ///
198 /// let a = Number::new_from_u16(0x0102, ByteOrder::BigEndian);
199 /// let mut value = Vec::with_capacity(4);
200 /// value.extend([0x03]);
201 /// let b = Number::new(value, ByteOrder::LittleEndian);
202 /// let pointer = b.value.as_ptr();
203 ///
204 /// // 0x01_02 * 3 = 0x03_06, big-endian, in the vector of `b`.
205 /// let product = &a * b;
206 /// assert_eq!(product.value, [0x03, 0x06]);
207 /// assert_eq!(product.value.as_ptr(), pointer);
208 /// ```
209 fn mul(self, mut rhs: Number) -> Number {
210 if small(self, &rhs) {
211 // SAFETY: both vectors hold at most 16 bytes.
212 let p = unsafe { small_product(self, &rhs) };
213 let order = self.byte_order;
214 put_small(&mut rhs.value, order, p);
215 return Number {
216 value: rhs.value,
217 negative: p.negative,
218 byte_order: order,
219 };
220 }
221 // `rhs` is dropped on return, with the vector it holds then, if any.
222 ref_times_value(self, &mut rhs)
223 }
224}
225
226impl Mul<Number> for Number {
227 type Output = Number;
228
229 /// Returns `self * rhs`, in the byte order of `self`, in the shortest form, as `self *= rhs`
230 /// does.
231 ///
232 /// The product is built in the vector of `self` when its capacity has room for it, or else
233 /// in the vector of `rhs` when that has room, also when `rhs` is in the other byte order;
234 /// otherwise it gets one new vector of exactly its length.
235 ///
236 /// # Examples
237 ///
238 /// ```
239 /// use a::math::numbers::{ByteOrder, Number};
240 ///
241 /// // -0x01_00 * -0x02_FF = 0x02_FF_00.
242 /// let a = Number::new_from_i32(-0x0100, ByteOrder::LittleEndian);
243 /// let b = Number::new_from_i32(-0x02FF, ByteOrder::BigEndian);
244 /// let product = a * b;
245 /// assert_eq!(product.value, [0x00, 0xFF, 0x02]);
246 /// assert!(!product.is_negative());
247 /// ```
248 fn mul(mut self, rhs: Number) -> Number {
249 self *= rhs;
250 self
251 }
252}
253
254impl MulAssign<&Number> for Number {
255 /// Multiplies `self` by `rhs`, keeping the byte order of `self`, in the shortest form.
256 ///
257 /// The product is built in the vector of `self` when its capacity has room for it, and
258 /// otherwise in one new vector of exactly its length, which replaces it. `rhs` is left as it
259 /// was.
260 ///
261 /// # Examples
262 ///
263 /// ```
264 /// use a::math::numbers::{ByteOrder, Number};
265 ///
266 /// let mut n = Number::new_from_u8(0x10, ByteOrder::LittleEndian);
267 /// n *= &Number::new_from_u16(0x0101, ByteOrder::BigEndian);
268 /// assert_eq!(n.value, [0x10, 0x10]);
269 /// n *= &Number::new_from_i8(-2, ByteOrder::BigEndian);
270 /// assert_eq!(n.value, [0x20, 0x20]);
271 /// assert!(n.is_negative());
272 /// ```
273 #[inline]
274 fn mul_assign(&mut self, rhs: &Number) {
275 if small(self, rhs) {
276 // SAFETY: both vectors hold at most 16 bytes.
277 let p = unsafe { small_product(self, rhs) };
278 put_small(&mut self.value, self.byte_order, p);
279 self.negative = p.negative;
280 return;
281 }
282 mul_assign_ref(self, rhs);
283 }
284}
285
286impl MulAssign<Number> for Number {
287 /// Multiplies `self` by `rhs`, keeping the byte order of `self`, in the shortest form.
288 ///
289 /// The product is built in the vector of `self` when its capacity has room for it, or else
290 /// in the vector of `rhs` when that has room, also when `rhs` is in the other byte order;
291 /// otherwise it gets one new vector of exactly its length.
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use a::math::numbers::{ByteOrder, Number};
297 ///
298 /// // `n` has no room for 0xFF * 0x01_02 = 0x01_00_FE, but the vector of the right operand
299 /// // has.
300 /// let mut n = Number::new_from_u8(0xFF, ByteOrder::BigEndian);
301 /// let mut value = Vec::with_capacity(3);
302 /// value.extend([1, 2]);
303 /// let b = Number::new(value, ByteOrder::BigEndian);
304 /// let pointer = b.value.as_ptr();
305 /// n *= b;
306 /// assert_eq!(n.value, [0x01, 0x00, 0xFE]);
307 /// assert_eq!(n.value.as_ptr(), pointer);
308 /// ```
309 #[inline]
310 fn mul_assign(&mut self, mut rhs: Number) {
311 if small(self, &rhs) {
312 // SAFETY: both vectors hold at most 16 bytes.
313 let p = unsafe { small_product(self, &rhs) };
314 if self.value.capacity() < p.len && rhs.value.capacity() >= p.len {
315 // The vector of `self` goes to `rhs`, and is dropped with it.
316 mem::swap(&mut self.value, &mut rhs.value);
317 }
318 put_small(&mut self.value, self.byte_order, p);
319 self.negative = p.negative;
320 return;
321 }
322 // `rhs` is dropped on return, with the vector it holds then.
323 mul_assign_value(self, &mut rhs);
324 }
325}
326
327// Small numbers
328
329/// The product of two small operands, as the small path computes it in registers.
330#[derive(Clone, Copy)]
331struct Small {
332 /// The four limbs of the magnitude, the least significant first.
333 limbs: [u64; 4],
334 /// The length of the product in the shortest form, from 1 to 32: one byte for zero.
335 len: usize,
336 /// Whether the product is negative; never for zero.
337 negative: bool,
338}
339
340/// The product of the small operands `a` and `b`.
341///
342/// # Safety
343///
344/// Both vectors must hold at most [`SMALL`] bytes.
345#[inline(always)]
346unsafe fn small_product(a: &Number, b: &Number) -> Small {
347 // SAFETY: as the caller guarantees.
348 let (x, y) = unsafe {
349 (
350 read_small(&a.value, a.byte_order),
351 read_small(&b.value, b.byte_order),
352 )
353 };
354 let (x0, x1) = (x as u64, (x >> 64) as u64);
355 let (y0, y1) = (y as u64, (y >> 64) as u64);
356 let p00 = u128::from(x0) * u128::from(y0);
357 let p01 = u128::from(x0) * u128::from(y1);
358 let p10 = u128::from(x1) * u128::from(y0);
359 let p11 = u128::from(x1) * u128::from(y1);
360 // Each column is at most three limbs' worth below 2^66, a `u128`.
361 let c1 = (p00 >> 64) + u128::from(p01 as u64) + u128::from(p10 as u64);
362 let c2 = (c1 >> 64) + (p01 >> 64) + (p10 >> 64) + u128::from(p11 as u64);
363 let c3 = (c2 >> 64) + (p11 >> 64);
364 let limbs = [p00 as u64, c1 as u64, c2 as u64, c3 as u64];
365 let zeros = if limbs[3] != 0 {
366 limbs[3].leading_zeros() / 8
367 } else if limbs[2] != 0 {
368 8 + limbs[2].leading_zeros() / 8
369 } else if limbs[1] != 0 {
370 16 + limbs[1].leading_zeros() / 8
371 } else {
372 24 + (limbs[0] | 1).leading_zeros() / 8
373 };
374 Small {
375 limbs,
376 len: 32 - zeros as usize,
377 negative: a.negative != b.negative && x != 0 && y != 0,
378 }
379}
380
381/// Writes the small product `p` over the first `p.len` bytes at `dst`, in the layout of `order`.
382/// Up to 16 bytes as [`write_small`] writes a sum; from 17, the 16 least significant bytes and the
383/// 16 most significant ones, which overlap and agree where they do.
384///
385/// # Safety
386///
387/// `dst` must be valid for writing `p.len` bytes.
388#[inline(always)]
389unsafe fn write_small_product(dst: *mut u8, order: ByteOrder, p: Small) {
390 let [l0, l1, l2, l3] = p.limbs;
391 let len = p.len;
392 // SAFETY: every write lies inside the `len` bytes at `dst`: `[0, 16)` and `[len - 16, len)`
393 // with `len > 16`, or what `write_small` writes for `len <= 16`.
394 unsafe {
395 if len <= SMALL {
396 write_small(dst, order, l0, l1, len);
397 return;
398 }
399 let low = u128::from(l0) | (u128::from(l1) << 64);
400 let high = u128::from(l2) | (u128::from(l3) << 64);
401 // Positions `len - 16..len`: the 256 bits shifted down by `8 (len - 16)`, 8 to 128.
402 let k = 8 * (len - 16) as u32;
403 let top = low.checked_shr(k).unwrap_or(0) | high << (128 - k);
404 if order == ByteOrder::BigEndian {
405 write(dst.add(len - 16), low.to_be_bytes());
406 write(dst, top.to_be_bytes());
407 } else {
408 write(dst, low.to_le_bytes());
409 write(dst.add(len - 16), top.to_le_bytes());
410 }
411 }
412}
413
414/// Writes the small product `p` as the whole value of `v`, in its vector when it has room, in a
415/// new one otherwise.
416#[inline(always)]
417fn put_small(v: &mut Vec<u8>, order: ByteOrder, p: Small) {
418 if v.capacity() >= p.len {
419 // SAFETY: the vector has room for the `p.len` bytes, all of which are written before the
420 // length is set.
421 unsafe {
422 write_small_product(v.as_mut_ptr(), order, p);
423 v.set_len(p.len);
424 }
425 } else {
426 replace_small(v, order, p);
427 }
428}
429
430/// Replaces `v` with one new vector of exactly the small product's length, holding it. Out of
431/// line and cold, so that the small path saves no registers for it.
432#[cold]
433#[inline(never)]
434fn replace_small(v: &mut Vec<u8>, order: ByteOrder, p: Small) {
435 *v = new_small(order, p);
436}
437
438/// The small product `p` in one new vector of exactly its length.
439#[inline(always)]
440fn new_small(order: ByteOrder, p: Small) -> Vec<u8> {
441 let mut v = Vec::with_capacity(p.len);
442 // SAFETY: the new vector has room for the `p.len` bytes, all of which are written before the
443 // length is set.
444 unsafe {
445 write_small_product(v.as_mut_ptr(), order, p);
446 v.set_len(p.len);
447 }
448 v
449}
450
451/// The small product `p` as a number in one new vector, in the byte order `order`. Out of line,
452/// so that the small path of `&a * &b` saves no registers for the allocation.
453#[inline(never)]
454fn small_number(order: ByteOrder, p: Small) -> Number {
455 Number {
456 value: new_small(order, p),
457 negative: p.negative,
458 byte_order: order,
459 }
460}
461
462// Operands of up to 32 bytes
463
464/// The longest vector, in bytes, of the operands that the general path multiplies in registers,
465/// four limbs each, as the short path does with two.
466pub(super) const WIDE: usize = 32;
467
468/// Whether `a * b` is computed in registers as four limbs each: both vectors hold at most
469/// [`WIDE`] bytes.
470#[inline(always)]
471fn wide(a: &Number, b: &Number) -> bool {
472 a.value.len() <= WIDE && b.value.len() <= WIDE
473}
474
475/// The product of two operands of up to 32 bytes, computed in registers.
476#[derive(Clone, Copy)]
477pub(super) struct Wide {
478 /// The eight limbs of the magnitude, the least significant first.
479 pub(super) limbs: [u64; 8],
480 /// The length of the product in the shortest form, from 1 to 64: one byte for zero.
481 pub(super) len: usize,
482 /// Whether the product is negative; never for zero.
483 pub(super) negative: bool,
484}
485
486/// The magnitude in the at most 32 bytes of `v`, in the layout of `order`, as four limbs: up to 16
487/// bytes as the addition reads them; from 17, the 16 least significant bytes and the 16 most
488/// significant ones, which overlap and agree where they do.
489///
490/// # Safety
491///
492/// `v` must hold at most [`WIDE`] bytes.
493#[inline(always)]
494pub(super) unsafe fn read_wide(v: &[u8], order: ByteOrder) -> [u64; 4] {
495 let (p, n) = (v.as_ptr(), v.len());
496 // SAFETY: every read lies inside the `n` bytes of `v`: `[0, 16)` and `[n - 16, n)` with
497 // `n > 16`, or what `read_small` reads.
498 unsafe {
499 if n <= SMALL {
500 let x = read_small(v, order);
501 return [x as u64, (x >> 64) as u64, 0, 0];
502 }
503 let (low, top) = if order == ByteOrder::BigEndian {
504 (
505 u128::from_be_bytes(read(p.add(n - 16))),
506 u128::from_be_bytes(read(p)),
507 )
508 } else {
509 (
510 u128::from_le_bytes(read(p)),
511 u128::from_le_bytes(read(p.add(n - 16))),
512 )
513 };
514 // `top` holds positions `n - 16..n`; those from 16 up are the high half, a shift by
515 // `8 (32 - n)` down, below 128.
516 let high = top >> (8 * (WIDE - n));
517 [
518 low as u64,
519 (low >> 64) as u64,
520 high as u64,
521 (high >> 64) as u64,
522 ]
523 }
524}
525
526/// The product of the operands `a` and `b` of up to 32 bytes: the schoolbook method on four limbs
527/// each, which the compiler unrolls and keeps in registers.
528///
529/// # Safety
530///
531/// Both vectors must hold at most [`WIDE`] bytes.
532#[inline(always)]
533unsafe fn wide_product(a: &Number, b: &Number) -> Wide {
534 // SAFETY: as the caller guarantees.
535 let (x, y) = unsafe {
536 (
537 read_wide(&a.value, a.byte_order),
538 read_wide(&b.value, b.byte_order),
539 )
540 };
541 let mut r = [0u64; 8];
542 for i in 0..4 {
543 let mut carry = 0u64;
544 for j in 0..4 {
545 let t = u128::from(x[i]) * u128::from(y[j]) + u128::from(r[i + j]) + u128::from(carry);
546 r[i + j] = t as u64;
547 carry = (t >> 64) as u64;
548 }
549 r[i + 4] = carry;
550 }
551 let top = r.iter().rposition(|&l| l != 0);
552 let len = match top {
553 Some(t) => 8 * t + 8 - r[t].leading_zeros() as usize / 8,
554 None => 1,
555 };
556 let nonzero = |v: [u64; 4]| v.iter().any(|&l| l != 0);
557 Wide {
558 limbs: r,
559 len,
560 negative: a.negative != b.negative && nonzero(x) && nonzero(y),
561 }
562}
563
564/// Writes the product `w` over the first `w.len` bytes at `dst`, in the layout of `order`: up to
565/// 32 bytes as the short path writes them; from 33, the 32 least significant bytes and the 32 most
566/// significant ones, which overlap and agree where they do.
567///
568/// # Safety
569///
570/// `dst` must be valid for writing `w.len` bytes.
571#[inline(always)]
572pub(super) unsafe fn write_wide(dst: *mut u8, order: ByteOrder, w: &Wide) {
573 let (l, len) = (w.limbs, w.len);
574 // SAFETY: every write lies inside the `len` bytes at `dst`.
575 unsafe {
576 if len <= 2 * SMALL {
577 let p = Small {
578 limbs: [l[0], l[1], l[2], l[3]],
579 len,
580 negative: w.negative,
581 };
582 write_small_product(dst, order, p);
583 return;
584 }
585 // Positions `len - 32..len`: the limbs shifted down by `8 (len - 32)` bits, 8 to 256.
586 let k = 8 * (len - 32);
587 let (q, b) = (k / 64, (k % 64) as u32);
588 let limb = |i: usize| if i < 8 { l[i] } else { 0 };
589 let mut t = [0u64; 4];
590 for (i, v) in t.iter_mut().enumerate() {
591 *v = if b == 0 {
592 limb(i + q)
593 } else {
594 (limb(i + q) >> b) | (limb(i + q + 1) << (64 - b))
595 };
596 }
597 let u = |a: u64, b: u64| u128::from(a) | (u128::from(b) << 64);
598 let (l01, l23, t01, t23) = (u(l[0], l[1]), u(l[2], l[3]), u(t[0], t[1]), u(t[2], t[3]));
599 if order == ByteOrder::BigEndian {
600 write(dst.add(len - 16), l01.to_be_bytes());
601 write(dst.add(len - 32), l23.to_be_bytes());
602 write(dst.add(16), t01.to_be_bytes());
603 write(dst, t23.to_be_bytes());
604 } else {
605 write(dst, l01.to_le_bytes());
606 write(dst.add(16), l23.to_le_bytes());
607 write(dst.add(len - 32), t01.to_le_bytes());
608 write(dst.add(len - 16), t23.to_le_bytes());
609 }
610 }
611}
612
613/// Writes the product `w` as the whole value of `v`, in its vector when it has room, in a new one
614/// otherwise.
615#[inline(always)]
616pub(super) fn put_wide(v: &mut Vec<u8>, order: ByteOrder, w: &Wide) {
617 if v.capacity() < w.len {
618 *v = Vec::with_capacity(w.len);
619 }
620 // SAFETY: the vector has room for the `w.len` bytes, all of which are written before the
621 // length is set.
622 unsafe {
623 write_wide(v.as_mut_ptr(), order, w);
624 v.set_len(w.len);
625 }
626}
627
628// Operands of up to 48 bytes
629
630/// The longest vector, in bytes, of the operands that the general path multiplies in registers as
631/// six limbs each, past [`WIDE`]. Measured on an Apple M4 Max, `a *= &b` of 33 to 48 bytes took
632/// 53 to 60 cycles so, against 70 to 91 through limbs in memory. Eight limbs each, up to 64 bytes,
633/// did not pay: the compiler's code for the 64 limb products took about twice the instructions.
634const WIDEST: usize = 48;
635
636/// Whether `a * b` is computed in registers as six limbs each: both vectors hold at most
637/// [`WIDEST`] bytes.
638#[inline(always)]
639fn widest(a: &Number, b: &Number) -> bool {
640 a.value.len() <= WIDEST && b.value.len() <= WIDEST
641}
642
643/// The product of two operands of up to 48 bytes, computed in registers.
644#[derive(Clone, Copy)]
645struct Widest {
646 /// The twelve limbs of the magnitude, the least significant first.
647 limbs: [u64; 12],
648 /// The length of the product in the shortest form, from 1 to 96: one byte for zero.
649 len: usize,
650 /// Whether the product is negative; never for zero.
651 negative: bool,
652}
653
654/// The magnitude in the at most 48 bytes of `v`, in the layout of `order`, as six limbs: up to 32
655/// bytes as [`read_wide`] reads them; from 33, the 32 least significant bytes and the 16 most
656/// significant ones, which overlap and agree where they do.
657///
658/// # Safety
659///
660/// `v` must hold at most [`WIDEST`] bytes.
661#[inline(always)]
662unsafe fn read_widest(v: &[u8], order: ByteOrder) -> [u64; 6] {
663 let (p, n) = (v.as_ptr(), v.len());
664 // SAFETY: every read lies inside the `n` bytes of `v`: `[0, 32)` and `[n - 16, n)` with
665 // `n > 32`, or what `read_wide` reads.
666 unsafe {
667 if n <= WIDE {
668 let [l0, l1, l2, l3] = read_wide(v, order);
669 return [l0, l1, l2, l3, 0, 0];
670 }
671 let (low, middle, top) = if order == ByteOrder::BigEndian {
672 (
673 u128::from_be_bytes(read(p.add(n - 16))),
674 u128::from_be_bytes(read(p.add(n - 32))),
675 u128::from_be_bytes(read(p)),
676 )
677 } else {
678 (
679 u128::from_le_bytes(read(p)),
680 u128::from_le_bytes(read(p.add(16))),
681 u128::from_le_bytes(read(p.add(n - 16))),
682 )
683 };
684 // `top` holds positions `n - 16..n`; those from 32 up are the high part, a shift by
685 // `8 (48 - n)` down, below 128.
686 let high = top >> (8 * (WIDEST - n));
687 [
688 low as u64,
689 (low >> 64) as u64,
690 middle as u64,
691 (middle >> 64) as u64,
692 high as u64,
693 (high >> 64) as u64,
694 ]
695 }
696}
697
698/// The product of the operands `a` and `b` of up to 48 bytes: the schoolbook method on six limbs
699/// each, which the compiler unrolls and keeps in registers.
700///
701/// # Safety
702///
703/// Both vectors must hold at most [`WIDEST`] bytes.
704#[inline(always)]
705unsafe fn widest_product(a: &Number, b: &Number) -> Widest {
706 // SAFETY: as the caller guarantees.
707 let (x, y) = unsafe {
708 (
709 read_widest(&a.value, a.byte_order),
710 read_widest(&b.value, b.byte_order),
711 )
712 };
713 let mut r = [0u64; 12];
714 for i in 0..6 {
715 let mut carry = 0u64;
716 for j in 0..6 {
717 let t = u128::from(x[i]) * u128::from(y[j]) + u128::from(r[i + j]) + u128::from(carry);
718 r[i + j] = t as u64;
719 carry = (t >> 64) as u64;
720 }
721 r[i + 6] = carry;
722 }
723 let top = r.iter().rposition(|&l| l != 0);
724 let len = match top {
725 Some(t) => 8 * t + 8 - r[t].leading_zeros() as usize / 8,
726 None => 1,
727 };
728 let nonzero = |v: [u64; 6]| v.iter().any(|&l| l != 0);
729 Widest {
730 limbs: r,
731 len,
732 negative: a.negative != b.negative && nonzero(x) && nonzero(y),
733 }
734}
735
736/// Writes the product `w` over the first `w.len` bytes at `dst`, in the layout of `order`: up to
737/// 64 bytes as [`write_wide`] writes them; from 65, the 64 least significant bytes and the 32 most
738/// significant ones, which overlap and agree where they do.
739///
740/// # Safety
741///
742/// `dst` must be valid for writing `w.len` bytes.
743#[inline(always)]
744unsafe fn write_widest(dst: *mut u8, order: ByteOrder, w: &Widest) {
745 let (l, len) = (w.limbs, w.len);
746 // SAFETY: every write lies inside the `len` bytes at `dst`.
747 unsafe {
748 if len <= 4 * SMALL {
749 let mut limbs = [0; 8];
750 limbs.copy_from_slice(&l[..8]);
751 let wide = Wide {
752 limbs,
753 len,
754 negative: w.negative,
755 };
756 write_wide(dst, order, &wide);
757 return;
758 }
759 // Positions `len - 32..len`: the limbs shifted down by `8 (len - 32)` bits, 264 to 512.
760 let k = 8 * (len - 32);
761 let (q, b) = (k / 64, (k % 64) as u32);
762 let limb = |i: usize| if i < 12 { l[i] } else { 0 };
763 let mut t = [0u64; 4];
764 for (i, v) in t.iter_mut().enumerate() {
765 *v = if b == 0 {
766 limb(i + q)
767 } else {
768 (limb(i + q) >> b) | (limb(i + q + 1) << (64 - b))
769 };
770 }
771 let u = |a: u64, b: u64| u128::from(a) | (u128::from(b) << 64);
772 let low = [u(l[0], l[1]), u(l[2], l[3]), u(l[4], l[5]), u(l[6], l[7])];
773 let (t01, t23) = (u(t[0], t[1]), u(t[2], t[3]));
774 if order == ByteOrder::BigEndian {
775 for (i, v) in low.iter().enumerate() {
776 write(dst.add(len - 16 * i - 16), v.to_be_bytes());
777 }
778 write(dst.add(16), t01.to_be_bytes());
779 write(dst, t23.to_be_bytes());
780 } else {
781 for (i, v) in low.iter().enumerate() {
782 write(dst.add(16 * i), v.to_le_bytes());
783 }
784 write(dst.add(len - 32), t01.to_le_bytes());
785 write(dst.add(len - 16), t23.to_le_bytes());
786 }
787 }
788}
789
790/// Writes the product `w` as the whole value of `v`, in its vector when it has room, in a new one
791/// otherwise.
792#[inline(always)]
793fn put_widest(v: &mut Vec<u8>, order: ByteOrder, w: &Widest) {
794 if v.capacity() < w.len {
795 *v = Vec::with_capacity(w.len);
796 }
797 // SAFETY: the vector has room for the `w.len` bytes, all of which are written before the
798 // length is set.
799 unsafe {
800 write_widest(v.as_mut_ptr(), order, w);
801 v.set_len(w.len);
802 }
803}
804
805// The general path: operands of more than 16 bytes, out of line, so that the small path saves no
806// registers for it.
807
808/// An operand of the general path: its vector, its byte order and its significant length.
809#[derive(Clone, Copy)]
810pub(super) struct Operand {
811 pub(super) ptr: *const u8,
812 pub(super) len: usize,
813 pub(super) big_endian: bool,
814 pub(super) significant: usize,
815}
816
817impl Operand {
818 pub(super) fn of(n: &Number) -> Self {
819 let big_endian = n.byte_order == ByteOrder::BigEndian;
820 let significant = if big_endian {
821 significant_len::<true>(&n.value)
822 } else {
823 significant_len::<false>(&n.value)
824 };
825 Operand {
826 ptr: n.value.as_ptr(),
827 len: n.value.len(),
828 big_endian,
829 significant,
830 }
831 }
832}
833
834/// `a * b` for borrowed operands, of which at least one has more than 16 bytes.
835#[inline(never)]
836fn ref_times_ref(a: &Number, b: &Number) -> Number {
837 let order = a.byte_order;
838 if wide(a, b) {
839 // SAFETY: both vectors hold at most 32 bytes.
840 let w = unsafe { wide_product(a, b) };
841 let mut value = Vec::new();
842 put_wide(&mut value, order, &w);
843 return Number {
844 value,
845 negative: w.negative,
846 byte_order: order,
847 };
848 }
849 if widest(a, b) {
850 // SAFETY: both vectors hold at most 48 bytes.
851 let w = unsafe { widest_product(a, b) };
852 let mut value = Vec::new();
853 put_widest(&mut value, order, &w);
854 return Number {
855 value,
856 negative: w.negative,
857 byte_order: order,
858 };
859 }
860 let (x, y) = (Operand::of(a), Operand::of(b));
861 // SAFETY: the operand read has at most eight significant bytes.
862 unsafe {
863 if y.significant <= 8
864 && a.byte_order == ByteOrder::LittleEndian
865 && let Some(p) = copy_times_limb(a, b.negative, limb_of(y), None)
866 {
867 return p;
868 }
869 if x.significant <= 8
870 && a.byte_order == ByteOrder::LittleEndian
871 && b.byte_order == ByteOrder::LittleEndian
872 && let Some(p) = copy_times_limb(b, a.negative, limb_of(x), None)
873 {
874 return p;
875 }
876 }
877 let negative = a.negative != b.negative;
878 let square = ptr::eq(a, b);
879 // SAFETY: the operands are those of `a` and `b`, which are borrowed until the product is
880 // computed.
881 unsafe {
882 product(x, y, square, negative, |p| {
883 let mut v = Vec::with_capacity(p.len);
884 p.write(v.as_mut_ptr(), order);
885 v.set_len(p.len);
886 Number {
887 value: v,
888 negative: p.negative,
889 byte_order: order,
890 }
891 })
892 }
893}
894
895/// `a * b` for a borrowed `a` and an owned `b`, of which at least one has more than 16 bytes.
896/// The product takes the vector of `b` when it is built there, leaving `b` an empty one; the
897/// caller drops `b` either way.
898#[inline(never)]
899fn ref_times_value(a: &Number, b: &mut Number) -> Number {
900 let order = a.byte_order;
901 if wide(a, b) {
902 // SAFETY: both vectors hold at most 32 bytes.
903 let w = unsafe { wide_product(a, b) };
904 put_wide(&mut b.value, order, &w);
905 return Number {
906 value: mem::take(&mut b.value),
907 negative: w.negative,
908 byte_order: order,
909 };
910 }
911 if widest(a, b) {
912 // SAFETY: both vectors hold at most 48 bytes.
913 let w = unsafe { widest_product(a, b) };
914 put_widest(&mut b.value, order, &w);
915 return Number {
916 value: mem::take(&mut b.value),
917 negative: w.negative,
918 byte_order: order,
919 };
920 }
921 let (x, y) = (Operand::of(a), Operand::of(b));
922 // SAFETY: the operand read has at most eight significant bytes.
923 unsafe {
924 if x.significant <= 8
925 && order == ByteOrder::LittleEndian
926 && b.byte_order == order
927 && times_limb(b, a.negative, limb_of(x), None)
928 {
929 return mem::take(b);
930 }
931 if y.significant <= 8 && order == ByteOrder::LittleEndian {
932 let negative = b.negative;
933 if let Some(p) = copy_times_limb(a, negative, limb_of(y), Some(&mut b.value)) {
934 return p;
935 }
936 }
937 }
938 let negative = a.negative != b.negative;
939 // SAFETY: the operands are those of `a` and `b`; `b`'s vector is written only once both are
940 // read into limbs.
941 unsafe {
942 product(x, y, false, negative, |p| {
943 p.put_in(&mut b.value, order);
944 Number {
945 value: mem::take(&mut b.value),
946 negative: p.negative,
947 byte_order: order,
948 }
949 })
950 }
951}
952
953/// `a *= b` for a borrowed `b`, where at least one of them has more than 16 bytes.
954#[inline(never)]
955fn mul_assign_ref(a: &mut Number, b: &Number) {
956 let order = a.byte_order;
957 if wide(a, b) {
958 // SAFETY: both vectors hold at most 32 bytes.
959 let w = unsafe { wide_product(a, b) };
960 put_wide(&mut a.value, order, &w);
961 a.negative = w.negative;
962 return;
963 }
964 if widest(a, b) {
965 // SAFETY: both vectors hold at most 48 bytes.
966 let w = unsafe { widest_product(a, b) };
967 put_widest(&mut a.value, order, &w);
968 a.negative = w.negative;
969 return;
970 }
971 let (x, y) = (Operand::of(a), Operand::of(b));
972 // SAFETY: the operand has at most eight significant bytes.
973 if y.significant <= 8
974 && order == ByteOrder::LittleEndian
975 && times_limb(a, b.negative, unsafe { limb_of(y) }, None)
976 {
977 return;
978 }
979 let negative = a.negative != b.negative;
980 // SAFETY: the operands are those of `a` and `b`; `a`'s vector is written only once both are
981 // read into limbs.
982 unsafe {
983 product(x, y, false, negative, |p| {
984 p.put_in(&mut a.value, order);
985 a.negative = p.negative;
986 });
987 }
988}
989
990/// `a *= b` for an owned `b`, where at least one of them has more than 16 bytes. When the
991/// product is built in the vector of `b`, the vector of `a` goes to `b`; the caller drops `b`
992/// either way.
993#[inline(never)]
994fn mul_assign_value(a: &mut Number, b: &mut Number) {
995 let order = a.byte_order;
996 if wide(a, b) {
997 // SAFETY: both vectors hold at most 32 bytes.
998 let w = unsafe { wide_product(a, b) };
999 if a.value.capacity() < w.len && b.value.capacity() >= w.len {
1000 mem::swap(&mut a.value, &mut b.value);
1001 }
1002 put_wide(&mut a.value, order, &w);
1003 a.negative = w.negative;
1004 return;
1005 }
1006 if widest(a, b) {
1007 // SAFETY: both vectors hold at most 48 bytes.
1008 let w = unsafe { widest_product(a, b) };
1009 if a.value.capacity() < w.len && b.value.capacity() >= w.len {
1010 mem::swap(&mut a.value, &mut b.value);
1011 }
1012 put_widest(&mut a.value, order, &w);
1013 a.negative = w.negative;
1014 return;
1015 }
1016 let (x, y) = (Operand::of(a), Operand::of(b));
1017 if y.significant <= 8 && order == ByteOrder::LittleEndian {
1018 let negative = b.negative;
1019 // SAFETY: the operand has at most eight significant bytes.
1020 if times_limb(a, negative, unsafe { limb_of(y) }, Some(&mut b.value)) {
1021 return;
1022 }
1023 }
1024 let negative = a.negative != b.negative;
1025 // SAFETY: as in `mul_assign_ref`.
1026 unsafe {
1027 product(x, y, false, negative, |p| {
1028 if a.value.capacity() < p.len && b.value.capacity() >= p.len {
1029 mem::swap(&mut a.value, &mut b.value);
1030 }
1031 p.put_in(&mut a.value, order);
1032 a.negative = p.negative;
1033 });
1034 }
1035}
1036
1037/// A product computed in limbs, before it is written out.
1038pub(super) struct Product {
1039 /// The limbs of the magnitude, the least significant first: `limbs` of them, or none for
1040 /// zero.
1041 pub(super) r: *const u64,
1042 /// The length of the product in the shortest form: one byte for zero.
1043 pub(super) len: usize,
1044 /// Whether the product is negative; never for zero.
1045 pub(super) negative: bool,
1046}
1047
1048impl Product {
1049 /// Writes the `len` bytes of the product at `dst`, in the layout of `order`.
1050 ///
1051 /// # Safety
1052 ///
1053 /// `dst` must be valid for writing `self.len` bytes, apart from the limbs.
1054 pub(super) unsafe fn write(&self, dst: *mut u8, order: ByteOrder) {
1055 // SAFETY: as the caller guarantees; the limbs hold at least `len` bytes.
1056 unsafe {
1057 if self.r.is_null() {
1058 *dst = 0;
1059 } else if self.len < 8 {
1060 // Operands with zero bytes above their most significant ones can have a short
1061 // product.
1062 write_small(dst, order, *self.r, 0, self.len);
1063 } else if order == ByteOrder::BigEndian {
1064 write_limbs::<true>(dst, self.r, self.len);
1065 } else {
1066 write_limbs::<false>(dst, self.r, self.len);
1067 }
1068 }
1069 }
1070
1071 /// Writes the product as the whole value of `v`, in its vector when it has room, and
1072 /// otherwise in one new vector of exactly its length, which replaces it.
1073 ///
1074 /// # Safety
1075 ///
1076 /// As for [`Product::write`]; the bytes of `v` are not needed any more.
1077 #[inline(always)]
1078 pub(super) unsafe fn put_in(&self, v: &mut Vec<u8>, order: ByteOrder) {
1079 if v.capacity() < self.len {
1080 *v = Vec::with_capacity(self.len);
1081 }
1082 // SAFETY: the vector has room for the `len` bytes, all of which are written before the
1083 // length is set.
1084 unsafe {
1085 self.write(v.as_mut_ptr(), order);
1086 v.set_len(self.len);
1087 }
1088 }
1089}
1090
1091/// The limbs of both operands together up to which a product is computed in a buffer on the stack
1092/// of the operator's own function, inline: below Karatsuba's method, so that it needs no scratch.
1093const MEDIUM_LIMBS: usize = 16;
1094
1095/// The limbs of working memory on the stack for longer products: those whose operands and scratch
1096/// fit take no allocation besides the vector of the result.
1097pub(super) const STACK_LIMBS: usize = 1024;
1098
1099/// Two limbs of working memory, aligned to 16 bytes: the kernels load and store limbs in pairs,
1100/// and pairs that straddle 16 bytes cost them up to a few percent (see [`compute`]).
1101#[derive(Clone, Copy)]
1102#[repr(C, align(16))]
1103struct Pair([MaybeUninit<u64>; 2]);
1104
1105/// An uninitialised pair of limbs.
1106const PAIR: Pair = Pair([MaybeUninit::uninit(); 2]);
1107
1108/// `n` rounded up to an even number of limbs, so that a buffer after `n` limbs of an aligned one
1109/// is aligned too.
1110#[inline(always)]
1111const fn even(n: usize) -> usize {
1112 (n + 1) & !1
1113}
1114
1115/// Computes the product of the operands `x` and `y` (with `square`, of `x` by itself) and hands
1116/// it to `place`, which writes it where it belongs; returns what `place` returns.
1117///
1118/// # Safety
1119///
1120/// The operands must describe vectors that stay valid for reads until `place` is called, and
1121/// `y` must be `x` when `square` is set.
1122#[inline(always)]
1123unsafe fn product<R>(
1124 x: Operand,
1125 y: Operand,
1126 square: bool,
1127 negative: bool,
1128 place: impl FnOnce(&Product) -> R,
1129) -> R {
1130 let (la, lb) = (x.significant, y.significant);
1131 if la == 0 || lb == 0 {
1132 return place(&Product {
1133 r: ptr::null(),
1134 len: 1,
1135 negative: false,
1136 });
1137 }
1138 let (nx, ny) = (la.div_ceil(8), lb.div_ceil(8));
1139 // SAFETY: each buffer holds the limbs that `compute` uses: the operands, the product and, for
1140 // the longer products, the scratch.
1141 unsafe {
1142 if nx + ny <= MEDIUM_LIMBS {
1143 // The operands, each in an even number of limbs, and the product.
1144 let mut s = [PAIR; MEDIUM_LIMBS + 1];
1145 return compute(s.as_mut_ptr().cast(), x, y, square, negative, place);
1146 }
1147 let (long, short) = if nx >= ny { (nx, ny) } else { (ny, nx) };
1148 let itch = if square {
1149 limbs::sqr_itch(nx)
1150 } else {
1151 limbs::mul_itch(long, short)
1152 };
1153 let operands = if square {
1154 even(nx)
1155 } else {
1156 even(nx) + even(ny)
1157 };
1158 let need = operands + even(nx + ny) + itch;
1159 let compute = |s: *mut MaybeUninit<u64>| compute(s.cast(), x, y, square, negative, place);
1160 if need <= STACK_LIMBS {
1161 on_stack(compute)
1162 } else {
1163 on_heap(need, compute)
1164 }
1165 }
1166}
1167
1168/// [`product`] in the working memory at `s`: the operands read into limbs, then the product of
1169/// the limbs after them, its length in the shortest form, and `place`.
1170///
1171/// The limbs are placed for the kernels, which load and store them in pairs, and `s` is aligned
1172/// to 16 bytes as the buffers of [`product`] are. The kernels take an odd limb first and the pairs
1173/// after it, so an operand of an odd number of limbs starts 8 bytes past 16, and its pairs on 16;
1174/// the product and the scratch start on 16 bytes. Pairs that straddle 16 bytes, just after
1175/// `to_limbs` has stored the operand a limb at a time, measured on an Apple M4 Max: 913 cycles
1176/// for `a *= &b` of 17 limbs each against 389, as the loads waited for the stores; with the
1177/// scratch 8 bytes off, Toom-Cook products of 1024 and 2048 limbs took 0.6 and 1.3 % longer.
1178///
1179/// # Safety
1180///
1181/// As for [`product`], with the operands not zero, and `s` valid for the operands' limbs, the
1182/// product's and the scratch the product needs, each rounded up to an even number.
1183#[inline(always)]
1184unsafe fn compute<R>(
1185 s: *mut u64,
1186 x: Operand,
1187 y: Operand,
1188 square: bool,
1189 negative: bool,
1190 place: impl FnOnce(&Product) -> R,
1191) -> R {
1192 let (la, lb) = (x.significant, y.significant);
1193 let (nx, ny) = (la.div_ceil(8), lb.div_ceil(8));
1194 // SAFETY: as the caller guarantees.
1195 unsafe {
1196 let (a, b) = (s.add(nx & 1), s.add(even(nx) + (ny & 1)));
1197 to_limbs(x, a);
1198 let operands = if square {
1199 even(nx)
1200 } else {
1201 even(nx) + even(ny)
1202 };
1203 let r = s.add(operands);
1204 let t = r.add(even(nx + ny));
1205 // Short operands go straight to the kernels, without the dispatch of the longer ones.
1206 let medium = nx + ny <= MEDIUM_LIMBS;
1207 if square {
1208 if medium {
1209 kernels::sqr_basecase(r, a, nx);
1210 } else {
1211 limbs::sqr(r, a, nx, t);
1212 }
1213 } else {
1214 to_limbs(y, b);
1215 let ((p, np), (q, nq)) = if nx >= ny {
1216 ((a, nx), (b, ny))
1217 } else {
1218 ((b, ny), (a, nx))
1219 };
1220 if medium {
1221 kernels::mul_basecase(r, p, np, q, nq);
1222 } else {
1223 limbs::mul(r, p, np, q, nq, t);
1224 }
1225 }
1226 // The product of magnitudes of `la` and `lb` significant bytes has `la + lb` of them, or
1227 // one fewer.
1228 let top = la + lb - 1;
1229 let byte = (*r.add(top / 8) >> (8 * (top % 8))) as u8;
1230 let len = if byte == 0 { top } else { top + 1 };
1231 place(&Product { r, len, negative })
1232 }
1233}
1234
1235/// Limbs of the buffer of [`on_stack`] past the [`STACK_LIMBS`] it hands out, 2 KiB.
1236const STACK_PAD: usize = 256;
1237
1238/// Runs `f` on [`STACK_LIMBS`] limbs of working memory on the stack, aligned to 16 bytes. Out of
1239/// line, so that the small path's frame does not hold them.
1240///
1241/// The buffer is the bottom of the frame, and the working memory its first limbs. A buffer of
1242/// exactly 8 KiB put them a multiple of 4 KiB below the caller's frame, and loads whose addresses
1243/// agree with those of earlier stores in their low 12 bits wait for those stores: measured on an
1244/// Apple M4 Max, `a *= &b` of 129 to 136 bytes took up to 2.5 times as long, some sizes from 65
1245/// bytes on 1.3 to 2.3 times, depending on the build. 2 KiB more of buffer keep them apart.
1246#[inline(never)]
1247pub(super) fn on_stack<R>(f: impl FnOnce(*mut MaybeUninit<u64>) -> R) -> R {
1248 let mut s = [PAIR; (STACK_LIMBS + STACK_PAD) / 2];
1249 f(s.as_mut_ptr().cast())
1250}
1251
1252/// Runs `f` on `need` limbs of working memory on the heap, aligned to 16 bytes, freed on return.
1253#[cold]
1254#[inline(never)]
1255pub(super) fn on_heap<R>(need: usize, f: impl FnOnce(*mut MaybeUninit<u64>) -> R) -> R {
1256 let mut s = Vec::<Pair>::with_capacity(need.div_ceil(2));
1257 f(s.as_mut_ptr().cast())
1258}
1259
1260/// Reads the significant bytes of the operand `x` into `x.significant.div_ceil(8)` limbs at
1261/// `out`, the least significant first.
1262///
1263/// # Safety
1264///
1265/// The operand's vector must be valid for reads, and `out` for writing its limbs.
1266#[inline(always)]
1267pub(super) unsafe fn to_limbs(x: Operand, out: *mut u64) {
1268 let l = x.significant;
1269 let (full, rem) = (l / 8, l % 8);
1270 // SAFETY: every read lies inside the significant bytes: big-endian the last `l` of the
1271 // vector, little-endian the first `l`.
1272 unsafe {
1273 // The `rem` bytes above the whole limbs: from 8 bytes up, the eight that end at the top,
1274 // shifted down to them; below, as the addition reads a short magnitude.
1275 let shift = 8 * (8 - rem) as u32;
1276 if x.big_endian {
1277 let end = x.ptr.add(x.len);
1278 for i in 0..full {
1279 *out.add(i) = u64::from_be_bytes(read(end.sub(8 * i + 8)));
1280 }
1281 if rem > 0 {
1282 *out.add(full) = if l >= 8 {
1283 u64::from_be_bytes(read(end.sub(l))) >> shift
1284 } else {
1285 read_small(
1286 core::slice::from_raw_parts(end.sub(l), rem),
1287 ByteOrder::BigEndian,
1288 ) as u64
1289 };
1290 }
1291 } else {
1292 for i in 0..full {
1293 *out.add(i) = u64::from_le_bytes(read(x.ptr.add(8 * i)));
1294 }
1295 if rem > 0 {
1296 *out.add(full) = if l >= 8 {
1297 u64::from_le_bytes(read(x.ptr.add(l - 8))) >> shift
1298 } else {
1299 read_small(
1300 core::slice::from_raw_parts(x.ptr, rem),
1301 ByteOrder::LittleEndian,
1302 ) as u64
1303 };
1304 }
1305 }
1306 }
1307}
1308
1309/// Writes the `len` least significant bytes of the limbs at `r` to the `len` bytes at `dst`, in
1310/// the layout `BE`: the whole limbs, then the bytes above them as the eight bytes that end at the
1311/// top, which agree with the limb below where they overlap it.
1312///
1313/// # Safety
1314///
1315/// `len >= 8`; the limbs must hold `len` bytes, and `dst` be valid for writing them.
1316#[inline(always)]
1317unsafe fn write_limbs<const BE: bool>(dst: *mut u8, r: *const u64, len: usize) {
1318 let (full, rem) = (len / 8, len % 8);
1319 // SAFETY: as the caller guarantees: limb `i` goes to `[8i, 8i + 8)`, big-endian to the eight
1320 // bytes that end at `len - 8i`, and the `rem` bytes of the top limb below the rest.
1321 unsafe {
1322 for i in 0..full {
1323 let v = *r.add(i);
1324 if BE {
1325 write(dst.add(len - 8 * i - 8), v.to_be_bytes());
1326 } else {
1327 write(dst.add(8 * i), v.to_le_bytes());
1328 }
1329 }
1330 if rem > 0 {
1331 // Positions `len - 8..len`: the top limb's `rem` bytes over the limb below.
1332 let k = 8 * rem as u32;
1333 let v = (*r.add(full - 1) >> k) | (*r.add(full) << (64 - k));
1334 if BE {
1335 write(dst, v.to_be_bytes());
1336 } else {
1337 write(dst.add(len - 8), v.to_le_bytes());
1338 }
1339 }
1340 }
1341}