omit bounds check in release mode

this eliminates panicking branches in the optimized version of the functions. We keep the bounds
checks when running the test suite to check that we never do an out of bounds access.

This commit also adds a "must link" test that ensures that future changes in our implementation
won't add panicking branches.

closes #129
This commit is contained in:
Jorge Aparicio 2018-07-25 13:16:10 -05:00
parent 861720b0ed
commit ce02130e55
7 changed files with 233 additions and 52 deletions

View File

@ -9,5 +9,9 @@ name = "libm"
repository = "https://github.com/japaric/libm"
version = "0.1.2"
[features]
# only used to run our test suite
checked = []
[workspace]
members = ["cb", "test-generator"]

View File

@ -12,11 +12,21 @@ main() {
# check that we can source import libm into compiler-builtins
cargo check --package cb
# test that the functions don't contain invocations of `panic!`
case $TARGET in
armv7-unknown-linux-gnueabihf)
cross build --release --target $TARGET --example no-panic
;;
esac
# run unit tests
cross test --lib --features checked --target $TARGET --release
# generate tests
cargo run --package test-generator --target x86_64-unknown-linux-musl
# run tests
cross test --target $TARGET --release
# run generated tests
cross test --tests --features checked --target $TARGET --release
# TODO need to fix overflow issues (cf. issue #4)
# cross test --target $TARGET

115
examples/no-panic.rs Normal file
View File

@ -0,0 +1,115 @@
#![feature(lang_items)]
#![feature(panic_implementation)]
#![no_main]
#![no_std]
extern crate libm;
use core::panic::PanicInfo;
use core::ptr;
macro_rules! force_eval {
($e:expr) => {
unsafe {
core::ptr::read_volatile(&$e);
}
};
}
#[no_mangle]
pub fn main() {
force_eval!(libm::acos(random()));
force_eval!(libm::acosf(random()));
force_eval!(libm::asin(random()));
force_eval!(libm::asinf(random()));
force_eval!(libm::atan(random()));
force_eval!(libm::atan2(random(), random()));
force_eval!(libm::atan2f(random(), random()));
force_eval!(libm::atanf(random()));
force_eval!(libm::cbrt(random()));
force_eval!(libm::cbrtf(random()));
force_eval!(libm::ceil(random()));
force_eval!(libm::ceilf(random()));
force_eval!(libm::cos(random()));
force_eval!(libm::cosf(random()));
force_eval!(libm::cosh(random()));
force_eval!(libm::coshf(random()));
force_eval!(libm::exp(random()));
force_eval!(libm::exp2(random()));
force_eval!(libm::exp2f(random()));
force_eval!(libm::expf(random()));
force_eval!(libm::expm1(random()));
force_eval!(libm::expm1f(random()));
force_eval!(libm::fabs(random()));
force_eval!(libm::fabsf(random()));
force_eval!(libm::fdim(random(), random()));
force_eval!(libm::fdimf(random(), random()));
force_eval!(libm::floor(random()));
force_eval!(libm::floorf(random()));
force_eval!(libm::fma(random(), random(), random()));
force_eval!(libm::fmaf(random(), random(), random()));
force_eval!(libm::fmod(random(), random()));
force_eval!(libm::fmodf(random(), random()));
force_eval!(libm::hypot(random(), random()));
force_eval!(libm::hypotf(random(), random()));
force_eval!(libm::log(random()));
force_eval!(libm::log2(random()));
force_eval!(libm::log10(random()));
force_eval!(libm::log10f(random()));
force_eval!(libm::log1p(random()));
force_eval!(libm::log1pf(random()));
force_eval!(libm::log2f(random()));
force_eval!(libm::logf(random()));
force_eval!(libm::pow(random(), random()));
force_eval!(libm::powf(random(), random()));
force_eval!(libm::round(random()));
force_eval!(libm::roundf(random()));
force_eval!(libm::scalbn(random(), random()));
force_eval!(libm::scalbnf(random(), random()));
force_eval!(libm::sin(random()));
force_eval!(libm::sinf(random()));
force_eval!(libm::sinh(random()));
force_eval!(libm::sinhf(random()));
force_eval!(libm::sqrt(random()));
force_eval!(libm::sqrtf(random()));
force_eval!(libm::tan(random()));
force_eval!(libm::tanf(random()));
force_eval!(libm::tanh(random()));
force_eval!(libm::tanhf(random()));
force_eval!(libm::trunc(random()));
force_eval!(libm::truncf(random()));
}
fn random<T>() -> T
where
T: Copy,
{
unsafe {
static mut X: usize = 0;
X += 8;
ptr::read_volatile(X as *const T)
}
}
#[panic_implementation]
#[no_mangle]
pub fn panic(_info: &PanicInfo) -> ! {
// loop {}
extern "C" {
fn thou_shalt_not_panic() -> !;
}
unsafe { thou_shalt_not_panic() }
}
#[link(name = "c")]
extern "C" {}
#[lang = "eh_personality"]
fn eh() {}
#[no_mangle]
pub extern "C" fn __aeabi_unwind_cpp_pr0() {}
#[no_mangle]
pub extern "C" fn __aeabi_unwind_cpp_pr1() {}

