131: omit bounds check in release mode r=japaric a=japaric

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 rust-lang/libm#129

Co-authored-by: Jorge Aparicio <jorge@japaric.io>
This commit is contained in:
bors[bot] 2018-07-25 20:03:34 +00:00
commit adaeaa64d7
8 changed files with 243 additions and 52 deletions

View file

@ -17,6 +17,9 @@ matrix:
- env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=cargo-fmt
rust: beta
# no-panic link test
- env: TARGET=armv7-unknown-linux-gnueabihf
rust: nightly
before_install: set -e

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

@ -6,17 +6,26 @@ main() {
return
fi
# test that the functions don't contain invocations of `panic!`
if [ $TRAVIS_RUST_VERSION = nightly ]; then
cross build --release --target $TARGET --example no-panic
return
fi
# quick check
cargo check
# check that we can source import libm into compiler-builtins
cargo check --package cb
# 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

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,58 @@ 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
}