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.
master
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) => {
impl Int for $ity {
type OtherSign = $sty;
fn bits() -> u32 { fn bits() -> u32 {
32 $bits
} }
} }
impl Int for i32 { impl Int for $sty {
fn bits() -> u32 { type OtherSign = $ity;
32 fn bits() -> u32 {
} $bits
} }
impl Int for u64 { }
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) -> $tyhigh {
(self >> $halfbits) as $tyhigh
}
fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
low as $ty | ((high as $ty) << $halfbits)
} }
fn high(self) -> u32 {
(self >> 32) as u32
} }
fn from_parts(low: u32, high: u32) -> u64 {
low as u64 | ((high as u64) << 32)
} }
} }
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,27 +96,48 @@ 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` /// Returns `n % d`
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg_attr(not(test), no_mangle)]
#[cfg_attr(not(test), no_mangle)] pub extern "C" fn $umod_intr(a: $ty, b: $ty) -> $tyret {
pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
use core::mem; use core::mem;
let mut rem = unsafe { mem::uninitialized() }; let mut rem = unsafe { mem::uninitialized() };
__udivmoddi4(a, b, Some(&mut rem)); $divmod_intr(a, b, Some(&mut rem));
rem ($conv)(rem)
}
}
} }
/// Returns `n / d` and sets `*rem = n % d` #[cfg(not(all(feature = "c", target_arch = "x86")))]
#[cfg_attr(not(test), no_mangle)] div_mod_intrinsics!(__udivdi3, __umoddi3: u64);
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
#[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) => {{
let (n, d, rem) = ($n, $d, $rem);
// NOTE X is unknown, K != 0 // NOTE X is unknown, K != 0
if n.high() == 0 { if n.high() == 0 {
if d.high() == 0 { if d.high() == 0 {
@ -125,9 +146,9 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
// 0 X // 0 X
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = u64::from(urem!(n.low(), d.low())); *rem = <$ty>::from(urem!(n.low(), d.low()));
} }
return u64::from(udiv!(n.low(), d.low())); return <$ty>::from(udiv!(n.low(), d.low()));
} else { } else {
// 0 X // 0 X
// --- // ---
@ -160,9 +181,9 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
// --- // ---
// K 0 // K 0
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = u64::from_parts(0, urem!(n.high(), d.high())); *rem = <$ty>::from_parts(0, urem!(n.high(), d.high()));
} }
return u64::from(udiv!(n.high(), d.high())); return <$ty>::from(udiv!(n.high(), d.high()));
} }
// K K // K K
@ -171,15 +192,15 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
if d.high().is_power_of_two() { if d.high().is_power_of_two() {
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = u64::from_parts(n.low(), n.high() & (d.high() - 1)); *rem = <$ty>::from_parts(n.low(), n.high() & (d.high() - 1));
} }
return u64::from(n.high() >> d.high().trailing_zeros()); return <$ty>::from(n.high() >> d.high().trailing_zeros());
} }
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());
// D > N // D > N
if sr > u32::bits() - 2 { if sr > <hty!($ty)>::bits() - 2 {
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = n; *rem = n;
} }
@ -188,8 +209,8 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
sr += 1; sr += 1;
// 1 <= sr <= u32::bits() - 1 // 1 <= sr <= <hty!($ty)>::bits() - 1
q = n << (u64::bits() - sr); q = n << (<$ty>::bits() - sr);
r = n >> sr; r = n >> sr;
} else if d.high() == 0 { } else if d.high() == 0 {
// K X // K X
@ -197,7 +218,7 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
// 0 K // 0 K
if d.low().is_power_of_two() { if d.low().is_power_of_two() {
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = u64::from(n.low() & (d.low() - 1)); *rem = <$ty>::from(n.low() & (d.low() - 1));
} }
if d.low() == 1 { if d.low() == 1 {
@ -208,10 +229,10 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
}; };
} }
sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros(); sr = 1 + <hty!($ty)>::bits() + d.low().leading_zeros() - n.high().leading_zeros();
// 2 <= sr <= u64::bits() - 1 // 2 <= sr <= u64::bits() - 1
q = n << (u64::bits() - sr); q = n << (<$ty>::bits() - sr);
r = n >> sr; r = n >> sr;
} else { } else {
// K X // K X
@ -220,7 +241,7 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());
// D > N // D > N
if sr > u32::bits() - 1 { if sr > <hty!($ty)>::bits() - 1 {
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = n; *rem = n;
} }
@ -229,8 +250,8 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
sr += 1; sr += 1;
// 1 <= sr <= u32::bits() // 1 <= sr <= <hty!($ty)>::bits()
q = n << (u64::bits() - sr); q = n << (<$ty>::bits() - sr);
r = n >> sr; r = n >> sr;
} }
@ -243,25 +264,54 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
for _ in 0..sr { for _ in 0..sr {
// r:q = ((r:q) << 1) | carry // r:q = ((r:q) << 1) | carry
r = (r << 1) | (q >> (u64::bits() - 1)); r = (r << 1) | (q >> (<$ty>::bits() - 1));
q = (q << 1) | carry as u64; q = (q << 1) | carry as $ty;
// carry = 0 // carry = 0
// if r >= d { // if r >= d {
// r -= d; // r -= d;
// carry = 1; // carry = 1;
// } // }
let s = (d.wrapping_sub(r).wrapping_sub(1)) as i64 >> (u64::bits() - 1); let s = (d.wrapping_sub(r).wrapping_sub(1)) as os_ty!($ty) >> (<$ty>::bits() - 1);
carry = (s & 1) as u32; carry = (s & 1) as hty!($ty);
r -= d & s as u64; r -= d & s as $ty;
} }
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = r; *rem = r;
} }
(q << 1) | carry as u64 (q << 1) | carry as $ty
}}
} }
/// 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)
}
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) => {