View File

@ -124,7 +124,7 @@ pub fn atan(x: f64) -> f64 {
return x - x * (s1 + s2);
}
let z = ATANHI[id as usize] - (x * (s1 + s2) - ATANLO[id as usize] - x);
let z = i!(ATANHI, id as usize) - (x * (s1 + s2) - i!(ATANLO, id as usize) - x);
if sign != 0 {
-z

View File

@ -6,6 +6,50 @@ macro_rules! force_eval {
};
}
#[cfg(not(feature = "checked"))]
macro_rules! i {
($array:expr, $index:expr) => {
unsafe { *$array.get_unchecked($index) }
};
($array:expr, $index:expr, =, $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) = $rhs; }
};
($array:expr, $index:expr, +=, $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) += $rhs; }
};
($array:expr, $index:expr, -=, $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) -= $rhs; }
};
($array:expr, $index:expr, &=, $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) &= $rhs; }
};
($array:expr, $index:expr, ==, $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) == $rhs }
};
}
#[cfg(feature = "checked")]
macro_rules! i {
($array:expr, $index:expr) => {
*$array.get($index).unwrap()
};
($array:expr, $index:expr, =, $rhs:expr) => {
*$array.get_mut($index).unwrap() = $rhs;
};
($array:expr, $index:expr, -=, $rhs:expr) => {
*$array.get_mut($index).unwrap() -= $rhs;
};
($array:expr, $index:expr, +=, $rhs:expr) => {
*$array.get_mut($index).unwrap() += $rhs;
};
($array:expr, $index:expr, &=, $rhs:expr) => {
*$array.get_mut($index).unwrap() &= $rhs;
};
($array:expr, $index:expr, ==, $rhs:expr) => {
*$array.get_mut($index).unwrap() == $rhs
};
}
// Public modules
mod acos;
mod acosf;

View File

