Merge pull request #36 from japaric/qc
quickcheck: better generation of input arguments
This commit is contained in:
commit
1f3bad04bc
@ -1,8 +1,8 @@
|
||||
#![allow(unused_features)]
|
||||
#![no_std]
|
||||
#![feature(asm)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(naked_functions)]
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
// TODO(rust-lang/rust#35021) uncomment when that PR lands
|
||||
// #![feature(rustc_builtins)]
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
#[macro_use]
|
||||
extern crate quickcheck;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate core;
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
pub mod arm;
|
||||
|
||||
@ -20,6 +23,9 @@ pub mod udiv;
|
||||
pub mod mul;
|
||||
pub mod shift;
|
||||
|
||||
#[cfg(test)]
|
||||
mod qc;
|
||||
|
||||
/// Trait for some basic operations on integers
|
||||
trait Int {
|
||||
fn bits() -> u32;
|
||||
|
13
src/mul.rs
13
src/mul.rs
@ -19,7 +19,7 @@ macro_rules! mul {
|
||||
low += (t & lower_mask) << half_bits;
|
||||
high += t >> half_bits;
|
||||
high += (a.low() >> half_bits) * (b.low() >> half_bits);
|
||||
high += a.high().wrapping_mul(b.low()) + a.low().wrapping_mul(b.high());
|
||||
high = high.wrapping_add(a.high().wrapping_mul(b.low()).wrapping_add(a.low().wrapping_mul(b.high())));
|
||||
<$ty>::from_parts(low, high)
|
||||
}
|
||||
}
|
||||
@ -72,13 +72,17 @@ mulo!(__mulodi4: i64);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use qc::{I32, I64, U64};
|
||||
|
||||
quickcheck! {
|
||||
fn muldi(a: u64, b: u64) -> bool {
|
||||
fn muldi(a: U64, b: U64) -> bool {
|
||||
let (a, b) = (a.0, b.0);
|
||||
let r = super::__muldi4(a, b);
|
||||
r == a.wrapping_mul(b)
|
||||
}
|
||||
|
||||
fn mulosi(a: i32, b: i32) -> bool {
|
||||
fn mulosi(a: I32, b: I32) -> bool {
|
||||
let (a, b) = (a.0, b.0);
|
||||
let mut overflow = 2;
|
||||
let r = super::__mulosi4(a, b, &mut overflow);
|
||||
if overflow != 0 && overflow != 1 {
|
||||
@ -87,7 +91,8 @@ mod tests {
|
||||
(r, overflow != 0) == a.overflowing_mul(b)
|
||||
}
|
||||
|
||||
fn mulodi(a: i64, b: i64) -> bool {
|
||||
fn mulodi(a: I64, b: I64) -> bool {
|
||||
let (a, b) = (a.0, b.0);
|
||||
let mut overflow = 2;
|
||||
let r = super::__mulodi4(a, b, &mut overflow);
|
||||
if overflow != 0 && overflow != 1 {
|
||||
|
123
src/qc.rs
Normal file
123
src/qc.rs
Normal file
@ -0,0 +1,123 @@
|
||||
// When testing functions, QuickCheck (QC) uses small values for integer (`u*`/`i*`) arguments
|
||||
// (~ `[-100, 100]`), but these values don't stress all the code paths in our intrinsics. Here we
|
||||
// create newtypes over the primitive integer types with the goal of having full control over the
|
||||
// random values that will be used to test our intrinsics.
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
use LargeInt;
|
||||
|
||||
// Generates values in the full range of the integer type
|
||||
macro_rules! arbitrary {
|
||||
($TY:ident : $ty:ident) => {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct $TY(pub $ty);
|
||||
|
||||
impl Arbitrary for $TY {
|
||||
fn arbitrary<G>(g: &mut G) -> $TY
|
||||
where G: Gen
|
||||
{
|
||||
$TY(g.gen())
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
|
||||
struct Shrinker {
|
||||
x: $ty,
|
||||
}
|
||||
|
||||
impl Iterator for Shrinker {
|
||||
type Item = $TY;
|
||||
|
||||
fn next(&mut self) -> Option<$TY> {
|
||||
self.x /= 2;
|
||||
if self.x == 0 {
|
||||
None
|
||||
} else {
|
||||
Some($TY(self.x))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.0 == 0 {
|
||||
::quickcheck::empty_shrinker()
|
||||
} else {
|
||||
Box::new(Shrinker { x: self.0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for $TY {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arbitrary!(I32: i32);
|
||||
arbitrary!(U32: u32);
|
||||
|
||||
// These integers are "too large". If we generate e.g. `u64` values in the full range then there's
|
||||
// only `1 / 2^32` chance of seeing a value smaller than `2^32` (i.e. whose higher "word" (32-bits)
|
||||
// is `0`)! But this is an important group of values to tests because we have special code paths for
|
||||
// them. Instead we'll generate e.g. `u64` integers this way: uniformly pick between (a) setting the
|
||||
// low word to 0 and generating a random high word, (b) vice versa: high word to 0 and random low
|
||||
// word or (c) generate both words randomly. This let's cover better the code paths in our
|
||||
// intrinsics.
|
||||
macro_rules! arbitrary_large {
|
||||
($TY:ident : $ty:ident) => {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct $TY(pub $ty);
|
||||
|
||||
impl Arbitrary for $TY {
|
||||
fn arbitrary<G>(g: &mut G) -> $TY
|
||||
where G: Gen
|
||||
{
|
||||
if g.gen() {
|
||||
$TY($ty::from_parts(g.gen(), g.gen()))
|
||||
} else if g.gen() {
|
||||
$TY($ty::from_parts(0, g.gen()))
|
||||
} else {
|
||||
$TY($ty::from_parts(g.gen(), 0))
|
||||
}
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
|
||||
struct Shrinker {
|
||||
x: $ty,
|
||||
}
|
||||
|
||||
impl Iterator for Shrinker {
|
||||
type Item = $TY;
|
||||
|
||||
fn next(&mut self) -> Option<$TY> {
|
||||
self.x /= 2;
|
||||
if self.x == 0 {
|
||||
None
|
||||
} else {
|
||||
Some($TY(self.x))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.0 == 0 {
|
||||
::quickcheck::empty_shrinker()
|
||||
} else {
|
||||
Box::new(Shrinker { x: self.0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for $TY {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arbitrary_large!(I64: i64);
|
||||
arbitrary_large!(U64: u64);
|
11
src/shift.rs
11
src/shift.rs
@ -61,9 +61,12 @@ lshr!(__lshrdi3: u64);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use quickcheck::TestResult;
|
||||
use qc::{I64, U64};
|
||||
|
||||
// NOTE We purposefully stick to `u32` for `b` here because we want "small" values (b < 64)
|
||||
quickcheck! {
|
||||
fn ashldi(a: u64, b: u32) -> TestResult {
|
||||
fn ashldi(a: U64, b: u32) -> TestResult {
|
||||
let a = a.0;
|
||||
if b >= 64 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -72,7 +75,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn ashrdi(a: i64, b: u32) -> TestResult {
|
||||
fn ashrdi(a: I64, b: u32) -> TestResult {
|
||||
let a = a.0;
|
||||
if b >= 64 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -81,7 +85,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn lshrdi(a: u64, b: u32) -> TestResult {
|
||||
fn lshrdi(a: U64, b: u32) -> TestResult {
|
||||
let a = a.0;
|
||||
if b >= 64 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
|
21
src/udiv.rs
21
src/udiv.rs
@ -186,8 +186,8 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
|
||||
if sr > u32::bits() - 1 {
|
||||
if let Some(rem) = rem {
|
||||
*rem = n;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sr += 1;
|
||||
@ -229,9 +229,11 @@ 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};
|
||||
|
||||
quickcheck!{
|
||||
fn udivdi3(n: u64, d: u64) -> TestResult {
|
||||
fn udivdi3(n: U64, d: U64) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -240,7 +242,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn umoddi3(n: u64, d: u64) -> TestResult {
|
||||
fn umoddi3(n: U64, d: U64) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -249,7 +252,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn udivmoddi4(n: u64, d: u64) -> TestResult {
|
||||
fn udivmoddi4(n: U64, d: U64) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -259,7 +263,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn udivsi3(n: u32, d: u32) -> TestResult {
|
||||
fn udivsi3(n: U32, d: U32) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -268,7 +273,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn umodsi3(n: u32, d: u32) -> TestResult {
|
||||
fn umodsi3(n: U32, d: U32) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
@ -277,7 +283,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn udivmodsi4(n: u32, d: u32) -> TestResult {
|
||||
fn udivmodsi4(n: U32, d: U32) -> TestResult {
|
||||
let (n, d) = (n.0, d.0);
|
||||
if d == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user