Auto merge of #133 - est31:i128, r=japaric

i128 intrinsics

Adds i128 intrinsics.

Note that this PR doesn't do float intrinsics, due to the missing presence of macros, those are however still required in order for rustc to switch to this crate.
This commit is contained in:
bors 2017-02-04 13:22:51 +00:00
commit 9debde0ecc
12 changed files with 567 additions and 233 deletions

View File

@ -193,9 +193,9 @@ features = ["c"]
These builtins are needed to support 128-bit integers, which are in the process of being added to Rust. These builtins are needed to support 128-bit integers, which are in the process of being added to Rust.
- [ ] ashlti3.c - [x] ashlti3.c
- [ ] ashrti3.c - [x] ashrti3.c
- [ ] divti3.c - [x] divti3.c
- [ ] fixdfti.c - [ ] fixdfti.c
- [ ] fixsfti.c - [ ] fixsfti.c
- [ ] fixunsdfti.c - [ ] fixunsdfti.c
@ -204,13 +204,13 @@ These builtins are needed to support 128-bit integers, which are in the process
- [ ] floattisf.c - [ ] floattisf.c
- [ ] floatuntidf.c - [ ] floatuntidf.c
- [ ] floatuntisf.c - [ ] floatuntisf.c
- [ ] lshrti3.c - [x] lshrti3.c
- [ ] modti3.c - [x] modti3.c
- [ ] muloti4.c - [x] muloti4.c
- [ ] multi3.c - [x] multi3.c
- [ ] udivmodti4.c - [x] udivmodti4.c
- [ ] udivti3.c - [x] udivti3.c
- [ ] umodti3.c - [x] umodti3.c
## Unimplemented functions ## Unimplemented functions

View File

