Skip to content

Commit

Permalink
[WIP] Leverage const_mut_refs
Browse files Browse the repository at this point in the history
Replaces macro-based code sharing between the stack-allocated and
heap-allocated `*Uint` types with `const fn` using `const_mut_refs`.
  • Loading branch information
tarcieri committed Sep 6, 2024
1 parent 3e15431 commit d72b59a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 115 deletions.
32 changes: 16 additions & 16 deletions .github/workflows/crypto-bigint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ jobs:
strategy:
matrix:
rust:
- 1.73.0 # MSRV
- stable
#- 1.73.0 # MSRV
- nightly
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
Expand All @@ -47,18 +47,18 @@ jobs:
matrix:
include:
# 32-bit Linux
#- target: i686-unknown-linux-gnu
# rust: 1.73.0 # MSRV
# deps: sudo apt update && sudo apt install gcc-multilib
- target: i686-unknown-linux-gnu
rust: 1.73.0 # MSRV
deps: sudo apt update && sudo apt install gcc-multilib
- target: i686-unknown-linux-gnu
rust: stable
rust: nightly
deps: sudo apt update && sudo apt install gcc-multilib

# 64-bit Linux
#- target: x86_64-unknown-linux-gnu
# rust: 1.73.0 # MSRV
- target: x86_64-unknown-linux-gnu
rust: 1.73.0 # MSRV
- target: x86_64-unknown-linux-gnu
rust: stable
rust: nightly
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
Expand All @@ -77,11 +77,11 @@ jobs:
include:
# ARM64
- target: aarch64-unknown-linux-gnu
rust: stable
rust: nightly

# PPC32 (big endian)
- target: powerpc-unknown-linux-gnu
rust: stable
rust: nightly

runs-on: ubuntu-latest
steps:
Expand All @@ -105,7 +105,7 @@ jobs:
with:
toolchain: nightly
- run: cargo update -Z minimal-versions
- run: cargo +stable build --release --all-features
- run: cargo +nightly build --release --all-features

miri:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -136,7 +136,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.80.0
toolchain: nightly
components: clippy
- run: cargo clippy --all --all-features -- -D warnings

Expand All @@ -146,7 +146,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly
components: rustfmt
- run: cargo fmt --all -- --check

Expand All @@ -156,7 +156,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.73.0
toolchain: nightly
- run: cargo build --benches
- run: cargo build --all-features --benches

Expand All @@ -166,5 +166,5 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: nightly
- run: cargo doc --all-features
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
trivial_numeric_casts,
unused_qualifications
)]
// TODO: remove when stable
#![feature(const_mut_refs)]

//! ## Usage
//!
Expand Down
191 changes: 92 additions & 99 deletions src/uint/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,125 +9,118 @@ use subtle::CtOption;

pub(crate) mod karatsuba;