@ -177,7 +177,7 @@ pub fn rem_pio2(x: f64) -> (i32, f64, f64) {
tx[2] = z;
/* skip zero terms, first term is non-zero */
let mut i = 2;
while tx[i] == 0.0 {
while i != 0 && tx[i] == 0.0 {
i -= 1;
}
let mut ty = [0.0; 3];

View File

@ -1,3 +1,4 @@
#![allow(unused_unsafe)]
/* origin: FreeBSD /usr/src/lib/msun/src/k_rem_pio2.c */
/*
* ====================================================
@ -257,17 +258,21 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
let mut j = (jv - jx) as i32;
let m = jx + jk;
for i in 0..=m {
f[i] = if j < 0 { 0. } else { IPIO2[j as usize] as f64 };
j += 1
i!(f, i, =, if j < 0 {
0.
} else {
i!(IPIO2, j as usize) as f64
});
j += 1;
}
/* compute q[0],q[1],...q[jk] */
for i in 0..=jk {
fw = 0f64;
for j in 0..=jx {
fw += x[j] * f[jx + i - j];
fw += i!(x, j) * i!(f, jx + i - j);
}
q[i] = fw;
i!(q, i, =, fw);
}
let mut jz = jk;
@ -275,11 +280,11 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
'recompute: loop {
/* distill q[] into iq[] reversingly */
let mut i = 0i32;
z = q[jz];
z = i!(q, jz);
for j in (1..=jz).rev() {
fw = (x1p_24 * z) as i32 as f64;
iq[i as usize] = (z - x1p24 * fw) as i32;
z = q[j - 1] + fw;
i!(iq, i as usize, =, (z - x1p24 * fw) as i32);
z = i!(q, j - 1) + fw;
i += 1;
}
@ -291,12 +296,12 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
ih = 0;
if q0 > 0 {
/* need iq[jz-1] to determine n */
i = iq[jz - 1] >> (24 - q0);
i = i!(iq, jz - 1) >> (24 - q0);
n += i;
iq[jz - 1] -= i << (24 - q0);
ih = iq[jz - 1] >> (23 - q0);
i!(iq, jz - 1, -=, i << (24 - q0));
ih = i!(iq, jz - 1) >> (23 - q0);
} else if q0 == 0 {
ih = iq[jz - 1] >> 23;
ih = i!(iq, jz - 1) >> 23;
} else if z >= 0.5 {
ih = 2;
}
@ -307,24 +312,24 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
let mut carry = 0i32;
for i in 0..jz {
/* compute 1-q */
let j = iq[i];
let j = i!(iq, i);
if carry == 0 {
if j != 0 {
carry = 1;
iq[i] = 0x1000000 - j;
i!(iq, i, =, 0x1000000 - j);
}
} else {
iq[i] = 0xffffff - j;
i!(iq, i, =, 0xffffff - j);
}
}
if q0 > 0 {
/* rare case: chance is 1 in 12 */
match q0 {
1 => {
iq[jz - 1] &= 0x7fffff;
i!(iq, jz - 1, &=, 0x7fffff);
}
2 => {
iq[jz - 1] &= 0x3fffff;
i!(iq, jz - 1, &=, 0x3fffff);
}
_ => {}
}
@ -341,23 +346,23 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
if z == 0. {
let mut j = 0;
for i in (jk..=jz - 1).rev() {
j |= iq[i];
j |= i!(iq, i);
}
if j == 0 {
/* need recomputation */
let mut k = 1;
while iq[jk - k] == 0 {
while i!(iq, jk - k, ==, 0) {
k += 1; /* k = no. of terms needed */
}
for i in (jz + 1)..=(jz + k) {
/* add q[jz+1] to q[jz+k] */
f[jx + i] = IPIO2[jv + i] as f64;
i!(f, jx + i, =, i!(IPIO2, jv + i) as f64);
fw = 0f64;
for j in 0..=jx {
fw += x[j] * f[jx + i - j];
fw += i!(x, j) * i!(f, jx + i - j);
}
q[i] = fw;
i!(q, i, =, fw);
}
jz += k;
continue 'recompute;
@ -371,7 +376,7 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
if z == 0. {
jz -= 1;
q0 -= 24;
while iq[jz] == 0 {
while i!(iq, jz) == 0 {
jz -= 1;
q0 -= 24;
}
@ -380,19 +385,19 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
z = scalbn(z, -q0);
if z >= x1p24 {
fw = (x1p_24 * z) as i32 as f64;
iq[jz] = (z - x1p24 * fw) as i32;
i!(iq, jz, =, (z - x1p24 * fw) as i32);
jz += 1;
q0 += 24;
iq[jz] = fw as i32;
i!(iq, jz, =, fw as i32);
} else {
iq[jz] = z as i32;
i!(iq, jz, =, z as i32);
}
}
/* convert integer "bit" chunk to floating-point value */
fw = scalbn(1., q0);
for i in (0..=jz).rev() {
q[i] = fw * (iq[i] as f64);
i!(q, i, =, fw * (i!(iq, i) as f64));
fw *= x1p_24;
}
@ -401,10 +406,10 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
fw = 0f64;
let mut k = 0;
while (k <= jp) && (k <= jz - i) {
fw += PIO2[k] * q[i + k];
fw += i!(PIO2, k) * i!(q, i + k);
k += 1;
}
fq[jz - i] = fw;
i!(fq, jz - i, =, fw);
}
/* compress fq[] into y[] */
@ -412,51 +417,54 @@ pub fn rem_pio2_large(x: &[f64], y: &mut [f64], e0: i32, prec: usize) -> i32 {
0 => {
fw = 0f64;
for i in (0..=jz).rev() {
fw += fq[i];
fw += i!(fq, i);
}
y[0] = if ih == 0 { fw } else { -fw };
i!(y, 0, =, if ih == 0 { fw } else { -fw });
}
1 | 2 => {
fw = 0f64;
for i in (0..=jz).rev() {
fw += fq[i];
fw += i!(fq, i);
}
// TODO: drop excess precision here once double_t is used
fw = fw as f64;
y[0] = if ih == 0 { fw } else { -fw };
fw = fq[0] - fw;
i!(y, 0, =, if ih == 0 { fw } else { -fw });
fw = i!(fq, 0) - fw;
for i in 1..=jz {
fw += fq[i];
fw += i!(fq, i);
}
y[1] = if ih == 0 { fw } else { -fw };
i!(y, 1, =, if ih == 0 { fw } else { -fw });
}
3 => {
/* painful */
for i in (1..=jz).rev() {
fw = fq[i - 1] + fq[i];
fq[i] += fq[i - 1] - fw;
fq[i - 1] = fw;
fw = i!(fq, i - 1) + i!(fq, i);
i!(fq, i, +=, i!(fq, i - 1) - fw);
i!(fq, i - 1, =, fw);
}
for i in (2..=jz).rev() {
fw = fq[i - 1] + fq[i];
fq[i] += fq[i - 1] - fw;
fq[i - 1] = fw;
fw = i!(fq, i - 1) + i!(fq, i);
i!(fq, i, +=, i!(fq, i - 1) - fw);
i!(fq, i - 1, =, fw);
}
fw = 0f64;
for i in (2..=jz).rev() {
fw += fq[i];
fw += i!(fq, i);
}
if ih == 0 {
y[0] = fq[0];
y[1] = fq[1];
y[2] = fw;
i!(y, 0, =, i!(fq, 0));
i!(y, 1, =, i!(fq, 1));
i!(y, 2, =, fw);
} else {
y[0] = -fq[0];
y[1] = -fq[1];
y[2] = -fw;
i!(y, 0, =, -i!(fq, 0));
i!(y, 1, =, -i!(fq, 1));
i!(y, 2, =, -fw);
}
}
#[cfg(feature = "checked")]
_ => unreachable!(),
#[cfg(not(feature = "checked"))]
_ => {},
}
n & 7
}