Auto merge of #67 - japaric:gcc_s, r=japaric

test our implementations against gcc_s

if it exposes the same intrinsics that we implement -- gcc_s doesn't
implement all the intrinsics for all the architectures.

closes #65

r? @Amanieu
Tested on Linux x86_64 and against the x86_64 and the arm-gnueabi targets. Unclear whether this works on osx or windows.
master
homunkulus 2016-09-22 22:01:46 +00:00
commit b46719ee42
10 changed files with 292 additions and 27 deletions

View File

@ -1,13 +1,23 @@
[package]
authors = ["Jorge Aparicio <japaricious@gmail.com>"]
build = "build.rs"
name = "rustc_builtins"
version = "0.1.0"
[dependencies]
rlibc = { git = "https://github.com/alexcrichton/rlibc", optional = true }
[dependencies.rlibc]
git = "https://github.com/alexcrichton/rlibc"
optional = true
[dev-dependencies]
quickcheck = "0.3.1"
rand = "0.3.14"
[dev-dependencies.gcc_s]
path = "gcc_s"
[features]
default = ["rlibc/weak"]
[workspace]

7
build.rs Normal file
View File

@ -0,0 +1,7 @@
use std::env;
fn main() {
if env::var("TARGET").unwrap().ends_with("gnueabihf") {
println!("cargo:rustc-cfg=gnueabihf")
}
}

7
gcc_s/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
authors = ["Jorge Aparicio <japaricious@gmail.com>"]
name = "gcc_s"
version = "0.1.0"
[dependencies]
libloading = "0.3.0"

68
gcc_s/src/lib.rs Normal file
View File

@ -0,0 +1,68 @@
#![feature(drop_types_in_const)]
extern crate libloading;
use std::sync::{Once, ONCE_INIT};
use libloading::Library;
static mut GCC_S: Option<Library> = None;
#[cfg(not(windows))]
fn gcc_s() -> &'static Library {
#[cfg(not(target_os = "macos"))]
const LIBGCC_S: &'static str = "libgcc_s.so.1";
#[cfg(target_os = "macos")]
const LIBGCC_S: &'static str = "libgcc_s.1.dylib";
unsafe {
static INIT: Once = ONCE_INIT;
INIT.call_once(|| {
GCC_S = Some(Library::new(LIBGCC_S).unwrap());
});
GCC_S.as_ref().unwrap()
}
}
#[cfg(windows)]
macro_rules! declare {
($symbol:ident: fn($($i:ty),+) -> $o:ty) => {
pub fn $symbol() -> Option<unsafe extern fn($($i),+) -> $o> {
None
}
}
}
#[cfg(not(windows))]
macro_rules! declare {
($symbol:ident: fn($($i:ty),+) -> $o:ty) => {
pub fn $symbol() -> Option<unsafe extern fn($($i),+) -> $o> {
unsafe {
gcc_s().get(concat!("__", stringify!($symbol)).as_bytes()).ok().map(|s| *s)
}
}
}
}
declare!(ashldi3: fn(u64, u32) -> u64);
declare!(ashrdi3: fn(i64, u32) -> i64);
declare!(divdi3: fn(i64, i64) -> i64);
declare!(divmoddi4: fn(i64, i64, &mut i64) -> i64);
declare!(divmodsi4: fn(i32, i32, &mut i32) -> i32);
declare!(divsi3: fn(i32, i32) -> i32);
declare!(lshrdi3: fn(u64, u32) -> u64);
declare!(moddi3: fn(i64, i64) -> i64);
declare!(modsi3: fn(i32, i32) -> i32);
declare!(muldi3: fn(u64, u64) -> u64);
declare!(mulodi4: fn(i64, i64, &mut i32) -> i64);
declare!(mulosi4: fn(i32, i32, &mut i32) -> i32);
declare!(udivdi3: fn(u64, u64) -> u64);
declare!(udivmoddi4: fn(u64, u64, Option<&mut u64>) -> u64);
declare!(udivmodsi4: fn(u32, u32, Option<&mut u32>) -> u32);
declare!(udivsi3: fn(u32, u32) -> u32);
declare!(umoddi3: fn(u64, u64) -> u64);
declare!(umodsi3: fn(u32, u32) -> u32);
declare!(addsf3: fn(f32, f32) -> f32);
declare!(adddf3: fn(f64, f64) -> f64);

View File