@ -150,7 +150,6 @@ fn main() {
"int_util.c", "int_util.c",
"muldc3.c", "muldc3.c",
"muldf3.c", "muldf3.c",
"muloti4.c",
"mulsc3.c", "mulsc3.c",
"mulsf3.c", "mulsf3.c",
"mulvdi3.c", "mulvdi3.c",
@ -179,13 +178,10 @@ fn main() {
sources.extend(&["absvti2.c", sources.extend(&["absvti2.c",
"addtf3.c", "addtf3.c",
"addvti3.c", "addvti3.c",
"ashlti3.c",
"ashrti3.c",
"clzti2.c", "clzti2.c",
"cmpti2.c", "cmpti2.c",
"ctzti2.c", "ctzti2.c",
"divtf3.c", "divtf3.c",
"divti3.c",
"ffsti2.c", "ffsti2.c",
"fixdfti.c", "fixdfti.c",
"fixsfti.c", "fixsfti.c",
@ -199,10 +195,7 @@ fn main() {
"floatuntidf.c", "floatuntidf.c",
"floatuntisf.c", "floatuntisf.c",
"floatuntixf.c", "floatuntixf.c",
"lshrti3.c",
"modti3.c",
"multf3.c", "multf3.c",
"multi3.c",
"mulvti3.c", "mulvti3.c",
"negti2.c", "negti2.c",
"negvti2.c", "negvti2.c",
@ -212,10 +205,7 @@ fn main() {
"subtf3.c", "subtf3.c",
"subvti3.c", "subvti3.c",
"trampoline_setup.c", "trampoline_setup.c",
"ucmpti2.c", "ucmpti2.c"]);
"udivmodti4.c",
"udivti3.c",
"umodti3.c"]);
} }
if target_vendor == "apple" { if target_vendor == "apple" {

View File

@ -60,6 +60,17 @@ fn main() {
"addsf3.c", "addsf3.c",
"powidf2.c", "powidf2.c",
"powisf2.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",
]); ]);
let builtins_dir = Path::new("compiler-rt/lib/builtins"); let builtins_dir = Path::new("compiler-rt/lib/builtins");

View File

@ -58,6 +58,36 @@ declare!(___adddf3, __adddf3);
declare!(___powisf2, __powisf2); declare!(___powisf2, __powisf2);
declare!(___powidf2, __powidf2); declare!(___powidf2, __powidf2);
#[cfg(all(not(windows),
not(target_arch = "mips64"),
not(target_arch = "mips64el"),
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"] #[lang = "eh_personality"]
fn eh_personality() {} fn eh_personality() {}
#[lang = "panic_fmt"] #[lang = "panic_fmt"]

View File

@ -12,6 +12,7 @@
#![feature(lang_items)] #![feature(lang_items)]
#![feature(libc)] #![feature(libc)]
#![feature(start)] #![feature(start)]
#![feature(i128_type)]
#![no_std] #![no_std]
#[cfg(not(thumb))] #[cfg(not(thumb))]
@ -300,6 +301,42 @@ mod intrinsics {
pub fn umoddi3(a: u64, b: u64) -> u64 { pub fn umoddi3(a: u64, b: u64) -> u64 {
a % b a % b
} }
pub fn muloti4(a: u128, b: u128) -> Option<u128> {
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")] #[cfg(feature = "c")]
@ -356,6 +393,15 @@ fn run() {
bb(powidf2(bb(2.), bb(3))); bb(powidf2(bb(2.), bb(3)));
bb(powisf2(bb(2.), bb(3))); bb(powisf2(bb(2.), bb(3)));
bb(umoddi3(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)))] #[cfg(all(feature = "c", not(thumb)))]

View File

@ -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 mul;
pub mod sdiv; pub mod sdiv;
@ -6,32 +17,33 @@ pub mod udiv;
/// Trait for some basic operations on integers /// Trait for some basic operations on integers
pub trait Int { pub trait Int {
/// Type with the same width but other signedness
type OtherSign;
/// Returns the bitwidth of the int type /// Returns the bitwidth of the int type
fn bits() -> u32; fn bits() -> u32;
} }
// TODO: Once i128/u128 support lands, we'll want to add impls for those as well macro_rules! int_impl {
impl Int for u32 { ($ity:ty, $sty:ty, $bits:expr) => {
fn bits() -> u32 { impl Int for $ity {
32 type OtherSign = $sty;
} fn bits() -> u32 {
} $bits
impl Int for i32 { }
fn bits() -> u32 { }
32 impl Int for $sty {
} type OtherSign = $ity;
} fn bits() -> u32 {
impl Int for u64 { $bits
fn bits() -> u32 { }
64 }
}
}
impl Int for i64 {
fn bits() -> u32 {
64
} }
} }
int_impl!(i32, u32, 32);
int_impl!(i64, u64, 64);
int_impl!(i128, u128, 128);
/// Trait to convert an integer to/from smaller parts /// Trait to convert an integer to/from smaller parts
pub trait LargeInt { pub trait LargeInt {
type LowHalf; type LowHalf;
@ -42,32 +54,26 @@ pub trait LargeInt {
fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self; 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 macro_rules! large_int {
impl LargeInt for u64 { ($ty:ty, $tylow:ty, $tyhigh:ty, $halfbits:expr) => {
type LowHalf = u32; impl LargeInt for $ty {
type HighHalf = u32; type LowHalf = $tylow;
type HighHalf = $tyhigh;
fn low(self) -> u32 { fn low(self) -> $tylow {
self as u32 self as $tylow
} }
fn high(self) -> u32 { fn high(self) -> $tyhigh {
(self >> 32) as u32 (self >> $halfbits) as $tyhigh
} }
fn from_parts(low: u32, high: u32) -> u64 { fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
low as u64 | ((high as u64) << 32) low as $ty | ((high as $ty) << $halfbits)
}
}
} }
} }
impl LargeInt for i64 {
type LowHalf = u32;
type HighHalf = i32;
fn low(self) -> u32 { large_int!(u64, u32, u32, 32);
self as u32 large_int!(i64, u32, i32, 32);
} large_int!(u128, u64, u64, 64);
fn high(self) -> i32 { large_int!(i128, u64, i64, 64);
(self >> 32) as i32
}
fn from_parts(low: u32, high: i32) -> i64 {
low as i64 | ((high as i64) << 32)
}
}

View File

@ -1,4 +1,3 @@
#[cfg(not(all(feature = "c", target_arch = "x86")))]
use int::LargeInt; use int::LargeInt;
use int::Int; use int::Int;
@ -14,14 +13,15 @@ macro_rules! mul {
low &= lower_mask; low &= lower_mask;
t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask); t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask);
low += (t & lower_mask) << half_bits; low += (t & lower_mask) << half_bits;
let mut high = t >> half_bits; let mut high = (t >> half_bits) as hty!($ty);
t = low >> half_bits; t = low >> half_bits;
low &= lower_mask; low &= lower_mask;
t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask); t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask);
low += (t & lower_mask) << half_bits; low += (t & lower_mask) << half_bits;
high += t >> half_bits; high += (t >> half_bits) as hty!($ty);
high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits); high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as hty!($ty);
high = high.wrapping_add(a.high().wrapping_mul(b.low()).wrapping_add(a.low().wrapping_mul(b.high()))); 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) <$ty>::from_parts(low, high)
} }
} }
@ -29,9 +29,13 @@ macro_rules! mul {
macro_rules! mulo { macro_rules! mulo {
($intrinsic:ident: $ty:ty) => { ($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 /// Returns `a * b` and sets `*overflow = 1` if `a * b` overflows
#[cfg_attr(not(test), no_mangle)] #[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; *overflow = 0;
let result = a.wrapping_mul(b); let result = a.wrapping_mul(b);
if a == <$ty>::min_value() { if a == <$ty>::min_value() {
@ -71,9 +75,16 @@ macro_rules! mulo {
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
mul!(__muldi3: u64); mul!(__muldi3: u64);
mul!(__multi3: i128);
mulo!(__mulosi4: i32); mulo!(__mulosi4: i32);
mulo!(__mulodi4: i64); 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)] #[cfg(test)]
mod tests { mod tests {
use qc::{I32, I64, U64}; use qc::{I32, I64, U64};
@ -91,7 +102,7 @@ mod tests {
let mut overflow = 2; let mut overflow = 2;
let r = f(a, b, &mut overflow); let r = f(a, b, &mut overflow);
if overflow != 0 && overflow != 1 { if overflow != 0 && overflow != 1 {
return None panic!("Invalid value {} for overflow", overflow);
} }
Some((r, overflow)) Some((r, overflow))
} }
@ -103,7 +114,34 @@ mod tests {
let mut overflow = 2; let mut overflow = 2;
let r = f(a, b, &mut overflow); let r = f(a, b, &mut overflow);
if overflow != 0 && overflow != 1 { if overflow != 0 && overflow != 1 {
return None panic!("Invalid value {} for overflow", overflow);
}
Some((r, overflow))
}
}
}
#[cfg(test)]
#[cfg(all(not(windows),
not(target_arch = "mips64"),
not(target_arch = "mips64el"),
target_pointer_width="64"))]
mod tests_i128 {
use qc::I128;
check! {
fn __multi3(f: extern fn(i128, i128) -> i128, a: I128, b: I128)
-> Option<i128> {
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 {
panic!("Invalid value {} for overflow", overflow);
} }
Some((r, overflow)) Some((r, overflow))
} }

View File

@ -2,9 +2,12 @@ use int::Int;
macro_rules! div { macro_rules! div {
($intrinsic:ident: $ty:ty, $uty:ty) => { ($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` /// Returns `a / b`
#[cfg_attr(not(test), no_mangle)] #[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_a = a >> (<$ty>::bits() - 1);
let s_b = b >> (<$ty>::bits() - 1); let s_b = b >> (<$ty>::bits() - 1);
let a = (a ^ s_a) - s_a; let a = (a ^ s_a) - s_a;
@ -12,23 +15,26 @@ macro_rules! div {
let s = s_a ^ s_b; let s = s_a ^ s_b;
let r = udiv!(a as $uty, b as $uty); let r = udiv!(a as $uty, b as $uty);
(r as $ty ^ s) - s ($conv)((r as $ty ^ s) - s)
} }
} }
} }
macro_rules! mod_ { macro_rules! mod_ {
($intrinsic:ident: $ty:ty, $uty:ty) => { ($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` /// Returns `a % b`
#[cfg_attr(not(test), no_mangle)] #[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 s = b >> (<$ty>::bits() - 1);
let b = (b ^ s) - s; let b = (b ^ s) - s;
let s = a >> (<$ty>::bits() - 1); let s = a >> (<$ty>::bits() - 1);
let a = (a ^ s) - s; let a = (a ^ s) - s;
let r = urem!(a as $uty, b as $uty); 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")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
div!(__divdi3: i64, u64); 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"))))] #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
mod_!(__modsi3: i32, u32); mod_!(__modsi3: i32, u32);
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
mod_!(__moddi3: i64, u64); 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"))))] #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
divmod!(__divmodsi4, __divsi3: i32); divmod!(__divmodsi4, __divsi3: i32);
@ -144,3 +162,32 @@ mod tests {
} }
} }
} }
#[cfg(test)]
#[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<i128> {
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<i128> {
let (n, d) = (n.0 as i128, d.0 as i128);
if d == 0 {
None
} else {
Some(f(n, d))
}
}
}
}

