Skip to content

Commit 1f3bad0

Browse files
author
Jorge Aparicio
authored
Merge pull request #36 from japaric/qc
quickcheck: better generation of input arguments
2 parents f300480 + 45aec94 commit 1f3bad0

File tree

5 files changed

+161
-15
lines changed

5 files changed

+161
-15
lines changed

src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#![allow(unused_features)]
2-
#![no_std]
32
#![feature(asm)]
43
#![feature(core_intrinsics)]
54
#![feature(naked_functions)]
5+
#![cfg_attr(not(test), no_std)]
66
// TODO(rust-lang/rust#35021) uncomment when that PR lands
77
// #![feature(rustc_builtins)]
88

@@ -13,13 +13,19 @@
1313
#[macro_use]
1414
extern crate quickcheck;
1515

16+
#[cfg(test)]
17+
extern crate core;
18+
1619
#[cfg(target_arch = "arm")]
1720
pub mod arm;
1821

1922
pub mod udiv;
2023
pub mod mul;
2124
pub mod shift;
2225

26+
#[cfg(test)]
27+
mod qc;
28+
2329
/// Trait for some basic operations on integers
2430
trait Int {
2531
fn bits() -> u32;

src/mul.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ macro_rules! mul {
1919
low += (t & lower_mask) << half_bits;
2020
high += t >> half_bits;
2121
high += (a.low() >> half_bits) * (b.low() >> half_bits);
22-
high += a.high().wrapping_mul(b.low()) + a.low().wrapping_mul(b.high());
22+
high = high.wrapping_add(a.high().wrapping_mul(b.low()).wrapping_add(a.low().wrapping_mul(b.high())));
2323
<$ty>::from_parts(low, high)
2424
}
2525
}
@@ -72,13 +72,17 @@ mulo!(__mulodi4: i64);
7272