@ -199,8 +199,12 @@ pub extern fn __aeabi_fadd(a: f32, b: f32) -> f32 {
#[cfg(test)]
mod tests {
use core::{f32, f64};
use qc::{U32, U64};
use float::Float;
use qc::{U32, U64};
use gcc_s;
use rand;
// NOTE The tests below have special handing for NaN values.
// Because NaN != NaN, the floating-point representations must be used
@ -213,15 +217,30 @@ mod tests {
fn addsf3(a: U32, b: U32) -> bool {
let (a, b) = (f32::from_repr(a.0), f32::from_repr(b.0));
let x = super::__addsf3(a, b);
let y = a + b;
x.eq_repr(y)
match gcc_s::addsf3() {
// NOTE(cfg) for some reason, on hard float targets, our implementation doesn't
// match the output of its gcc_s counterpart. Until we investigate further, we'll
// just avoid testing against gcc_s on those targets. Do note that our
// implementation matches the output of the FPU instruction on *hard* float targets
// and matches its gcc_s counterpart on *soft* float targets.
#[cfg(not(gnueabihf))]
Some(addsf3) if rand::random() => x.eq_repr(unsafe { addsf3(a, b) }),
_ => x.eq_repr(a + b),
}
}
fn adddf3(a: U64, b: U64) -> bool {
let (a, b) = (f64::from_repr(a.0), f64::from_repr(b.0));
let x = super::__adddf3(a, b);
let y = a + b;
x.eq_repr(y)
match gcc_s::adddf3() {
// NOTE(cfg) See NOTE above
#[cfg(not(gnueabihf))]
Some(adddf3) if rand::random() => x.eq_repr(unsafe { adddf3(a, b) }),
_ => x.eq_repr(a + b),
}
}
}

View File

@ -74,11 +74,18 @@ mulo!(__mulodi4: i64);
mod tests {
use qc::{I32, I64, U64};
use gcc_s;
use rand;
quickcheck! {
fn muldi(a: U64, b: U64) -> bool {
let (a, b) = (a.0, b.0);
let r = super::__muldi3(a, b);
r == a.wrapping_mul(b)
match gcc_s::muldi3() {
Some(muldi3) if rand::random() => r == unsafe { muldi3(a, b) },
_ => r == a.wrapping_mul(b),
}
}
fn mulosi(a: I32, b: I32) -> bool {
@ -88,7 +95,18 @@ mod tests {
if overflow != 0 && overflow != 1 {
return false;
}
(r, overflow != 0) == a.overflowing_mul(b)
match gcc_s::mulosi4() {
Some(mulosi4) if rand::random() => {
let mut gcc_s_overflow = 2;
let gcc_s_r = unsafe {
mulosi4(a, b, &mut gcc_s_overflow)
};
(r, overflow) == (gcc_s_r, gcc_s_overflow)
},
_ => (r, overflow != 0) == a.overflowing_mul(b),
}
}
fn mulodi(a: I64, b: I64) -> bool {
@ -98,7 +116,18 @@ mod tests {
if overflow != 0 && overflow != 1 {
return false;
}
(r, overflow != 0) == a.overflowing_mul(b)
match gcc_s::mulodi4() {
Some(mulodi4) if rand::random() => {
let mut gcc_s_overflow = 2;
let gcc_s_r = unsafe {
mulodi4(a, b, &mut gcc_s_overflow)
};
(r, overflow) == (gcc_s_r, gcc_s_overflow)
},
_ => (r, overflow != 0) == a.overflowing_mul(b),
}
}
}
}

View File

@ -52,9 +52,12 @@ divmod!(__divmoddi4, __divdi3: i64);
#[cfg(test)]
mod tests {
use quickcheck::TestResult;
use qc::{U32, U64};
use gcc_s;
use quickcheck::TestResult;
use rand;
quickcheck!{
fn divdi3(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0 as i64, d.0 as i64);
@ -62,7 +65,13 @@ mod tests {
TestResult::discard()
} else {
let q = super::__divdi3(n, d);
TestResult::from_bool(q == n / d)
match gcc_s::divdi3() {
Some(divdi3) if rand::random() => {
TestResult::from_bool(q == unsafe { divdi3(n, d) })
},
_ => TestResult::from_bool(q == n / d),
}
}
}
@ -72,7 +81,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__moddi3(n, d);
TestResult::from_bool(r == n % d)
match gcc_s::moddi3() {
Some(moddi3) if rand::random() => {
TestResult::from_bool(r == unsafe { moddi3(n, d) })
},
_ => TestResult::from_bool(r == n % d),
}
}
}
@ -83,7 +98,18 @@ mod tests {
} else {
let mut r = 0;
let q = super::__divmoddi4(n, d, &mut r);
TestResult::from_bool(q == n / d && r == n % d)
match gcc_s::divmoddi4() {
Some(divmoddi4) if rand::random() => {
let mut gcc_s_r = 0;
let gcc_s_q = unsafe {
divmoddi4(n, d, &mut gcc_s_r)
};
TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
},
_ => TestResult::from_bool(q == n / d && r == n % d),
}
}
}
@ -93,7 +119,13 @@ mod tests {
TestResult::discard()
} else {
let q = super::__divsi3(n, d);
TestResult::from_bool(q == n / d)
match gcc_s::divsi3() {
Some(divsi3) if rand::random() => {
TestResult::from_bool(q == unsafe { divsi3(n, d)})
},
_ => TestResult::from_bool(q == n / d),
}
}
}
@ -103,7 +135,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__modsi3(n, d);
TestResult::from_bool(r == n % d)
match gcc_s::modsi3() {
Some(modsi3) if rand::random() => {
TestResult::from_bool(r == unsafe { modsi3(n, d) })
},
_ => TestResult::from_bool(r == n % d),
}
}
}
@ -114,7 +152,18 @@ mod tests {
} else {
let mut r = 0;
let q = super::__divmodsi4(n, d, &mut r);
TestResult::from_bool(q == n / d && r == n % d)
match gcc_s::divmodsi4() {
Some(divmodsi4) if rand::random() => {
let mut gcc_s_r = 0;
let gcc_s_q = unsafe {
divmodsi4(n, d, &mut gcc_s_r)
};
TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
},
_ => TestResult::from_bool(q == n / d && r == n % d),
}
}
}
}

