Skip to main content

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}