Skip to content

Commit 54006d7

Browse files
committed
Improve field macro
Compile time Montgomery multiplication!
1 parent faa2f88 commit 54006d7

File tree

18 files changed

+632
-330
lines changed

18 files changed

+632
-330
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"serialize",
55
"serialize-derive",
66

7+
"ff-macros",
78
"ff-asm",
89
"ff",
910

ff-macros/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "ark-ff-macros"
3+
version = "0.1.0"
4+
authors = [ "arkworks contributors" ]
5+
description = "A library for generating x86-64 assembly for finite field multiplication"
6+
homepage = "https://arworks.rs"
7+
repository = "https://github.com/arkworks/algebra"
8+
documentation = "https://docs.rs/ark-ff-asm/"
9+
keywords = ["cryptography", "finite fields", "assembly" ]
10+
categories = ["cryptography"]
11+
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
12+
license = "MIT/Apache-2.0"
13+
edition = "2018"
14+
15+
[dependencies]
16+
quote = "1.0.0"
17+
syn = { version = "1.0.0", features = ["full", "parsing", "extra-traits"]}
18+
num-bigint = { version = "0.3", default-features = false }
19+
num-traits = { version = "0.2", default-features = false }
20+
21+
[lib]
22+
proc-macro = true

ff-macros/LICENSE-APACHE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-APACHE

ff-macros/LICENSE-MIT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-MIT

ff-macros/src/lib.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#![deny(
2+
warnings,
3+
unused,
4+
future_incompatible,
5+
nonstandard_style,
6+
rust_2018_idioms
7+
)]
8+
#![forbid(unsafe_code)]
9+
#![recursion_limit = "128"]
10+
11+
use num_bigint::{BigInt, Sign};
12+
use proc_macro::TokenStream;
13+
use std::str::FromStr;
14+
use syn::{Expr, Lit};
15+
16+
fn parse_string(input: TokenStream) -> Option<String> {
17+
let input: Expr = syn::parse(input).unwrap();
18+
let input = if let Expr::Group(syn::ExprGroup { expr, .. }) = input {
19+
expr
20+
} else {
21+
panic!("could not parse");
22+
};
23+
match *input {
24+
Expr::Lit(expr_lit) => match expr_lit.lit {
25+
Lit::Str(s) => Some(s.value()),
26+
_ => None,
27+
},
28+
_ => None,
29+
}
30+
}
31+
32+
fn str_to_limbs(num: &str) -> (bool, Vec<String>) {
33+
let (sign, digits) = BigInt::from_str(num)
34+
.expect("could not parse to bigint")
35+
.to_radix_le(16);
36+
let limbs = digits
37+
.chunks(16)
38+
.map(|chunk| {
39+
let mut this = 0u64;
40+
for (i, hexit) in chunk.iter().enumerate() {
41+
this += (*hexit as u64) << (4 * i);
42+
}
43+
format!("{}u64", this)
44+
})
45+
.collect::<Vec<_>>();
46+
47+
let sign_is_positive = sign != Sign::Minus;
48+
(sign_is_positive, limbs)
49+
}
50+
51+
#[proc_macro]
52+
pub fn to_sign_and_limbs(input: TokenStream) -> TokenStream {
53+
let num = parse_string(input).expect("expected decimal string");
54+
let (is_positive, limbs) = str_to_limbs(&num);
55+
56+
let limbs: String = limbs.join(", ");
57+
let limbs_and_sign = format!("({}", is_positive) + ", [" + &limbs + "])";
58+
let tuple: Expr = syn::parse_str(&limbs_and_sign).unwrap();
59+
quote::quote!(#tuple).into()
60+
}
61+
62+
#[test]
63+
fn test_str_to_limbs() {
64+
let (is_positive, limbs) = str_to_limbs("-5");
65+
assert!(!is_positive);
66+
assert_eq!(&limbs, &["5u64".to_string()]);
67+
68+
let (is_positive, limbs) = str_to_limbs("100");
69+
assert!(is_positive);
70+
assert_eq!(&limbs, &["100u64".to_string()]);
71+
72+
let large_num = -((1i128 << 64) + 101234001234i128);
73+
let (is_positive, limbs) = str_to_limbs(&large_num.to_string());
74+
assert!(!is_positive);
75+
assert_eq!(&limbs, &["101234001234u64".to_string(), "1u64".to_string()]);
76+
77+
let num = "80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410946";
78+
let (is_positive, limbs) = str_to_limbs(&num.to_string());
79+
assert!(is_positive);
80+
let expected_limbs = [
81+
format!("{}u64", 0x8508c00000000002u64),
82+
format!("{}u64", 0x452217cc90000000u64),
83+
format!("{}u64", 0xc5ed1347970dec00u64),
84+
format!("{}u64", 0x619aaf7d34594aabu64),
85+
format!("{}u64", 0x9b3af05dd14f6ecu64),
86+
];
87+
assert_eq!(&limbs, &expected_limbs);
88+
}

ff/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ build = "build.rs"
1515

1616
[dependencies]
1717
ark-ff-asm = { path = "../ff-asm" }
18+
ark-ff-macros = { path = "../ff-macros" }
1819
ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false }
1920
ark-serialize = { path = "../serialize", default-features = false }
2021
derivative = { version = "2", features = ["use_core"] }
@@ -28,7 +29,6 @@ rustc_version = "0.3"
2829