View File

@ -60,9 +60,12 @@ lshr!(__lshrdi3: u64);
#[cfg(test)]
mod tests {
use quickcheck::TestResult;
use qc::{I64, U64};
use gcc_s;
use quickcheck::TestResult;
use rand;
// NOTE We purposefully stick to `u32` for `b` here because we want "small" values (b < 64)
quickcheck! {
fn ashldi(a: U64, b: u32) -> TestResult {
@ -71,7 +74,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__ashldi3(a, b);
TestResult::from_bool(r == a << b)
match gcc_s::ashldi3() {
Some(ashldi3) if rand::random() => {
TestResult::from_bool(r == unsafe { ashldi3(a, b) })
},
_ => TestResult::from_bool(r == a << b),
}
}
}
@ -81,7 +90,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__ashrdi3(a, b);
TestResult::from_bool(r == a >> b)
match gcc_s::ashrdi3() {
Some(ashrdi3) if rand::random() => {
TestResult::from_bool(r == unsafe { ashrdi3(a, b) })
},
_ => TestResult::from_bool(r == a >> b),
}
}
}
@ -91,7 +106,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__lshrdi3(a, b);
TestResult::from_bool(r == a >> b)
match gcc_s::lshrdi3() {
Some(lshrdi3) if rand::random() => {
TestResult::from_bool(r == unsafe { lshrdi3(a, b) })
},
_ => TestResult::from_bool(r == a >> b),
}
}
}
}

View File

@ -228,9 +228,12 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
#[cfg(test)]
mod tests {
use quickcheck::TestResult;
use qc::{U32, U64};
use gcc_s;
use quickcheck::TestResult;
use rand;
quickcheck!{
fn udivdi3(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0, d.0);
@ -238,7 +241,13 @@ mod tests {
TestResult::discard()
} else {
let q = super::__udivdi3(n, d);
TestResult::from_bool(q == n / d)
match gcc_s::udivdi3() {
Some(udivdi3) if rand::random() => {
TestResult::from_bool(q == unsafe { udivdi3(n, d) })
},
_ => TestResult::from_bool(q == n / d),
}
}
}
@ -248,7 +257,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__umoddi3(n, d);
TestResult::from_bool(r == n % d)
match gcc_s::umoddi3() {
Some(umoddi3) if rand::random() => {
TestResult::from_bool(r == unsafe { umoddi3(n, d) })
},
_ => TestResult::from_bool(r == n % d),
}
}
}
@ -259,7 +274,18 @@ mod tests {
} else {
let mut r = 0;
let q = super::__udivmoddi4(n, d, Some(&mut r));
TestResult::from_bool(q == n / d && r == n % d)
match gcc_s::udivmoddi4() {
Some(udivmoddi4) if rand::random() => {
let mut gcc_s_r = 0;
let gcc_s_q = unsafe {
udivmoddi4(n, d, Some(&mut gcc_s_r))
};
TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
},
_ => TestResult::from_bool(q == n / d && r == n % d),
}
}
}
@ -269,7 +295,13 @@ mod tests {
TestResult::discard()
} else {
let q = super::__udivsi3(n, d);
TestResult::from_bool(q == n / d)
match gcc_s::udivsi3() {
Some(udivsi3) if rand::random() => {
TestResult::from_bool(q == unsafe { udivsi3(n, d) })
},
_ => TestResult::from_bool(q == n / d),
}
}
}
@ -279,7 +311,13 @@ mod tests {
TestResult::discard()
} else {
let r = super::__umodsi3(n, d);
TestResult::from_bool(r == n % d)
match gcc_s::umodsi3() {
Some(umodsi3) if rand::random() => {
TestResult::from_bool(r == unsafe { umodsi3(n, d) })
},
_ => TestResult::from_bool(r == n % d),
}
}
}
@ -290,7 +328,18 @@ mod tests {
} else {
let mut r = 0;
let q = super::__udivmodsi4(n, d, Some(&mut r));
TestResult::from_bool(q == n / d && r == n % d)
match gcc_s::udivmodsi4() {
Some(udivmodsi4) if rand::random() => {
let mut gcc_s_r = 0;
let gcc_s_q = unsafe {
udivmodsi4(n, d, Some(&mut gcc_s_r))
};
TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
},
_ => TestResult::from_bool(q == n / d && r == n % d),
}
}
}
}

View File

@ -17,6 +17,12 @@ extern crate quickcheck;
#[cfg(test)]
extern crate core;
#[cfg(test)]
extern crate gcc_s;
#[cfg(test)]
extern crate rand;
#[cfg(all(not(windows), not(target_os = "macos")))]
extern crate rlibc;