From a2e2ec1a189694d02c9d5c05e8c45890535db5d2 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 4 Jan 2017 01:42:57 +0100 Subject: [PATCH 01/11] Add i128 to lib.rs as feature --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 04bf2d0..21d7902 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![feature(core_intrinsics)] #![feature(naked_functions)] #![feature(staged_api)] +#![feature(i128_type)] #![no_builtins] #![unstable(feature = "compiler_builtins_lib", reason = "Compiler builtins. Will never become stable.", From b356429374c87564a838154cfe01c601e3959f99 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 4 Jan 2017 01:52:47 +0100 Subject: [PATCH 02/11] int module: macro-ify trait impls and add {u,i}128 support --- src/int/mod.rs | 79 ++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/src/int/mod.rs b/src/int/mod.rs index 37e0537..bbe4b7b 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -10,28 +10,25 @@ pub trait Int { fn bits() -> u32; } -// TODO: Once i128/u128 support lands, we'll want to add impls for those as well -impl Int for u32 { - fn bits() -> u32 { - 32 - } -} -impl Int for i32 { - fn bits() -> u32 { - 32 - } -} -impl Int for u64 { - fn bits() -> u32 { - 64 - } -} -impl Int for i64 { - fn bits() -> u32 { - 64 +macro_rules! int_impl { + ($ity:ty, $sty:ty, $bits:expr) => { + impl Int for $ity { + fn bits() -> u32 { + $bits + } + } + impl Int for $sty { + fn bits() -> u32 { + $bits + } + } } } +int_impl!(i32, u32, 32); +int_impl!(i64, u64, 64); +int_impl!(i128, u128, 128); + /// Trait to convert an integer to/from smaller parts pub trait LargeInt { type LowHalf; @@ -42,32 +39,26 @@ pub trait LargeInt { fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self; } -// TODO: Once i128/u128 support lands, we'll want to add impls for those as well -impl LargeInt for u64 { - type LowHalf = u32; - type HighHalf = u32; +macro_rules! large_int { + ($ty:ty, $tylow:ty, $tyhigh:ty, $halfbits:expr) => { + impl LargeInt for $ty { + type LowHalf = $tylow; + type HighHalf = $tyhigh; - fn low(self) -> u32 { - self as u32 - } - fn high(self) -> u32 { - (self >> 32) as u32 - } - fn from_parts(low: u32, high: u32) -> u64 { - low as u64 | ((high as u64) << 32) + fn low(self) -> $tylow { + self as $tylow + } + fn high(self) -> $tyhigh { + (self >> $halfbits) as $tyhigh + } + fn from_parts(low: $tylow, high: $tyhigh) -> $ty { + low as $ty | ((high as $ty) << $halfbits) + } + } } } -impl LargeInt for i64 { - type LowHalf = u32; - type HighHalf = i32; - fn low(self) -> u32 { - self as u32 - } - fn high(self) -> i32 { - (self >> 32) as i32 - } - fn from_parts(low: u32, high: i32) -> i64 { - low as i64 | ((high as i64) << 32) - } -} +large_int!(u64, u32, u32, 32); +large_int!(i64, u32, i32, 32); +large_int!(u128, u64, u64, 64); +large_int!(i128, u64, i64, 64); From 3055aa21b6f2d27322dd41fff006fe8dd5dd487d Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 4 Jan 2017 02:42:03 +0100 Subject: [PATCH 03/11] i128 mul intrinsics --- README.md | 4 ++-- build.rs | 2 -- src/int/mul.rs | 27 +++++++++++++++++++-------- src/lib.rs | 3 +++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d66d888..8e81dde 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,8 @@ These builtins are needed to support 128-bit integers, which are in the process - [ ] floatuntisf.c - [ ] lshrti3.c - [ ] modti3.c -- [ ] muloti4.c -- [ ] multi3.c +- [x] muloti4.c +- [x] multi3.c - [ ] udivmodti4.c - [ ] udivti3.c - [ ] umodti3.c diff --git a/build.rs b/build.rs index e6ad4c6..0ecb0d0 100644 --- a/build.rs +++ b/build.rs @@ -150,7 +150,6 @@ fn main() { "int_util.c", "muldc3.c", "muldf3.c", - "muloti4.c", "mulsc3.c", "mulsf3.c", "mulvdi3.c", @@ -202,7 +201,6 @@ fn main() { "lshrti3.c", "modti3.c", "multf3.c", - "multi3.c", "mulvti3.c", "negti2.c", "negvti2.c", diff --git a/src/int/mul.rs b/src/int/mul.rs index 0403aa9..d46bc90 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -1,9 +1,8 @@ -#[cfg(not(all(feature = "c", target_arch = "x86")))] use int::LargeInt; use int::Int; macro_rules! mul { - ($intrinsic:ident: $ty:ty) => { + ($intrinsic:ident: $ty:ty, $tyh:ty) => { /// Returns `a * b` #[cfg_attr(not(test), no_mangle)] pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty { @@ -14,14 +13,15 @@ macro_rules! mul { low &= lower_mask; t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask); low += (t & lower_mask) << half_bits; - let mut high = t >> half_bits; + let mut high = (t >> half_bits) as $tyh; t = low >> half_bits; low &= lower_mask; t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask); low += (t & lower_mask) << half_bits; - high += t >> half_bits; - high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits); - high = high.wrapping_add(a.high().wrapping_mul(b.low()).wrapping_add(a.low().wrapping_mul(b.high()))); + high += (t >> half_bits) as $tyh; + high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as $tyh; + high = high.wrapping_add(a.high().wrapping_mul(b.low() as $tyh)) + .wrapping_add((a.low() as $tyh).wrapping_mul(b.high())); <$ty>::from_parts(low, high) } } @@ -29,9 +29,13 @@ macro_rules! mul { macro_rules! mulo { ($intrinsic:ident: $ty:ty) => { + // Default is "C" ABI + mulo!($intrinsic: $ty, "C"); + }; + ($intrinsic:ident: $ty:ty, $abi:tt) => { /// Returns `a * b` and sets `*overflow = 1` if `a * b` overflows #[cfg_attr(not(test), no_mangle)] - pub extern "C" fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty { + pub extern $abi fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty { *overflow = 0; let result = a.wrapping_mul(b); if a == <$ty>::min_value() { @@ -69,11 +73,18 @@ macro_rules! mulo { } #[cfg(not(all(feature = "c", target_arch = "x86")))] -mul!(__muldi3: u64); +mul!(__muldi3: u64, u32); + +mul!(__multi3: i128, i64); mulo!(__mulosi4: i32); mulo!(__mulodi4: i64); +#[cfg(all(windows, target_pointer_width="64"))] +mulo!(__muloti4: i128, "unadjusted"); +#[cfg(not(all(windows, target_pointer_width="64")))] +mulo!(__muloti4: i128); + #[cfg(test)] mod tests { use qc::{I32, I64, U64}; diff --git a/src/lib.rs b/src/lib.rs index 21d7902..f637ff5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,9 @@ #![feature(naked_functions)] #![feature(staged_api)] #![feature(i128_type)] +#![feature(repr_simd)] +#![feature(abi_unadjusted)] +#![allow(unused_features)] #![no_builtins] #![unstable(feature = "compiler_builtins_lib", reason = "Compiler builtins. Will never become stable.", From 1e27c3f937e2cf54195697942c5c5bf907ae568a Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 4 Jan 2017 03:04:27 +0100 Subject: [PATCH 04/11] i128 shift intrinsics --- README.md | 6 +++--- build.rs | 3 --- src/int/shift.rs | 7 ++++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8e81dde..8a8c2d1 100644 --- a/README.md +++ b/README.md @@ -193,8 +193,8 @@ features = ["c"] These builtins are needed to support 128-bit integers, which are in the process of being added to Rust. -- [ ] ashlti3.c -- [ ] ashrti3.c +- [x] ashlti3.c +- [x] ashrti3.c - [ ] divti3.c - [ ] fixdfti.c - [ ] fixsfti.c @@ -204,7 +204,7 @@ These builtins are needed to support 128-bit integers, which are in the process - [ ] floattisf.c - [ ] floatuntidf.c - [ ] floatuntisf.c -- [ ] lshrti3.c +- [x] lshrti3.c - [ ] modti3.c - [x] muloti4.c - [x] multi3.c diff --git a/build.rs b/build.rs index 0ecb0d0..0825925 100644 --- a/build.rs +++ b/build.rs @@ -178,8 +178,6 @@ fn main() { sources.extend(&["absvti2.c", "addtf3.c", "addvti3.c", - "ashlti3.c", - "ashrti3.c", "clzti2.c", "cmpti2.c", "ctzti2.c", @@ -198,7 +196,6 @@ fn main() { "floatuntidf.c", "floatuntisf.c", "floatuntixf.c", - "lshrti3.c", "modti3.c", "multf3.c", "mulvti3.c", diff --git a/src/int/shift.rs b/src/int/shift.rs index 9338f07..5c8dd5c 100644 --- a/src/int/shift.rs +++ b/src/int/shift.rs @@ -1,4 +1,3 @@ -#[cfg(not(all(feature = "c", target_arch = "x86")))] use int::{Int, LargeInt}; macro_rules! ashl { @@ -58,12 +57,18 @@ macro_rules! lshr { #[cfg(not(all(feature = "c", target_arch = "x86")))] ashl!(__ashldi3: u64); +ashl!(__ashlti3: u128); + #[cfg(not(all(feature = "c", target_arch = "x86")))] ashr!(__ashrdi3: i64); +ashr!(__ashrti3: i128); + #[cfg(not(all(feature = "c", target_arch = "x86")))] lshr!(__lshrdi3: u64); +lshr!(__lshrti3: u128); + #[cfg(test)] mod tests { use qc::{I64, U64}; From 8fe50d813c0755340c876860a79b2bb9db73e773 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 5 Jan 2017 02:22:44 +0100 Subject: [PATCH 05/11] Macro-ify udivmod --- src/int/mod.rs | 15 +++ src/int/mul.rs | 16 +-- src/int/udiv.rs | 273 +++++++++++++++++++++++++----------------------- 3 files changed, 163 insertions(+), 141 deletions(-) diff --git a/src/int/mod.rs b/src/int/mod.rs index bbe4b7b..647410c 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -1,3 +1,14 @@ +macro_rules! hty { + ($ty:ty) => { + <$ty as LargeInt>::HighHalf + } +} + +macro_rules! os_ty { + ($ty:ty) => { + <$ty as Int>::OtherSign + } +} pub mod mul; pub mod sdiv; @@ -6,6 +17,8 @@ pub mod udiv; /// Trait for some basic operations on integers pub trait Int { + /// Type with the same width but other signedness + type OtherSign; /// Returns the bitwidth of the int type fn bits() -> u32; } @@ -13,11 +26,13 @@ pub trait Int { macro_rules! int_impl { ($ity:ty, $sty:ty, $bits:expr) => { impl Int for $ity { + type OtherSign = $sty; fn bits() -> u32 { $bits } } impl Int for $sty { + type OtherSign = $ity; fn bits() -> u32 { $bits } diff --git a/src/int/mul.rs b/src/int/mul.rs index d46bc90..45a7128 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -2,7 +2,7 @@ use int::LargeInt; use int::Int; macro_rules! mul { - ($intrinsic:ident: $ty:ty, $tyh:ty) => { + ($intrinsic:ident: $ty:ty) => { /// Returns `a * b` #[cfg_attr(not(test), no_mangle)] pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty { @@ -13,15 +13,15 @@ macro_rules! mul { low &= lower_mask; t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask); low += (t & lower_mask) << half_bits; - let mut high = (t >> half_bits) as $tyh; + let mut high = (t >> half_bits) as hty!($ty); t = low >> half_bits; low &= lower_mask; t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask); low += (t & lower_mask) << half_bits; - high += (t >> half_bits) as $tyh; - high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as $tyh; - high = high.wrapping_add(a.high().wrapping_mul(b.low() as $tyh)) - .wrapping_add((a.low() as $tyh).wrapping_mul(b.high())); + high += (t >> half_bits) as hty!($ty); + high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as hty!($ty); + high = high.wrapping_add(a.high().wrapping_mul(b.low() as hty!($ty))) + .wrapping_add((a.low() as hty!($ty)).wrapping_mul(b.high())); <$ty>::from_parts(low, high) } } @@ -73,9 +73,9 @@ macro_rules! mulo { } #[cfg(not(all(feature = "c", target_arch = "x86")))] -mul!(__muldi3: u64, u32); +mul!(__muldi3: u64); -mul!(__multi3: i128, i64); +mul!(__multi3: i128); mulo!(__mulosi4: i32); mulo!(__mulodi4: i64); diff --git a/src/int/udiv.rs b/src/int/udiv.rs index 3c5629e..a1a9503 100644 --- a/src/int/udiv.rs +++ b/src/int/udiv.rs @@ -114,152 +114,159 @@ pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 { rem } -/// Returns `n / d` and sets `*rem = n % d` -#[cfg_attr(not(test), no_mangle)] -pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { - // NOTE X is unknown, K != 0 - if n.high() == 0 { - if d.high() == 0 { - // 0 X - // --- - // 0 X +macro_rules! udivmod_inner { + ($n:expr, $d:expr, $rem:expr, $ty:ty) => {{ + let (n, d, rem) = ($n, $d, $rem); + // NOTE X is unknown, K != 0 + if n.high() == 0 { + if d.high() == 0 { + // 0 X + // --- + // 0 X - if let Some(rem) = rem { - *rem = u64::from(urem!(n.low(), d.low())); - } - return u64::from(udiv!(n.low(), d.low())); - } else { - // 0 X - // --- - // K X - if let Some(rem) = rem { - *rem = n; - } - return 0; - }; - } - - let mut sr; - let mut q; - let mut r; - - if d.low() == 0 { - if d.high() == 0 { - // K X - // --- - // 0 0 - // NOTE This should be unreachable in safe Rust because the program will panic before - // this intrinsic is called - unsafe { - intrinsics::abort() - } - } - - if n.low() == 0 { - // K 0 - // --- - // K 0 - if let Some(rem) = rem { - *rem = u64::from_parts(0, urem!(n.high(), d.high())); - } - return u64::from(udiv!(n.high(), d.high())); - } - - // K K - // --- - // K 0 - - if d.high().is_power_of_two() { - if let Some(rem) = rem { - *rem = u64::from_parts(n.low(), n.high() & (d.high() - 1)); - } - return u64::from(n.high() >> d.high().trailing_zeros()); - } - - sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); - - // D > N - if sr > u32::bits() - 2 { - if let Some(rem) = rem { - *rem = n; - } - return 0; - } - - sr += 1; - - // 1 <= sr <= u32::bits() - 1 - q = n << (u64::bits() - sr); - r = n >> sr; - } else if d.high() == 0 { - // K X - // --- - // 0 K - if d.low().is_power_of_two() { - if let Some(rem) = rem { - *rem = u64::from(n.low() & (d.low() - 1)); - } - - if d.low() == 1 { - return n; + if let Some(rem) = rem { + *rem = <$ty>::from(urem!(n.low(), d.low())); + } + return <$ty>::from(udiv!(n.low(), d.low())); } else { - let sr = d.low().trailing_zeros(); - return n >> sr; + // 0 X + // --- + // K X + if let Some(rem) = rem { + *rem = n; + } + return 0; }; } - sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros(); + let mut sr; + let mut q; + let mut r; - // 2 <= sr <= u64::bits() - 1 - q = n << (u64::bits() - sr); - r = n >> sr; - } else { - // K X - // --- - // K K - sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); - - // D > N - if sr > u32::bits() - 1 { - if let Some(rem) = rem { - *rem = n; + if d.low() == 0 { + if d.high() == 0 { + // K X + // --- + // 0 0 + // NOTE This should be unreachable in safe Rust because the program will panic before + // this intrinsic is called + unsafe { + intrinsics::abort() + } } - return 0; + + if n.low() == 0 { + // K 0 + // --- + // K 0 + if let Some(rem) = rem { + *rem = <$ty>::from_parts(0, urem!(n.high(), d.high())); + } + return <$ty>::from(udiv!(n.high(), d.high())); + } + + // K K + // --- + // K 0 + + if d.high().is_power_of_two() { + if let Some(rem) = rem { + *rem = <$ty>::from_parts(n.low(), n.high() & (d.high() - 1)); + } + return <$ty>::from(n.high() >> d.high().trailing_zeros()); + } + + sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); + + // D > N + if sr > ::bits() - 2 { + if let Some(rem) = rem { + *rem = n; + } + return 0; + } + + sr += 1; + + // 1 <= sr <= ::bits() - 1 + q = n << (<$ty>::bits() - sr); + r = n >> sr; + } else if d.high() == 0 { + // K X + // --- + // 0 K + if d.low().is_power_of_two() { + if let Some(rem) = rem { + *rem = <$ty>::from(n.low() & (d.low() - 1)); + } + + if d.low() == 1 { + return n; + } else { + let sr = d.low().trailing_zeros(); + return n >> sr; + }; + } + + sr = 1 + ::bits() + d.low().leading_zeros() - n.high().leading_zeros(); + + // 2 <= sr <= u64::bits() - 1 + q = n << (<$ty>::bits() - sr); + r = n >> sr; + } else { + // K X + // --- + // K K + sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); + + // D > N + if sr > ::bits() - 1 { + if let Some(rem) = rem { + *rem = n; + } + return 0; + } + + sr += 1; + + // 1 <= sr <= ::bits() + q = n << (<$ty>::bits() - sr); + r = n >> sr; } - sr += 1; + // Not a special case + // q and r are initialized with + // q = n << (u64::bits() - sr) + // r = n >> sr + // 1 <= sr <= u64::bits() - 1 + let mut carry = 0; - // 1 <= sr <= u32::bits() - q = n << (u64::bits() - sr); - r = n >> sr; - } + for _ in 0..sr { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> (<$ty>::bits() - 1)); + q = (q << 1) | carry as $ty; - // Not a special case - // q and r are initialized with - // q = n << (u64::bits() - sr) - // r = n >> sr - // 1 <= sr <= u64::bits() - 1 - let mut carry = 0; + // carry = 0 + // if r >= d { + // r -= d; + // carry = 1; + // } + let s = (d.wrapping_sub(r).wrapping_sub(1)) as os_ty!($ty) >> (<$ty>::bits() - 1); + carry = (s & 1) as hty!($ty); + r -= d & s as $ty; + } - for _ in 0..sr { - // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> (u64::bits() - 1)); - q = (q << 1) | carry as u64; + if let Some(rem) = rem { + *rem = r; + } + (q << 1) | carry as $ty + }} +} - // carry = 0 - // if r >= d { - // r -= d; - // carry = 1; - // } - let s = (d.wrapping_sub(r).wrapping_sub(1)) as i64 >> (u64::bits() - 1); - carry = (s & 1) as u32; - r -= d & s as u64; - } - - if let Some(rem) = rem { - *rem = r; - } - (q << 1) | carry as u64 +/// Returns `n / d` and sets `*rem = n % d` +#[cfg_attr(not(test), no_mangle)] +pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { + udivmod_inner!(n, d, rem, u64) } #[cfg(test)] From 2f3c534cfe2150beac4d4ab99db71d3b6b56d062 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 5 Jan 2017 15:08:56 +0100 Subject: [PATCH 06/11] u128 udiv intrinsics --- README.md | 6 ++--- build.rs | 5 +--- src/int/udiv.rs | 69 +++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 11 ++++++++ 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8a8c2d1..7397810 100644 --- a/README.md +++ b/README.md @@ -208,9 +208,9 @@ These builtins are needed to support 128-bit integers, which are in the process - [ ] modti3.c - [x] muloti4.c - [x] multi3.c -- [ ] udivmodti4.c -- [ ] udivti3.c -- [ ] umodti3.c +- [x] udivmodti4.c +- [x] udivti3.c +- [x] umodti3.c ## Unimplemented functions diff --git a/build.rs b/build.rs index 0825925..5219a04 100644 --- a/build.rs +++ b/build.rs @@ -207,10 +207,7 @@ fn main() { "subtf3.c", "subvti3.c", "trampoline_setup.c", - "ucmpti2.c", - "udivmodti4.c", - "udivti3.c", - "umodti3.c"]); + "ucmpti2.c"]); } if target_vendor == "apple" { diff --git a/src/int/udiv.rs b/src/int/udiv.rs index a1a9503..44a1149 100644 --- a/src/int/udiv.rs +++ b/src/int/udiv.rs @@ -96,23 +96,44 @@ pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 { q } -/// Returns `n / d` -#[cfg_attr(not(test), no_mangle)] -#[cfg(not(all(feature = "c", target_arch = "x86")))] -pub extern "C" fn __udivdi3(n: u64, d: u64) -> u64 { - __udivmoddi4(n, d, None) +macro_rules! div_mod_intrinsics { + ($udiv_intr:ident, $umod_intr:ident : $ty:ty) => { + div_mod_intrinsics!($udiv_intr, $umod_intr : $ty, + __udivmoddi4); + }; + ($udiv_intr:ident, $umod_intr:ident : $ty:ty, $divmod_intr:expr) => { + div_mod_intrinsics!($udiv_intr, $umod_intr : $ty, + $divmod_intr, $ty, |i|{ i }); + }; + ($udiv_intr:ident, $umod_intr:ident : $ty:ty, $divmod_intr:expr, + $tyret:ty, $conv:expr) => { + /// Returns `n / d` + #[cfg_attr(not(test), no_mangle)] + pub extern "C" fn $udiv_intr(n: $ty, d: $ty) -> $tyret { + let r = $divmod_intr(n, d, None); + ($conv)(r) + } + + /// Returns `n % d` + #[cfg_attr(not(test), no_mangle)] + pub extern "C" fn $umod_intr(a: $ty, b: $ty) -> $tyret { + use core::mem; + + let mut rem = unsafe { mem::uninitialized() }; + $divmod_intr(a, b, Some(&mut rem)); + ($conv)(rem) + } + } } -/// Returns `n % d` #[cfg(not(all(feature = "c", target_arch = "x86")))] -#[cfg_attr(not(test), no_mangle)] -pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 { - use core::mem; +div_mod_intrinsics!(__udivdi3, __umoddi3: u64); - let mut rem = unsafe { mem::uninitialized() }; - __udivmoddi4(a, b, Some(&mut rem)); - rem -} +#[cfg(not(all(windows, target_pointer_width="64")))] +div_mod_intrinsics!(__udivti3, __umodti3: u128, u128_div_mod); + +#[cfg(all(windows, target_pointer_width="64"))] +div_mod_intrinsics!(__udivti3, __umodti3: u128, u128_div_mod, ::U64x2, ::conv); macro_rules! udivmod_inner { ($n:expr, $d:expr, $rem:expr, $ty:ty) => {{ @@ -269,6 +290,28 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { udivmod_inner!(n, d, rem, u64) } +macro_rules! udivmodti4 { + ($tyret:ty, $conv:expr) => { + /// Returns `n / d` and sets `*rem = n % d` + #[cfg_attr(not(test), no_mangle)] + pub extern "C" fn __udivmodti4(n: u128, d: u128, rem: Option<&mut u128>) -> $tyret { + let r = u128_div_mod(n, d, rem); + ($conv)(r) + } + } +} + +/// Returns `n / d` and sets `*rem = n % d` +fn u128_div_mod(n: u128, d: u128, rem: Option<&mut u128>) -> u128 { + udivmod_inner!(n, d, rem, u128) +} + +#[cfg(all(windows, target_pointer_width="64"))] +udivmodti4!(::U64x2, ::conv); + +#[cfg(not(all(windows, target_pointer_width="64")))] +udivmodti4!(u128, |i|{ i }); + #[cfg(test)] mod tests { use qc::{U32, U64}; diff --git a/src/lib.rs b/src/lib.rs index f637ff5..559e1a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,17 @@ macro_rules! srem { } } +// Hack for LLVM expectations for ABI on windows +#[cfg(all(windows, target_pointer_width="64"))] +#[repr(simd)] +pub struct U64x2(u64, u64); + +#[cfg(all(windows, target_pointer_width="64"))] +fn conv(i: u128) -> U64x2 { + use int::LargeInt; + U64x2(i.low(), i.high()) +} + #[cfg(test)] #[cfg_attr(target_arch = "arm", macro_use)] extern crate quickcheck; From 9013dbef02ce915673e2d897e9af2247079d0987 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 5 Jan 2017 15:23:45 +0100 Subject: [PATCH 07/11] u128 sdiv intrinsics --- README.md | 4 ++-- build.rs | 2 -- src/int/sdiv.rs | 26 ++++++++++++++++++++++---- src/lib.rs | 7 +++++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7397810..0810edf 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ These builtins are needed to support 128-bit integers, which are in the process - [x] ashlti3.c - [x] ashrti3.c -- [ ] divti3.c +- [x] divti3.c - [ ] fixdfti.c - [ ] fixsfti.c - [ ] fixunsdfti.c @@ -205,7 +205,7 @@ These builtins are needed to support 128-bit integers, which are in the process - [ ] floatuntidf.c - [ ] floatuntisf.c - [x] lshrti3.c -- [ ] modti3.c +- [x] modti3.c - [x] muloti4.c - [x] multi3.c - [x] udivmodti4.c diff --git a/build.rs b/build.rs index 5219a04..ca58b67 100644 --- a/build.rs +++ b/build.rs @@ -182,7 +182,6 @@ fn main() { "cmpti2.c", "ctzti2.c", "divtf3.c", - "divti3.c", "ffsti2.c", "fixdfti.c", "fixsfti.c", @@ -196,7 +195,6 @@ fn main() { "floatuntidf.c", "floatuntisf.c", "floatuntixf.c", - "modti3.c", "multf3.c", "mulvti3.c", "negti2.c", diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index b541322..84f72f1 100644 --- a/src/int/sdiv.rs +++ b/src/int/sdiv.rs @@ -2,9 +2,12 @@ use int::Int; macro_rules! div { ($intrinsic:ident: $ty:ty, $uty:ty) => { + div!($intrinsic: $ty, $uty, $ty, |i| {i}); + }; + ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => { /// Returns `a / b` #[cfg_attr(not(test), no_mangle)] - pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty { + pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret { let s_a = a >> (<$ty>::bits() - 1); let s_b = b >> (<$ty>::bits() - 1); let a = (a ^ s_a) - s_a; @@ -12,23 +15,26 @@ macro_rules! div { let s = s_a ^ s_b; let r = udiv!(a as $uty, b as $uty); - (r as $ty ^ s) - s + ($conv)((r as $ty ^ s) - s) } } } macro_rules! mod_ { ($intrinsic:ident: $ty:ty, $uty:ty) => { + mod_!($intrinsic: $ty, $uty, $ty, |i| {i}); + }; + ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => { /// Returns `a % b` #[cfg_attr(not(test), no_mangle)] - pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty { + pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret { let s = b >> (<$ty>::bits() - 1); let b = (b ^ s) - s; let s = a >> (<$ty>::bits() - 1); let a = (a ^ s) - s; let r = urem!(a as $uty, b as $uty); - (r as $ty ^ s) - s + ($conv)((r as $ty ^ s) - s) } } } @@ -61,12 +67,24 @@ div!(__divsi3: i32, u32); #[cfg(not(all(feature = "c", target_arch = "x86")))] div!(__divdi3: i64, u64); +#[cfg(not(all(windows, target_pointer_width="64")))] +div!(__divti3: i128, u128); + +#[cfg(all(windows, target_pointer_width="64"))] +div!(__divti3: i128, u128, ::U64x2, ::sconv); + #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))] mod_!(__modsi3: i32, u32); #[cfg(not(all(feature = "c", target_arch = "x86")))] mod_!(__moddi3: i64, u64); +#[cfg(not(all(windows, target_pointer_width="64")))] +mod_!(__modti3: i128, u128); + +#[cfg(all(windows, target_pointer_width="64"))] +mod_!(__modti3: i128, u128, ::U64x2, ::sconv); + #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))] divmod!(__divmodsi4, __divsi3: i32); diff --git a/src/lib.rs b/src/lib.rs index 559e1a7..0a80ad2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,13 @@ fn conv(i: u128) -> U64x2 { U64x2(i.low(), i.high()) } +#[cfg(all(windows, target_pointer_width="64"))] +fn sconv(i: i128) -> U64x2 { + use int::LargeInt; + let j = i as u128; + U64x2(j.low(), j.high()) +} + #[cfg(test)] #[cfg_attr(target_arch = "arm", macro_use)] extern crate quickcheck; From 417b07b821976e5c635d0dd23ea318e62816a40f Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 6 Jan 2017 06:30:37 +0100 Subject: [PATCH 08/11] Add newly implemented intrinsics to test file --- src/bin/intrinsics.rs | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/bin/intrinsics.rs b/src/bin/intrinsics.rs index e0b619f..4c5c884 100644 --- a/src/bin/intrinsics.rs +++ b/src/bin/intrinsics.rs @@ -12,6 +12,7 @@ #![feature(lang_items)] #![feature(libc)] #![feature(start)] +#![feature(i128_type)] #![no_std] #[cfg(not(thumb))] @@ -300,6 +301,42 @@ mod intrinsics { pub fn umoddi3(a: u64, b: u64) -> u64 { a % b } + + pub fn muloti4(a: u128, b: u128) -> Option { + a.checked_mul(b) + } + + pub fn multi3(a: u128, b: u128) -> u128 { + a.wrapping_mul(b) + } + + pub fn ashlti3(a: u128, b: usize) -> u128 { + a >> b + } + + pub fn ashrti3(a: u128, b: usize) -> u128 { + a << b + } + + pub fn lshrti3(a: i128, b: usize) -> i128 { + a >> b + } + + pub fn udivti3(a: u128, b: u128) -> u128 { + a / b + } + + pub fn umodti3(a: u128, b: u128) -> u128 { + a % b + } + + pub fn divti3(a: i128, b: i128) -> i128 { + a / b + } + + pub fn modti3(a: i128, b: i128) -> i128 { + a % b + } } #[cfg(feature = "c")] @@ -356,6 +393,15 @@ fn run() { bb(powidf2(bb(2.), bb(3))); bb(powisf2(bb(2.), bb(3))); bb(umoddi3(bb(2), bb(3))); + bb(muloti4(bb(2), bb(2))); + bb(multi3(bb(2), bb(2))); + bb(ashlti3(bb(2), bb(2))); + bb(ashrti3(bb(2), bb(2))); + bb(lshrti3(bb(2), bb(2))); + bb(udivti3(bb(2), bb(2))); + bb(umodti3(bb(2), bb(2))); + bb(divti3(bb(2), bb(2))); + bb(modti3(bb(2), bb(2))); } #[cfg(all(feature = "c", not(thumb)))] From b78e956f0834d224f98473003494436856f14490 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 3 Feb 2017 22:06:36 +0100 Subject: [PATCH 09/11] Add quickcheck tests --- compiler-rt/compiler-rt-cdylib/build.rs | 11 ++++++ compiler-rt/compiler-rt-cdylib/src/lib.rs | 27 ++++++++++++++ src/int/mul.rs | 24 ++++++++++++ src/int/sdiv.rs | 26 +++++++++++++ src/int/shift.rs | 36 ++++++++++++++++++ src/int/udiv.rs | 45 +++++++++++++++++++++++ src/qc.rs | 2 + 7 files changed, 171 insertions(+) diff --git a/compiler-rt/compiler-rt-cdylib/build.rs b/compiler-rt/compiler-rt-cdylib/build.rs index 4eb7638..5fc18f1 100644 --- a/compiler-rt/compiler-rt-cdylib/build.rs +++ b/compiler-rt/compiler-rt-cdylib/build.rs @@ -60,6 +60,17 @@ fn main() { "addsf3.c", "powidf2.c", "powisf2.c", + // 128 bit integers + "lshrti3.c", + "modti3.c", + "muloti4.c", + "multi3.c", + "udivmodti4.c", + "udivti3.c", + "umodti3.c", + "ashlti3.c", + "ashrti3.c", + "divti3.c", ]); for src in sources.files.iter() { diff --git a/compiler-rt/compiler-rt-cdylib/src/lib.rs b/compiler-rt/compiler-rt-cdylib/src/lib.rs index 81affa2..268da50 100644 --- a/compiler-rt/compiler-rt-cdylib/src/lib.rs +++ b/compiler-rt/compiler-rt-cdylib/src/lib.rs @@ -58,6 +58,33 @@ declare!(___adddf3, __adddf3); declare!(___powisf2, __powisf2); declare!(___powidf2, __powidf2); +#[cfg(all(not(windows), target_pointer_width="64"))] +pub mod int_128 { + extern { + fn __lshrti3(); + fn __modti3(); + fn __muloti4(); + fn __multi3(); + fn __udivmodti4(); + fn __udivti3(); + fn __umodti3(); + fn __ashlti3(); + fn __ashrti3(); + fn __divti3(); + } + + declare!(___lshrti3, __lshrti3); + declare!(___modti3, __modti3); + declare!(___muloti4, __muloti4); + declare!(___multi3, __multi3); + declare!(___udivmodti4, __udivmodti4); + declare!(___udivti3, __udivti3); + declare!(___umodti3, __umodti3); + declare!(___ashlti3, __ashlti3); + declare!(___ashrti3, __ashrti3); + declare!(___divti3, __divti3); +} + #[lang = "eh_personality"] fn eh_personality() {} #[lang = "panic_fmt"] diff --git a/src/int/mul.rs b/src/int/mul.rs index 45a7128..c2aece0 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -120,3 +120,27 @@ mod tests { } } } + +#[cfg(test)] +#[cfg(all(not(windows), target_pointer_width="64"))] +mod tests_i128 { + use qc::I128; + + check! { + fn __multi3(f: extern fn(i128, i128) -> i128, a: I128, b: I128) + -> Option { + Some(f(a.0, b.0)) + } + fn __muloti4(f: extern fn(i128, i128, &mut i32) -> i128, + a: I128, + b: I128) -> Option<(i128, i32)> { + let (a, b) = (a.0, b.0); + let mut overflow = 2; + let r = f(a, b, &mut overflow); + if overflow != 0 && overflow != 1 { + return None + } + Some((r, overflow)) + } + } +} diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index 84f72f1..97e1939 100644 --- a/src/int/sdiv.rs +++ b/src/int/sdiv.rs @@ -162,3 +162,29 @@ mod tests { } } } + +#[cfg(test)] +#[cfg(all(not(windows), target_pointer_width="64"))] +mod tests_i128 { + use qc::U128; + + check! { + fn __divti3(f: extern fn(i128, i128) -> i128, n: U128, d: U128) -> Option { + let (n, d) = (n.0 as i128, d.0 as i128); + if d == 0 { + None + } else { + Some(f(n, d)) + } + } + + fn __modti3(f: extern fn(i128, i128) -> i128, n: U128, d: U128) -> Option { + let (n, d) = (n.0 as i128, d.0 as i128); + if d == 0 { + None + } else { + Some(f(n, d)) + } + } + } +} diff --git a/src/int/shift.rs b/src/int/shift.rs index 5c8dd5c..2993d92 100644 --- a/src/int/shift.rs +++ b/src/int/shift.rs @@ -103,3 +103,39 @@ mod tests { } } } + +#[cfg(test)] +#[cfg(all(not(windows), target_pointer_width="64"))] +mod tests_i128 { + use qc::{I128, U128}; + + // NOTE We purposefully stick to `u32` for `b` here because we want "small" values (b < 64) + check! { + fn __ashlti3(f: extern fn(u128, u32) -> u128, a: U128, b: u32) -> Option { + let a = a.0; + if b >= 64 { + None + } else { + Some(f(a, b)) + } + } + + fn __ashrti3(f: extern fn(i128, u32) -> i128, a: I128, b: u32) -> Option { + let a = a.0; + if b >= 64 { + None + } else { + Some(f(a, b)) + } + } + + fn __lshrti3(f: extern fn(u128, u32) -> u128, a: U128, b: u32) -> Option { + let a = a.0; + if b >= 128 { + None + } else { + Some(f(a, b)) + } + } + } +} diff --git a/src/int/udiv.rs b/src/int/udiv.rs index 44a1149..735d388 100644 --- a/src/int/udiv.rs +++ b/src/int/udiv.rs @@ -380,3 +380,48 @@ mod tests { } } } + +#[cfg(test)] +#[cfg(all(not(windows), target_pointer_width="64"))] +mod tests_i128 { + use qc::U128; + + check! { + fn __udivti3(f: extern fn(u128, u128) -> u128, + n: U128, + d: U128) -> Option { + let (n, d) = (n.0, d.0); + if d == 0 { + None + } else { + Some(f(n, d)) + } + } + + fn __umodti3(f: extern fn(u128, u128) -> u128, + n: U128, + d: U128) -> Option { + let (n, d) = (n.0, d.0); + if d == 0 { + None + } else { + Some(f(n, d)) + } + } + + fn __udivmodti4(f: extern fn(u128, u128, Option<&mut u128>) -> u128, + n: U128, + d: U128) -> Option { + let (n, d) = (n.0, d.0); + if d == 0 { + None + } else { + // FIXME fix the segfault when the remainder is requested + /*let mut r = 0; + let q = f(n, d, Some(&mut r)); + Some((q, r))*/ + Some(f(n, d, None)) + } + } + } +} diff --git a/src/qc.rs b/src/qc.rs index 5dbc56f..ea0faac 100644 --- a/src/qc.rs +++ b/src/qc.rs @@ -144,6 +144,8 @@ macro_rules! arbitrary_large { arbitrary_large!(I64: i64); arbitrary_large!(U64: u64); +arbitrary_large!(I128: i128); +arbitrary_large!(U128: u128); macro_rules! arbitrary_float { ($TY:ident : $ty:ident) => { From f8a4e3fd70de62b2844883710f8ff22eb77c50aa Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 4 Feb 2017 01:39:19 +0100 Subject: [PATCH 10/11] Panic when invalid overflow value is returned --- src/int/mul.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/int/mul.rs b/src/int/mul.rs index c2aece0..1ccaf04 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -102,7 +102,7 @@ mod tests { let mut overflow = 2; let r = f(a, b, &mut overflow); if overflow != 0 && overflow != 1 { - return None + panic!("Invalid value {} for overflow", overflow); } Some((r, overflow)) } @@ -114,7 +114,7 @@ mod tests { let mut overflow = 2; let r = f(a, b, &mut overflow); if overflow != 0 && overflow != 1 { - return None + panic!("Invalid value {} for overflow", overflow); } Some((r, overflow)) } @@ -138,7 +138,7 @@ mod tests_i128 { let mut overflow = 2; let r = f(a, b, &mut overflow); if overflow != 0 && overflow != 1 { - return None + panic!("Invalid value {} for overflow", overflow); } Some((r, overflow)) } From 37d349015ea391f7afb078cfcdd5740248e73260 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 4 Feb 2017 12:17:04 +0100 Subject: [PATCH 11/11] Disable quickcheck tests on mips Two reasons: * the C versions __divti3 and __modti3 are apparently broken, at least when used in quickcheck. They change their own arguments. * compiler_rt's support for mips is disabled already on clang [1]. Its desireable to support working "cargo test" on that compiler as well, and not greet the tester with linker errors. [1]: http://llvm.org/viewvc/llvm-project?view=revision&revision=224488 --- compiler-rt/compiler-rt-cdylib/src/lib.rs | 5 ++++- src/int/mul.rs | 5 ++++- src/int/sdiv.rs | 7 +++++-- src/int/shift.rs | 5 ++++- src/int/udiv.rs | 5 ++++- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler-rt/compiler-rt-cdylib/src/lib.rs b/compiler-rt/compiler-rt-cdylib/src/lib.rs index 268da50..a724614 100644 --- a/compiler-rt/compiler-rt-cdylib/src/lib.rs +++ b/compiler-rt/compiler-rt-cdylib/src/lib.rs @@ -58,7 +58,10 @@ declare!(___adddf3, __adddf3); declare!(___powisf2, __powisf2); declare!(___powidf2, __powidf2); -#[cfg(all(not(windows), target_pointer_width="64"))] +#[cfg(all(not(windows), + not(target_arch = "mips64"), + not(target_arch = "mips64el"), + target_pointer_width="64"))] pub mod int_128 { extern { fn __lshrti3(); diff --git a/src/int/mul.rs b/src/int/mul.rs index 1ccaf04..27709e5 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -122,7 +122,10 @@ mod tests { } #[cfg(test)] -#[cfg(all(not(windows), target_pointer_width="64"))] +#[cfg(all(not(windows), + not(target_arch = "mips64"), + not(target_arch = "mips64el"), + target_pointer_width="64"))] mod tests_i128 { use qc::I128; diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index 97e1939..023fad4 100644 --- a/src/int/sdiv.rs +++ b/src/int/sdiv.rs @@ -164,11 +164,14 @@ mod tests { } #[cfg(test)] -#[cfg(all(not(windows), target_pointer_width="64"))] +#[cfg(all(not(windows), + not(target_arch = "mips64"), + not(target_arch = "mips64el"), + target_pointer_width="64"))] mod tests_i128 { use qc::U128; - check! { + fn __divti3(f: extern fn(i128, i128) -> i128, n: U128, d: U128) -> Option { let (n, d) = (n.0 as i128, d.0 as i128); if d == 0 { diff --git a/src/int/shift.rs b/src/int/shift.rs index 2993d92..e5dc38f 100644 --- a/src/int/shift.rs +++ b/src/int/shift.rs @@ -105,7 +105,10 @@ mod tests { } #[cfg(test)] -#[cfg(all(not(windows), target_pointer_width="64"))] +#[cfg(all(not(windows), + not(target_arch = "mips64"), + not(target_arch = "mips64el"), + target_pointer_width="64"))] mod tests_i128 { use qc::{I128, U128}; diff --git a/src/int/udiv.rs b/src/int/udiv.rs index 735d388..57d5fe5 100644 --- a/src/int/udiv.rs +++ b/src/int/udiv.rs @@ -382,7 +382,10 @@ mod tests { } #[cfg(test)] -#[cfg(all(not(windows), target_pointer_width="64"))] +#[cfg(all(not(windows), + not(target_arch = "mips64"), + not(target_arch = "mips64el"), + target_pointer_width="64"))] mod tests_i128 { use qc::U128;