2930
[dev-dependencies]
3031
rand_xorshift = "0.2"
31-
ark-test-curves = { path = "../test-curves", default-features = false, features = [ "bls12_381_curve"] }
3232

3333
[features]
3434
default = []

ff/src/biginteger/arithmetic.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use ark_std::vec::Vec;
2+
3+
/// Calculate a + b + carry, returning the sum and modifying the
4+
/// carry value.
5+
macro_rules! adc {
6+
($a:expr, $b:expr, &mut $carry:expr$(,)?) => {{
7+
let tmp = ($a as u128) + ($b as u128) + ($carry as u128);
8+
9+
$carry = (tmp >> 64) as u64;
10+
11+
tmp as u64
12+
}};
13+
}
14+
15+
/// Calculate a + (b * c) + carry, returning the least significant digit
16+
/// and setting carry to the most significant digit.
17+
macro_rules! mac_with_carry {
18+
($a:expr, $b:expr, $c:expr, &mut $carry:expr$(,)?) => {{
19+
let tmp = ($a as u128) + ($b as u128 * $c as u128) + ($carry as u128);
20+
21+
$carry = (tmp >> 64) as u64;
22+
23+
tmp as u64
24+
}};
25+
}
26+
27+
/// Calculate a - b - borrow, returning the result and modifying
28+
/// the borrow value.
29+
macro_rules! sbb {
30+
($a:expr, $b:expr, &mut $borrow:expr$(,)?) => {{
31+
let tmp = (1u128 << 64) + ($a as u128) - ($b as u128) - ($borrow as u128);
32+
33+
$borrow = if tmp >> 64 == 0 { 1 } else { 0 };
34+
35+
tmp as u64
36+
}};
37+
}
38+
39+
#[inline(always)]
40+
pub(crate) fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
41+
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
42+
43+
*carry = (tmp >> 64) as u64;
44+
45+
tmp as u64
46+
}
47+
48+
#[inline(always)]
49+
pub(crate) fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) {
50+
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
51+
52+
*carry = (tmp >> 64) as u64;
53+
}
54+
55+
pub fn find_wnaf(num: &[u64]) -> Vec<i64> {
56+
let is_zero = |num: &[u64]| num.iter().all(|x| *x == 0u64);
57+
let is_odd = |num: &[u64]| num[0] & 1 == 1;
58+
let sub_noborrow = |num: &mut [u64], z: u64| {
59+
let mut other = vec![0u64; num.len()];
60+
other[0] = z;
61+
let mut borrow = 0;
62+
63+
for (a, b) in num.iter_mut().zip(other) {
64+
*a = sbb!(*a, b, &mut borrow);
65+
}
66+
};
67+
let add_nocarry = |num: &mut [u64], z: u64| {
68+
let mut other = vec![0u64; num.len()];
69+
other[0] = z;
70+
let mut carry = 0;
71+
72+
for (a, b) in num.iter_mut().zip(other) {
73+
*a = adc!(*a, b, &mut carry);
74+
}
75+
};
76+
let div2 = |num: &mut [u64]| {
77+
let mut t = 0;
78+
for i in num.iter_mut().rev() {
79+
let t2 = *i << 63;
80+
*i >>= 1;
81+
*i |= t;
82+
t = t2;
83+
}
84+
};
85+
86+
let mut num = num.to_vec();
87+
let mut res = vec![];
88+
89+
while !is_zero(&num) {
90+
let z: i64;
91+
if is_odd(&num) {
92+
z = 2 - (num[0] % 4) as i64;
93+
if z >= 0 {
94+
sub_noborrow(&mut num, z as u64)
95+
} else {
96+
add_nocarry(&mut num, (-z) as u64)
97+
}
98+
} else {
99+
z = 0;
100+
}
101+
res.push(z);
102+
div2(&mut num);
103+
}
104+
105+
res
106+
}