7373
#[cfg(test)]
7474
mod tests {
75+
use qc::{I32, I64, U64};
76+
7577
quickcheck! {
76-
fn muldi(a: u64, b: u64) -> bool {
78+
fn muldi(a: U64, b: U64) -> bool {
79+
let (a, b) = (a.0, b.0);
7780
let r = super::__muldi4(a, b);
7881
r == a.wrapping_mul(b)
7982
}
8083

81-
fn mulosi(a: i32, b: i32) -> bool {
84+
fn mulosi(a: I32, b: I32) -> bool {
85+
let (a, b) = (a.0, b.0);
8286
let mut overflow = 2;
8387
let r = super::__mulosi4(a, b, &mut overflow);
8488
if overflow != 0 && overflow != 1 {
@@ -87,7 +91,8 @@ mod tests {
8791
(r, overflow != 0) == a.overflowing_mul(b)
8892
}
8993

90-
fn mulodi(a: i64, b: i64) -> bool {
94+
fn mulodi(a: I64, b: I64) -> bool {
95+
let (a, b) = (a.0, b.0);
9196
let mut overflow = 2;
9297
let r = super::__mulodi4(a, b, &mut overflow);
9398
if overflow != 0 && overflow != 1 {

src/qc.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// When testing functions, QuickCheck (QC) uses small values for integer (`u*`/`i*`) arguments
2+
// (~ `[-100, 100]`), but these values don't stress all the code paths in our intrinsics. Here we
3+
// create newtypes over the primitive integer types with the goal of having full control over the
4+
// random values that will be used to test our intrinsics.
5+
6+
use std::boxed::Box;
7+
use std::fmt;
8+
9+
use quickcheck::{Arbitrary, Gen};
10+
11+
use LargeInt;
12+
13+
// Generates values in the full range of the integer type
14+
macro_rules! arbitrary {
15+
($TY:ident : $ty:ident) => {
16+
#[derive(Clone, Copy)]
17+
pub struct $TY(pub $ty);
18+
19+
impl Arbitrary for $TY {
20+
fn arbitrary<G>(g: &mut G) -> $TY
21+
where G: Gen
22+
{
23+
$TY(g.gen())
24+
}
25+
26+
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
27+
struct Shrinker {
28+
x: $ty,
29+
}
30+
31+
impl Iterator for Shrinker {
32+
type Item = $TY;
33+
34+
fn next(&mut self) -> Option<$TY> {
35+
self.x /= 2;
36+
if self.x == 0 {
37+
None
38+
} else {
39+
Some($TY(self.x))
40+
}
41+
}
42+
}
43+
44+
if self.0 == 0 {
45+
::quickcheck::empty_shrinker()
46+
} else {
47+
Box::new(Shrinker { x: self.0 })
48+
}
49+
}
50+
}
51+
52+
impl fmt::Debug for $TY {
53+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54+
fmt::Debug::fmt(&self.0, f)
55+
}
56+
}
57+
}
58+
}
59+
60+
arbitrary!(I32: i32);
61+
arbitrary!(U32: u32);
62+
63+
// These integers are "too large". If we generate e.g. `u64` values in the full range then there's
64+
// only `1 / 2^32` chance of seeing a value smaller than `2^32` (i.e. whose higher "word" (32-bits)
65+
// is `0`)! But this is an important group of values to tests because we have special code paths for
66+
// them. Instead we'll generate e.g. `u64` integers this way: uniformly pick between (a) setting the
67+
// low word to 0 and generating a random high word, (b) vice versa: high word to 0 and random low
68+
// word or (c) generate both words randomly. This let's cover better the code paths in our
69+
// intrinsics.
70+
macro_rules! arbitrary_large {
71+
($TY:ident : $ty:ident) => {
72+
#[derive(Clone, Copy)]
73+
pub struct $TY(pub $ty);
74+
75+
impl Arbitrary for $TY {
76+
fn arbitrary<G>(g: &mut G) -> $TY
77+
where G: Gen
78+
{
79+
if g.gen() {
80+
$TY($ty::from_parts(g.gen(), g.gen()))
81+
} else if g.gen() {
82+
$TY($ty::from_parts(0, g.gen()))
83+
} else {
84+
$TY($ty::from_parts(g.gen(), 0))
85+
}
86+
}
87+
88+
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
89+
struct Shrinker {
90+
x: $ty,
91+
}
92+
93+
impl Iterator for Shrinker {
94+
type Item = $TY;
95+
96+
fn next(&mut self) -> Option<$TY> {
97+
self.x /= 2;
98+
if self.x == 0 {
99+
None
100+
} else {
101+
Some($TY(self.x))
102+
}
103+
}
104+
}
105+
106+
if self.0 == 0 {
107+
::quickcheck::empty_shrinker()
108+
} else {
109+
Box::new(Shrinker { x: self.0 })
110+
}
111+
}
112+
}
113+
114+
impl fmt::Debug for $TY {
115+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116+
fmt::Debug::fmt(&self.0, f)
117+
}
118+
}
119+
}
120+
}
121+
122+
arbitrary_large!(I64: i64);
123+
arbitrary_large!(U64: u64);

src/shift.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ lshr!(__lshrdi3: u64);
6161
#[cfg(test)]
6262
mod tests {
6363
use quickcheck::TestResult;
64+
use qc::{I64, U64};
6465

66+
// NOTE We purposefully stick to `u32` for `b` here because we want "small" values (b < 64)
6567
quickcheck! {
66-
fn ashldi(a: u64, b: u32) -> TestResult {
68+
fn ashldi(a: U64, b: u32) -> TestResult {
69+
let a = a.0;
6770
if b >= 64 {
6871
TestResult::discard()
6972
} else {
@@ -72,7 +75,8 @@ mod tests {
7275
}
7376
}
7477

75-
fn ashrdi(a: i64, b: u32) -> TestResult {
78+
fn ashrdi(a: I64, b: u32) -> TestResult {
79+
let a = a.0;
7680
if b >= 64 {
7781
TestResult::discard()
7882
} else {
@@ -81,7 +85,8 @@ mod tests {
8185
}
8286
}
8387

84-
fn lshrdi(a: u64, b: u32) -> TestResult {
88+
fn lshrdi(a: U64, b: u32) -> TestResult {
89+
let a = a.0;
8590
if b >= 64 {
8691
TestResult::discard()
8792
} else {

src/udiv.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
186186
if sr > u32::bits() - 1 {
187187
if let Some(rem) = rem {
188188
*rem = n;
189-
return 0;
190189
}
190+
return 0;
191191
}
192192

193193
sr += 1;
@@ -229,9 +229,11 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
229229
#[cfg(test)]
230230
mod tests {
231231
use quickcheck::TestResult;
232+
use qc::{U32, U64};
232233

233234
quickcheck!{
234-
fn udivdi3(n: u64, d: u64) -> TestResult {
235+
fn udivdi3(n: U64, d: U64) -> TestResult {
236+
let (n, d) = (n.0, d.0);
235237
if d == 0 {
236238
TestResult::discard()
237239
} else {
@@ -240,7 +242,8 @@ mod tests {
240242
}
241243
}
242244

243-
fn umoddi3(n: u64, d: u64) -> TestResult {
245+
fn umoddi3(n: U64, d: U64) -> TestResult {
246+
let (n, d) = (n.0, d.0);
244247
if d == 0 {
245248
TestResult::discard()
246249
} else {
@@ -249,7 +252,8 @@ mod tests {
249252
}
250253
}
251254

252-
fn udivmoddi4(n: u64, d: u64) -> TestResult {
255+
fn udivmoddi4(n: U64, d: U64) -> TestResult {
256+
let (n, d) = (n.0, d.0);
253257
if d == 0 {
254258
TestResult::discard()
255259
} else {
@@ -259,7 +263,8 @@ mod tests {
259263
}
260264
}
261265

262-
fn udivsi3(n: u32, d: u32) -> TestResult {
266+
fn udivsi3(n: U32, d: U32) -> TestResult {
267+
let (n, d) = (n.0, d.0);
263268
if d == 0 {
264269
TestResult::discard()
265270
} else {
@@ -268,7 +273,8 @@ mod tests {
268273
}
269274
}
270275

271-
fn umodsi3(n: u32, d: u32) -> TestResult {
276+
fn umodsi3(n: U32, d: U32) -> TestResult {
277+
let (n, d) = (n.0, d.0);
272278
if d == 0 {
273279
TestResult::discard()
274280
} else {
@@ -277,7 +283,8 @@ mod tests {
277283
}
278284
}
279285

280-
fn udivmodsi4(n: u32, d: u32) -> TestResult {
286+
fn udivmodsi4(n: U32, d: U32) -> TestResult {
287+
let (n, d) = (n.0, d.0);
281288
if d == 0 {
282289
TestResult::discard()
283290
} else {

0 commit comments

Comments
 (0)