View File

@ -1,4 +1,3 @@
#[cfg(not(all(feature = "c", target_arch = "x86")))]
use int::{Int, LargeInt}; use int::{Int, LargeInt};
macro_rules! ashl { macro_rules! ashl {
@ -58,12 +57,18 @@ macro_rules! lshr {
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
ashl!(__ashldi3: u64); ashl!(__ashldi3: u64);
ashl!(__ashlti3: u128);
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
ashr!(__ashrdi3: i64); ashr!(__ashrdi3: i64);
ashr!(__ashrti3: i128);
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
lshr!(__lshrdi3: u64); lshr!(__lshrdi3: u64);
lshr!(__lshrti3: u128);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use qc::{I64, U64}; use qc::{I64, U64};
@ -98,3 +103,42 @@ mod tests {
} }
} }
} }
#[cfg(test)]
#[cfg(all(not(windows),
not(target_arch = "mips64"),
not(target_arch = "mips64el"),
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<u128> {
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<i128> {
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<u128> {
let a = a.0;
if b >= 128 {
None
} else {
Some(f(a, b))
}
}
}
}

View File

@ -96,172 +96,222 @@ pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
q q
} }
/// Returns `n / d` macro_rules! div_mod_intrinsics {
#[cfg_attr(not(test), no_mangle)] ($udiv_intr:ident, $umod_intr:ident : $ty:ty) => {
#[cfg(not(all(feature = "c", target_arch = "x86")))] div_mod_intrinsics!($udiv_intr, $umod_intr : $ty,
pub extern "C" fn __udivdi3(n: u64, d: u64) -> u64 { __udivmoddi4);
__udivmoddi4(n, d, None) };
($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(not(all(feature = "c", target_arch = "x86")))]
#[cfg_attr(not(test), no_mangle)] div_mod_intrinsics!(__udivdi3, __umoddi3: u64);
pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
use core::mem;
let mut rem = unsafe { mem::uninitialized() }; #[cfg(not(all(windows, target_pointer_width="64")))]
__udivmoddi4(a, b, Some(&mut rem)); div_mod_intrinsics!(__udivti3, __umodti3: u128, u128_div_mod);
rem
#[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) => {{
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 = <$ty>::from(urem!(n.low(), d.low()));
}
return <$ty>::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 = <$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 > <hty!($ty)>::bits() - 2 {
if let Some(rem) = rem {
*rem = n;
}
return 0;
}
sr += 1;
// 1 <= sr <= <hty!($ty)>::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 + <hty!($ty)>::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 > <hty!($ty)>::bits() - 1 {
if let Some(rem) = rem {
*rem = n;
}
return 0;
}
sr += 1;
// 1 <= sr <= <hty!($ty)>::bits()
q = n << (<$ty>::bits() - sr);
r = n >> sr;
}
// 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;
for _ in 0..sr {
// r:q = ((r:q) << 1) | carry
r = (r << 1) | (q >> (<$ty>::bits() - 1));
q = (q << 1) | carry as $ty;
// 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;
}
if let Some(rem) = rem {
*rem = r;
}
(q << 1) | carry as $ty
}}
} }
/// Returns `n / d` and sets `*rem = n % d` /// Returns `n / d` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)] #[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
// NOTE X is unknown, K != 0 udivmod_inner!(n, d, rem, u64)
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;
} else {
let sr = d.low().trailing_zeros();
return n >> sr;
};
}
sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros();
// 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;
}
return 0;
}
sr += 1;
// 1 <= sr <= u32::bits()
q = n << (u64::bits() - sr);
r = n >> sr;
}
// 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;
for _ in 0..sr {
// r:q = ((r:q) << 1) | carry
r = (r << 1) | (q >> (u64::bits() - 1));
q = (q << 1) | carry as u64;
// 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
} }
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)] #[cfg(test)]
mod tests { mod tests {
use qc::{U32, U64}; use qc::{U32, U64};
@ -330,3 +380,51 @@ mod tests {
} }
} }
} }
#[cfg(test)]
#[cfg(all(not(windows),
not(target_arch = "mips64"),
not(target_arch = "mips64el"),
target_pointer_width="64"))]
mod tests_i128 {
use qc::U128;
check! {
fn __udivti3(f: extern fn(u128, u128) -> u128,
n: U128,
d: U128) -> Option<u128> {
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<u128> {
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<u128> {
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))
}
}
}
}

View File

@ -13,6 +13,10 @@
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(i128_type)]
#![feature(repr_simd)]
#![feature(abi_unadjusted)]
#![allow(unused_features)]
#![no_builtins] #![no_builtins]
#![unstable(feature = "compiler_builtins_lib", #![unstable(feature = "compiler_builtins_lib",
reason = "Compiler builtins. Will never become stable.", reason = "Compiler builtins. Will never become stable.",
@ -85,6 +89,24 @@ 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(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(test)]
#[cfg_attr(target_arch = "arm", macro_use)] #[cfg_attr(target_arch = "arm", macro_use)]
extern crate quickcheck; extern crate quickcheck;

View File

@ -144,6 +144,8 @@ macro_rules! arbitrary_large {
arbitrary_large!(I64: i64); arbitrary_large!(I64: i64);
arbitrary_large!(U64: u64); arbitrary_large!(U64: u64);
arbitrary_large!(I128: i128);
arbitrary_large!(U128: u128);
macro_rules! arbitrary_float { macro_rules! arbitrary_float {
($TY:ident : $ty:ident) => { ($TY:ident : $ty:ident) => {