ff/src/biginteger/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ macro_rules! bigint_impl {
1717
let mut carry = 0;
1818

1919
for (a, b) in self.0.iter_mut().zip(other.0.iter()) {
20-
*a = arithmetic::adc(*a, *b, &mut carry);
20+
*a = adc!(*a, *b, &mut carry);
2121
}
2222

2323
carry != 0
@@ -28,7 +28,7 @@ macro_rules! bigint_impl {
2828
let mut borrow = 0;
2929

3030
for (a, b) in self.0.iter_mut().zip(other.0.iter()) {
31-
*a = arithmetic::sbb(*a, *b, &mut borrow);
31+
*a = sbb!(*a, *b, &mut borrow);
3232
}
3333

3434
borrow != 0

ff/src/biginteger/mod.rs

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use rand::{
1717
};
1818
use zeroize::Zeroize;
1919

20+
#[macro_use]
21+
pub mod arithmetic;
2022
#[macro_use]
2123
mod macros;
2224

@@ -119,108 +121,3 @@ pub trait BigInteger:
119121
Ok(())
120122
}
121123
}
122-
123-
pub mod arithmetic {
124-
use ark_std::vec::Vec;
125-
pub fn find_wnaf(num: &[u64]) -> Vec<i64> {
126-
let is_zero = |num: &[u64]| num.iter().all(|x| *x == 0u64);
127-
let is_odd = |num: &[u64]| num[0] & 1 == 1;
128-
let sub_noborrow = |num: &mut [u64], z: u64| {
129-
let mut other = vec![0u64; num.len()];
130-
other[0] = z;
131-
let mut borrow = 0;
132-
133-
for (a, b) in num.iter_mut().zip(other) {
134-
*a = sbb(*a, b, &mut borrow);
135-
}
136-
};
137-
let add_nocarry = |num: &mut [u64], z: u64| {
138-
let mut other = vec![0u64; num.len()];
139-
other[0] = z;
140-
let mut carry = 0;
141-
142-
for (a, b) in num.iter_mut().zip(other) {
143-
*a = adc(*a, b, &mut carry);
144-
}
145-
};
146-
let div2 = |num: &mut [u64]| {
147-
let mut t = 0;
148-
for i in num.iter_mut().rev() {
149-
let t2 = *i << 63;
150-
*i >>= 1;
151-
*i |= t;
152-
t = t2;
153-
}
154-
};
155-
156-
let mut num = num.to_vec();
157-
let mut res = vec![];
158-
159-
while !is_zero(&num) {
160-
let z: i64;
161-
if is_odd(&num) {
162-
z = 2 - (num[0] % 4) as i64;
163-
if z >= 0 {
164-
sub_noborrow(&mut num, z as u64)
165-
} else {
166-
add_nocarry(&mut num, (-z) as u64)
167-
}
168-
} else {
169-
z = 0;
170-
}
171-
res.push(z);
172-
div2(&mut num);
173-
}
174-
175-
res
176-
}
177-
178-
/// Calculate a + b + carry, returning the sum and modifying the
179-
/// carry value.
180-
#[inline(always)]
181-
pub(crate) fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
182-
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
183-
184-
*carry = (tmp >> 64) as u64;
185-
186-
tmp as u64
187-
}
188-
189-
/// Calculate a - b - borrow, returning the result and modifying
190-
/// the borrow value.
191-
#[inline(always)]
192-
pub(crate) fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
193-
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
194-
195-
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
196-
197-
tmp as u64
198-
}
199-
200-
/// Calculate a + (b * c) + carry, returning the least significant digit
201-
/// and setting carry to the most significant digit.
202-
#[inline(always)]
203-
pub(crate) fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
204-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
205-
206-
*carry = (tmp >> 64) as u64;
207-
208-
tmp as u64
209-
}
210-
211-
#[inline(always)]
212-
pub(crate) fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
213-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
214-
215-
*carry = (tmp >> 64) as u64;
216-
217-
tmp as u64
218-
}
219-
220-
#[inline(always)]
221-
pub(crate) fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) {
222-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
223-
224-
*carry = (tmp >> 64) as u64;
225-
}
226-
}

0 commit comments

Comments
 (0)