Skip to content

Commit 0be00d8

Browse files
ejmahlercuviper
authored andcommitted
Moved the platform-specific code to adc and sbb, added a build.res entry
1 parent 4e20fc3 commit 0be00d8

File tree

2 files changed

+38
-107
lines changed

2 files changed

+38
-107
lines changed

build.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ use std::path::Path;
66

77
fn main() {
88
let pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH");
9-
if pointer_width.as_ref().map(String::as_str) == Ok("64") {
9+
let u64_digit = pointer_width.as_ref().map(String::as_str) == Ok("64");
10+
if u64_digit {
1011
autocfg::emit("u64_digit");
1112
}
1213
let ac = autocfg::new();
1314
if ac.probe_path("std::convert::TryFrom") || ac.probe_path("core::convert::TryFrom") {
1415
autocfg::emit("has_try_from");
1516
}
1617

18+
if u64_digit && (ac.probe_path("core::arch::x86_64::_addcarry_u64") || ac.probe_path("std::arch::x86_64::_addcarry_u64")) {
19+
autocfg::emit("use_addcarry_u64");
20+
}
21+
1722
autocfg::rerun_path("build.rs");
1823

1924
write_radix_bases().unwrap();

src/algorithms.rs

Lines changed: 32 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,42 @@ use crate::bigint::BigInt;
1212
use crate::bigint::Sign;
1313
use crate::bigint::Sign::{Minus, NoSign, Plus};
1414

15-
use crate::big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit};
15+
use crate::big_digit::{self, BigDigit, DoubleBigDigit};
16+
17+
#[cfg(not(use_addcarry_u64))] // only needed for the fallback implementation of `sbb`
18+
use crate::big_digit::SignedDoubleBigDigit;
1619

1720
// Generic functions for add/subtract/multiply with carry/borrow:
1821

1922
// Add with carry:
20-
#[allow(unused)]
23+
#[cfg(use_addcarry_u64)]
2124
#[inline]
22-
fn adc(a: BigDigit, b: BigDigit, acc: &mut DoubleBigDigit) -> BigDigit {
23-
*acc += DoubleBigDigit::from(a);
24-
*acc += DoubleBigDigit::from(b);
25-
let lo = *acc as BigDigit;
26-
*acc >>= big_digit::BITS;
27-
lo
25+
fn adc(carry: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 {
26+
unsafe { core::arch::x86_64::_addcarry_u64(carry, a, b, out) }
27+
}
28+
29+
#[cfg(not(use_addcarry_u64))] // fallback for environments where we don't have an addcarry intrinsic
30+
#[inline]
31+
fn adc(mut carry: DoubleBigDigit, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> DoubleBigDigit {
32+
carry += DoubleBigDigit::from(a);
33+
carry += DoubleBigDigit::from(b);
34+
*out = carry as BigDigit;
35+
carry >> big_digit::BITS
2836
}
2937

3038
// Subtract with borrow:
31-
#[allow(unused)]
39+
#[cfg(use_addcarry_u64)]
3240
#[inline]
33-
fn sbb(a: BigDigit, b: BigDigit, acc: &mut SignedDoubleBigDigit) -> BigDigit {
34-
*acc += SignedDoubleBigDigit::from(a);
35-
*acc -= SignedDoubleBigDigit::from(b);
36-
let lo = *acc as BigDigit;
37-
*acc >>= big_digit::BITS;
38-
lo
41+
fn sbb(carry: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 {
42+
unsafe { core::arch::x86_64::_subborrow_u64(carry, a, b, out) }
43+
}
44+
#[cfg(not(use_addcarry_u64))] // fallback for environments where we don't have an addcarry intrinsic
45+
#[inline]
46+
fn sbb(mut carry: SignedDoubleBigDigit, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> SignedDoubleBigDigit {
47+
carry += SignedDoubleBigDigit::from(a);
48+
carry -= SignedDoubleBigDigit::from(b);
49+
*out = carry as BigDigit;
50+
carry >> big_digit::BITS
3951
}
4052

4153
#[inline]
@@ -134,41 +146,6 @@ pub(crate) fn rem_digit(a: &BigUint, b: BigDigit) -> BigDigit {
134146
/// the addition first hoping that it will fit.
135147
///
136148
/// The caller _must_ ensure that `a` is at least as long as `b`.
137-
#[cfg(all(u64_digit, target_arch = "x86_64"))] // only run on x86_64, when we have u64 digits
138-
#[inline]
139-
pub(crate) fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
140-
debug_assert!(a.len() >= b.len());
141-
142-
use core::arch::x86_64::_addcarry_u64;
143-
144-
let mut carry = 0;
145-
let (a_lo, a_hi) = a.split_at_mut(b.len());
146-
147-
for (a, b) in a_lo.iter_mut().zip(b) {
148-
// Safety: There are absolutely no safety concerns with calling _addcarry_u64, it's just unsafe for API consistency with other intrinsics
149-
carry = unsafe { _addcarry_u64(carry, *a, *b, a) };
150-
}
151-
152-
if carry != 0 {
153-
for a in a_hi {
154-
// Safety: There are absolutely no safety concerns with calling _addcarry_u64, it's just unsafe for API consistency with other intrinsics
155-
carry = unsafe { _addcarry_u64(carry, *a, 0, a) };
156-
if carry == 0 {
157-
break;
158-
}
159-
}
160-
}
161-
162-
carry as BigDigit
163-
}
164-
165-
/// Two argument addition of raw slices, `a += b`, returning the carry.
166-
///
167-
/// This is used when the data `Vec` might need to resize to push a non-zero carry, so we perform
168-
/// the addition first hoping that it will fit.
169-
///
170-
/// The caller _must_ ensure that `a` is at least as long as `b`.
171-
#[cfg(not(all(u64_digit, target_arch = "x86_64")))] // run if we aren't using 64-bit digits, or if we're not running on x86_64
172149
#[inline]
173150
pub(crate) fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
174151
debug_assert!(a.len() >= b.len());
@@ -177,12 +154,12 @@ pub(crate) fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
177154
let (a_lo, a_hi) = a.split_at_mut(b.len());
178155

179156
for (a, b) in a_lo.iter_mut().zip(b) {
180-
*a = adc(*a, *b, &mut carry);
157+
carry = adc(carry, *a, *b, a);
181158
}
182159

183160
if carry != 0 {
184161
for a in a_hi {
185-
*a = adc(*a, 0, &mut carry);
162+
carry = adc(carry, *a, 0, a);
186163
if carry == 0 {
187164
break;
188165
}
@@ -203,39 +180,6 @@ pub(crate) fn add2(a: &mut [BigDigit], b: &[BigDigit]) {
203180
debug_assert!(carry == 0);
204181
}
205182

206-
#[cfg(all(u64_digit, target_arch = "x86_64"))] // only run on x86_64, when we have u64 digits
207-
pub(crate) fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
208-
use core::arch::x86_64::_subborrow_u64;
209-
210-
let mut borrow = 0;
211-
212-
let len = cmp::min(a.len(), b.len());
213-
let (a_lo, a_hi) = a.split_at_mut(len);
214-
let (b_lo, b_hi) = b.split_at(len);
215-
216-
for (a, b) in a_lo.iter_mut().zip(b_lo) {
217-
// Safety: There are absolutely no safety concerns with calling _subborrow_u64, it's just unsafe for API consistency with other intrinsics
218-
borrow = unsafe { _subborrow_u64(borrow, *a, *b, a) };
219-
}
220-
221-
if borrow != 0 {
222-
for a in a_hi {
223-
// Safety: There are absolutely no safety concerns with calling _subborrow_u64, it's just unsafe for API consistency with other intrinsics
224-
borrow = unsafe { _subborrow_u64(borrow, *a, 0, a) };
225-
if borrow == 0 {
226-
break;
227-
}
228-
}
229-
}
230-
231-
// note: we're _required_ to fail on underflow
232-
assert!(
233-
borrow == 0 && b_hi.iter().all(|x| *x == 0),
234-
"Cannot subtract b from a because b is larger than a."
235-
);
236-
}
237-
238-
#[cfg(not(all(u64_digit, target_arch = "x86_64")))] // run if we aren't using 64-bit digits, or if we're not running on x86_64
239183
pub(crate) fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
240184
let mut borrow = 0;
241185

@@ -244,12 +188,12 @@ pub(crate) fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
244188
let (b_lo, b_hi) = b.split_at(len);
245189

246190
for (a, b) in a_lo.iter_mut().zip(b_lo) {
247-
*a = sbb(*a, *b, &mut borrow);
191+
borrow = sbb(borrow, *a, *b, a);
248192
}
249193

250194
if borrow != 0 {
251195
for a in a_hi {
252-
*a = sbb(*a, 0, &mut borrow);
196+
borrow = sbb(borrow, *a, 0, a);
253197
if borrow == 0 {
254198
break;
255199
}
@@ -264,32 +208,14 @@ pub(crate) fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
264208
}
265209

266210
// Only for the Sub impl. `a` and `b` must have same length.
267-
#[cfg(all(u64_digit, target_arch = "x86_64"))] // only run on x86_64, when we have u64 digits
268-
#[inline]
269-
pub(crate) fn __sub2rev(a: &[BigDigit], b: &mut [BigDigit]) -> BigDigit {
270-
use core::arch::x86_64::_subborrow_u64;
271-
debug_assert!(b.len() == a.len());
272-
273-
let mut borrow = 0;
274-
275-
for (ai, bi) in a.iter().zip(b) {
276-
// Safety: There are absolutely no safety concerns with calling _subborrow_u64, it's just unsafe for API consistency with other intrinsics
277-
borrow = unsafe { _subborrow_u64(borrow, *ai, *bi, bi) };
278-
}
279-
280-
borrow as BigDigit
281-
}
282-
283-
// Only for the Sub impl. `a` and `b` must have same length.
284-
#[cfg(not(all(u64_digit, target_arch = "x86_64")))] // run if we aren't using 64-bit digits, or if we're not running on x86_64
285211
#[inline]
286212
pub(crate) fn __sub2rev(a: &[BigDigit], b: &mut [BigDigit]) -> BigDigit {
287213
debug_assert!(b.len() == a.len());
288214

289215
let mut borrow = 0;
290216

291217
for (ai, bi) in a.iter().zip(b) {
292-
*bi = sbb(*ai, *bi, &mut borrow);
218+
borrow = sbb(borrow, *ai, *bi, bi);
293219
}
294220

295221
borrow as BigDigit

0 commit comments

Comments
 (0)