/// Implement the core schoolbook multiplication algorithm.
/// Schoolbook multiplication a.k.a. long multiplication, i.e. the traditional method taught in
/// schools.
///
/// This is implemented as a macro to abstract over `const fn` and boxed use cases, since the latter
/// needs mutable references and thus the unstable `const_mut_refs` feature (rust-lang/rust#57349).
///
/// It allows us to have a single place (this module) to improve the multiplication implementation
/// which will also be reused for `BoxedUint`.
// TODO(tarcieri): change this into a `const fn` when `const_mut_refs` is stable
macro_rules! impl_schoolbook_multiplication {
($lhs:expr, $rhs:expr, $lo:expr, $hi:expr) => {{
if $lhs.len() != $lo.len() || $rhs.len() != $hi.len() {
panic!("schoolbook multiplication length mismatch");
}

let mut i = 0;
while i < $lhs.len() {
let mut j = 0;
let mut carry = Limb::ZERO;
let xi = $lhs[i];

while j < $rhs.len() {
let k = i + j;
/// The most efficient method for small numbers.
const fn schoolbook_multiplication(lhs: &[Limb], rhs: &[Limb], lo: &mut [Limb], hi: &mut [Limb]) {
if lhs.len() != lo.len() || rhs.len() != hi.len() {
panic!("schoolbook multiplication length mismatch");
}

if k >= $lhs.len() {
($hi[k - $lhs.len()], carry) = $hi[k - $lhs.len()].mac(xi, $rhs[j], carry);
} else {
($lo[k], carry) = $lo[k].mac(xi, $rhs[j], carry);
}
let mut i = 0;
while i < lhs.len() {
let mut j = 0;
let mut carry = Limb::ZERO;
let xi = lhs[i];

j += 1;
}
while j < rhs.len() {
let k = i + j;

if i + j >= $lhs.len() {
$hi[i + j - $lhs.len()] = carry;
if k >= lhs.len() {
(hi[k - lhs.len()], carry) = hi[k - lhs.len()].mac(xi, rhs[j], carry);
} else {
$lo[i + j] = carry;
(lo[k], carry) = lo[k].mac(xi, rhs[j], carry);
}
i += 1;

j += 1;
}
}};

if i + j >= lhs.len() {
hi[i + j - lhs.len()] = carry;
} else {
lo[i + j] = carry;
}
i += 1;
}
}

/// Implement the schoolbook method for squaring.
/// Schoolbook method of squaring.
///
/// Like schoolbook multiplication, but only considering half of the multiplication grid.
// TODO: change this into a `const fn` when `const_mut_refs` is stable.
macro_rules! impl_schoolbook_squaring {
($limbs:expr, $lo:expr, $hi:expr) => {{
// Translated from https://github.com/ucbrise/jedi-pairing/blob/c4bf151/include/core/bigint.hpp#L410
//
// Permission to relicense the resulting translation as Apache 2.0 + MIT was given
// by the original author Sam Kumar: https://github.com/RustCrypto/crypto-bigint/pull/133#discussion_r1056870411

if $limbs.len() != $lo.len() || $lo.len() != $hi.len() {
panic!("schoolbook squaring length mismatch");
}

let mut i = 1;
while i < $limbs.len() {
let mut j = 0;
let mut carry = Limb::ZERO;
let xi = $limbs[i];
pub(crate) const fn schoolbook_squaring(limbs: &[Limb], lo: &mut [Limb], hi: &mut [Limb]) {
// Translated from https://github.com/ucbrise/jedi-pairing/blob/c4bf151/include/core/bigint.hpp#L410
//
// Permission to relicense the resulting translation as Apache 2.0 + MIT was given
// by the original author Sam Kumar: https://github.com/RustCrypto/crypto-bigint/pull/133#discussion_r1056870411

if limbs.len() != lo.len() || lo.len() != hi.len() {
panic!("schoolbook squaring length mismatch");
}

while j < i {
let k = i + j;
let mut i = 1;
while i < limbs.len() {
let mut j = 0;
let mut carry = Limb::ZERO;
let xi = limbs[i];

if k >= $limbs.len() {
($hi[k - $limbs.len()], carry) = $hi[k - $limbs.len()].mac(xi, $limbs[j], carry);
} else {
($lo[k], carry) = $lo[k].mac(xi, $limbs[j], carry);
}
while j < i {
let k = i + j;

j += 1;
}

if (2 * i) < $limbs.len() {
$lo[2 * i] = carry;
if k >= limbs.len() {
(hi[k - limbs.len()], carry) = hi[k - limbs.len()].mac(xi, limbs[j], carry);
} else {
$hi[2 * i - $limbs.len()] = carry;
(lo[k], carry) = lo[k].mac(xi, limbs[j], carry);
}

i += 1;
j += 1;
}

// Double the current result, this accounts for the other half of the multiplication grid.
// The top word is empty, so we use a special purpose shl.
let mut carry = Limb::ZERO;
let mut i = 0;
while i < $limbs.len() {
($lo[i].0, carry) = ($lo[i].0 << 1 | carry.0, $lo[i].shr(Limb::BITS - 1));
i += 1;
if (2 * i) < limbs.len() {
lo[2 * i] = carry;
} else {
hi[2 * i - limbs.len()] = carry;
}
i = 0;
while i < $limbs.len() - 1 {
($hi[i].0, carry) = ($hi[i].0 << 1 | carry.0, $hi[i].shr(Limb::BITS - 1));
i += 1;
}
$hi[$limbs.len() - 1] = carry;

// Handle the diagonal of the multiplication grid, which finishes the multiplication grid.
let mut carry = Limb::ZERO;
let mut i = 0;
while i < $limbs.len() {
let xi = $limbs[i];
if (i * 2) < $limbs.len() {
($lo[i * 2], carry) = $lo[i * 2].mac(xi, xi, carry);
} else {
($hi[i * 2 - $limbs.len()], carry) = $hi[i * 2 - $limbs.len()].mac(xi, xi, carry);
}
i += 1;
}

if (i * 2 + 1) < $limbs.len() {
($lo[i * 2 + 1], carry) = $lo[i * 2 + 1].overflowing_add(carry);
} else {
($hi[i * 2 + 1 - $limbs.len()], carry) = $hi[i * 2 + 1 - $limbs.len()].overflowing_add(carry);
}
// Double the current result, this accounts for the other half of the multiplication grid.
// The top word is empty, so we use a special purpose shl.
let mut carry = Limb::ZERO;
let mut i = 0;
while i < limbs.len() {
(lo[i].0, carry) = (lo[i].0 << 1 | carry.0, lo[i].shr(Limb::BITS - 1));
i += 1;
}

i += 1;
let mut i = 0;
while i < limbs.len() - 1 {
(hi[i].0, carry) = (hi[i].0 << 1 | carry.0, hi[i].shr(Limb::BITS - 1));
i += 1;
}
hi[limbs.len() - 1] = carry;

// Handle the diagonal of the multiplication grid, which finishes the multiplication grid.
let mut carry = Limb::ZERO;
let mut i = 0;
while i < limbs.len() {
let xi = limbs[i];
if (i * 2) < limbs.len() {
(lo[i * 2], carry) = lo[i * 2].mac(xi, xi, carry);
} else {
(hi[i * 2 - limbs.len()], carry) = hi[i * 2 - limbs.len()].mac(xi, xi, carry);
}
}};

if (i * 2 + 1) < limbs.len() {
(lo[i * 2 + 1], carry) = lo[i * 2 + 1].overflowing_add(carry);
} else {
(hi[i * 2 + 1 - limbs.len()], carry) =
hi[i * 2 + 1 - limbs.len()].overflowing_add(carry);
}

i += 1;
}
}

impl<const LIMBS: usize> Uint<LIMBS> {
Expand Down Expand Up @@ -316,7 +309,7 @@ pub(crate) const fn uint_mul_limbs<const LIMBS: usize, const RHS_LIMBS: usize>(
debug_assert!(lhs.len() == LIMBS && rhs.len() == RHS_LIMBS);
let mut lo: Uint<LIMBS> = Uint::<LIMBS>::ZERO;
let mut hi = Uint::<RHS_LIMBS>::ZERO;
impl_schoolbook_multiplication!(lhs, rhs, lo.limbs, hi.limbs);
schoolbook_multiplication(lhs, rhs, &mut lo.limbs, &mut hi.limbs);
(lo, hi)
}

Expand All @@ -327,7 +320,7 @@ pub(crate) const fn uint_square_limbs<const LIMBS: usize>(
) -> (Uint<LIMBS>, Uint<LIMBS>) {
let mut lo = Uint::<LIMBS>::ZERO;
let mut hi = Uint::<LIMBS>::ZERO;
impl_schoolbook_squaring!(limbs, lo.limbs, hi.limbs);
schoolbook_squaring(limbs, &mut lo.limbs, &mut hi.limbs);
(lo, hi)
}

Expand All @@ -336,15 +329,15 @@ pub(crate) const fn uint_square_limbs<const LIMBS: usize>(
pub(crate) fn mul_limbs(lhs: &[Limb], rhs: &[Limb], out: &mut [Limb]) {
debug_assert_eq!(lhs.len() + rhs.len(), out.len());
let (lo, hi) = out.split_at_mut(lhs.len());
impl_schoolbook_multiplication!(lhs, rhs, lo, hi);
schoolbook_multiplication(lhs, rhs, lo, hi);
}

/// Wrapper function used by `BoxedUint`
#[cfg(feature = "alloc")]
pub(crate) fn square_limbs(limbs: &[Limb], out: &mut [Limb]) {
debug_assert_eq!(limbs.len() * 2, out.len());
let (lo, hi) = out.split_at_mut(limbs.len());
impl_schoolbook_squaring!(limbs, lo, hi);
schoolbook_squaring(limbs, lo, hi);
}

#[cfg(test)]
Expand Down

0 comments on commit d72b59a

Please sign in to comment.