From 311f945c47d016c499876ea9002c9d3528c1efa4 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 11:39:17 +0200 Subject: [PATCH 01/43] Move libm to the crates directory --- Cargo.toml | 34 ++----------------- .../compiler-builtins-smoke-test/src/lib.rs | 2 +- crates/libm-bench/Cargo.toml | 2 +- crates/libm/Cargo.toml | 26 ++++++++++++++ {src => crates/libm/src}/lib.rs | 0 {src => crates/libm/src}/math/acos.rs | 0 {src => crates/libm/src}/math/acosf.rs | 0 {src => crates/libm/src}/math/acosh.rs | 0 {src => crates/libm/src}/math/acoshf.rs | 0 {src => crates/libm/src}/math/asin.rs | 0 {src => crates/libm/src}/math/asinf.rs | 0 {src => crates/libm/src}/math/asinh.rs | 0 {src => crates/libm/src}/math/asinhf.rs | 0 {src => crates/libm/src}/math/atan.rs | 0 {src => crates/libm/src}/math/atan2.rs | 0 {src => crates/libm/src}/math/atan2f.rs | 0 {src => crates/libm/src}/math/atanf.rs | 0 {src => crates/libm/src}/math/atanh.rs | 0 {src => crates/libm/src}/math/atanhf.rs | 0 {src => crates/libm/src}/math/cbrt.rs | 0 {src => crates/libm/src}/math/cbrtf.rs | 0 {src => crates/libm/src}/math/ceil.rs | 0 {src => crates/libm/src}/math/ceilf.rs | 0 {src => crates/libm/src}/math/copysign.rs | 0 {src => crates/libm/src}/math/copysignf.rs | 0 {src => crates/libm/src}/math/cos.rs | 0 {src => crates/libm/src}/math/cosf.rs | 0 {src => crates/libm/src}/math/cosh.rs | 0 {src => crates/libm/src}/math/coshf.rs | 0 {src => crates/libm/src}/math/erf.rs | 0 {src => crates/libm/src}/math/erff.rs | 0 {src => crates/libm/src}/math/exp.rs | 0 {src => crates/libm/src}/math/exp10.rs | 0 {src => crates/libm/src}/math/exp10f.rs | 0 {src => crates/libm/src}/math/exp2.rs | 0 {src => crates/libm/src}/math/exp2f.rs | 0 {src => crates/libm/src}/math/expf.rs | 0 {src => crates/libm/src}/math/expm1.rs | 0 {src => crates/libm/src}/math/expm1f.rs | 0 {src => crates/libm/src}/math/expo2.rs | 0 {src => crates/libm/src}/math/fabs.rs | 0 {src => crates/libm/src}/math/fabsf.rs | 0 {src => crates/libm/src}/math/fdim.rs | 0 {src => crates/libm/src}/math/fdimf.rs | 0 {src => crates/libm/src}/math/fenv.rs | 0 {src => crates/libm/src}/math/floor.rs | 0 {src => crates/libm/src}/math/floorf.rs | 0 {src => crates/libm/src}/math/fma.rs | 0 {src => crates/libm/src}/math/fmaf.rs | 0 {src => crates/libm/src}/math/fmax.rs | 0 {src => crates/libm/src}/math/fmaxf.rs | 0 {src => crates/libm/src}/math/fmin.rs | 0 {src => crates/libm/src}/math/fminf.rs | 0 {src => crates/libm/src}/math/fmod.rs | 0 {src => crates/libm/src}/math/fmodf.rs | 0 {src => crates/libm/src}/math/frexp.rs | 0 {src => crates/libm/src}/math/frexpf.rs | 0 {src => crates/libm/src}/math/hypot.rs | 0 {src => crates/libm/src}/math/hypotf.rs | 0 {src => crates/libm/src}/math/ilogb.rs | 0 {src => crates/libm/src}/math/ilogbf.rs | 0 {src => crates/libm/src}/math/j0.rs | 0 {src => crates/libm/src}/math/j0f.rs | 0 {src => crates/libm/src}/math/j1.rs | 0 {src => crates/libm/src}/math/j1f.rs | 0 {src => crates/libm/src}/math/jn.rs | 0 {src => crates/libm/src}/math/jnf.rs | 0 {src => crates/libm/src}/math/k_cos.rs | 0 {src => crates/libm/src}/math/k_cosf.rs | 0 {src => crates/libm/src}/math/k_expo2.rs | 0 {src => crates/libm/src}/math/k_expo2f.rs | 0 {src => crates/libm/src}/math/k_sin.rs | 0 {src => crates/libm/src}/math/k_sinf.rs | 0 {src => crates/libm/src}/math/k_tan.rs | 0 {src => crates/libm/src}/math/k_tanf.rs | 0 {src => crates/libm/src}/math/ldexp.rs | 0 {src => crates/libm/src}/math/ldexpf.rs | 0 {src => crates/libm/src}/math/lgamma.rs | 0 {src => crates/libm/src}/math/lgamma_r.rs | 0 {src => crates/libm/src}/math/lgammaf.rs | 0 {src => crates/libm/src}/math/lgammaf_r.rs | 0 {src => crates/libm/src}/math/log.rs | 0 {src => crates/libm/src}/math/log10.rs | 0 {src => crates/libm/src}/math/log10f.rs | 0 {src => crates/libm/src}/math/log1p.rs | 0 {src => crates/libm/src}/math/log1pf.rs | 0 {src => crates/libm/src}/math/log2.rs | 0 {src => crates/libm/src}/math/log2f.rs | 0 {src => crates/libm/src}/math/logf.rs | 0 {src => crates/libm/src}/math/mod.rs | 0 {src => crates/libm/src}/math/modf.rs | 0 {src => crates/libm/src}/math/modff.rs | 0 {src => crates/libm/src}/math/pow.rs | 0 {src => crates/libm/src}/math/powf.rs | 0 {src => crates/libm/src}/math/rem_pio2.rs | 0 .../libm/src}/math/rem_pio2_large.rs | 0 {src => crates/libm/src}/math/rem_pio2f.rs | 0 {src => crates/libm/src}/math/remainder.rs | 0 {src => crates/libm/src}/math/remainderf.rs | 0 {src => crates/libm/src}/math/remquo.rs | 0 {src => crates/libm/src}/math/remquof.rs | 0 {src => crates/libm/src}/math/round.rs | 0 {src => crates/libm/src}/math/roundf.rs | 0 {src => crates/libm/src}/math/scalbn.rs | 0 {src => crates/libm/src}/math/scalbnf.rs | 0 {src => crates/libm/src}/math/sin.rs | 0 {src => crates/libm/src}/math/sincos.rs | 0 {src => crates/libm/src}/math/sincosf.rs | 0 {src => crates/libm/src}/math/sinf.rs | 0 {src => crates/libm/src}/math/sinh.rs | 0 {src => crates/libm/src}/math/sinhf.rs | 0 {src => crates/libm/src}/math/sqrt.rs | 0 {src => crates/libm/src}/math/sqrtf.rs | 0 {src => crates/libm/src}/math/tan.rs | 0 {src => crates/libm/src}/math/tanf.rs | 0 {src => crates/libm/src}/math/tanh.rs | 0 {src => crates/libm/src}/math/tanhf.rs | 0 {src => crates/libm/src}/math/tgamma.rs | 0 {src => crates/libm/src}/math/tgammaf.rs | 0 {src => crates/libm/src}/math/trunc.rs | 0 {src => crates/libm/src}/math/truncf.rs | 0 121 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 crates/libm/Cargo.toml rename {src => crates/libm/src}/lib.rs (100%) rename {src => crates/libm/src}/math/acos.rs (100%) rename {src => crates/libm/src}/math/acosf.rs (100%) rename {src => crates/libm/src}/math/acosh.rs (100%) rename {src => crates/libm/src}/math/acoshf.rs (100%) rename {src => crates/libm/src}/math/asin.rs (100%) rename {src => crates/libm/src}/math/asinf.rs (100%) rename {src => crates/libm/src}/math/asinh.rs (100%) rename {src => crates/libm/src}/math/asinhf.rs (100%) rename {src => crates/libm/src}/math/atan.rs (100%) rename {src => crates/libm/src}/math/atan2.rs (100%) rename {src => crates/libm/src}/math/atan2f.rs (100%) rename {src => crates/libm/src}/math/atanf.rs (100%) rename {src => crates/libm/src}/math/atanh.rs (100%) rename {src => crates/libm/src}/math/atanhf.rs (100%) rename {src => crates/libm/src}/math/cbrt.rs (100%) rename {src => crates/libm/src}/math/cbrtf.rs (100%) rename {src => crates/libm/src}/math/ceil.rs (100%) rename {src => crates/libm/src}/math/ceilf.rs (100%) rename {src => crates/libm/src}/math/copysign.rs (100%) rename {src => crates/libm/src}/math/copysignf.rs (100%) rename {src => crates/libm/src}/math/cos.rs (100%) rename {src => crates/libm/src}/math/cosf.rs (100%) rename {src => crates/libm/src}/math/cosh.rs (100%) rename {src => crates/libm/src}/math/coshf.rs (100%) rename {src => crates/libm/src}/math/erf.rs (100%) rename {src => crates/libm/src}/math/erff.rs (100%) rename {src => crates/libm/src}/math/exp.rs (100%) rename {src => crates/libm/src}/math/exp10.rs (100%) rename {src => crates/libm/src}/math/exp10f.rs (100%) rename {src => crates/libm/src}/math/exp2.rs (100%) rename {src => crates/libm/src}/math/exp2f.rs (100%) rename {src => crates/libm/src}/math/expf.rs (100%) rename {src => crates/libm/src}/math/expm1.rs (100%) rename {src => crates/libm/src}/math/expm1f.rs (100%) rename {src => crates/libm/src}/math/expo2.rs (100%) rename {src => crates/libm/src}/math/fabs.rs (100%) rename {src => crates/libm/src}/math/fabsf.rs (100%) rename {src => crates/libm/src}/math/fdim.rs (100%) rename {src => crates/libm/src}/math/fdimf.rs (100%) rename {src => crates/libm/src}/math/fenv.rs (100%) rename {src => crates/libm/src}/math/floor.rs (100%) rename {src => crates/libm/src}/math/floorf.rs (100%) rename {src => crates/libm/src}/math/fma.rs (100%) rename {src => crates/libm/src}/math/fmaf.rs (100%) rename {src => crates/libm/src}/math/fmax.rs (100%) rename {src => crates/libm/src}/math/fmaxf.rs (100%) rename {src => crates/libm/src}/math/fmin.rs (100%) rename {src => crates/libm/src}/math/fminf.rs (100%) rename {src => crates/libm/src}/math/fmod.rs (100%) rename {src => crates/libm/src}/math/fmodf.rs (100%) rename {src => crates/libm/src}/math/frexp.rs (100%) rename {src => crates/libm/src}/math/frexpf.rs (100%) rename {src => crates/libm/src}/math/hypot.rs (100%) rename {src => crates/libm/src}/math/hypotf.rs (100%) rename {src => crates/libm/src}/math/ilogb.rs (100%) rename {src => crates/libm/src}/math/ilogbf.rs (100%) rename {src => crates/libm/src}/math/j0.rs (100%) rename {src => crates/libm/src}/math/j0f.rs (100%) rename {src => crates/libm/src}/math/j1.rs (100%) rename {src => crates/libm/src}/math/j1f.rs (100%) rename {src => crates/libm/src}/math/jn.rs (100%) rename {src => crates/libm/src}/math/jnf.rs (100%) rename {src => crates/libm/src}/math/k_cos.rs (100%) rename {src => crates/libm/src}/math/k_cosf.rs (100%) rename {src => crates/libm/src}/math/k_expo2.rs (100%) rename {src => crates/libm/src}/math/k_expo2f.rs (100%) rename {src => crates/libm/src}/math/k_sin.rs (100%) rename {src => crates/libm/src}/math/k_sinf.rs (100%) rename {src => crates/libm/src}/math/k_tan.rs (100%) rename {src => crates/libm/src}/math/k_tanf.rs (100%) rename {src => crates/libm/src}/math/ldexp.rs (100%) rename {src => crates/libm/src}/math/ldexpf.rs (100%) rename {src => crates/libm/src}/math/lgamma.rs (100%) rename {src => crates/libm/src}/math/lgamma_r.rs (100%) rename {src => crates/libm/src}/math/lgammaf.rs (100%) rename {src => crates/libm/src}/math/lgammaf_r.rs (100%) rename {src => crates/libm/src}/math/log.rs (100%) rename {src => crates/libm/src}/math/log10.rs (100%) rename {src => crates/libm/src}/math/log10f.rs (100%) rename {src => crates/libm/src}/math/log1p.rs (100%) rename {src => crates/libm/src}/math/log1pf.rs (100%) rename {src => crates/libm/src}/math/log2.rs (100%) rename {src => crates/libm/src}/math/log2f.rs (100%) rename {src => crates/libm/src}/math/logf.rs (100%) rename {src => crates/libm/src}/math/mod.rs (100%) rename {src => crates/libm/src}/math/modf.rs (100%) rename {src => crates/libm/src}/math/modff.rs (100%) rename {src => crates/libm/src}/math/pow.rs (100%) rename {src => crates/libm/src}/math/powf.rs (100%) rename {src => crates/libm/src}/math/rem_pio2.rs (100%) rename {src => crates/libm/src}/math/rem_pio2_large.rs (100%) rename {src => crates/libm/src}/math/rem_pio2f.rs (100%) rename {src => crates/libm/src}/math/remainder.rs (100%) rename {src => crates/libm/src}/math/remainderf.rs (100%) rename {src => crates/libm/src}/math/remquo.rs (100%) rename {src => crates/libm/src}/math/remquof.rs (100%) rename {src => crates/libm/src}/math/round.rs (100%) rename {src => crates/libm/src}/math/roundf.rs (100%) rename {src => crates/libm/src}/math/scalbn.rs (100%) rename {src => crates/libm/src}/math/scalbnf.rs (100%) rename {src => crates/libm/src}/math/sin.rs (100%) rename {src => crates/libm/src}/math/sincos.rs (100%) rename {src => crates/libm/src}/math/sincosf.rs (100%) rename {src => crates/libm/src}/math/sinf.rs (100%) rename {src => crates/libm/src}/math/sinh.rs (100%) rename {src => crates/libm/src}/math/sinhf.rs (100%) rename {src => crates/libm/src}/math/sqrt.rs (100%) rename {src => crates/libm/src}/math/sqrtf.rs (100%) rename {src => crates/libm/src}/math/tan.rs (100%) rename {src => crates/libm/src}/math/tanf.rs (100%) rename {src => crates/libm/src}/math/tanh.rs (100%) rename {src => crates/libm/src}/math/tanhf.rs (100%) rename {src => crates/libm/src}/math/tgamma.rs (100%) rename {src => crates/libm/src}/math/tgammaf.rs (100%) rename {src => crates/libm/src}/math/trunc.rs (100%) rename {src => crates/libm/src}/math/truncf.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 3e6817851..b73842626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,6 @@ -[package] -authors = ["Jorge Aparicio "] -categories = ["no-std"] -description = "libm in pure Rust" -documentation = "https://docs.rs/libm" -keywords = ["libm", "math"] -license = "MIT OR Apache-2.0" -name = "libm" -repository = "https://github.com/rust-lang-nursery/libm" -version = "0.2.0" -edition = "2018" - -[features] -# only used to run our test suite -default = ['stable'] -stable = [] - -# Generate tests which are random inputs and the outputs are calculated with -# musl libc. -musl-reference-tests = ['rand'] - -# Used checked array indexing instead of unchecked array indexing in this -# library. -checked = [] - [workspace] members = [ + "crates/libm", "crates/compiler-builtins-smoke-test", "crates/libm-bench", -] - -[dev-dependencies] -no-panic = "0.1.8" - -[build-dependencies] -rand = { version = "0.6.5", optional = true } +] \ No newline at end of file diff --git a/crates/compiler-builtins-smoke-test/src/lib.rs b/crates/compiler-builtins-smoke-test/src/lib.rs index 7fad301b9..9407aa765 100644 --- a/crates/compiler-builtins-smoke-test/src/lib.rs +++ b/crates/compiler-builtins-smoke-test/src/lib.rs @@ -5,5 +5,5 @@ #![allow(dead_code)] #![no_std] -#[path = "../../../src/math/mod.rs"] +#[path = "../../libm/src/math/mod.rs"] mod libm; diff --git a/crates/libm-bench/Cargo.toml b/crates/libm-bench/Cargo.toml index ba65dbd5f..686627f5f 100644 --- a/crates/libm-bench/Cargo.toml +++ b/crates/libm-bench/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" [dependencies] -libm = { path = "../..", default-features = false } +libm = { path = "../libm", default-features = false } rand = "0.6.5" paste = "0.1.5" diff --git a/crates/libm/Cargo.toml b/crates/libm/Cargo.toml new file mode 100644 index 000000000..40cbd2b33 --- /dev/null +++ b/crates/libm/Cargo.toml @@ -0,0 +1,26 @@ +[package] +authors = ["Jorge Aparicio "] +categories = ["no-std"] +description = "libm in pure Rust" +documentation = "https://docs.rs/libm" +keywords = ["libm", "math"] +license = "MIT OR Apache-2.0" +name = "libm" +repository = "https://github.com/rust-lang-nursery/libm" +version = "0.2.0" +edition = "2018" + +[features] +# only used to run our test suite +default = ['stable'] +stable = [] + +# Used checked array indexing instead of unchecked array indexing in this +# library. +checked = [] + +[dev-dependencies] +no-panic = "0.1.8" + +[build-dependencies] +rand = { version = "0.6.5", optional = true } diff --git a/src/lib.rs b/crates/libm/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/libm/src/lib.rs diff --git a/src/math/acos.rs b/crates/libm/src/math/acos.rs similarity index 100% rename from src/math/acos.rs rename to crates/libm/src/math/acos.rs diff --git a/src/math/acosf.rs b/crates/libm/src/math/acosf.rs similarity index 100% rename from src/math/acosf.rs rename to crates/libm/src/math/acosf.rs diff --git a/src/math/acosh.rs b/crates/libm/src/math/acosh.rs similarity index 100% rename from src/math/acosh.rs rename to crates/libm/src/math/acosh.rs diff --git a/src/math/acoshf.rs b/crates/libm/src/math/acoshf.rs similarity index 100% rename from src/math/acoshf.rs rename to crates/libm/src/math/acoshf.rs diff --git a/src/math/asin.rs b/crates/libm/src/math/asin.rs similarity index 100% rename from src/math/asin.rs rename to crates/libm/src/math/asin.rs diff --git a/src/math/asinf.rs b/crates/libm/src/math/asinf.rs similarity index 100% rename from src/math/asinf.rs rename to crates/libm/src/math/asinf.rs diff --git a/src/math/asinh.rs b/crates/libm/src/math/asinh.rs similarity index 100% rename from src/math/asinh.rs rename to crates/libm/src/math/asinh.rs diff --git a/src/math/asinhf.rs b/crates/libm/src/math/asinhf.rs similarity index 100% rename from src/math/asinhf.rs rename to crates/libm/src/math/asinhf.rs diff --git a/src/math/atan.rs b/crates/libm/src/math/atan.rs similarity index 100% rename from src/math/atan.rs rename to crates/libm/src/math/atan.rs diff --git a/src/math/atan2.rs b/crates/libm/src/math/atan2.rs similarity index 100% rename from src/math/atan2.rs rename to crates/libm/src/math/atan2.rs diff --git a/src/math/atan2f.rs b/crates/libm/src/math/atan2f.rs similarity index 100% rename from src/math/atan2f.rs rename to crates/libm/src/math/atan2f.rs diff --git a/src/math/atanf.rs b/crates/libm/src/math/atanf.rs similarity index 100% rename from src/math/atanf.rs rename to crates/libm/src/math/atanf.rs diff --git a/src/math/atanh.rs b/crates/libm/src/math/atanh.rs similarity index 100% rename from src/math/atanh.rs rename to crates/libm/src/math/atanh.rs diff --git a/src/math/atanhf.rs b/crates/libm/src/math/atanhf.rs similarity index 100% rename from src/math/atanhf.rs rename to crates/libm/src/math/atanhf.rs diff --git a/src/math/cbrt.rs b/crates/libm/src/math/cbrt.rs similarity index 100% rename from src/math/cbrt.rs rename to crates/libm/src/math/cbrt.rs diff --git a/src/math/cbrtf.rs b/crates/libm/src/math/cbrtf.rs similarity index 100% rename from src/math/cbrtf.rs rename to crates/libm/src/math/cbrtf.rs diff --git a/src/math/ceil.rs b/crates/libm/src/math/ceil.rs similarity index 100% rename from src/math/ceil.rs rename to crates/libm/src/math/ceil.rs diff --git a/src/math/ceilf.rs b/crates/libm/src/math/ceilf.rs similarity index 100% rename from src/math/ceilf.rs rename to crates/libm/src/math/ceilf.rs diff --git a/src/math/copysign.rs b/crates/libm/src/math/copysign.rs similarity index 100% rename from src/math/copysign.rs rename to crates/libm/src/math/copysign.rs diff --git a/src/math/copysignf.rs b/crates/libm/src/math/copysignf.rs similarity index 100% rename from src/math/copysignf.rs rename to crates/libm/src/math/copysignf.rs diff --git a/src/math/cos.rs b/crates/libm/src/math/cos.rs similarity index 100% rename from src/math/cos.rs rename to crates/libm/src/math/cos.rs diff --git a/src/math/cosf.rs b/crates/libm/src/math/cosf.rs similarity index 100% rename from src/math/cosf.rs rename to crates/libm/src/math/cosf.rs diff --git a/src/math/cosh.rs b/crates/libm/src/math/cosh.rs similarity index 100% rename from src/math/cosh.rs rename to crates/libm/src/math/cosh.rs diff --git a/src/math/coshf.rs b/crates/libm/src/math/coshf.rs similarity index 100% rename from src/math/coshf.rs rename to crates/libm/src/math/coshf.rs diff --git a/src/math/erf.rs b/crates/libm/src/math/erf.rs similarity index 100% rename from src/math/erf.rs rename to crates/libm/src/math/erf.rs diff --git a/src/math/erff.rs b/crates/libm/src/math/erff.rs similarity index 100% rename from src/math/erff.rs rename to crates/libm/src/math/erff.rs diff --git a/src/math/exp.rs b/crates/libm/src/math/exp.rs similarity index 100% rename from src/math/exp.rs rename to crates/libm/src/math/exp.rs diff --git a/src/math/exp10.rs b/crates/libm/src/math/exp10.rs similarity index 100% rename from src/math/exp10.rs rename to crates/libm/src/math/exp10.rs diff --git a/src/math/exp10f.rs b/crates/libm/src/math/exp10f.rs similarity index 100% rename from src/math/exp10f.rs rename to crates/libm/src/math/exp10f.rs diff --git a/src/math/exp2.rs b/crates/libm/src/math/exp2.rs similarity index 100% rename from src/math/exp2.rs rename to crates/libm/src/math/exp2.rs diff --git a/src/math/exp2f.rs b/crates/libm/src/math/exp2f.rs similarity index 100% rename from src/math/exp2f.rs rename to crates/libm/src/math/exp2f.rs diff --git a/src/math/expf.rs b/crates/libm/src/math/expf.rs similarity index 100% rename from src/math/expf.rs rename to crates/libm/src/math/expf.rs diff --git a/src/math/expm1.rs b/crates/libm/src/math/expm1.rs similarity index 100% rename from src/math/expm1.rs rename to crates/libm/src/math/expm1.rs diff --git a/src/math/expm1f.rs b/crates/libm/src/math/expm1f.rs similarity index 100% rename from src/math/expm1f.rs rename to crates/libm/src/math/expm1f.rs diff --git a/src/math/expo2.rs b/crates/libm/src/math/expo2.rs similarity index 100% rename from src/math/expo2.rs rename to crates/libm/src/math/expo2.rs diff --git a/src/math/fabs.rs b/crates/libm/src/math/fabs.rs similarity index 100% rename from src/math/fabs.rs rename to crates/libm/src/math/fabs.rs diff --git a/src/math/fabsf.rs b/crates/libm/src/math/fabsf.rs similarity index 100% rename from src/math/fabsf.rs rename to crates/libm/src/math/fabsf.rs diff --git a/src/math/fdim.rs b/crates/libm/src/math/fdim.rs similarity index 100% rename from src/math/fdim.rs rename to crates/libm/src/math/fdim.rs diff --git a/src/math/fdimf.rs b/crates/libm/src/math/fdimf.rs similarity index 100% rename from src/math/fdimf.rs rename to crates/libm/src/math/fdimf.rs diff --git a/src/math/fenv.rs b/crates/libm/src/math/fenv.rs similarity index 100% rename from src/math/fenv.rs rename to crates/libm/src/math/fenv.rs diff --git a/src/math/floor.rs b/crates/libm/src/math/floor.rs similarity index 100% rename from src/math/floor.rs rename to crates/libm/src/math/floor.rs diff --git a/src/math/floorf.rs b/crates/libm/src/math/floorf.rs similarity index 100% rename from src/math/floorf.rs rename to crates/libm/src/math/floorf.rs diff --git a/src/math/fma.rs b/crates/libm/src/math/fma.rs similarity index 100% rename from src/math/fma.rs rename to crates/libm/src/math/fma.rs diff --git a/src/math/fmaf.rs b/crates/libm/src/math/fmaf.rs similarity index 100% rename from src/math/fmaf.rs rename to crates/libm/src/math/fmaf.rs diff --git a/src/math/fmax.rs b/crates/libm/src/math/fmax.rs similarity index 100% rename from src/math/fmax.rs rename to crates/libm/src/math/fmax.rs diff --git a/src/math/fmaxf.rs b/crates/libm/src/math/fmaxf.rs similarity index 100% rename from src/math/fmaxf.rs rename to crates/libm/src/math/fmaxf.rs diff --git a/src/math/fmin.rs b/crates/libm/src/math/fmin.rs similarity index 100% rename from src/math/fmin.rs rename to crates/libm/src/math/fmin.rs diff --git a/src/math/fminf.rs b/crates/libm/src/math/fminf.rs similarity index 100% rename from src/math/fminf.rs rename to crates/libm/src/math/fminf.rs diff --git a/src/math/fmod.rs b/crates/libm/src/math/fmod.rs similarity index 100% rename from src/math/fmod.rs rename to crates/libm/src/math/fmod.rs diff --git a/src/math/fmodf.rs b/crates/libm/src/math/fmodf.rs similarity index 100% rename from src/math/fmodf.rs rename to crates/libm/src/math/fmodf.rs diff --git a/src/math/frexp.rs b/crates/libm/src/math/frexp.rs similarity index 100% rename from src/math/frexp.rs rename to crates/libm/src/math/frexp.rs diff --git a/src/math/frexpf.rs b/crates/libm/src/math/frexpf.rs similarity index 100% rename from src/math/frexpf.rs rename to crates/libm/src/math/frexpf.rs diff --git a/src/math/hypot.rs b/crates/libm/src/math/hypot.rs similarity index 100% rename from src/math/hypot.rs rename to crates/libm/src/math/hypot.rs diff --git a/src/math/hypotf.rs b/crates/libm/src/math/hypotf.rs similarity index 100% rename from src/math/hypotf.rs rename to crates/libm/src/math/hypotf.rs diff --git a/src/math/ilogb.rs b/crates/libm/src/math/ilogb.rs similarity index 100% rename from src/math/ilogb.rs rename to crates/libm/src/math/ilogb.rs diff --git a/src/math/ilogbf.rs b/crates/libm/src/math/ilogbf.rs similarity index 100% rename from src/math/ilogbf.rs rename to crates/libm/src/math/ilogbf.rs diff --git a/src/math/j0.rs b/crates/libm/src/math/j0.rs similarity index 100% rename from src/math/j0.rs rename to crates/libm/src/math/j0.rs diff --git a/src/math/j0f.rs b/crates/libm/src/math/j0f.rs similarity index 100% rename from src/math/j0f.rs rename to crates/libm/src/math/j0f.rs diff --git a/src/math/j1.rs b/crates/libm/src/math/j1.rs similarity index 100% rename from src/math/j1.rs rename to crates/libm/src/math/j1.rs diff --git a/src/math/j1f.rs b/crates/libm/src/math/j1f.rs similarity index 100% rename from src/math/j1f.rs rename to crates/libm/src/math/j1f.rs diff --git a/src/math/jn.rs b/crates/libm/src/math/jn.rs similarity index 100% rename from src/math/jn.rs rename to crates/libm/src/math/jn.rs diff --git a/src/math/jnf.rs b/crates/libm/src/math/jnf.rs similarity index 100% rename from src/math/jnf.rs rename to crates/libm/src/math/jnf.rs diff --git a/src/math/k_cos.rs b/crates/libm/src/math/k_cos.rs similarity index 100% rename from src/math/k_cos.rs rename to crates/libm/src/math/k_cos.rs diff --git a/src/math/k_cosf.rs b/crates/libm/src/math/k_cosf.rs similarity index 100% rename from src/math/k_cosf.rs rename to crates/libm/src/math/k_cosf.rs diff --git a/src/math/k_expo2.rs b/crates/libm/src/math/k_expo2.rs similarity index 100% rename from src/math/k_expo2.rs rename to crates/libm/src/math/k_expo2.rs diff --git a/src/math/k_expo2f.rs b/crates/libm/src/math/k_expo2f.rs similarity index 100% rename from src/math/k_expo2f.rs rename to crates/libm/src/math/k_expo2f.rs diff --git a/src/math/k_sin.rs b/crates/libm/src/math/k_sin.rs similarity index 100% rename from src/math/k_sin.rs rename to crates/libm/src/math/k_sin.rs diff --git a/src/math/k_sinf.rs b/crates/libm/src/math/k_sinf.rs similarity index 100% rename from src/math/k_sinf.rs rename to crates/libm/src/math/k_sinf.rs diff --git a/src/math/k_tan.rs b/crates/libm/src/math/k_tan.rs similarity index 100% rename from src/math/k_tan.rs rename to crates/libm/src/math/k_tan.rs diff --git a/src/math/k_tanf.rs b/crates/libm/src/math/k_tanf.rs similarity index 100% rename from src/math/k_tanf.rs rename to crates/libm/src/math/k_tanf.rs diff --git a/src/math/ldexp.rs b/crates/libm/src/math/ldexp.rs similarity index 100% rename from src/math/ldexp.rs rename to crates/libm/src/math/ldexp.rs diff --git a/src/math/ldexpf.rs b/crates/libm/src/math/ldexpf.rs similarity index 100% rename from src/math/ldexpf.rs rename to crates/libm/src/math/ldexpf.rs diff --git a/src/math/lgamma.rs b/crates/libm/src/math/lgamma.rs similarity index 100% rename from src/math/lgamma.rs rename to crates/libm/src/math/lgamma.rs diff --git a/src/math/lgamma_r.rs b/crates/libm/src/math/lgamma_r.rs similarity index 100% rename from src/math/lgamma_r.rs rename to crates/libm/src/math/lgamma_r.rs diff --git a/src/math/lgammaf.rs b/crates/libm/src/math/lgammaf.rs similarity index 100% rename from src/math/lgammaf.rs rename to crates/libm/src/math/lgammaf.rs diff --git a/src/math/lgammaf_r.rs b/crates/libm/src/math/lgammaf_r.rs similarity index 100% rename from src/math/lgammaf_r.rs rename to crates/libm/src/math/lgammaf_r.rs diff --git a/src/math/log.rs b/crates/libm/src/math/log.rs similarity index 100% rename from src/math/log.rs rename to crates/libm/src/math/log.rs diff --git a/src/math/log10.rs b/crates/libm/src/math/log10.rs similarity index 100% rename from src/math/log10.rs rename to crates/libm/src/math/log10.rs diff --git a/src/math/log10f.rs b/crates/libm/src/math/log10f.rs similarity index 100% rename from src/math/log10f.rs rename to crates/libm/src/math/log10f.rs diff --git a/src/math/log1p.rs b/crates/libm/src/math/log1p.rs similarity index 100% rename from src/math/log1p.rs rename to crates/libm/src/math/log1p.rs diff --git a/src/math/log1pf.rs b/crates/libm/src/math/log1pf.rs similarity index 100% rename from src/math/log1pf.rs rename to crates/libm/src/math/log1pf.rs diff --git a/src/math/log2.rs b/crates/libm/src/math/log2.rs similarity index 100% rename from src/math/log2.rs rename to crates/libm/src/math/log2.rs diff --git a/src/math/log2f.rs b/crates/libm/src/math/log2f.rs similarity index 100% rename from src/math/log2f.rs rename to crates/libm/src/math/log2f.rs diff --git a/src/math/logf.rs b/crates/libm/src/math/logf.rs similarity index 100% rename from src/math/logf.rs rename to crates/libm/src/math/logf.rs diff --git a/src/math/mod.rs b/crates/libm/src/math/mod.rs similarity index 100% rename from src/math/mod.rs rename to crates/libm/src/math/mod.rs diff --git a/src/math/modf.rs b/crates/libm/src/math/modf.rs similarity index 100% rename from src/math/modf.rs rename to crates/libm/src/math/modf.rs diff --git a/src/math/modff.rs b/crates/libm/src/math/modff.rs similarity index 100% rename from src/math/modff.rs rename to crates/libm/src/math/modff.rs diff --git a/src/math/pow.rs b/crates/libm/src/math/pow.rs similarity index 100% rename from src/math/pow.rs rename to crates/libm/src/math/pow.rs diff --git a/src/math/powf.rs b/crates/libm/src/math/powf.rs similarity index 100% rename from src/math/powf.rs rename to crates/libm/src/math/powf.rs diff --git a/src/math/rem_pio2.rs b/crates/libm/src/math/rem_pio2.rs similarity index 100% rename from src/math/rem_pio2.rs rename to crates/libm/src/math/rem_pio2.rs diff --git a/src/math/rem_pio2_large.rs b/crates/libm/src/math/rem_pio2_large.rs similarity index 100% rename from src/math/rem_pio2_large.rs rename to crates/libm/src/math/rem_pio2_large.rs diff --git a/src/math/rem_pio2f.rs b/crates/libm/src/math/rem_pio2f.rs similarity index 100% rename from src/math/rem_pio2f.rs rename to crates/libm/src/math/rem_pio2f.rs diff --git a/src/math/remainder.rs b/crates/libm/src/math/remainder.rs similarity index 100% rename from src/math/remainder.rs rename to crates/libm/src/math/remainder.rs diff --git a/src/math/remainderf.rs b/crates/libm/src/math/remainderf.rs similarity index 100% rename from src/math/remainderf.rs rename to crates/libm/src/math/remainderf.rs diff --git a/src/math/remquo.rs b/crates/libm/src/math/remquo.rs similarity index 100% rename from src/math/remquo.rs rename to crates/libm/src/math/remquo.rs diff --git a/src/math/remquof.rs b/crates/libm/src/math/remquof.rs similarity index 100% rename from src/math/remquof.rs rename to crates/libm/src/math/remquof.rs diff --git a/src/math/round.rs b/crates/libm/src/math/round.rs similarity index 100% rename from src/math/round.rs rename to crates/libm/src/math/round.rs diff --git a/src/math/roundf.rs b/crates/libm/src/math/roundf.rs similarity index 100% rename from src/math/roundf.rs rename to crates/libm/src/math/roundf.rs diff --git a/src/math/scalbn.rs b/crates/libm/src/math/scalbn.rs similarity index 100% rename from src/math/scalbn.rs rename to crates/libm/src/math/scalbn.rs diff --git a/src/math/scalbnf.rs b/crates/libm/src/math/scalbnf.rs similarity index 100% rename from src/math/scalbnf.rs rename to crates/libm/src/math/scalbnf.rs diff --git a/src/math/sin.rs b/crates/libm/src/math/sin.rs similarity index 100% rename from src/math/sin.rs rename to crates/libm/src/math/sin.rs diff --git a/src/math/sincos.rs b/crates/libm/src/math/sincos.rs similarity index 100% rename from src/math/sincos.rs rename to crates/libm/src/math/sincos.rs diff --git a/src/math/sincosf.rs b/crates/libm/src/math/sincosf.rs similarity index 100% rename from src/math/sincosf.rs rename to crates/libm/src/math/sincosf.rs diff --git a/src/math/sinf.rs b/crates/libm/src/math/sinf.rs similarity index 100% rename from src/math/sinf.rs rename to crates/libm/src/math/sinf.rs diff --git a/src/math/sinh.rs b/crates/libm/src/math/sinh.rs similarity index 100% rename from src/math/sinh.rs rename to crates/libm/src/math/sinh.rs diff --git a/src/math/sinhf.rs b/crates/libm/src/math/sinhf.rs similarity index 100% rename from src/math/sinhf.rs rename to crates/libm/src/math/sinhf.rs diff --git a/src/math/sqrt.rs b/crates/libm/src/math/sqrt.rs similarity index 100% rename from src/math/sqrt.rs rename to crates/libm/src/math/sqrt.rs diff --git a/src/math/sqrtf.rs b/crates/libm/src/math/sqrtf.rs similarity index 100% rename from src/math/sqrtf.rs rename to crates/libm/src/math/sqrtf.rs diff --git a/src/math/tan.rs b/crates/libm/src/math/tan.rs similarity index 100% rename from src/math/tan.rs rename to crates/libm/src/math/tan.rs diff --git a/src/math/tanf.rs b/crates/libm/src/math/tanf.rs similarity index 100% rename from src/math/tanf.rs rename to crates/libm/src/math/tanf.rs diff --git a/src/math/tanh.rs b/crates/libm/src/math/tanh.rs similarity index 100% rename from src/math/tanh.rs rename to crates/libm/src/math/tanh.rs diff --git a/src/math/tanhf.rs b/crates/libm/src/math/tanhf.rs similarity index 100% rename from src/math/tanhf.rs rename to crates/libm/src/math/tanhf.rs diff --git a/src/math/tgamma.rs b/crates/libm/src/math/tgamma.rs similarity index 100% rename from src/math/tgamma.rs rename to crates/libm/src/math/tgamma.rs diff --git a/src/math/tgammaf.rs b/crates/libm/src/math/tgammaf.rs similarity index 100% rename from src/math/tgammaf.rs rename to crates/libm/src/math/tgammaf.rs diff --git a/src/math/trunc.rs b/crates/libm/src/math/trunc.rs similarity index 100% rename from src/math/trunc.rs rename to crates/libm/src/math/trunc.rs diff --git a/src/math/truncf.rs b/crates/libm/src/math/truncf.rs similarity index 100% rename from src/math/truncf.rs rename to crates/libm/src/math/truncf.rs From 0d329849c3efea9bdf709315b8165dce22452b93 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 12:28:56 +0200 Subject: [PATCH 02/43] WIP: Verification of libm API --- Cargo.toml | 2 + crates/libm-analyze/Cargo.toml | 16 ++++++ crates/libm-analyze/src/lib.rs | 96 ++++++++++++++++++++++++++++++++ crates/libm-verify/Cargo.toml | 10 ++++ crates/libm-verify/src/lib.rs | 1 + crates/libm-verify/tests/musl.rs | 3 + 6 files changed, 128 insertions(+) create mode 100644 crates/libm-analyze/Cargo.toml create mode 100644 crates/libm-analyze/src/lib.rs create mode 100644 crates/libm-verify/Cargo.toml create mode 100644 crates/libm-verify/src/lib.rs create mode 100644 crates/libm-verify/tests/musl.rs diff --git a/Cargo.toml b/Cargo.toml index b73842626..5d2abd571 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,8 @@ [workspace] members = [ "crates/libm", + "crates/libm-analyze", + "crates/libm-verify", "crates/compiler-builtins-smoke-test", "crates/libm-bench", ] \ No newline at end of file diff --git a/crates/libm-analyze/Cargo.toml b/crates/libm-analyze/Cargo.toml new file mode 100644 index 000000000..58c23ce11 --- /dev/null +++ b/crates/libm-analyze/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "libm-analyze" +version = "0.1.0" +authors = ["gnzlbg "] +edition = "2018" + +[lib] +proc-macro = true +test = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = { version = "0.15", features = ["full", "extra-traits"] } +quote = "0.6" +walkdir = "2.2.8" \ No newline at end of file diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs new file mode 100644 index 000000000..a69dcb6d9 --- /dev/null +++ b/crates/libm-analyze/src/lib.rs @@ -0,0 +1,96 @@ +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; +use self::proc_macro::TokenStream; + +#[proc_macro] +pub fn for_each_api(input: TokenStream) -> TokenStream { + let files = get_libm_files(); + let functions = get_functions(files); + input +} + +/// Traverses the libm crate directory, parsing all .rs files +fn get_libm_files() -> Vec<(syn::File, String)> { + let root_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + dbg!(&root_dir); + let libm_dir = root_dir + .parent() + .expect("couldn't access crates/ dir") + .join("libm"); + dbg!(&libm_dir); + let libm_src_dir = libm_dir.join("src"); + dbg!(&libm_src_dir); + + let mut files = Vec::new(); + for entry in walkdir::WalkDir::new(libm_src_dir) + .into_iter() + .filter_map(|e| e.ok()) + { + use std::io::Read; + let file_path = entry.path(); + if file_path.is_dir() + || !file_path + .to_str() + .expect("can't format file path") + .ends_with(".rs") + { + continue; + } + + eprintln!("{}", file_path.display()); + let mut file_string = String::new(); + std::fs::File::open(&file_path) + .unwrap_or_else(|_| panic!("can't open file at path: {}", file_path.display())) + .read_to_string(&mut file_string) + .expect("failed to read file to string"); + let file = syn::parse_file(&file_string).expect("failed to parse"); + files.push((file, file_path.to_str().unwrap().to_string())); + } + files +} + +struct FnSig { + ident: syn::Ident, + unsafety: bool, + constness: bool, + asyncness: bool, + attrs: Vec, + abi: &'static str, + +} + +/// Extracts all public functions from the libm files. +fn get_functions(files: Vec<(syn::File, String)>) { + //let mut functions = Vec::new(); + for item in files.iter().flat_map(|f| f.0.items.iter()) { + match item { + syn::Item::Fn(syn::ItemFn { + vis: syn::Visibility::Public(_), + ident, + constness, + asyncness, + unsafety, + attrs, + abi, + decl, + block: _, + }) => { + if let Some(syn::Abi { name: Some(l), extern_token: _ }) = abi { + println!("{:#?}", l); + if l.value() != "C" { + l.span().unwrap().warning( + "public libm function is not `extern \"C\"" + ).emit(); + } + } else { + ident.span().unwrap().warning( + "public libm function is not `extern \"C\"" + ).emit(); + } + println!("{:#?}", ident); + } + _ => (), + } + } +} diff --git a/crates/libm-verify/Cargo.toml b/crates/libm-verify/Cargo.toml new file mode 100644 index 000000000..face54038 --- /dev/null +++ b/crates/libm-verify/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "libm-verify" +version = "0.1.0" +authors = ["gnzlbg "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +libm-analyze = { path = "../libm-analyze" } diff --git a/crates/libm-verify/src/lib.rs b/crates/libm-verify/src/lib.rs new file mode 100644 index 000000000..46330df36 --- /dev/null +++ b/crates/libm-verify/src/lib.rs @@ -0,0 +1 @@ +//! TODO diff --git a/crates/libm-verify/tests/musl.rs b/crates/libm-verify/tests/musl.rs new file mode 100644 index 000000000..6a4dc6b92 --- /dev/null +++ b/crates/libm-verify/tests/musl.rs @@ -0,0 +1,3 @@ +#![cfg(test)] + +libm_analyze::for_each_api!(const X: i32 = 0;); From 5570b2f96097f84e254d1577e056c2965b5dc3ca Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 17:33:13 +0200 Subject: [PATCH 03/43] libm-analyze: a proc macro to analyze libm APIs This proc macro takes a macro of the form: ``` macro_rules! nop { ( id: $id:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; ret: $ty:ty; ) => {}; } ``` as input and expands it on every public libm API. This facilitates generating tests, benchmarks, etc. for all APIs. --- Cargo.toml | 2 +- crates/libm-analyze/Cargo.toml | 11 +- crates/libm-analyze/src/lib.rs | 219 ++++++++++++++++++++++++--- crates/libm-analyze/tests/analyze.rs | 13 ++ 4 files changed, 219 insertions(+), 26 deletions(-) create mode 100644 crates/libm-analyze/tests/analyze.rs diff --git a/Cargo.toml b/Cargo.toml index 5d2abd571..acfea457a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ "crates/libm", "crates/libm-analyze", - "crates/libm-verify", + "crates/libm-test", "crates/compiler-builtins-smoke-test", "crates/libm-bench", ] \ No newline at end of file diff --git a/crates/libm-analyze/Cargo.toml b/crates/libm-analyze/Cargo.toml index 58c23ce11..7debe8948 100644 --- a/crates/libm-analyze/Cargo.toml +++ b/crates/libm-analyze/Cargo.toml @@ -1,16 +1,19 @@ [package] name = "libm-analyze" version = "0.1.0" -authors = ["gnzlbg "] +authors = ["Gonzalo Brito Gadeschi "] edition = "2018" [lib] proc-macro = true test = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] syn = { version = "0.15", features = ["full", "extra-traits"] } quote = "0.6" -walkdir = "2.2.8" \ No newline at end of file +walkdir = "2.2.8" +proc-macro2 = "0.4" + +[features] +default = ["analyze"] +analyze = [] \ No newline at end of file diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index a69dcb6d9..39d57ceb3 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -2,25 +2,41 @@ extern crate proc_macro; use self::proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; #[proc_macro] pub fn for_each_api(input: TokenStream) -> TokenStream { let files = get_libm_files(); let functions = get_functions(files); - input + let input = parse_macro_input!(input as syn::Ident); + let mut tokens = proc_macro2::TokenStream::new(); + for function in functions { + let id = function.ident; + let ret_ty = function.ret_ty; + let arg_tys = function.arg_tys; + let arg_ids = get_arg_ids(arg_tys.len()); + let t = quote! { + #input! { + id: #id; + arg_tys: #(#arg_tys),*; + arg_ids: #(#arg_ids),*; + ret: #ret_ty; + } + }; + tokens.extend(t); + } + tokens.into() } /// Traverses the libm crate directory, parsing all .rs files fn get_libm_files() -> Vec<(syn::File, String)> { let root_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - dbg!(&root_dir); let libm_dir = root_dir .parent() .expect("couldn't access crates/ dir") .join("libm"); - dbg!(&libm_dir); let libm_src_dir = libm_dir.join("src"); - dbg!(&libm_src_dir); let mut files = Vec::new(); for entry in walkdir::WalkDir::new(libm_src_dir) @@ -38,7 +54,6 @@ fn get_libm_files() -> Vec<(syn::File, String)> { continue; } - eprintln!("{}", file_path.display()); let mut file_string = String::new(); std::fs::File::open(&file_path) .unwrap_or_else(|_| panic!("can't open file at path: {}", file_path.display())) @@ -53,17 +68,33 @@ fn get_libm_files() -> Vec<(syn::File, String)> { struct FnSig { ident: syn::Ident, unsafety: bool, - constness: bool, - asyncness: bool, - attrs: Vec, - abi: &'static str, + c_abi: bool, + ret_ty: Option, + arg_tys: Vec, +} + +impl FnSig { + fn name(&self) -> String { + self.ident.to_string() + } +} +macro_rules! syn_to_str { + ($e:expr) => {{ + let t = $e; + let tokens = quote! { + #t + }; + format!("{}", tokens) + }}; } /// Extracts all public functions from the libm files. -fn get_functions(files: Vec<(syn::File, String)>) { - //let mut functions = Vec::new(); +fn get_functions(files: Vec<(syn::File, String)>) -> Vec { + let mut error = false; + let mut functions = Vec::new(); for item in files.iter().flat_map(|f| f.0.items.iter()) { + let mut e = false; match item { syn::Item::Fn(syn::ItemFn { vis: syn::Visibility::Public(_), @@ -76,21 +107,167 @@ fn get_functions(files: Vec<(syn::File, String)>) { decl, block: _, }) => { - if let Some(syn::Abi { name: Some(l), extern_token: _ }) = abi { - println!("{:#?}", l); - if l.value() != "C" { - l.span().unwrap().warning( - "public libm function is not `extern \"C\"" - ).emit(); + let mut fn_sig = FnSig { + ident: ident.clone(), + unsafety: true, + c_abi: false, + arg_tys: Vec::new(), + ret_ty: None, + }; + macro_rules! err { + ($msg:expr) => {{ + #[cfg(feature = "analyze")] + { + eprintln!("[error]: Function \"{}\" {}", fn_sig.name(), $msg); + } + #[allow(unused_assignments)] + { + e = true; + } + () + }}; + } + if let Some(syn::Abi { + name: Some(l), + extern_token: _, + }) = abi + { + if l.value() == "C" { + fn_sig.c_abi = true; + } + } + if let Some(_) = constness { + err!("is const"); + } + if let Some(_) = asyncness { + err!("is async"); + } + if &None == unsafety { + fn_sig.unsafety = false; + } + let syn::FnDecl { + fn_token: _, + generics, + paren_token: _, + inputs, + variadic, + output, + } = (**decl).clone(); + + if variadic.is_some() { + err!(format!( + "contains variadic arguments \"{}\"", + syn_to_str!(variadic.unwrap()) + )); + } + if generics.type_params().into_iter().count() != 0 { + err!(format!( + "contains generic parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + if generics.lifetimes().into_iter().count() != 0 { + err!(format!( + "contains lifetime parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + if generics.const_params().into_iter().count() != 0 { + err!(format!( + "contains const parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + if attrs.is_empty() { + err!(format!( + "missing `#[inline]` and `#[no_panic]` attributes {}", + attrs + .iter() + .map(|a| syn_to_str!(a)) + .collect::>() + .join(",") + )); + } // TODO: might want to check other attributes as well + if !fn_sig.c_abi { + // FIXME: do not disable test if this fails - otherwise no test passes + let e2 = e; + err!("not `extern \"C\"`"); + e = e2; + } + match output { + syn::ReturnType::Default => (), + syn::ReturnType::Type(_, ref b) if valid_ty(&b) => { + fn_sig.ret_ty = Some(*b.clone()) + } + other => err!(format!("returns unsupported type {}", syn_to_str!(other))), + } + for input in inputs { + match input { + syn::FnArg::Captured(ref c) if valid_ty(&c.ty) => { + fn_sig.arg_tys.push(c.ty.clone()) + } + other => err!(format!( + "takes unsupported argument type {}", + syn_to_str!(other) + )), } + } + if !e { + functions.push(fn_sig); } else { - ident.span().unwrap().warning( - "public libm function is not `extern \"C\"" - ).emit(); + error = true; } - println!("{:#?}", ident); } _ => (), } } + if error { + // too many errors: + // panic!("errors found"); + } + functions +} + +/// Parses a type into a String - arg is true if the type is an argument, and +/// false if its a return value. +fn valid_ty(t: &syn::Type) -> bool { + match t { + syn::Type::Ptr(p) => { + let c = p.const_token.is_some(); + let m = p.mutability.is_some(); + assert!(!(c && m)); + match &*p.elem { + syn::Type::Path(_) => valid_ty(&p.elem), + // Only one layer of pointers allowed: + _ => false, + } + } + syn::Type::Path(p) => { + assert!(p.qself.is_none()); + assert_eq!(p.path.segments.len(), 1); + let s = p + .path + .segments + .first() + .unwrap() + .into_value() + .ident + .to_string(); + match s.as_str() { + "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize" + | "f32" | "f64" => true, + _ => false, + } + } + _ => false, + } +} + +fn get_arg_ids(len: usize) -> Vec { + let mut ids = Vec::new(); + for i in 0..len { + let x = format!("x{}", i); + ids.push(syn::Ident::new(&x, proc_macro2::Span::call_site())); + } + ids } diff --git a/crates/libm-analyze/tests/analyze.rs b/crates/libm-analyze/tests/analyze.rs new file mode 100644 index 000000000..d4dbe74d2 --- /dev/null +++ b/crates/libm-analyze/tests/analyze.rs @@ -0,0 +1,13 @@ +//! Tests that the proc-macro accepts macros with +//! the following pattern: + +macro_rules! nop { + ( + id: $id:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ty:ty; + ) => {}; +} + +libm_analyze::for_each_api!(nop); From fa2dde07dfec89772f1687f42ea5701259158411 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 17:35:20 +0200 Subject: [PATCH 04/43] The FP environment functions are private --- crates/libm/src/math/fenv.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/libm/src/math/fenv.rs b/crates/libm/src/math/fenv.rs index 63bb20368..652e60324 100644 --- a/crates/libm/src/math/fenv.rs +++ b/crates/libm/src/math/fenv.rs @@ -1,33 +1,33 @@ // src: musl/src/fenv/fenv.c /* Dummy functions for archs lacking fenv implementation */ -pub const FE_UNDERFLOW: i32 = 0; -pub const FE_INEXACT: i32 = 0; +pub(crate) const FE_UNDERFLOW: i32 = 0; +pub(crate) const FE_INEXACT: i32 = 0; -pub const FE_TONEAREST: i32 = 0; -pub const FE_TOWARDZERO: i32 = 0; +pub(crate) const FE_TONEAREST: i32 = 0; +pub(crate) const FE_TOWARDZERO: i32 = 0; #[inline] -pub fn feclearexcept(_mask: i32) -> i32 { +pub(crate) fn feclearexcept(_mask: i32) -> i32 { 0 } #[inline] -pub fn feraiseexcept(_mask: i32) -> i32 { +pub(crate) fn feraiseexcept(_mask: i32) -> i32 { 0 } #[inline] -pub fn fetestexcept(_mask: i32) -> i32 { +pub(crate) fn fetestexcept(_mask: i32) -> i32 { 0 } #[inline] -pub fn fegetround() -> i32 { +pub(crate) fn fegetround() -> i32 { FE_TONEAREST } #[inline] -pub fn fesetround(_r: i32) -> i32 { +pub(crate) fn fesetround(_r: i32) -> i32 { 0 } From 28d7851d6a9cc86c304cd9c6cb63df7f65d98b45 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 17:35:29 +0200 Subject: [PATCH 05/43] libm-test: expands tests for all libm APIs Currently, only the randomly generated tests from the old build.rs are generated. --- crates/libm-test/Cargo.toml | 14 +++ crates/libm-test/build.rs | 12 +++ crates/libm-test/tests/system_libm.rs | 142 ++++++++++++++++++++++++++ crates/libm-verify/Cargo.toml | 10 -- crates/libm-verify/src/lib.rs | 1 - crates/libm-verify/tests/musl.rs | 3 - 6 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 crates/libm-test/Cargo.toml create mode 100644 crates/libm-test/build.rs create mode 100644 crates/libm-test/tests/system_libm.rs delete mode 100644 crates/libm-verify/Cargo.toml delete mode 100644 crates/libm-verify/src/lib.rs delete mode 100644 crates/libm-verify/tests/musl.rs diff --git a/crates/libm-test/Cargo.toml b/crates/libm-test/Cargo.toml new file mode 100644 index 000000000..9e8f56ad3 --- /dev/null +++ b/crates/libm-test/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libm-test" +version = "0.1.0" +authors = ["Gonzalo Brito Gadeschi "] +edition = "2018" + +[dev-dependencies] +libm = { path = "../libm" } +libm-analyze = { path = "../libm-analyze", no-default-features = true } +rand = "0.7" + +[features] +default = [] +checked = ["libm/checked"] diff --git a/crates/libm-test/build.rs b/crates/libm-test/build.rs new file mode 100644 index 000000000..80837c5b8 --- /dev/null +++ b/crates/libm-test/build.rs @@ -0,0 +1,12 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + if !cfg!(feature = "checked") { + let lvl = env::var("OPT_LEVEL").unwrap(); + if lvl != "0" { + println!("cargo:rustc-cfg=assert_no_panic"); + } + } +} diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs new file mode 100644 index 000000000..5b3713ca7 --- /dev/null +++ b/crates/libm-test/tests/system_libm.rs @@ -0,0 +1,142 @@ +//! Compare the results of the `libm` implementation against the system's libm. +#![cfg(test)] + +// Number of tests to generate for each function +const NTESTS: usize = 500; + +// FIXME: should be 1 +const ULP_TOL: usize = 3; + +macro_rules! system_libm { + ( + id: $id:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => { + #[test] + #[allow(unused)] + fn $id() { + use crate::Call; + let mut rng = rand::thread_rng(); + for _ in 0..NTESTS { + let args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); + extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { + libm::$id($($arg_ids),*) + } + let result = args.call(libm_fn); + extern "C" { + fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; + } + let expected = args.call($id); + if !result.eq(expected) { + eprintln!("result = {} != {} (expected)", result, expected); + panic!(); + } + } + } + } +} + +libm_analyze::for_each_api!(system_libm); + +trait Call { + type Ret; + fn call(self, f: F) -> Self::Ret; +} + + +macro_rules! impl_call { + (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { + impl Call $ret_ty> for ($($arg_tys),+) { + type Ret = $ret_ty; + fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { + let $self_ = self; + unsafe { f($($xs),*) } + } + } + }; +} + +impl_call!((f32) -> f32: x: x); +impl_call!((f32,f32) -> f32: x: x.0, x.1); +impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2); +impl_call!((f64) -> f64: x: x); +impl_call!((f64,f64) -> f64: x: x.0, x.1); +impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2); + +impl_call!((f64, i32) -> f64: x: x.0, x.1); +impl_call!((f32, i32) -> f32: x: x.0, x.1); + +trait Rand { + fn gen(rng: &mut rand::rngs::ThreadRng) -> Self; +} + +macro_rules! impl_rand { + ($id:ident: [$($e:expr),*]) => { + impl Rand for $id { + fn gen(r: &mut rand::rngs::ThreadRng) -> Self { + use rand::Rng; + use rand::seq::SliceRandom; + let r = if r.gen_range(0, 20) < 1 { + *[$($e),*].choose(r).unwrap() + } else { + r.gen::<$id>() + }; + unsafe { std::mem::transmute(r) } + } + } + } +} + +impl_rand!(f32: [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY]); +impl_rand!(f64: [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY]); +impl_rand!(i32: [i32::max_value(), 0_i32, i32::min_value()]); + +trait Equal { + fn eq(self, other: Self) -> bool; +} + +macro_rules! impl_eq_f { + ($f_ty:ty, $i_ty:ty) => { + impl Equal for $f_ty { + fn eq(self, y: $f_ty) -> bool { + let x = self; + if x.is_nan() != y.is_nan() { + // one is nan but the other is not + return false; + } + if x.is_nan() && y.is_nan() { + return true; + } + if x.is_infinite() != y.is_infinite() { + // one is inf but the other is not + return false; + } + if x.is_infinite() != y.is_infinite() { + // one is inf but the other is not + return false; + } + let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; + let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; + if (xi < 0) != (yi < 0) { + // different sign + return false; + } + let ulps = (xi - yi).abs(); + ulps <= ULP_TOL as _ + } + } + } +} + +impl_eq_f!(f32, i32); +impl_eq_f!(f64, i64); + +impl Equal for i32 { + fn eq(self, y: i32) -> bool { + let x = self; + let ulps = (x - y).abs(); + ulps <= 1 + } +} diff --git a/crates/libm-verify/Cargo.toml b/crates/libm-verify/Cargo.toml deleted file mode 100644 index face54038..000000000 --- a/crates/libm-verify/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "libm-verify" -version = "0.1.0" -authors = ["gnzlbg "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dev-dependencies] -libm-analyze = { path = "../libm-analyze" } diff --git a/crates/libm-verify/src/lib.rs b/crates/libm-verify/src/lib.rs deleted file mode 100644 index 46330df36..000000000 --- a/crates/libm-verify/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -//! TODO diff --git a/crates/libm-verify/tests/musl.rs b/crates/libm-verify/tests/musl.rs deleted file mode 100644 index 6a4dc6b92..000000000 --- a/crates/libm-verify/tests/musl.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![cfg(test)] - -libm_analyze::for_each_api!(const X: i32 = 0;); From 4787bfcc9a0f4e2e8e60a9a6a721663b31efd533 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 17:55:35 +0200 Subject: [PATCH 06/43] Remove old build.rs testing --- build.rs | 444 ------------------------------------------------------- 1 file changed, 444 deletions(-) delete mode 100644 build.rs diff --git a/build.rs b/build.rs deleted file mode 100644 index 9af6dec93..000000000 --- a/build.rs +++ /dev/null @@ -1,444 +0,0 @@ -use std::env; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - #[cfg(feature = "musl-reference-tests")] - musl_reference_tests::generate(); - - if !cfg!(feature = "checked") { - let lvl = env::var("OPT_LEVEL").unwrap(); - if lvl != "0" { - println!("cargo:rustc-cfg=assert_no_panic"); - } - } -} - -#[cfg(feature = "musl-reference-tests")] -mod musl_reference_tests { - use rand::seq::SliceRandom; - use rand::Rng; - use std::fs; - use std::process::Command; - - // Number of tests to generate for each function - const NTESTS: usize = 500; - - // These files are all internal functions or otherwise miscellaneous, not - // defining a function we want to test. - const IGNORED_FILES: &[&str] = &["fenv.rs"]; - - struct Function { - name: String, - args: Vec, - ret: Vec, - tests: Vec, - } - - enum Ty { - F32, - F64, - I32, - Bool, - } - - struct Test { - inputs: Vec, - outputs: Vec, - } - - pub fn generate() { - let files = fs::read_dir("src/math") - .unwrap() - .map(|f| f.unwrap().path()) - .collect::>(); - - let mut math = Vec::new(); - for file in files { - if IGNORED_FILES.iter().any(|f| file.ends_with(f)) { - continue; - } - - println!("generating musl reference tests in {:?}", file); - - let contents = fs::read_to_string(file).unwrap(); - let mut functions = contents.lines().filter(|f| f.starts_with("pub fn")); - while let Some(function_to_test) = functions.next() { - math.push(parse(function_to_test)); - } - } - - // Generate a bunch of random inputs for each function. This will - // attempt to generate a good set of uniform test cases for exercising - // all the various functionality. - generate_random_tests(&mut math, &mut rand::thread_rng()); - - // After we have all our inputs, use the x86_64-unknown-linux-musl - // target to generate the expected output. - generate_test_outputs(&mut math); - //panic!("Boo"); - // ... and now that we have both inputs and expected outputs, do a bunch - // of codegen to create the unit tests which we'll actually execute. - generate_unit_tests(&math); - } - - /// A "poor man's" parser for the signature of a function - fn parse(s: &str) -> Function { - let s = eat(s, "pub fn "); - let pos = s.find('(').unwrap(); - let name = &s[..pos]; - let s = &s[pos + 1..]; - let end = s.find(')').unwrap(); - let args = s[..end] - .split(',') - .map(|arg| { - let colon = arg.find(':').unwrap(); - parse_ty(arg[colon + 1..].trim()) - }) - .collect::>(); - let tail = &s[end + 1..]; - let tail = eat(tail, " -> "); - let ret = parse_retty(tail.replace("{", "").trim()); - - return Function { - name: name.to_string(), - args, - ret, - tests: Vec::new(), - }; - - fn parse_ty(s: &str) -> Ty { - match s { - "f32" => Ty::F32, - "f64" => Ty::F64, - "i32" => Ty::I32, - "bool" => Ty::Bool, - other => panic!("unknown type `{}`", other), - } - } - - fn parse_retty(s: &str) -> Vec { - match s { - "(f32, f32)" => vec![Ty::F32, Ty::F32], - "(f32, i32)" => vec![Ty::F32, Ty::I32], - "(f64, f64)" => vec![Ty::F64, Ty::F64], - "(f64, i32)" => vec![Ty::F64, Ty::I32], - other => vec![parse_ty(other)], - } - } - - fn eat<'a>(s: &'a str, prefix: &str) -> &'a str { - if s.starts_with(prefix) { - &s[prefix.len()..] - } else { - panic!("{:?} didn't start with {:?}", s, prefix) - } - } - } - - fn generate_random_tests(functions: &mut [Function], rng: &mut R) { - for function in functions { - for _ in 0..NTESTS { - function.tests.push(generate_test(function, rng)); - } - } - - fn generate_test(function: &Function, rng: &mut R) -> Test { - let mut inputs = function - .args - .iter() - .map(|ty| ty.gen_i64(rng)) - .collect::>(); - - // First argument to this function appears to be a number of - // iterations, so passing in massive random numbers causes it to - // take forever to execute, so make sure we're not running random - // math code until the heat death of the universe. - if function.name == "jn" || function.name == "jnf" { - inputs[0] &= 0xffff; - } - - Test { - inputs, - // zero output for now since we'll generate it later - outputs: vec![], - } - } - } - - impl Ty { - fn gen_i64(&self, r: &mut R) -> i64 { - use std::f32; - use std::f64; - - return match self { - Ty::F32 => { - if r.gen_range(0, 20) < 1 { - let i = *[f32::NAN, f32::INFINITY, f32::NEG_INFINITY] - .choose(r) - .unwrap(); - i.to_bits().into() - } else { - r.gen::().to_bits().into() - } - } - Ty::F64 => { - if r.gen_range(0, 20) < 1 { - let i = *[f64::NAN, f64::INFINITY, f64::NEG_INFINITY] - .choose(r) - .unwrap(); - i.to_bits() as i64 - } else { - r.gen::().to_bits() as i64 - } - } - Ty::I32 => { - if r.gen_range(0, 10) < 1 { - let i = *[i32::max_value(), 0, i32::min_value()].choose(r).unwrap(); - i.into() - } else { - r.gen::().into() - } - } - Ty::Bool => r.gen::() as i64, - }; - } - - fn libc_ty(&self) -> &'static str { - match self { - Ty::F32 => "f32", - Ty::F64 => "f64", - Ty::I32 => "i32", - Ty::Bool => "i32", - } - } - - fn libc_pty(&self) -> &'static str { - match self { - Ty::F32 => "*mut f32", - Ty::F64 => "*mut f64", - Ty::I32 => "*mut i32", - Ty::Bool => "*mut i32", - } - } - - fn default(&self) -> &'static str { - match self { - Ty::F32 => "0_f32", - Ty::F64 => "0_f64", - Ty::I32 => "0_i32", - Ty::Bool => "false", - } - } - - fn to_i64(&self) -> &'static str { - match self { - Ty::F32 => ".to_bits() as i64", - Ty::F64 => ".to_bits() as i64", - Ty::I32 => " as i64", - Ty::Bool => " as i64", - } - } - } - - fn generate_test_outputs(functions: &mut [Function]) { - let mut src = String::new(); - let dst = std::env::var("OUT_DIR").unwrap(); - - // Generate a program which will run all tests with all inputs in - // `functions`. This program will write all outputs to stdout (in a - // binary format). - src.push_str("use std::io::Write;"); - src.push_str("fn main() {"); - src.push_str("let mut result = Vec::new();"); - for function in functions.iter_mut() { - src.push_str("unsafe {"); - src.push_str("extern { fn "); - src.push_str(&function.name); - src.push_str("("); - - let (ret, retptr) = match function.name.as_str() { - "sincos" | "sincosf" => (None, &function.ret[..]), - _ => (Some(&function.ret[0]), &function.ret[1..]), - }; - for (i, arg) in function.args.iter().enumerate() { - src.push_str(&format!("arg{}: {},", i, arg.libc_ty())); - } - for (i, ret) in retptr.iter().enumerate() { - src.push_str(&format!("argret{}: {},", i, ret.libc_pty())); - } - src.push_str(")"); - if let Some(ty) = ret { - src.push_str(" -> "); - src.push_str(ty.libc_ty()); - } - src.push_str("; }"); - - src.push_str(&format!("static TESTS: &[[i64; {}]]", function.args.len())); - src.push_str(" = &["); - for test in function.tests.iter() { - src.push_str("["); - for val in test.inputs.iter() { - src.push_str(&val.to_string()); - src.push_str(","); - } - src.push_str("],"); - } - src.push_str("];"); - - src.push_str("for test in TESTS {"); - for (i, arg) in retptr.iter().enumerate() { - src.push_str(&format!("let mut argret{} = {};", i, arg.default())); - } - src.push_str("let output = "); - src.push_str(&function.name); - src.push_str("("); - for (i, arg) in function.args.iter().enumerate() { - src.push_str(&match arg { - Ty::F32 => format!("f32::from_bits(test[{}] as u32)", i), - Ty::F64 => format!("f64::from_bits(test[{}] as u64)", i), - Ty::I32 => format!("test[{}] as i32", i), - Ty::Bool => format!("test[{}] as i32", i), - }); - src.push_str(","); - } - for (i, _) in retptr.iter().enumerate() { - src.push_str(&format!("&mut argret{},", i)); - } - src.push_str(");"); - if let Some(ty) = &ret { - src.push_str(&format!("let output = output{};", ty.to_i64())); - src.push_str("result.extend_from_slice(&output.to_le_bytes());"); - } - - for (i, ret) in retptr.iter().enumerate() { - src.push_str(&format!( - "result.extend_from_slice(&(argret{}{}).to_le_bytes());", - i, - ret.to_i64(), - )); - } - src.push_str("}"); - - src.push_str("}"); - } - - src.push_str("std::io::stdout().write_all(&result).unwrap();"); - - src.push_str("}"); - - let path = format!("{}/gen.rs", dst); - fs::write(&path, src).unwrap(); - - // Make it somewhat pretty if something goes wrong - drop(Command::new("rustfmt").arg(&path).status()); - - // Compile and execute this tests for the musl target, assuming we're an - // x86_64 host effectively. - let status = Command::new("rustc") - .current_dir(&dst) - .arg(&path) - .arg("--target=x86_64-unknown-linux-musl") - .status() - .unwrap(); - assert!(status.success()); - let output = Command::new("./gen").current_dir(&dst).output().unwrap(); - assert!(output.status.success()); - assert!(output.stderr.is_empty()); - - // Map all the output bytes back to an `i64` and then shove it all into - // the expected results. - let mut results = output.stdout.chunks_exact(8).map(|buf| { - let mut exact = [0; 8]; - exact.copy_from_slice(buf); - i64::from_le_bytes(exact) - }); - - for f in functions.iter_mut() { - for test in f.tests.iter_mut() { - test.outputs = (0..f.ret.len()).map(|_| results.next().unwrap()).collect(); - } - } - assert!(results.next().is_none()); - } - - /// Codegens a file which has a ton of `#[test]` annotations for all the - /// tests that we generated above. - fn generate_unit_tests(functions: &[Function]) { - let mut src = String::new(); - let dst = std::env::var("OUT_DIR").unwrap(); - - for function in functions { - src.push_str("#[test]"); - src.push_str("fn "); - src.push_str(&function.name); - src.push_str("_matches_musl() {"); - src.push_str(&format!( - "static TESTS: &[([i64; {}], [i64; {}])]", - function.args.len(), - function.ret.len(), - )); - src.push_str(" = &["); - for test in function.tests.iter() { - src.push_str("(["); - for val in test.inputs.iter() { - src.push_str(&val.to_string()); - src.push_str(","); - } - src.push_str("],"); - src.push_str("["); - for val in test.outputs.iter() { - src.push_str(&val.to_string()); - src.push_str(","); - } - src.push_str("],"); - src.push_str("),"); - } - src.push_str("];"); - - src.push_str("for (test, expected) in TESTS {"); - src.push_str("let output = "); - src.push_str(&function.name); - src.push_str("("); - for (i, arg) in function.args.iter().enumerate() { - src.push_str(&match arg { - Ty::F32 => format!("f32::from_bits(test[{}] as u32)", i), - Ty::F64 => format!("f64::from_bits(test[{}] as u64)", i), - Ty::I32 => format!("test[{}] as i32", i), - Ty::Bool => format!("test[{}] as i32", i), - }); - src.push_str(","); - } - src.push_str(");"); - - for (i, ret) in function.ret.iter().enumerate() { - let get = if function.ret.len() == 1 { - String::new() - } else { - format!(".{}", i) - }; - src.push_str(&(match ret { - Ty::F32 => format!("if _eqf(output{}, f32::from_bits(expected[{}] as u32)).is_ok() {{ continue }}", get, i), - Ty::F64 => format!("if _eq(output{}, f64::from_bits(expected[{}] as u64)).is_ok() {{ continue }}", get, i), - Ty::I32 => format!("if output{} as i64 == expected[{}] {{ continue }}", get, i), - Ty::Bool => unreachable!(), - })); - } - - src.push_str( - r#" - panic!("INPUT: {:?} EXPECTED: {:?} ACTUAL {:?}", test, expected, output); - "#, - ); - src.push_str("}"); - - src.push_str("}"); - } - - let path = format!("{}/musl-tests.rs", dst); - fs::write(&path, src).unwrap(); - - // Try to make it somewhat pretty - drop(Command::new("rustfmt").arg(&path).status()); - } -} From 38b18281b1fe9868634ecca2b0e8e10e9812759e Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 17:55:49 +0200 Subject: [PATCH 07/43] Move most unit tests to the libm-test crate --- crates/libm-test/tests/system_libm.rs | 3 +- crates/libm-test/tests/unit.rs | 346 ++++++++++++++++++++++++++ crates/libm/src/lib.rs | 39 --- crates/libm/src/math/atan.rs | 48 ---- crates/libm/src/math/atan2.rs | 10 - crates/libm/src/math/ceil.rs | 9 - crates/libm/src/math/exp2.rs | 6 - crates/libm/src/math/expm1.rs | 8 - crates/libm/src/math/floorf.rs | 8 - crates/libm/src/math/j1f.rs | 14 -- crates/libm/src/math/pow.rs | 224 ----------------- crates/libm/src/math/remquo.rs | 11 - crates/libm/src/math/round.rs | 10 - crates/libm/src/math/roundf.rs | 10 - crates/libm/src/math/sin.rs | 7 - crates/libm/src/math/trunc.rs | 8 - crates/libm/src/math/truncf.rs | 8 - 17 files changed, 347 insertions(+), 422 deletions(-) create mode 100644 crates/libm-test/tests/unit.rs diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 5b3713ca7..e207b252f 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -45,7 +45,6 @@ trait Call { fn call(self, f: F) -> Self::Ret; } - macro_rules! impl_call { (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { impl Call $ret_ty> for ($($arg_tys),+) { @@ -127,7 +126,7 @@ macro_rules! impl_eq_f { ulps <= ULP_TOL as _ } } - } + }; } impl_eq_f!(f32, i32); diff --git a/crates/libm-test/tests/unit.rs b/crates/libm-test/tests/unit.rs new file mode 100644 index 000000000..ed5d95199 --- /dev/null +++ b/crates/libm-test/tests/unit.rs @@ -0,0 +1,346 @@ +#![cfg(test)] + +use libm::*; +#[test] +fn remquo_q_overflow() { + // 0xc000000000000001, 0x04c0000000000004 + let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); +} + +#[test] +fn ceil_sanity_check() { + assert_eq!(ceil(1.1), 2.0); + assert_eq!(ceil(2.9), 3.0); +} + +#[test] +fn atan2_sanity_check() { + use std::f64::consts::PI; + assert_eq!(atan2(0.0, 1.0), 0.0); + assert_eq!(atan2(0.0, -1.0), PI); + assert_eq!(atan2(-0.0, -1.0), -PI); + assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0)); + assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI); + assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI); +} + +#[test] +fn floor_overflow() { + assert_eq!(floorf(0.5), 0.0); +} + +#[test] +fn trunc_sanity_check() { + assert_eq!(trunc(1.1), 1.0); +} + +mod pow { + use libm::*; + use std::f64::consts::{E, PI}; + use std::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY}; + + const POS_ZERO: &[f64] = &[0.0]; + const NEG_ZERO: &[f64] = &[-0.0]; + const POS_ONE: &[f64] = &[1.0]; + const NEG_ONE: &[f64] = &[-1.0]; + const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI]; + const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI]; + const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON]; + const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON]; + const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX]; + const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; + const POS_ODDS: &[f64] = &[3.0, 7.0]; + const NEG_ODDS: &[f64] = &[-7.0, -3.0]; + const NANS: &[f64] = &[NAN]; + const POS_INF: &[f64] = &[INFINITY]; + const NEG_INF: &[f64] = &[NEG_INFINITY]; + + const ALL: &[&[f64]] = &[ + POS_ZERO, + NEG_ZERO, + NANS, + NEG_SMALL_FLOATS, + POS_SMALL_FLOATS, + NEG_FLOATS, + POS_FLOATS, + NEG_EVENS, + POS_EVENS, + NEG_ODDS, + POS_ODDS, + NEG_INF, + POS_INF, + NEG_ONE, + POS_ONE, + ]; + const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; + const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; + + fn pow_test(base: f64, exponent: f64, expected: f64) { + let res = pow(base, exponent); + assert!( + if expected.is_nan() { + res.is_nan() + } else { + pow(base, exponent) == expected + }, + "{} ** {} was {} instead of {}", + base, + exponent, + res, + expected + ); + } + + fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { + sets.iter() + .for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected))); + } + + fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { + sets.iter() + .for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected))); + } + + fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { + sets.iter().for_each(|s| { + s.iter().for_each(|val| { + let exp = expected(*val); + let res = computed(*val); + + assert!( + if exp.is_nan() { + res.is_nan() + } else { + exp == res + }, + "test for {} was {} instead of {}", + val, + res, + exp + ); + }) + }); + } + + #[test] + fn zero_as_exponent() { + test_sets_as_base(ALL, 0.0, 1.0); + test_sets_as_base(ALL, -0.0, 1.0); + } + + #[test] + fn one_as_base() { + test_sets_as_exponent(1.0, ALL, 1.0); + } + + #[test] + fn nan_inputs() { + // NAN as the base: + // (NAN ^ anything *but 0* should be NAN) + test_sets_as_exponent(NAN, &ALL[2..], NAN); + + // NAN as the exponent: + // (anything *but 1* ^ NAN should be NAN) + test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN); + } + + #[test] + fn infinity_as_base() { + // Positive Infinity as the base: + // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) + test_sets_as_exponent(INFINITY, &POS[1..], INFINITY); + + // (+Infinity ^ negative anything except 0 and NAN should be 0.0) + test_sets_as_exponent(INFINITY, &NEG[1..], 0.0); + + // Negative Infinity as the base: + // (-Infinity ^ positive odd ints should be -Infinity) + test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY); + + // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) + // We can lump in pos/neg odd ints here because they don't seem to + // cause panics (div by zero) in release mode (I think). + test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v)); + } + + #[test] + fn infinity_as_exponent() { + // Positive/Negative base greater than 1: + // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) + test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY); + + // (pos/neg > 1 ^ -Infinity should be 0.0) + test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0); + + // Positive/Negative base less than 1: + let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; + + // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) + test_sets_as_base(base_below_one, INFINITY, 0.0); + + // (pos/neg < 1 ^ -Infinity should be Infinity) + test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY); + + // Positive/Negative 1 as the base: + // (pos/neg 1 ^ Infinity should be 1) + test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0); + + // (pos/neg 1 ^ -Infinity should be 1) + test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0); + } + + #[test] + fn zero_as_base() { + // Positive Zero as the base: + // (+0 ^ anything positive but 0 and NAN should be +0) + test_sets_as_exponent(0.0, &POS[1..], 0.0); + + // (+0 ^ anything negative but 0 and NAN should be Infinity) + // (this should panic because we're dividing by zero) + test_sets_as_exponent(0.0, &NEG[1..], INFINITY); + + // Negative Zero as the base: + // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) + test_sets_as_exponent(-0.0, &POS[3..], 0.0); + + // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) + // (should panic because of divide by zero) + test_sets_as_exponent(-0.0, &NEG[3..], INFINITY); + + // (-0 ^ positive odd ints should be -0) + test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); + + // (-0 ^ negative odd ints should be -Infinity) + // (should panic because of divide by zero) + test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY); + } + + #[test] + fn special_cases() { + // One as the exponent: + // (anything ^ 1 should be anything - i.e. the base) + test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v); + + // Negative One as the exponent: + // (anything ^ -1 should be 1/anything) + test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v); + + // Factoring -1 out: + // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) + &[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS] + .iter() + .for_each(|int_set| { + int_set.iter().for_each(|int| { + test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| { + pow(-1.0, *int) * pow(v, *int) + }); + }) + }); + + // Negative base (imaginary results): + // (-anything except 0 and Infinity ^ non-integer should be NAN) + &NEG[1..(NEG.len() - 1)].iter().for_each(|set| { + set.iter().for_each(|val| { + test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN); + }) + }); + } + + #[test] + fn normal_cases() { + assert_eq!(pow(2.0, 20.0), (1 << 20) as f64); + assert_eq!(pow(-1.0, 9.0), -1.0); + assert!(pow(-1.0, 2.2).is_nan()); + assert!(pow(-1.0, -1.14).is_nan()); + } +} + +mod atan { + use libm::atan; + use std::f64; + + #[test] + fn sanity_check() { + for (input, answer) in [ + (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), + (1.0, f64::consts::FRAC_PI_4), + (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), + (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), + (-1.0, -f64::consts::FRAC_PI_4), + (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), + ] + .iter() + { + assert!( + (atan(*input) - answer) / answer < 1e-5, + "\natan({:.4}/16) = {:.4}, actual: {}", + input * 16.0, + answer, + atan(*input) + ); + } + } + + #[test] + fn zero() { + assert_eq!(atan(0.0), 0.0); + } + + #[test] + fn infinity() { + assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); + } + + #[test] + fn minus_infinity() { + assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); + } + + #[test] + fn nan() { + assert!(atan(f64::NAN).is_nan()); + } +} + +#[test] +fn sin_near_pi() { + let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707 + let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7 + assert_eq!(sin(x), sx); +} + +#[test] +fn truncf_sanity_check() { + assert_eq!(truncf(1.1), 1.0); +} + +#[test] +fn expm1_sanity_check() { + assert_eq!(expm1(1.1), 2.0041660239464334); +} + +#[test] +fn roundf_negative_zero() { + assert_eq!(roundf(-0.0_f32).to_bits(), (-0.0_f32).to_bits()); +} + +#[test] +fn exp2_i0_wrap_test() { + let x = -3.0 / 256.0; + assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514)); +} + +#[test] +fn round_negative_zero() { + assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits()); +} + +#[test] +fn j1f_2488() { + // 0x401F3E49 + assert_eq!(j1f(2.4881766_f32), 0.49999475_f32); +} +#[test] +fn y1f_2002() { + assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); +} diff --git a/crates/libm/src/lib.rs b/crates/libm/src/lib.rs index b15857dbe..527fcb4f9 100644 --- a/crates/libm/src/lib.rs +++ b/crates/libm/src/lib.rs @@ -7,43 +7,4 @@ )] mod math; - -use core::{f32, f64}; - pub use self::math::*; - -/// Approximate equality with 1 ULP of tolerance -#[doc(hidden)] -#[inline] -pub fn _eqf(a: f32, b: f32) -> Result<(), u32> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i32).wrapping_sub(b.to_bits() as i32).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u32) - } - } -} - -#[doc(hidden)] -#[inline] -pub fn _eq(a: f64, b: f64) -> Result<(), u64> { - if a.is_nan() && b.is_nan() { - Ok(()) - } else { - let err = (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs(); - - if err <= 1 { - Ok(()) - } else { - Err(err as u64) - } - } -} - -#[cfg(all(test, feature = "musl-reference-tests"))] -include!(concat!(env!("OUT_DIR"), "/musl-tests.rs")); diff --git a/crates/libm/src/math/atan.rs b/crates/libm/src/math/atan.rs index d2684ece8..f79fc0eb0 100644 --- a/crates/libm/src/math/atan.rs +++ b/crates/libm/src/math/atan.rs @@ -135,51 +135,3 @@ pub fn atan(x: f64) -> f64 { z } } - -#[cfg(test)] -mod tests { - use super::atan; - use core::f64; - - #[test] - fn sanity_check() { - for (input, answer) in [ - (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), - (1.0, f64::consts::FRAC_PI_4), - (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), - (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), - (-1.0, -f64::consts::FRAC_PI_4), - (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), - ] - .iter() - { - assert!( - (atan(*input) - answer) / answer < 1e-5, - "\natan({:.4}/16) = {:.4}, actual: {}", - input * 16.0, - answer, - atan(*input) - ); - } - } - - #[test] - fn zero() { - assert_eq!(atan(0.0), 0.0); - } - - #[test] - fn infinity() { - assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); - } - - #[test] - fn minus_infinity() { - assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); - } - - #[test] - fn nan() { - assert!(atan(f64::NAN).is_nan()); - } -} diff --git a/crates/libm/src/math/atan2.rs b/crates/libm/src/math/atan2.rs index 08385cd10..a303dcc60 100644 --- a/crates/libm/src/math/atan2.rs +++ b/crates/libm/src/math/atan2.rs @@ -115,13 +115,3 @@ pub fn atan2(y: f64, x: f64) -> f64 { _ => (z - PI_LO) - PI, /* atan(-,-) */ } } - -#[test] -fn sanity_check() { - assert_eq!(atan2(0.0, 1.0), 0.0); - assert_eq!(atan2(0.0, -1.0), PI); - assert_eq!(atan2(-0.0, -1.0), -PI); - assert_eq!(atan2(3.0, 2.0), atan(3.0 / 2.0)); - assert_eq!(atan2(2.0, -1.0), atan(2.0 / -1.0) + PI); - assert_eq!(atan2(-2.0, -1.0), atan(-2.0 / -1.0) - PI); -} diff --git a/crates/libm/src/math/ceil.rs b/crates/libm/src/math/ceil.rs index 59883a8a7..bb10eea53 100644 --- a/crates/libm/src/math/ceil.rs +++ b/crates/libm/src/math/ceil.rs @@ -40,12 +40,3 @@ pub fn ceil(x: f64) -> f64 { x + y } } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::ceil(1.1), 2.0); - assert_eq!(super::ceil(2.9), 3.0); - } -} diff --git a/crates/libm/src/math/exp2.rs b/crates/libm/src/math/exp2.rs index c2192fde5..96f230ef1 100644 --- a/crates/libm/src/math/exp2.rs +++ b/crates/libm/src/math/exp2.rs @@ -387,9 +387,3 @@ pub fn exp2(mut x: f64) -> f64 { scalbn(r, ki) } - -#[test] -fn i0_wrap_test() { - let x = -3.0 / 256.0; - assert_eq!(exp2(x), f64::from_bits(0x3fefbdba3692d514)); -} diff --git a/crates/libm/src/math/expm1.rs b/crates/libm/src/math/expm1.rs index 0d43b4e10..a5f5e4338 100644 --- a/crates/libm/src/math/expm1.rs +++ b/crates/libm/src/math/expm1.rs @@ -135,11 +135,3 @@ pub fn expm1(mut x: f64) -> f64 { } y } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::expm1(1.1), 2.0041660239464334); - } -} diff --git a/crates/libm/src/math/floorf.rs b/crates/libm/src/math/floorf.rs index c04f18aee..ca85e210f 100644 --- a/crates/libm/src/math/floorf.rs +++ b/crates/libm/src/math/floorf.rs @@ -40,11 +40,3 @@ pub fn floorf(x: f32) -> f32 { } f32::from_bits(ui) } - -#[cfg(test)] -mod tests { - #[test] - fn no_overflow() { - assert_eq!(super::floorf(0.5), 0.0); - } -} diff --git a/crates/libm/src/math/j1f.rs b/crates/libm/src/math/j1f.rs index 5095894d7..e0b692a7d 100644 --- a/crates/libm/src/math/j1f.rs +++ b/crates/libm/src/math/j1f.rs @@ -356,17 +356,3 @@ fn qonef(x: f32) -> f32 { s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * (q[4] + z * q[5]))))); return (0.375 + r / s) / x; } - -#[cfg(test)] -mod tests { - use super::{j1f, y1f}; - #[test] - fn test_j1f_2488() { - // 0x401F3E49 - assert_eq!(j1f(2.4881766_f32), 0.49999475_f32); - } - #[test] - fn test_y1f_2002() { - assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); - } -} diff --git a/crates/libm/src/math/pow.rs b/crates/libm/src/math/pow.rs index 068a4ec47..dca70f99b 100644 --- a/crates/libm/src/math/pow.rs +++ b/crates/libm/src/math/pow.rs @@ -408,227 +408,3 @@ pub fn pow(x: f64, y: f64) -> f64 { s * z } - -#[cfg(test)] -mod tests { - extern crate core; - - use self::core::f64::consts::{E, PI}; - use self::core::f64::{EPSILON, INFINITY, MAX, MIN, MIN_POSITIVE, NAN, NEG_INFINITY}; - use super::pow; - - const POS_ZERO: &[f64] = &[0.0]; - const NEG_ZERO: &[f64] = &[-0.0]; - const POS_ONE: &[f64] = &[1.0]; - const NEG_ONE: &[f64] = &[-1.0]; - const POS_FLOATS: &[f64] = &[99.0 / 70.0, E, PI]; - const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -E, -PI]; - const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), MIN_POSITIVE, EPSILON]; - const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -MIN_POSITIVE, -EPSILON]; - const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, MAX]; - const NEG_EVENS: &[f64] = &[MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; - const POS_ODDS: &[f64] = &[3.0, 7.0]; - const NEG_ODDS: &[f64] = &[-7.0, -3.0]; - const NANS: &[f64] = &[NAN]; - const POS_INF: &[f64] = &[INFINITY]; - const NEG_INF: &[f64] = &[NEG_INFINITY]; - - const ALL: &[&[f64]] = &[ - POS_ZERO, - NEG_ZERO, - NANS, - NEG_SMALL_FLOATS, - POS_SMALL_FLOATS, - NEG_FLOATS, - POS_FLOATS, - NEG_EVENS, - POS_EVENS, - NEG_ODDS, - POS_ODDS, - NEG_INF, - POS_INF, - NEG_ONE, - POS_ONE, - ]; - const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; - const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; - - fn pow_test(base: f64, exponent: f64, expected: f64) { - let res = pow(base, exponent); - assert!( - if expected.is_nan() { - res.is_nan() - } else { - pow(base, exponent) == expected - }, - "{} ** {} was {} instead of {}", - base, - exponent, - res, - expected - ); - } - - fn test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { - sets.iter() - .for_each(|s| s.iter().for_each(|val| pow_test(*val, exponent, expected))); - } - - fn test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { - sets.iter() - .for_each(|s| s.iter().for_each(|val| pow_test(base, *val, expected))); - } - - fn test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { - sets.iter().for_each(|s| { - s.iter().for_each(|val| { - let exp = expected(*val); - let res = computed(*val); - - assert!( - if exp.is_nan() { - res.is_nan() - } else { - exp == res - }, - "test for {} was {} instead of {}", - val, - res, - exp - ); - }) - }); - } - - #[test] - fn zero_as_exponent() { - test_sets_as_base(ALL, 0.0, 1.0); - test_sets_as_base(ALL, -0.0, 1.0); - } - - #[test] - fn one_as_base() { - test_sets_as_exponent(1.0, ALL, 1.0); - } - - #[test] - fn nan_inputs() { - // NAN as the base: - // (NAN ^ anything *but 0* should be NAN) - test_sets_as_exponent(NAN, &ALL[2..], NAN); - - // NAN as the exponent: - // (anything *but 1* ^ NAN should be NAN) - test_sets_as_base(&ALL[..(ALL.len() - 2)], NAN, NAN); - } - - #[test] - fn infinity_as_base() { - // Positive Infinity as the base: - // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) - test_sets_as_exponent(INFINITY, &POS[1..], INFINITY); - - // (+Infinity ^ negative anything except 0 and NAN should be 0.0) - test_sets_as_exponent(INFINITY, &NEG[1..], 0.0); - - // Negative Infinity as the base: - // (-Infinity ^ positive odd ints should be -Infinity) - test_sets_as_exponent(NEG_INFINITY, &[POS_ODDS], NEG_INFINITY); - - // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) - // We can lump in pos/neg odd ints here because they don't seem to - // cause panics (div by zero) in release mode (I think). - test_sets(ALL, &|v: f64| pow(NEG_INFINITY, v), &|v: f64| pow(-0.0, -v)); - } - - #[test] - fn infinity_as_exponent() { - // Positive/Negative base greater than 1: - // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) - test_sets_as_base(&ALL[5..(ALL.len() - 2)], INFINITY, INFINITY); - - // (pos/neg > 1 ^ -Infinity should be 0.0) - test_sets_as_base(&ALL[5..ALL.len() - 2], NEG_INFINITY, 0.0); - - // Positive/Negative base less than 1: - let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; - - // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) - test_sets_as_base(base_below_one, INFINITY, 0.0); - - // (pos/neg < 1 ^ -Infinity should be Infinity) - test_sets_as_base(base_below_one, NEG_INFINITY, INFINITY); - - // Positive/Negative 1 as the base: - // (pos/neg 1 ^ Infinity should be 1) - test_sets_as_base(&[NEG_ONE, POS_ONE], INFINITY, 1.0); - - // (pos/neg 1 ^ -Infinity should be 1) - test_sets_as_base(&[NEG_ONE, POS_ONE], NEG_INFINITY, 1.0); - } - - #[test] - fn zero_as_base() { - // Positive Zero as the base: - // (+0 ^ anything positive but 0 and NAN should be +0) - test_sets_as_exponent(0.0, &POS[1..], 0.0); - - // (+0 ^ anything negative but 0 and NAN should be Infinity) - // (this should panic because we're dividing by zero) - test_sets_as_exponent(0.0, &NEG[1..], INFINITY); - - // Negative Zero as the base: - // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) - test_sets_as_exponent(-0.0, &POS[3..], 0.0); - - // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) - // (should panic because of divide by zero) - test_sets_as_exponent(-0.0, &NEG[3..], INFINITY); - - // (-0 ^ positive odd ints should be -0) - test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); - - // (-0 ^ negative odd ints should be -Infinity) - // (should panic because of divide by zero) - test_sets_as_exponent(-0.0, &[NEG_ODDS], NEG_INFINITY); - } - - #[test] - fn special_cases() { - // One as the exponent: - // (anything ^ 1 should be anything - i.e. the base) - test_sets(ALL, &|v: f64| pow(v, 1.0), &|v: f64| v); - - // Negative One as the exponent: - // (anything ^ -1 should be 1/anything) - test_sets(ALL, &|v: f64| pow(v, -1.0), &|v: f64| 1.0 / v); - - // Factoring -1 out: - // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) - &[POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS] - .iter() - .for_each(|int_set| { - int_set.iter().for_each(|int| { - test_sets(ALL, &|v: f64| pow(-v, *int), &|v: f64| { - pow(-1.0, *int) * pow(v, *int) - }); - }) - }); - - // Negative base (imaginary results): - // (-anything except 0 and Infinity ^ non-integer should be NAN) - &NEG[1..(NEG.len() - 1)].iter().for_each(|set| { - set.iter().for_each(|val| { - test_sets(&ALL[3..7], &|v: f64| pow(*val, v), &|_| NAN); - }) - }); - } - - #[test] - fn normal_cases() { - assert_eq!(pow(2.0, 20.0), (1 << 20) as f64); - assert_eq!(pow(-1.0, 9.0), -1.0); - assert!(pow(-1.0, 2.2).is_nan()); - assert!(pow(-1.0, -1.14).is_nan()); - } -} diff --git a/crates/libm/src/math/remquo.rs b/crates/libm/src/math/remquo.rs index c72c8f187..8e027f242 100644 --- a/crates/libm/src/math/remquo.rs +++ b/crates/libm/src/math/remquo.rs @@ -96,14 +96,3 @@ pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { (x, quo) } } - -#[cfg(test)] -mod tests { - use super::remquo; - - #[test] - fn test_q_overflow() { - // 0xc000000000000001, 0x04c0000000000004 - let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); - } -} diff --git a/crates/libm/src/math/round.rs b/crates/libm/src/math/round.rs index 67590d2c1..13049fb25 100644 --- a/crates/libm/src/math/round.rs +++ b/crates/libm/src/math/round.rs @@ -35,13 +35,3 @@ pub fn round(mut x: f64) -> f64 { y } } - -#[cfg(test)] -mod tests { - use super::round; - - #[test] - fn negative_zero() { - assert_eq!(round(-0.0_f64).to_bits(), (-0.0_f64).to_bits()); - } -} diff --git a/crates/libm/src/math/roundf.rs b/crates/libm/src/math/roundf.rs index 85114be4b..d6f15e58d 100644 --- a/crates/libm/src/math/roundf.rs +++ b/crates/libm/src/math/roundf.rs @@ -33,13 +33,3 @@ pub fn roundf(mut x: f32) -> f32 { y } } - -#[cfg(test)] -mod tests { - use super::roundf; - - #[test] - fn negative_zero() { - assert_eq!(roundf(-0.0_f32).to_bits(), (-0.0_f32).to_bits()); - } -} diff --git a/crates/libm/src/math/sin.rs b/crates/libm/src/math/sin.rs index 51aed88a8..b73074416 100644 --- a/crates/libm/src/math/sin.rs +++ b/crates/libm/src/math/sin.rs @@ -77,10 +77,3 @@ pub fn sin(x: f64) -> f64 { _ => -k_cos(y0, y1), } } - -#[test] -fn test_near_pi() { - let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707 - let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7 - assert_eq!(sin(x), sx); -} diff --git a/crates/libm/src/math/trunc.rs b/crates/libm/src/math/trunc.rs index 1ee46fc7d..0b882daa7 100644 --- a/crates/libm/src/math/trunc.rs +++ b/crates/libm/src/math/trunc.rs @@ -31,11 +31,3 @@ pub fn trunc(x: f64) -> f64 { i &= !m; f64::from_bits(i) } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::trunc(1.1), 1.0); - } -} diff --git a/crates/libm/src/math/truncf.rs b/crates/libm/src/math/truncf.rs index f93383269..b366f08dd 100644 --- a/crates/libm/src/math/truncf.rs +++ b/crates/libm/src/math/truncf.rs @@ -31,11 +31,3 @@ pub fn truncf(x: f32) -> f32 { i &= !m; f32::from_bits(i) } - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::truncf(1.1), 1.0); - } -} From 412595b3b6dcd5da5db310467b35962cfa6f842d Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 18:48:41 +0200 Subject: [PATCH 08/43] Only run system tests on x86_64-unknown-linux-musl for now --- ci/run.sh | 20 +++++- crates/libm-analyze/src/lib.rs | 63 +++++++++++++----- crates/libm-test/Cargo.toml | 6 +- crates/libm-test/tests/system_libm.rs | 96 ++++++++++++++++++++++++--- 4 files changed, 154 insertions(+), 31 deletions(-) diff --git a/ci/run.sh b/ci/run.sh index 37ffb8793..2db4a9b59 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -1,9 +1,18 @@ #!/usr/bin/env sh +if [ -z "$1" ]; then + echo "This script takes the $TARGET triple as its argument" + exit 1 +fi + set -ex + TARGET=$1 -CMD="cargo test --all --no-default-features --target $TARGET" +CMD="cargo test \ + --manifest-path=crates/libm-test/Cargo.toml --all \ + --no-default-features \ + --target $TARGET " $CMD $CMD --release @@ -11,5 +20,10 @@ $CMD --release $CMD --features 'stable' $CMD --release --features 'stable' -$CMD --features 'stable checked musl-reference-tests' -$CMD --release --features 'stable checked musl-reference-tests' +if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then + export TARGET=x86_64-unknown-linux-musl + + $CMD --features 'stable checked system_libm' + $CMD --release --features 'stable checked system_libm' +fi + diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 39d57ceb3..4765f24a5 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -5,6 +5,10 @@ use self::proc_macro::TokenStream; use quote::quote; use syn::parse_macro_input; +/// `input` contains a single identifier, corresponding to a user-defined macro. +/// This identifier is expanded for each libm public API. +/// +/// See tests/analyze or below for the API. #[proc_macro] pub fn for_each_api(input: TokenStream) -> TokenStream { let files = get_libm_files(); @@ -30,7 +34,8 @@ pub fn for_each_api(input: TokenStream) -> TokenStream { } /// Traverses the libm crate directory, parsing all .rs files -fn get_libm_files() -> Vec<(syn::File, String)> { +fn get_libm_files() -> Vec { + // Find the directory of the libm crate: let root_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let libm_dir = root_dir .parent() @@ -38,6 +43,7 @@ fn get_libm_files() -> Vec<(syn::File, String)> { .join("libm"); let libm_src_dir = libm_dir.join("src"); + // Traverse all Rust files, parsing them as `syn::File` let mut files = Vec::new(); for entry in walkdir::WalkDir::new(libm_src_dir) .into_iter() @@ -51,23 +57,25 @@ fn get_libm_files() -> Vec<(syn::File, String)> { .expect("can't format file path") .ends_with(".rs") { + // If the path is a directory or not a ".rs" file => skip it. continue; } + // Read the file into a string, and parse it into an AST using syn. let mut file_string = String::new(); std::fs::File::open(&file_path) .unwrap_or_else(|_| panic!("can't open file at path: {}", file_path.display())) .read_to_string(&mut file_string) .expect("failed to read file to string"); let file = syn::parse_file(&file_string).expect("failed to parse"); - files.push((file, file_path.to_str().unwrap().to_string())); + files.push(file); } files } +/// Function signature that will be expanded for the user macro. struct FnSig { ident: syn::Ident, - unsafety: bool, c_abi: bool, ret_ty: Option, arg_tys: Vec, @@ -89,11 +97,13 @@ macro_rules! syn_to_str { }}; } -/// Extracts all public functions from the libm files. -fn get_functions(files: Vec<(syn::File, String)>) -> Vec { +/// Extracts all public functions from the libm files while +/// doing some sanity checks on the function signatures. +fn get_functions(files: Vec) -> Vec { let mut error = false; let mut functions = Vec::new(); - for item in files.iter().flat_map(|f| f.0.items.iter()) { + // Traverse all files matching function items + for item in files.iter().flat_map(|f| f.items.iter()) { let mut e = false; match item { syn::Item::Fn(syn::ItemFn { @@ -107,9 +117,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { decl, block: _, }) => { + // Build a function signature while doing some sanity checks let mut fn_sig = FnSig { ident: ident.clone(), - unsafety: true, c_abi: false, arg_tys: Vec::new(), ret_ty: None, @@ -136,14 +146,31 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { fn_sig.c_abi = true; } } + // If the function signature isn't extern "C", we aren't ABI compatible + // with libm. + if !fn_sig.c_abi { + // FIXME: we should error here, but right that would break everything, + // so we disable erroring. + let e2 = e; + err!("not `extern \"C\"`"); + e = e2; + } + // Right now no functions are const fn - they could be, but that + // change should be explicit - so error if somebody tries. if let Some(_) = constness { err!("is const"); } + // No functions should be async fn if let Some(_) = asyncness { err!("is async"); } - if &None == unsafety { - fn_sig.unsafety = false; + // FIXME: Math functions shouldn't be unsafe. Some functions + // that should take pointers use repr(Rust) tuples. When we fix + // those, they should use references are not pointers. + if let Some(_) = unsafety { + let e2 = e; + err!("is unsafe"); + e = e2; } let syn::FnDecl { fn_token: _, @@ -154,6 +181,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { output, } = (**decl).clone(); + // Forbid generic parameters, lifetimes, and consts in public APIs: if variadic.is_some() { err!(format!( "contains variadic arguments \"{}\"", @@ -178,7 +206,13 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { syn_to_str!(generics.clone()) )); } + // FIXME: we can do better here, but right now, we should + // error if inline and no_panic are not used, which is the + // case if the public API has no attributes. + // + // We might also want to check other attributes as well. if attrs.is_empty() { + let e2 = e; err!(format!( "missing `#[inline]` and `#[no_panic]` attributes {}", attrs @@ -187,13 +221,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { .collect::>() .join(",") )); - } // TODO: might want to check other attributes as well - if !fn_sig.c_abi { - // FIXME: do not disable test if this fails - otherwise no test passes - let e2 = e; - err!("not `extern \"C\"`"); e = e2; } + // Validate and parse output parameters and function arguments: match output { syn::ReturnType::Default => (), syn::ReturnType::Type(_, ref b) if valid_ty(&b) => { @@ -212,6 +242,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec { )), } } + // If there was an error, we skip the function: if !e { functions.push(fn_sig); } else { @@ -254,7 +285,8 @@ fn valid_ty(t: &syn::Type) -> bool { .ident .to_string(); match s.as_str() { - "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize" + | "i8" | "i16" | "i32" | "i64" | "isize" + | "u8" | "u16" | "u32" | "u64" | "usize" | "f32" | "f64" => true, _ => false, } @@ -263,6 +295,7 @@ fn valid_ty(t: &syn::Type) -> bool { } } +/// Returns a vector containing `len` identifiers. fn get_arg_ids(len: usize) -> Vec { let mut ids = Vec::new(); for i in 0..len { diff --git a/crates/libm-test/Cargo.toml b/crates/libm-test/Cargo.toml index 9e8f56ad3..4cb40b6a2 100644 --- a/crates/libm-test/Cargo.toml +++ b/crates/libm-test/Cargo.toml @@ -5,10 +5,12 @@ authors = ["Gonzalo Brito Gadeschi "] edition = "2018" [dev-dependencies] -libm = { path = "../libm" } -libm-analyze = { path = "../libm-analyze", no-default-features = true } +libm = { path = "../libm", default-features = false } +libm-analyze = { path = "../libm-analyze", default-features = true } rand = "0.7" [features] default = [] checked = ["libm/checked"] +stable = ["libm/stable"] +system_libm = [] \ No newline at end of file diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index e207b252f..f834ad9e1 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,13 +1,65 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] - +#![cfg(feature = "system_libm")] // Number of tests to generate for each function const NTESTS: usize = 500; // FIXME: should be 1 -const ULP_TOL: usize = 3; +const ULP_TOL: usize = 4; macro_rules! system_libm { + // Skip those parts of the API that are not + // exposed by the system libm library: + ( + id: j0f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: j1f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: jnf; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: y0f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: y1f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: ynf; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: exp10; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + ( + id: exp10f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ret_ty:ty; + ) => {}; + + // Generate random tests for all others: ( id: $id:ident; arg_tys: $($arg_tys:ty),*; @@ -20,17 +72,34 @@ macro_rules! system_libm { use crate::Call; let mut rng = rand::thread_rng(); for _ in 0..NTESTS { - let args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); - extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { + let mut args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); + + match stringify!($id) { + "j1" | "jn" => { + // First argument to this function appears to be a number of + // iterations, so passing in massive random numbers causes it to + // take forever to execute, so make sure we're not running random + // math code until the heat death of the universe. + let p = &mut args as *mut _ as *mut i32; + unsafe { p.write(p.read() & 0xffff) } + }, + _ => (), + } + + unsafe extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { libm::$id($($arg_ids),*) } - let result = args.call(libm_fn); + let result = <($($arg_tys),*) as Call< + unsafe extern "C" fn($($arg_tys),*) -> $ret_ty + >>::call(args, libm_fn); extern "C" { fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; } - let expected = args.call($id); + let expected = <($($arg_tys),*) as Call< + unsafe extern "C" fn($($arg_tys),*) -> $ret_ty + >>::call(args, $id); if !result.eq(expected) { - eprintln!("result = {} != {} (expected)", result, expected); + eprintln!("result = {:?} != {:?} (expected)", result, expected); panic!(); } } @@ -58,14 +127,19 @@ macro_rules! impl_call { } impl_call!((f32) -> f32: x: x); -impl_call!((f32,f32) -> f32: x: x.0, x.1); -impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2); impl_call!((f64) -> f64: x: x); -impl_call!((f64,f64) -> f64: x: x.0, x.1); -impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2); +impl_call!((f64) -> i32: x: x); +impl_call!((f32) -> i32: x: x); +impl_call!((f32,f32) -> f32: x: x.0, x.1); +impl_call!((f64,f64) -> f64: x: x.0, x.1); impl_call!((f64, i32) -> f64: x: x.0, x.1); impl_call!((f32, i32) -> f32: x: x.0, x.1); +impl_call!((i32, f64) -> f64: x: x.0, x.1); +impl_call!((i32, f32) -> f32: x: x.0, x.1); + +impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2); +impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2); trait Rand { fn gen(rng: &mut rand::rngs::ThreadRng) -> Self; From a3838d94ef89e3e5c704ffbaaec905ae5e577f16 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 18:58:05 +0200 Subject: [PATCH 09/43] libm-analyze does not require proc_macro_diagnostics anymore --- crates/libm-analyze/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 4765f24a5..0421b107c 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(proc_macro_diagnostic)] - extern crate proc_macro; use self::proc_macro::TokenStream; use quote::quote; From fae0b0aefafeedc5432c73aec70d72b729819d64 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 19:00:05 +0200 Subject: [PATCH 10/43] Add readmes to the two new crates --- crates/libm-analyze/README.md | 22 ++++++++++++++++++++++ crates/libm-test/README.md | 9 +++++++++ 2 files changed, 31 insertions(+) create mode 100644 crates/libm-analyze/README.md create mode 100644 crates/libm-test/README.md diff --git a/crates/libm-analyze/README.md b/crates/libm-analyze/README.md new file mode 100644 index 000000000..12eef661f --- /dev/null +++ b/crates/libm-analyze/README.md @@ -0,0 +1,22 @@ +# libm-analyze + +This crate provides a single macro, `for_each_api`: + +```rust +macro_rules! nop { + ( + id: $id:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret: $ty:ty; + ) => {}; +} + +libm_analyze::for_each_api!(nop); +``` + +This macro takes a user-provided macro, and expands it for all libm APIs. + +For example, see how the `libm-test` crate `tests/system.rs` test uses it to +test all `libm` APIs against random inputs, and verify the results against the +system's libm library. diff --git a/crates/libm-test/README.md b/crates/libm-test/README.md new file mode 100644 index 000000000..3e420e9fb --- /dev/null +++ b/crates/libm-test/README.md @@ -0,0 +1,9 @@ +# libm-test + +This crate contains different types of test for the `libm` crate. + +* `tests/system.rs`: generate random inputs, and tests that the results of the + `libm` crate are within the tolerance required by the IEEE from those of the + system's libm library (e.g. musl, glibc's libm, libSystem_m, etc.). + +* `tests/unit.rs`: contains some small unit tests. From 6ef8fa70a201f318d9e096bc38b202b241c4437d Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 22:00:17 +0200 Subject: [PATCH 11/43] Improve doc comments --- crates/libm-analyze/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 0421b107c..d38e81abf 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -153,8 +153,9 @@ fn get_functions(files: Vec) -> Vec { err!("not `extern \"C\"`"); e = e2; } - // Right now no functions are const fn - they could be, but that - // change should be explicit - so error if somebody tries. + // Right now there are no const fn functions. We might add them + // in the future, and at that point, we should tune this here. + // In the mean time, error if somebody tries. if let Some(_) = constness { err!("is const"); } @@ -162,9 +163,11 @@ fn get_functions(files: Vec) -> Vec { if let Some(_) = asyncness { err!("is async"); } - // FIXME: Math functions shouldn't be unsafe. Some functions - // that should take pointers use repr(Rust) tuples. When we fix - // those, they should use references are not pointers. + // FIXME: Math functions are not unsafe. Some functions in the + // libm C API take pointers, but in our API take repr(Rust) + // tuples (for some reason). Once we fix those to have the same + // API as C libm, we should use references on their signature + // instead, and make them safe. if let Some(_) = unsafety { let e2 = e; err!("is unsafe"); From e272698f5ae9aa675013987e360dd07a4bbf94c0 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 22:00:26 +0200 Subject: [PATCH 12/43] Remove duplicate check in equal --- crates/libm-test/tests/system_libm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index f834ad9e1..a5779c5b7 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -186,10 +186,7 @@ macro_rules! impl_eq_f { // one is inf but the other is not return false; } - if x.is_infinite() != y.is_infinite() { - // one is inf but the other is not - return false; - } + let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; if (xi < 0) != (yi < 0) { From 7d73ae1ac9c0ab9bb8e56ef3f848ca6913ed036a Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 4 Jul 2019 23:11:12 +0200 Subject: [PATCH 13/43] Document and refactor libm-test a bit --- crates/libm-test/src/lib.rs | 51 +++++++++++ crates/libm-test/tests/system_libm.rs | 126 ++++++++++---------------- 2 files changed, 98 insertions(+), 79 deletions(-) create mode 100644 crates/libm-test/src/lib.rs diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs new file mode 100644 index 000000000..5a41fadf0 --- /dev/null +++ b/crates/libm-test/src/lib.rs @@ -0,0 +1,51 @@ +//! Testing utilities required by most tests. + +pub trait WithinUlps { + /// Returns true if two numbers are closer than `ulp_tol` to each other. + fn within_ulps(self, other: Self, ulp_tol: usize) -> bool; +} + +// TODO: this should be moved to the libm-test/src/lib.rs library. And we +// should make ulps configurable. + +// Stamp the impls for floats: +macro_rules! impl_within_ulps_f { + ($f_ty:ty, $i_ty:ty) => { + impl WithinUlps for $f_ty { + fn within_ulps(self, y: $f_ty, ulp_tol: usize) -> bool { + let x = self; + if x.is_nan() != y.is_nan() { + // one is nan but the other is not + return false; + } + if x.is_nan() && y.is_nan() { + return true; + } + if x.is_infinite() != y.is_infinite() { + // one is inf but the other is not + return false; + } + + let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; + let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; + if (xi < 0) != (yi < 0) { + // different sign, e.g., -0.0 != +0.0: + return false; + } + let ulps = (xi - yi).abs(); + ulps <= ulp_tol as _ + } + } + }; +} + +impl_within_ulps_f!(f32, i32); +impl_within_ulps_f!(f64, i64); + +impl WithinUlps for i32 { + fn within_ulps(self, y: i32, ulp_tol: usize) -> bool { + let x = self; + let ulps = (x - y).abs(); + ulps <= ulp_tol as _ + } +} diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index a5779c5b7..8e611563e 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,15 +1,19 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] #![cfg(feature = "system_libm")] + +use libm_test::WithinUlps; + // Number of tests to generate for each function const NTESTS: usize = 500; -// FIXME: should be 1 const ULP_TOL: usize = 4; macro_rules! system_libm { // Skip those parts of the API that are not // exposed by the system libm library: + // + // FIXME: maybe we can skip them as part of libm-analyze? ( id: j0f; arg_tys: $($arg_tys:ty),*; @@ -72,33 +76,34 @@ macro_rules! system_libm { use crate::Call; let mut rng = rand::thread_rng(); for _ in 0..NTESTS { - let mut args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); - - match stringify!($id) { - "j1" | "jn" => { - // First argument to this function appears to be a number of - // iterations, so passing in massive random numbers causes it to - // take forever to execute, so make sure we're not running random - // math code until the heat death of the universe. - let p = &mut args as *mut _ as *mut i32; - unsafe { p.write(p.read() & 0xffff) } - }, - _ => (), - } - - unsafe extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { + // Type of the system libm fn: + type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + // FIXME: extern "C" wrapper over our libm functions + // Shouldn't be needed once they are all extern "C" + extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { libm::$id($($arg_ids),*) } - let result = <($($arg_tys),*) as Call< - unsafe extern "C" fn($($arg_tys),*) -> $ret_ty - >>::call(args, libm_fn); extern "C" { + // The system's libm function: fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; } - let expected = <($($arg_tys),*) as Call< - unsafe extern "C" fn($($arg_tys),*) -> $ret_ty - >>::call(args, $id); - if !result.eq(expected) { + + // Generate a tuple of arguments containing random values: + let mut args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); + + // HACK + if let "j1" | "jn" = stringify!($id) { + // First argument to these functions are a number of + // iterations and passing large random numbers takes forever + // to execute, so check if their higher bits are set and + // zero them: + let p = &mut args as *mut _ as *mut i32; + unsafe { p.write(p.read() & 0xffff) } + } + + let result = args.call(libm_fn as FnTy); + let expected = args.call($id as FnTy); + if !result.within_ulps(expected, ULP_TOL) { eprintln!("result = {:?} != {:?} (expected)", result, expected); panic!(); } @@ -109,6 +114,12 @@ macro_rules! system_libm { libm_analyze::for_each_api!(system_libm); +// This implements function dispatch for tuples of arguments used in the tests +// above, so that we can: (f32, 32).call(fn(f32, f32) -> f32) generically. +// +// We need the input parameter F to support dispatching, e.g., (f32,f32) with +// functions that return both f32 or i32. Those are two different types, so we +// need to be parametric over them. trait Call { type Ret; fn call(self, f: F) -> Self::Ret; @@ -116,6 +127,7 @@ trait Call { macro_rules! impl_call { (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { + // We only care about unsafe extern "C" functions here, safe functions coerce to them: impl Call $ret_ty> for ($($arg_tys),+) { type Ret = $ret_ty; fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { @@ -130,17 +142,18 @@ impl_call!((f32) -> f32: x: x); impl_call!((f64) -> f64: x: x); impl_call!((f64) -> i32: x: x); impl_call!((f32) -> i32: x: x); - -impl_call!((f32,f32) -> f32: x: x.0, x.1); -impl_call!((f64,f64) -> f64: x: x.0, x.1); +impl_call!((f32, f32) -> f32: x: x.0, x.1); +impl_call!((f64, f64) -> f64: x: x.0, x.1); impl_call!((f64, i32) -> f64: x: x.0, x.1); impl_call!((f32, i32) -> f32: x: x.0, x.1); impl_call!((i32, f64) -> f64: x: x.0, x.1); impl_call!((i32, f32) -> f32: x: x.0, x.1); +impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); +impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); -impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2); -impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2); - +// We need to be able to generate random numbers for the types involved. +// +// Rand does this well, but we want to also test some other specific values. trait Rand { fn gen(rng: &mut rand::rngs::ThreadRng) -> Self; } @@ -149,64 +162,19 @@ macro_rules! impl_rand { ($id:ident: [$($e:expr),*]) => { impl Rand for $id { fn gen(r: &mut rand::rngs::ThreadRng) -> Self { - use rand::Rng; - use rand::seq::SliceRandom; - let r = if r.gen_range(0, 20) < 1 { + use rand::{Rng, seq::SliceRandom}; + // 1/20 probability of picking a non-random value + if r.gen_range(0, 20) < 1 { *[$($e),*].choose(r).unwrap() } else { r.gen::<$id>() - }; - unsafe { std::mem::transmute(r) } + } } } } } +// Some interesting random values impl_rand!(f32: [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY]); impl_rand!(f64: [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY]); impl_rand!(i32: [i32::max_value(), 0_i32, i32::min_value()]); - -trait Equal { - fn eq(self, other: Self) -> bool; -} - -macro_rules! impl_eq_f { - ($f_ty:ty, $i_ty:ty) => { - impl Equal for $f_ty { - fn eq(self, y: $f_ty) -> bool { - let x = self; - if x.is_nan() != y.is_nan() { - // one is nan but the other is not - return false; - } - if x.is_nan() && y.is_nan() { - return true; - } - if x.is_infinite() != y.is_infinite() { - // one is inf but the other is not - return false; - } - - let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; - let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; - if (xi < 0) != (yi < 0) { - // different sign - return false; - } - let ulps = (xi - yi).abs(); - ulps <= ULP_TOL as _ - } - } - }; -} - -impl_eq_f!(f32, i32); -impl_eq_f!(f64, i64); - -impl Equal for i32 { - fn eq(self, y: i32) -> bool { - let x = self; - let ulps = (x - y).abs(); - ulps <= 1 - } -} From 7b9f17ff29b59005565cf6cf92cb377f1ed980c7 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:31:01 +0200 Subject: [PATCH 14/43] Reformat libm-analyze --- crates/libm-analyze/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index d38e81abf..1cc04e802 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -286,8 +286,7 @@ fn valid_ty(t: &syn::Type) -> bool { .ident .to_string(); match s.as_str() { - | "i8" | "i16" | "i32" | "i64" | "isize" - | "u8" | "u16" | "u32" | "u64" | "usize" + "i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize" | "f32" | "f64" => true, _ => false, } From 2460d47e46048ad5d53ab52e86121fe6564a91cd Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:35:41 +0200 Subject: [PATCH 15/43] wasm32 should only build the libm crate on Azure --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c89346c73..49d31094c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -47,9 +47,9 @@ jobs: - template: ci/azure-install-rust.yml - script: rustup target add wasm32-unknown-unknown displayName: "Install rust wasm target" - - script: cargo build --target wasm32-unknown-unknown + - script: cargo build -p libm --target wasm32-unknown-unknown displayName: "Build for wasm" - - script: cargo build --target wasm32-unknown-unknown --no-default-features + - script: cargo build -p libm --target wasm32-unknown-unknown --no-default-features displayName: "Build for wasm (no default features)" variables: TOOLCHAIN: nightly From 36958fd6cca8b6fcf82571e5fffba8a1859d730f Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:35:59 +0200 Subject: [PATCH 16/43] Use of core::intrinsics was improperly feature-gated for wasm32 --- crates/libm/src/math/ceil.rs | 2 +- crates/libm/src/math/ceilf.rs | 2 +- crates/libm/src/math/fabs.rs | 2 +- crates/libm/src/math/fabsf.rs | 2 +- crates/libm/src/math/floor.rs | 2 +- crates/libm/src/math/floorf.rs | 2 +- crates/libm/src/math/sqrt.rs | 2 +- crates/libm/src/math/sqrtf.rs | 2 +- crates/libm/src/math/trunc.rs | 2 +- crates/libm/src/math/truncf.rs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/libm/src/math/ceil.rs b/crates/libm/src/math/ceil.rs index bb10eea53..4b964c769 100644 --- a/crates/libm/src/math/ceil.rs +++ b/crates/libm/src/math/ceil.rs @@ -12,7 +12,7 @@ pub fn ceil(x: f64) -> f64 { // `f64.ceil` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::ceilf64(x) } } } diff --git a/crates/libm/src/math/ceilf.rs b/crates/libm/src/math/ceilf.rs index 151a4f210..8ab4e48f8 100644 --- a/crates/libm/src/math/ceilf.rs +++ b/crates/libm/src/math/ceilf.rs @@ -10,7 +10,7 @@ pub fn ceilf(x: f32) -> f32 { // `f32.ceil` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::ceilf32(x) } } } diff --git a/crates/libm/src/math/fabs.rs b/crates/libm/src/math/fabs.rs index 52a9adcbf..a218cb514 100644 --- a/crates/libm/src/math/fabs.rs +++ b/crates/libm/src/math/fabs.rs @@ -10,7 +10,7 @@ pub fn fabs(x: f64) -> f64 { // `f64.abs` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::fabsf64(x) } } } diff --git a/crates/libm/src/math/fabsf.rs b/crates/libm/src/math/fabsf.rs index 5942d983a..a2fb611d3 100644 --- a/crates/libm/src/math/fabsf.rs +++ b/crates/libm/src/math/fabsf.rs @@ -8,7 +8,7 @@ pub fn fabsf(x: f32) -> f32 { // `f32.abs` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::fabsf32(x) } } } diff --git a/crates/libm/src/math/floor.rs b/crates/libm/src/math/floor.rs index f6068c697..5be314949 100644 --- a/crates/libm/src/math/floor.rs +++ b/crates/libm/src/math/floor.rs @@ -12,7 +12,7 @@ pub fn floor(x: f64) -> f64 { // `f64.floor` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::floorf64(x) } } } diff --git a/crates/libm/src/math/floorf.rs b/crates/libm/src/math/floorf.rs index ca85e210f..82000dfdb 100644 --- a/crates/libm/src/math/floorf.rs +++ b/crates/libm/src/math/floorf.rs @@ -10,7 +10,7 @@ pub fn floorf(x: f32) -> f32 { // `f32.floor` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::floorf32(x) } } } diff --git a/crates/libm/src/math/sqrt.rs b/crates/libm/src/math/sqrt.rs index 14404d4eb..7894cfc5c 100644 --- a/crates/libm/src/math/sqrt.rs +++ b/crates/libm/src/math/sqrt.rs @@ -88,7 +88,7 @@ pub fn sqrt(x: f64) -> f64 { // `f64.sqrt` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return if x < 0.0 { f64::NAN } else { diff --git a/crates/libm/src/math/sqrtf.rs b/crates/libm/src/math/sqrtf.rs index b9365c617..fa853e857 100644 --- a/crates/libm/src/math/sqrtf.rs +++ b/crates/libm/src/math/sqrtf.rs @@ -22,7 +22,7 @@ pub fn sqrtf(x: f32) -> f32 { // `f32.sqrt` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return if x < 0.0 { ::core::f32::NAN } else { diff --git a/crates/libm/src/math/trunc.rs b/crates/libm/src/math/trunc.rs index 0b882daa7..b626cd9eb 100644 --- a/crates/libm/src/math/trunc.rs +++ b/crates/libm/src/math/trunc.rs @@ -7,7 +7,7 @@ pub fn trunc(x: f64) -> f64 { // `f64.trunc` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::truncf64(x) } } } diff --git a/crates/libm/src/math/truncf.rs b/crates/libm/src/math/truncf.rs index b366f08dd..6d8a9e742 100644 --- a/crates/libm/src/math/truncf.rs +++ b/crates/libm/src/math/truncf.rs @@ -7,7 +7,7 @@ pub fn truncf(x: f32) -> f32 { // `f32.trunc` native instruction, so we can leverage this for both code size // and speed. llvm_intrinsically_optimized! { - #[cfg(target_arch = "wasm32")] { + #[cfg(all(target_arch = "wasm32", not(feature = "stable")))] { return unsafe { ::core::intrinsics::truncf32(x) } } } From c3afab46c2c486f16fa083d247b3459f3c35a450 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:45:30 +0200 Subject: [PATCH 17/43] Improve error messages for libm-test --- crates/libm-test/tests/system_libm.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 8e611563e..b462d8bb8 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,6 +1,6 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] -#![cfg(feature = "system_libm")] +//#![cfg(feature = "system_libm")] use libm_test::WithinUlps; @@ -89,7 +89,7 @@ macro_rules! system_libm { } // Generate a tuple of arguments containing random values: - let mut args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ ); + let mut args: ( $($arg_tys,)+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng),)+ ); // HACK if let "j1" | "jn" = stringify!($id) { @@ -104,7 +104,7 @@ macro_rules! system_libm { let result = args.call(libm_fn as FnTy); let expected = args.call($id as FnTy); if !result.within_ulps(expected, ULP_TOL) { - eprintln!("result = {:?} != {:?} (expected)", result, expected); + eprintln!("{}{:?} returns = {:?} != {:?} (expected)", stringify!($id), args, result, expected); panic!(); } } @@ -128,7 +128,7 @@ trait Call { macro_rules! impl_call { (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { // We only care about unsafe extern "C" functions here, safe functions coerce to them: - impl Call $ret_ty> for ($($arg_tys),+) { + impl Call $ret_ty> for ($($arg_tys,)+) { type Ret = $ret_ty; fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { let $self_ = self; @@ -138,10 +138,10 @@ macro_rules! impl_call { }; } -impl_call!((f32) -> f32: x: x); -impl_call!((f64) -> f64: x: x); -impl_call!((f64) -> i32: x: x); -impl_call!((f32) -> i32: x: x); +impl_call!((f32) -> f32: x: x.0); +impl_call!((f64) -> f64: x: x.0); +impl_call!((f64) -> i32: x: x.0); +impl_call!((f32) -> i32: x: x.0); impl_call!((f32, f32) -> f32: x: x.0, x.1); impl_call!((f64, f64) -> f64: x: x.0, x.1); impl_call!((f64, i32) -> f64: x: x.0, x.1); From 17234d6898083e02ee8451e564154365200ca520 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:47:43 +0200 Subject: [PATCH 18/43] Only run system tests for musl --- ci/run.sh | 13 +++++++------ crates/libm-test/tests/system_libm.rs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ci/run.sh b/ci/run.sh index 2db4a9b59..728bb41ae 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -9,10 +9,11 @@ set -ex TARGET=$1 -CMD="cargo test \ +CMD_="cargo test \ --manifest-path=crates/libm-test/Cargo.toml --all \ - --no-default-features \ - --target $TARGET " + --no-default-features " + +CMD="${CMD} --target ${TARGET}" $CMD $CMD --release @@ -22,8 +23,8 @@ $CMD --release --features 'stable' if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then export TARGET=x86_64-unknown-linux-musl - - $CMD --features 'stable checked system_libm' - $CMD --release --features 'stable checked system_libm' + + $CMD_ --target $TARGET --features 'stable checked system_libm' + $CMD_ --target $TARGET --release --features 'stable checked system_libm' fi diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index b462d8bb8..29b8d422b 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,6 +1,6 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] -//#![cfg(feature = "system_libm")] +#![cfg(feature = "system_libm")] use libm_test::WithinUlps; From 5e854998024dea802f53ce99af6acdd860105368 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 00:52:41 +0200 Subject: [PATCH 19/43] Fix typo --- ci/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/run.sh b/ci/run.sh index 728bb41ae..12395c02b 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -13,7 +13,7 @@ CMD_="cargo test \ --manifest-path=crates/libm-test/Cargo.toml --all \ --no-default-features " -CMD="${CMD} --target ${TARGET}" +CMD="${CMD_} --target ${TARGET}" $CMD $CMD --release From 8abd2f991a6b1e0efd4637a9d07ead6938189b03 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 01:08:34 +0200 Subject: [PATCH 20/43] Nitpicks --- ci/run.sh | 1 - crates/libm-analyze/src/lib.rs | 292 +++++++++++++++++---------------- crates/libm-test/src/lib.rs | 6 +- 3 files changed, 151 insertions(+), 148 deletions(-) diff --git a/ci/run.sh b/ci/run.sh index 12395c02b..209704721 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -27,4 +27,3 @@ if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then $CMD_ --target $TARGET --features 'stable checked system_libm' $CMD_ --target $TARGET --release --features 'stable checked system_libm' fi - diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 1cc04e802..aba2188a6 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -1,3 +1,7 @@ +//! A proc macro to analyze the libm APIs We want to exhaustively match all +// fields here to create a compilation error if new fields are added. +#![allow(clippy::unneeded_field_pattern)] + extern crate proc_macro; use self::proc_macro::TokenStream; use quote::quote; @@ -10,7 +14,7 @@ use syn::parse_macro_input; #[proc_macro] pub fn for_each_api(input: TokenStream) -> TokenStream { let files = get_libm_files(); - let functions = get_functions(files); + let functions = get_functions(&files); let input = parse_macro_input!(input as syn::Ident); let mut tokens = proc_macro2::TokenStream::new(); for function in functions { @@ -45,7 +49,7 @@ fn get_libm_files() -> Vec { let mut files = Vec::new(); for entry in walkdir::WalkDir::new(libm_src_dir) .into_iter() - .filter_map(|e| e.ok()) + .filter_map(Result::ok) { use std::io::Read; let file_path = entry.path(); @@ -97,160 +101,158 @@ macro_rules! syn_to_str { /// Extracts all public functions from the libm files while /// doing some sanity checks on the function signatures. -fn get_functions(files: Vec) -> Vec { +fn get_functions(files: &[syn::File]) -> Vec { let mut error = false; let mut functions = Vec::new(); // Traverse all files matching function items for item in files.iter().flat_map(|f| f.items.iter()) { let mut e = false; - match item { - syn::Item::Fn(syn::ItemFn { - vis: syn::Visibility::Public(_), - ident, - constness, - asyncness, - unsafety, - attrs, - abi, - decl, - block: _, - }) => { - // Build a function signature while doing some sanity checks - let mut fn_sig = FnSig { - ident: ident.clone(), - c_abi: false, - arg_tys: Vec::new(), - ret_ty: None, - }; - macro_rules! err { - ($msg:expr) => {{ - #[cfg(feature = "analyze")] - { - eprintln!("[error]: Function \"{}\" {}", fn_sig.name(), $msg); - } - #[allow(unused_assignments)] - { - e = true; - } - () - }}; - } - if let Some(syn::Abi { - name: Some(l), - extern_token: _, - }) = abi - { - if l.value() == "C" { - fn_sig.c_abi = true; + if let syn::Item::Fn(syn::ItemFn { + vis: syn::Visibility::Public(_), + ident, + constness, + asyncness, + unsafety, + attrs, + abi, + decl, + block: _, + }) = item + { + // Build a function signature while doing some sanity checks + let mut fn_sig = FnSig { + ident: ident.clone(), + c_abi: false, + arg_tys: Vec::new(), + ret_ty: None, + }; + macro_rules! err { + ($msg:expr) => {{ + #[cfg(feature = "analyze")] + { + eprintln!("[error]: Function \"{}\" {}", fn_sig.name(), $msg); } - } - // If the function signature isn't extern "C", we aren't ABI compatible - // with libm. - if !fn_sig.c_abi { - // FIXME: we should error here, but right that would break everything, - // so we disable erroring. - let e2 = e; - err!("not `extern \"C\"`"); - e = e2; - } - // Right now there are no const fn functions. We might add them - // in the future, and at that point, we should tune this here. - // In the mean time, error if somebody tries. - if let Some(_) = constness { - err!("is const"); - } - // No functions should be async fn - if let Some(_) = asyncness { - err!("is async"); - } - // FIXME: Math functions are not unsafe. Some functions in the - // libm C API take pointers, but in our API take repr(Rust) - // tuples (for some reason). Once we fix those to have the same - // API as C libm, we should use references on their signature - // instead, and make them safe. - if let Some(_) = unsafety { - let e2 = e; - err!("is unsafe"); - e = e2; - } - let syn::FnDecl { - fn_token: _, - generics, - paren_token: _, - inputs, - variadic, - output, - } = (**decl).clone(); - - // Forbid generic parameters, lifetimes, and consts in public APIs: - if variadic.is_some() { - err!(format!( - "contains variadic arguments \"{}\"", - syn_to_str!(variadic.unwrap()) - )); - } - if generics.type_params().into_iter().count() != 0 { - err!(format!( - "contains generic parameters \"{}\"", - syn_to_str!(generics.clone()) - )); - } - if generics.lifetimes().into_iter().count() != 0 { - err!(format!( - "contains lifetime parameters \"{}\"", - syn_to_str!(generics.clone()) - )); - } - if generics.const_params().into_iter().count() != 0 { - err!(format!( - "contains const parameters \"{}\"", - syn_to_str!(generics.clone()) - )); - } - // FIXME: we can do better here, but right now, we should - // error if inline and no_panic are not used, which is the - // case if the public API has no attributes. - // - // We might also want to check other attributes as well. - if attrs.is_empty() { - let e2 = e; - err!(format!( - "missing `#[inline]` and `#[no_panic]` attributes {}", - attrs - .iter() - .map(|a| syn_to_str!(a)) - .collect::>() - .join(",") - )); - e = e2; - } - // Validate and parse output parameters and function arguments: - match output { - syn::ReturnType::Default => (), - syn::ReturnType::Type(_, ref b) if valid_ty(&b) => { - fn_sig.ret_ty = Some(*b.clone()) + #[allow(unused_assignments)] + { + e = true; } - other => err!(format!("returns unsupported type {}", syn_to_str!(other))), + () + }}; + } + if let Some(syn::Abi { + name: Some(l), + extern_token: _, + }) = abi + { + if l.value() == "C" { + fn_sig.c_abi = true; } - for input in inputs { - match input { - syn::FnArg::Captured(ref c) if valid_ty(&c.ty) => { - fn_sig.arg_tys.push(c.ty.clone()) - } - other => err!(format!( - "takes unsupported argument type {}", - syn_to_str!(other) - )), + } + // If the function signature isn't extern "C", we aren't ABI compatible + // with libm. + if !fn_sig.c_abi { + // FIXME: we should error here, but right that would break everything, + // so we disable erroring. + let e2 = e; + err!("not `extern \"C\"`"); + e = e2; + } + // Right now there are no const fn functions. We might add them + // in the future, and at that point, we should tune this here. + // In the mean time, error if somebody tries. + if constness.is_some() { + err!("is const"); + } + // No functions should be async fn + if asyncness.is_some() { + err!("is async"); + } + // FIXME: Math functions are not unsafe. Some functions in the + // libm C API take pointers, but in our API take repr(Rust) + // tuples (for some reason). Once we fix those to have the same + // API as C libm, we should use references on their signature + // instead, and make them safe. + if unsafety.is_some() { + let e2 = e; + err!("is unsafe"); + e = e2; + } + let syn::FnDecl { + fn_token: _, + generics, + paren_token: _, + inputs, + variadic, + output, + } = (**decl).clone(); + + // Forbid generic parameters, lifetimes, and consts in public APIs: + if variadic.is_some() { + err!(format!( + "contains variadic arguments \"{}\"", + syn_to_str!(variadic.unwrap()) + )); + } + if generics.type_params().count() != 0 { + err!(format!( + "contains generic parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + if generics.lifetimes().count() != 0 { + err!(format!( + "contains lifetime parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + if generics.const_params().count() != 0 { + err!(format!( + "contains const parameters \"{}\"", + syn_to_str!(generics.clone()) + )); + } + // FIXME: we can do better here, but right now, we should + // error if inline and no_panic are not used, which is the + // case if the public API has no attributes. + // + // We might also want to check other attributes as well. + if attrs.is_empty() { + let e2 = e; + err!(format!( + "missing `#[inline]` and `#[no_panic]` attributes {}", + attrs + .iter() + .map(|a| syn_to_str!(a)) + .collect::>() + .join(",") + )); + e = e2; + } + // Validate and parse output parameters and function arguments: + match output { + syn::ReturnType::Default => (), + syn::ReturnType::Type(_, ref b) if valid_ty(&b) => fn_sig.ret_ty = Some(*b.clone()), + other => err!(format!("returns unsupported type {}", syn_to_str!(other))), + } + for input in inputs { + match input { + syn::FnArg::Captured(ref c) if valid_ty(&c.ty) => { + fn_sig.arg_tys.push(c.ty.clone()) } + other => err!(format!( + "takes unsupported argument type {}", + syn_to_str!(other) + )), } - // If there was an error, we skip the function: - if !e { - functions.push(fn_sig); - } else { - error = true; - } } - _ => (), + // If there was an error, we skip the function. + // Otherwise, the user macro is expanded with + // the function: + if e { + error = true; + } else { + functions.push(fn_sig); + } } } if error { diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 5a41fadf0..593080590 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -12,7 +12,8 @@ pub trait WithinUlps { macro_rules! impl_within_ulps_f { ($f_ty:ty, $i_ty:ty) => { impl WithinUlps for $f_ty { - fn within_ulps(self, y: $f_ty, ulp_tol: usize) -> bool { + #[allow(clippy::cast_possible_truncation)] + fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { let x = self; if x.is_nan() != y.is_nan() { // one is nan but the other is not @@ -43,7 +44,8 @@ impl_within_ulps_f!(f32, i32); impl_within_ulps_f!(f64, i64); impl WithinUlps for i32 { - fn within_ulps(self, y: i32, ulp_tol: usize) -> bool { + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] + fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { let x = self; let ulps = (x - y).abs(); ulps <= ulp_tol as _ From 1d77d0a10cd1c8f66664b9fc377e33df55b42e88 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 01:14:09 +0200 Subject: [PATCH 21/43] Refactor the Call trait into the test lib --- crates/libm-test/src/lib.rs | 37 +++++++++++++++++++++++++ crates/libm-test/tests/system_libm.rs | 39 +-------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 593080590..e8da2b264 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -51,3 +51,40 @@ impl WithinUlps for i32 { ulps <= ulp_tol as _ } } + +/// This implements function dispatch for tuples of arguments used in the tests +/// above, so that we can: (f32, 32).call(fn(f32, f32) -> f32) generically. +/// +/// We need the input parameter F to support dispatching, e.g., (f32,f32) with +/// functions that return both f32 or i32. Those are two different types, so we +/// need to be parametric over them. +pub trait Call { + type Ret; + fn call(self, f: F) -> Self::Ret; +} + +macro_rules! impl_call { + (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { + // We only care about unsafe extern "C" functions here, safe functions coerce to them: + impl Call $ret_ty> for ($($arg_tys,)+) { + type Ret = $ret_ty; + fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { + let $self_ = self; + unsafe { f($($xs),*) } + } + } + }; +} + +impl_call!((f32) -> f32: x: x.0); +impl_call!((f64) -> f64: x: x.0); +impl_call!((f64) -> i32: x: x.0); +impl_call!((f32) -> i32: x: x.0); +impl_call!((f32, f32) -> f32: x: x.0, x.1); +impl_call!((f64, f64) -> f64: x: x.0, x.1); +impl_call!((f64, i32) -> f64: x: x.0, x.1); +impl_call!((f32, i32) -> f32: x: x.0, x.1); +impl_call!((i32, f64) -> f64: x: x.0, x.1); +impl_call!((i32, f32) -> f32: x: x.0, x.1); +impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); +impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 29b8d422b..9f7326ab9 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -2,7 +2,7 @@ #![cfg(test)] #![cfg(feature = "system_libm")] -use libm_test::WithinUlps; +use libm_test::{Call, WithinUlps}; // Number of tests to generate for each function const NTESTS: usize = 500; @@ -114,43 +114,6 @@ macro_rules! system_libm { libm_analyze::for_each_api!(system_libm); -// This implements function dispatch for tuples of arguments used in the tests -// above, so that we can: (f32, 32).call(fn(f32, f32) -> f32) generically. -// -// We need the input parameter F to support dispatching, e.g., (f32,f32) with -// functions that return both f32 or i32. Those are two different types, so we -// need to be parametric over them. -trait Call { - type Ret; - fn call(self, f: F) -> Self::Ret; -} - -macro_rules! impl_call { - (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { - // We only care about unsafe extern "C" functions here, safe functions coerce to them: - impl Call $ret_ty> for ($($arg_tys,)+) { - type Ret = $ret_ty; - fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { - let $self_ = self; - unsafe { f($($xs),*) } - } - } - }; -} - -impl_call!((f32) -> f32: x: x.0); -impl_call!((f64) -> f64: x: x.0); -impl_call!((f64) -> i32: x: x.0); -impl_call!((f32) -> i32: x: x.0); -impl_call!((f32, f32) -> f32: x: x.0, x.1); -impl_call!((f64, f64) -> f64: x: x.0, x.1); -impl_call!((f64, i32) -> f64: x: x.0, x.1); -impl_call!((f32, i32) -> f32: x: x.0, x.1); -impl_call!((i32, f64) -> f64: x: x.0, x.1); -impl_call!((i32, f32) -> f32: x: x.0, x.1); -impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); -impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); - // We need to be able to generate random numbers for the types involved. // // Rand does this well, but we want to also test some other specific values. From 82a5c69eb9029c01ed295476ad6a964d7ddab83c Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 02:24:32 +0200 Subject: [PATCH 22/43] Migrate libm-bench to libm-analyze --- crates/libm-analyze/README.md | 2 +- crates/libm-analyze/src/lib.rs | 2 +- crates/libm-analyze/tests/analyze.rs | 2 +- crates/libm-bench/Cargo.toml | 3 +- crates/libm-bench/benches/bench.rs | 139 +++++++------------------- crates/libm-test/src/lib.rs | 24 ++++- crates/libm-test/tests/system_libm.rs | 34 +++---- 7 files changed, 73 insertions(+), 133 deletions(-) diff --git a/crates/libm-analyze/README.md b/crates/libm-analyze/README.md index 12eef661f..742fbc852 100644 --- a/crates/libm-analyze/README.md +++ b/crates/libm-analyze/README.md @@ -8,7 +8,7 @@ macro_rules! nop { id: $id:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ty:ty; + ret_ty: $ty:ty; ) => {}; } diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index aba2188a6..dd1abdbc1 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -27,7 +27,7 @@ pub fn for_each_api(input: TokenStream) -> TokenStream { id: #id; arg_tys: #(#arg_tys),*; arg_ids: #(#arg_ids),*; - ret: #ret_ty; + ret_ty: #ret_ty; } }; tokens.extend(t); diff --git a/crates/libm-analyze/tests/analyze.rs b/crates/libm-analyze/tests/analyze.rs index d4dbe74d2..cee3c39bd 100644 --- a/crates/libm-analyze/tests/analyze.rs +++ b/crates/libm-analyze/tests/analyze.rs @@ -6,7 +6,7 @@ macro_rules! nop { id: $id:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ty:ty; + ret_ty: $ret_ty:ty; ) => {}; } diff --git a/crates/libm-bench/Cargo.toml b/crates/libm-bench/Cargo.toml index 686627f5f..24211d1bc 100644 --- a/crates/libm-bench/Cargo.toml +++ b/crates/libm-bench/Cargo.toml @@ -7,8 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] libm = { path = "../libm", default-features = false } +libm-analyze = { path = "../libm-analyze", default-features = false } +libm-test = { path = "../libm-test", default-features = false } rand = "0.6.5" -paste = "0.1.5" [features] default = [] diff --git a/crates/libm-bench/benches/bench.rs b/crates/libm-bench/benches/bench.rs index b6d874153..2ca565244 100644 --- a/crates/libm-bench/benches/bench.rs +++ b/crates/libm-bench/benches/bench.rs @@ -1,115 +1,44 @@ #![feature(test)] extern crate test; +use libm_test::{adjust_input, Call}; use rand::Rng; use test::Bencher; -macro_rules! unary { - ($($func:ident),*) => ($( - paste::item! { - #[bench] - pub fn [<$func>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func>](x))) - } - #[bench] - pub fn [<$func f>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func f>](x))) - } - } - )*); -} -macro_rules! binary { - ($($func:ident),*) => ($( - paste::item! { - #[bench] - pub fn [<$func>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let y = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func>](x, y))) - } - #[bench] - pub fn [<$func f>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let y = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func f>](x, y))) - } - } - )*); - ($($func:ident);*) => ($( - paste::item! { - #[bench] - pub fn [<$func>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let n = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func>](x, n))) - } - #[bench] - pub fn [<$func f>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let n = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func f>](x, n))) - } - } - )*); -} -macro_rules! trinary { - ($($func:ident),*) => ($( - paste::item! { - #[bench] - pub fn [<$func>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let y = rng.gen::(); - let z = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func>](x, y, z))) - } - #[bench] - pub fn [<$func f>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let y = rng.gen::(); - let z = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func f>](x, y, z))) - } - } - )*); -} -macro_rules! bessel { - ($($func:ident),*) => ($( - paste::item! { - #[bench] - pub fn [<$func>](bh: &mut Bencher) { - let mut rng = rand::thread_rng(); - let mut n = rng.gen::(); - n &= 0xffff; - let x = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func>](n, x))) - } - #[bench] - pub fn [<$func f>](bh: &mut Bencher) { +macro_rules! bench_fn { + // FIXME: jnf benches never terminate + ( + id: jnf; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + ( + id: $id:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => { + #[bench] + #[allow(unused_mut)] + pub fn $id(bh: &mut Bencher) { + // Type of the system libm fn: + type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + + // FIXME: extern "C" wrapper + extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { + libm::$id($($arg_ids),*) + } + + // Generate a tuple of arguments containing random values: let mut rng = rand::thread_rng(); - let mut n = rng.gen::(); - n &= 0xffff; - let x = rng.gen::(); - bh.iter(|| test::black_box(libm::[<$func f>](n, x))) + let mut x: ( $($arg_tys,)+ ) = ( $(rng.gen::<$arg_tys>(),)+ ); + + adjust_input!(fn: $id, input: x); + + bh.iter(|| test::black_box(x).call(libm_fn as FnTy)) } - } - )*); + }; } -unary!( - acos, acosh, asin, atan, cbrt, ceil, cos, cosh, erf, exp, exp2, exp10, expm1, fabs, floor, j0, - j1, lgamma, log, log1p, log2, log10, round, sin, sinh, sqrt, tan, tanh, tgamma, trunc, y0, y1 -); -binary!(atan2, copysign, fdim, fmax, fmin, fmod, hypot, pow); -trinary!(fma); -bessel!(jn, yn); -binary!(ldexp; scalbn); +libm_analyze::for_each_api!(bench_fn); diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index e8da2b264..1f3a9f38e 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -5,9 +5,6 @@ pub trait WithinUlps { fn within_ulps(self, other: Self, ulp_tol: usize) -> bool; } -// TODO: this should be moved to the libm-test/src/lib.rs library. And we -// should make ulps configurable. - // Stamp the impls for floats: macro_rules! impl_within_ulps_f { ($f_ty:ty, $i_ty:ty) => { @@ -65,7 +62,6 @@ pub trait Call { macro_rules! impl_call { (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { - // We only care about unsafe extern "C" functions here, safe functions coerce to them: impl Call $ret_ty> for ($($arg_tys,)+) { type Ret = $ret_ty; fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { @@ -88,3 +84,23 @@ impl_call!((i32, f64) -> f64: x: x.0, x.1); impl_call!((i32, f32) -> f32: x: x.0, x.1); impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); + +// Adjust the input of a function. +#[macro_export] +macro_rules! adjust_input { + (fn: j1, input: $arg:ident) => { + adjust_input!(adjust: $arg) + }; + (fn: jn, input: $arg:ident) => { + adjust_input!(adjust: $arg) + }; + (fn: $id:ident, input: $args:ident) => {}; + (adjust: $arg:ident) => { + // First argument to these functions are a number of + // iterations and passing large random numbers takes forever + // to execute, so check if their higher bits are set and + // zero them: + let p = &mut $arg as *mut _ as *mut i32; + unsafe { p.write(p.read() & 0xff_ffff) } + }; +} diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 9f7326ab9..08bcf161c 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,8 +1,8 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] -#![cfg(feature = "system_libm")] +//#![cfg(feature = "system_libm")] -use libm_test::{Call, WithinUlps}; +use libm_test::{adjust_input, Call, WithinUlps}; // Number of tests to generate for each function const NTESTS: usize = 500; @@ -18,49 +18,49 @@ macro_rules! system_libm { id: j0f; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: j1f; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: jnf; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: y0f; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: y1f; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: ynf; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: exp10; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; ( id: exp10f; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => {}; // Generate random tests for all others: @@ -68,7 +68,7 @@ macro_rules! system_libm { id: $id:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; - ret: $ret_ty:ty; + ret_ty: $ret_ty:ty; ) => { #[test] #[allow(unused)] @@ -91,15 +91,9 @@ macro_rules! system_libm { // Generate a tuple of arguments containing random values: let mut args: ( $($arg_tys,)+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng),)+ ); - // HACK - if let "j1" | "jn" = stringify!($id) { - // First argument to these functions are a number of - // iterations and passing large random numbers takes forever - // to execute, so check if their higher bits are set and - // zero them: - let p = &mut args as *mut _ as *mut i32; - unsafe { p.write(p.read() & 0xffff) } - } + // Some APIs need their inputs to be "adjusted" (see macro): + // correct_input!(fn: $id, input: args); + adjust_input!(fn: $id, input: args); let result = args.call(libm_fn as FnTy); let expected = args.call($id as FnTy); From dd177774b1bc67a0f7689a2f56dace77fc5cc417 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 02:30:02 +0200 Subject: [PATCH 23/43] Fix typo --- crates/libm-test/tests/system_libm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 08bcf161c..f6c60a230 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -1,6 +1,6 @@ //! Compare the results of the `libm` implementation against the system's libm. #![cfg(test)] -//#![cfg(feature = "system_libm")] +#![cfg(feature = "system_libm")] use libm_test::{adjust_input, Call, WithinUlps}; From 8c06185a7770452267ba65bfedfd11d7754be5c2 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:20:42 +0200 Subject: [PATCH 24/43] Add exhaustive testing of the 32-bit APIs --- crates/libm-test/build.rs | 18 +++++- crates/libm-test/src/lib.rs | 16 ++++- crates/libm-test/tests/exhaustive32.rs | 82 ++++++++++++++++++++++++++ crates/libm-test/tests/system_libm.rs | 17 +++--- 4 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 crates/libm-test/tests/exhaustive32.rs diff --git a/crates/libm-test/build.rs b/crates/libm-test/build.rs index 80837c5b8..057985f2b 100644 --- a/crates/libm-test/build.rs +++ b/crates/libm-test/build.rs @@ -2,11 +2,23 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); - + let target = env::var("TARGET").expect("TARGET was not set"); + let profile = env::var("PROFILE").unwrap_or(String::new()); + let opt_level: i32 = env::var("OPT_LEVEL").unwrap().parse().unwrap(); if !cfg!(feature = "checked") { - let lvl = env::var("OPT_LEVEL").unwrap(); - if lvl != "0" { + if opt_level != 0 { println!("cargo:rustc-cfg=assert_no_panic"); } } + + if profile == "release" || opt_level > 0 { + match target.as_str() { + "x86_64-unknown-linux-gnu" | + "x86_64-apple-darwin" | + "x86_64-pc-windows-msvc" => { + println!("cargo:rustc-cfg=exhaustive32"); + } + _ => (), + } + } } diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 1f3a9f38e..adbbf1c81 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -101,6 +101,20 @@ macro_rules! adjust_input { // to execute, so check if their higher bits are set and // zero them: let p = &mut $arg as *mut _ as *mut i32; - unsafe { p.write(p.read() & 0xff_ffff) } + unsafe { p.write(p.read() & 0xffff) } }; } + +#[macro_export] +macro_rules! assert_approx_eq { + ($result:ident == $expected:ident, + id: $id:ident, arg: $arg:ident, ulp: $ulps:expr) => { + if !$crate::WithinUlps::within_ulps($result, $expected, $ulps) { + let f = format!( + "{}{:?} returns = {:?} != {:?} (expected)", + stringify!($id), $arg, $result, $expected + ); + panic!(f); + } + } +} diff --git a/crates/libm-test/tests/exhaustive32.rs b/crates/libm-test/tests/exhaustive32.rs new file mode 100644 index 000000000..0c160a0d5 --- /dev/null +++ b/crates/libm-test/tests/exhaustive32.rs @@ -0,0 +1,82 @@ +//! Exhaustively test unary APIs taking 32-bit wide arguments. +#![cfg(test)] +#![cfg(exhaustive32)] +use libm_test::{assert_approx_eq}; + +macro_rules! exhaustive32 { + // Skip those parts of the API that are not + // exposed by the system libm library: + // + // FIXME: maybe we can skip them as part of libm-analyze? + ( + id: j0f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + ( + id: j1f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + ( + id: y0f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + ( + id: y1f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + ( + id: exp10f; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + // Too expensive: + ( + id: jn; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; + // match only unary functions taking f32: + ( + id: $id:ident; + arg_tys: f32; + arg_ids: $arg_id:ident; + ret_ty: $ret_ty:ty; + ) => { + #[test] + #[allow(unused)] + fn $id() { + extern "C" { + // The system's libm function: + fn $id($arg_id: f32) -> $ret_ty; + } + + for i in 0..u32::max_value() { + let arg: f32 = unsafe { std::mem::transmute(i) }; + let result = libm::$id(arg); + let expected = unsafe { $id(arg) }; + assert_approx_eq!( + result == expected, + id: $id, arg: arg, ulp: 4 + ); + } + } + }; + ( + id: $id:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; +} + +libm_analyze::for_each_api!(exhaustive32); diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index f6c60a230..7d196df4b 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -2,7 +2,7 @@ #![cfg(test)] #![cfg(feature = "system_libm")] -use libm_test::{adjust_input, Call, WithinUlps}; +use libm_test::{adjust_input, Call, assert_approx_eq}; // Number of tests to generate for each function const NTESTS: usize = 500; @@ -77,7 +77,9 @@ macro_rules! system_libm { let mut rng = rand::thread_rng(); for _ in 0..NTESTS { // Type of the system libm fn: - type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + type FnTy + = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + // FIXME: extern "C" wrapper over our libm functions // Shouldn't be needed once they are all extern "C" extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { @@ -89,7 +91,8 @@ macro_rules! system_libm { } // Generate a tuple of arguments containing random values: - let mut args: ( $($arg_tys,)+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng),)+ ); + let mut args: ( $($arg_tys,)+ ) + = ( $(<$arg_tys as Rand>::gen(&mut rng),)+ ); // Some APIs need their inputs to be "adjusted" (see macro): // correct_input!(fn: $id, input: args); @@ -97,10 +100,10 @@ macro_rules! system_libm { let result = args.call(libm_fn as FnTy); let expected = args.call($id as FnTy); - if !result.within_ulps(expected, ULP_TOL) { - eprintln!("{}{:?} returns = {:?} != {:?} (expected)", stringify!($id), args, result, expected); - panic!(); - } + assert_approx_eq!( + result == expected, + id: $id, arg: args, ulp: ULP_TOL + ); } } } From 63ec2efedd0224dbe23655d97a0cd8494960a585 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:29:06 +0200 Subject: [PATCH 25/43] Add missing inline and nopanic attributes --- crates/libm/src/math/exp10.rs | 2 ++ crates/libm/src/math/exp10f.rs | 2 ++ crates/libm/src/math/frexp.rs | 2 ++ crates/libm/src/math/frexpf.rs | 2 ++ crates/libm/src/math/ilogb.rs | 2 ++ crates/libm/src/math/ilogbf.rs | 2 ++ crates/libm/src/math/j0.rs | 4 ++++ crates/libm/src/math/j0f.rs | 5 ++++- crates/libm/src/math/j1.rs | 4 ++++ crates/libm/src/math/j1f.rs | 4 ++++ crates/libm/src/math/jn.rs | 4 ++++ crates/libm/src/math/jnf.rs | 4 ++++ crates/libm/src/math/lgamma.rs | 2 ++ crates/libm/src/math/lgamma_r.rs | 2 ++ crates/libm/src/math/lgammaf.rs | 2 ++ crates/libm/src/math/lgammaf_r.rs | 3 ++- crates/libm/src/math/modf.rs | 2 ++ crates/libm/src/math/modff.rs | 2 ++ crates/libm/src/math/remquo.rs | 2 ++ crates/libm/src/math/remquof.rs | 2 ++ crates/libm/src/math/sincos.rs | 2 ++ crates/libm/src/math/sincosf.rs | 2 ++ crates/libm/src/math/tgamma.rs | 2 ++ crates/libm/src/math/tgammaf.rs | 2 ++ 24 files changed, 60 insertions(+), 2 deletions(-) diff --git a/crates/libm/src/math/exp10.rs b/crates/libm/src/math/exp10.rs index 9537f76f1..4b73fa083 100644 --- a/crates/libm/src/math/exp10.rs +++ b/crates/libm/src/math/exp10.rs @@ -6,6 +6,8 @@ const P10: &[f64] = &[ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, ]; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn exp10(x: f64) -> f64 { let (mut y, n) = modf(x); let u: u64 = n.to_bits(); diff --git a/crates/libm/src/math/exp10f.rs b/crates/libm/src/math/exp10f.rs index d45fff36e..5b3533571 100644 --- a/crates/libm/src/math/exp10f.rs +++ b/crates/libm/src/math/exp10f.rs @@ -6,6 +6,8 @@ const P10: &[f32] = &[ 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, ]; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn exp10f(x: f32) -> f32 { let (mut y, n) = modff(x); let u = n.to_bits(); diff --git a/crates/libm/src/math/frexp.rs b/crates/libm/src/math/frexp.rs index badad786a..92fd5ec39 100644 --- a/crates/libm/src/math/frexp.rs +++ b/crates/libm/src/math/frexp.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn frexp(x: f64) -> (f64, i32) { let mut y = x.to_bits(); let ee = ((y >> 52) & 0x7ff) as i32; diff --git a/crates/libm/src/math/frexpf.rs b/crates/libm/src/math/frexpf.rs index 2919c0ab0..a93544d61 100644 --- a/crates/libm/src/math/frexpf.rs +++ b/crates/libm/src/math/frexpf.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn frexpf(x: f32) -> (f32, i32) { let mut y = x.to_bits(); let ee: i32 = ((y >> 23) & 0xff) as i32; diff --git a/crates/libm/src/math/ilogb.rs b/crates/libm/src/math/ilogb.rs index 0a380b7ef..21e5445ca 100644 --- a/crates/libm/src/math/ilogb.rs +++ b/crates/libm/src/math/ilogb.rs @@ -1,6 +1,8 @@ const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; const FP_ILOGB0: i32 = FP_ILOGBNAN; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn ilogb(x: f64) -> i32 { let mut i: u64 = x.to_bits(); let e = ((i >> 52) & 0x7ff) as i32; diff --git a/crates/libm/src/math/ilogbf.rs b/crates/libm/src/math/ilogbf.rs index b384fa4b2..4d8c6b562 100644 --- a/crates/libm/src/math/ilogbf.rs +++ b/crates/libm/src/math/ilogbf.rs @@ -1,6 +1,8 @@ const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; const FP_ILOGB0: i32 = FP_ILOGBNAN; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn ilogbf(x: f32) -> i32 { let mut i = x.to_bits(); let e = ((i >> 23) & 0xff) as i32; diff --git a/crates/libm/src/math/j0.rs b/crates/libm/src/math/j0.rs index c4258ccca..7103729c1 100644 --- a/crates/libm/src/math/j0.rs +++ b/crates/libm/src/math/j0.rs @@ -109,6 +109,8 @@ const S02: f64 = 1.16926784663337450260e-04; /* 0x3F1EA6D2, 0xDD57DBF4 */ const S03: f64 = 5.13546550207318111446e-07; /* 0x3EA13B54, 0xCE84D5A9 */ const S04: f64 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn j0(mut x: f64) -> f64 { let z: f64; let r: f64; @@ -162,6 +164,8 @@ const V02: f64 = 7.60068627350353253702e-05; /* 0x3F13ECBB, 0xF578C6C1 */ const V03: f64 = 2.59150851840457805467e-07; /* 0x3E91642D, 0x7FF202FD */ const V04: f64 = 4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn y0(x: f64) -> f64 { let z: f64; let u: f64; diff --git a/crates/libm/src/math/j0f.rs b/crates/libm/src/math/j0f.rs index 91c03dbbc..b98a4c436 100644 --- a/crates/libm/src/math/j0f.rs +++ b/crates/libm/src/math/j0f.rs @@ -62,6 +62,8 @@ const S02: f32 = 1.1692678527e-04; /* 0x38f53697 */ const S03: f32 = 5.1354652442e-07; /* 0x3509daa6 */ const S04: f32 = 1.1661400734e-09; /* 0x30a045e8 */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn j0f(mut x: f32) -> f32 { let z: f32; let r: f32; @@ -106,7 +108,8 @@ const V01: f32 = 1.2730483897e-02; /* 0x3c509385 */ const V02: f32 = 7.6006865129e-05; /* 0x389f65e0 */ const V03: f32 = 2.5915085189e-07; /* 0x348b216c */ const V04: f32 = 4.4111031494e-10; /* 0x2ff280c2 */ - +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn y0f(x: f32) -> f32 { let z: f32; let u: f32; diff --git a/crates/libm/src/math/j1.rs b/crates/libm/src/math/j1.rs index 02a65ca5a..8b6352a10 100644 --- a/crates/libm/src/math/j1.rs +++ b/crates/libm/src/math/j1.rs @@ -113,6 +113,8 @@ const S03: f64 = 1.17718464042623683263e-06; /* 0x3EB3BFF8, 0x333F8498 */ const S04: f64 = 5.04636257076217042715e-09; /* 0x3E35AC88, 0xC97DFF2C */ const S05: f64 = 1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn j1(x: f64) -> f64 { let mut z: f64; let r: f64; @@ -158,6 +160,8 @@ const V0: [f64; 5] = [ 1.66559246207992079114e-11, /* 0x3DB25039, 0xDACA772A */ ]; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn y1(x: f64) -> f64 { let z: f64; let u: f64; diff --git a/crates/libm/src/math/j1f.rs b/crates/libm/src/math/j1f.rs index e0b692a7d..90e84f065 100644 --- a/crates/libm/src/math/j1f.rs +++ b/crates/libm/src/math/j1f.rs @@ -63,6 +63,8 @@ const S03: f32 = 1.1771846857e-06; /* 0x359dffc2 */ const S04: f32 = 5.0463624390e-09; /* 0x31ad6446 */ const S05: f32 = 1.2354227016e-11; /* 0x2d59567e */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn j1f(x: f32) -> f32 { let mut z: f32; let r: f32; @@ -107,6 +109,8 @@ const V0: [f32; 5] = [ 1.6655924903e-11, /* 0x2d9281cf */ ]; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn y1f(x: f32) -> f32 { let z: f32; let u: f32; diff --git a/crates/libm/src/math/jn.rs b/crates/libm/src/math/jn.rs index 1be167f84..900da69d5 100644 --- a/crates/libm/src/math/jn.rs +++ b/crates/libm/src/math/jn.rs @@ -38,6 +38,8 @@ use super::{cos, fabs, get_high_word, get_low_word, j0, j1, log, sin, sqrt, y0, const INVSQRTPI: f64 = 5.64189583547756279280e-01; /* 0x3FE20DD7, 0x50429B6D */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn jn(n: i32, mut x: f64) -> f64 { let mut ix: u32; let lx: u32; @@ -251,6 +253,8 @@ pub fn jn(n: i32, mut x: f64) -> f64 { } } +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn yn(n: i32, x: f64) -> f64 { let mut ix: u32; let lx: u32; diff --git a/crates/libm/src/math/jnf.rs b/crates/libm/src/math/jnf.rs index 360f62e20..e81cc987d 100644 --- a/crates/libm/src/math/jnf.rs +++ b/crates/libm/src/math/jnf.rs @@ -15,6 +15,8 @@ use super::{fabsf, j0f, j1f, logf, y0f, y1f}; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn jnf(n: i32, mut x: f32) -> f32 { let mut ix: u32; let mut nm1: i32; @@ -195,6 +197,8 @@ pub fn jnf(n: i32, mut x: f32) -> f32 { } } +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn ynf(n: i32, x: f32) -> f32 { let mut ix: u32; let mut ib: u32; diff --git a/crates/libm/src/math/lgamma.rs b/crates/libm/src/math/lgamma.rs index 5bc87e85e..3f04ada29 100644 --- a/crates/libm/src/math/lgamma.rs +++ b/crates/libm/src/math/lgamma.rs @@ -1,5 +1,7 @@ use super::lgamma_r; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn lgamma(x: f64) -> f64 { lgamma_r(x).0 } diff --git a/crates/libm/src/math/lgamma_r.rs b/crates/libm/src/math/lgamma_r.rs index 382a501fc..f1ab295d0 100644 --- a/crates/libm/src/math/lgamma_r.rs +++ b/crates/libm/src/math/lgamma_r.rs @@ -164,6 +164,8 @@ fn sin_pi(mut x: f64) -> f64 { } } +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn lgamma_r(mut x: f64) -> (f64, i32) { let u: u64 = x.to_bits(); let mut t: f64; diff --git a/crates/libm/src/math/lgammaf.rs b/crates/libm/src/math/lgammaf.rs index dfdc87f96..d97be1a94 100644 --- a/crates/libm/src/math/lgammaf.rs +++ b/crates/libm/src/math/lgammaf.rs @@ -1,5 +1,7 @@ use super::lgammaf_r; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn lgammaf(x: f32) -> f32 { lgammaf_r(x).0 } diff --git a/crates/libm/src/math/lgammaf_r.rs b/crates/libm/src/math/lgammaf_r.rs index 0745359a2..e7af0da3d 100644 --- a/crates/libm/src/math/lgammaf_r.rs +++ b/crates/libm/src/math/lgammaf_r.rs @@ -98,7 +98,8 @@ fn sin_pi(mut x: f32) -> f32 { 0 | _ => k_sinf(y), } } - +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn lgammaf_r(mut x: f32) -> (f32, i32) { let u = x.to_bits(); let mut t: f32; diff --git a/crates/libm/src/math/modf.rs b/crates/libm/src/math/modf.rs index bcab33a81..6b345fc03 100644 --- a/crates/libm/src/math/modf.rs +++ b/crates/libm/src/math/modf.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn modf(x: f64) -> (f64, f64) { let rv2: f64; let mut u = x.to_bits(); diff --git a/crates/libm/src/math/modff.rs b/crates/libm/src/math/modff.rs index 56ece12e3..f6e5f3806 100644 --- a/crates/libm/src/math/modff.rs +++ b/crates/libm/src/math/modff.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn modff(x: f32) -> (f32, f32) { let rv2: f32; let mut u: u32 = x.to_bits(); diff --git a/crates/libm/src/math/remquo.rs b/crates/libm/src/math/remquo.rs index 8e027f242..176765b5d 100644 --- a/crates/libm/src/math/remquo.rs +++ b/crates/libm/src/math/remquo.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { let ux: u64 = x.to_bits(); let mut uy: u64 = y.to_bits(); diff --git a/crates/libm/src/math/remquof.rs b/crates/libm/src/math/remquof.rs index 871d0c7d6..66ac72bd6 100644 --- a/crates/libm/src/math/remquof.rs +++ b/crates/libm/src/math/remquof.rs @@ -1,3 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn remquof(mut x: f32, mut y: f32) -> (f32, i32) { let ux: u32 = x.to_bits(); let mut uy: u32 = y.to_bits(); diff --git a/crates/libm/src/math/sincos.rs b/crates/libm/src/math/sincos.rs index 750908df4..755d5a71f 100644 --- a/crates/libm/src/math/sincos.rs +++ b/crates/libm/src/math/sincos.rs @@ -12,6 +12,8 @@ use super::{get_high_word, k_cos, k_sin, rem_pio2}; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn sincos(x: f64) -> (f64, f64) { let s: f64; let c: f64; diff --git a/crates/libm/src/math/sincosf.rs b/crates/libm/src/math/sincosf.rs index bb9a00392..96a07c8a6 100644 --- a/crates/libm/src/math/sincosf.rs +++ b/crates/libm/src/math/sincosf.rs @@ -23,6 +23,8 @@ const S2PIO2: f32 = 2.0 * PI_2; /* 0x400921FB, 0x54442D18 */ const S3PIO2: f32 = 3.0 * PI_2; /* 0x4012D97C, 0x7F3321D2 */ const S4PIO2: f32 = 4.0 * PI_2; /* 0x401921FB, 0x54442D18 */ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn sincosf(x: f32) -> (f32, f32) { let s: f32; let c: f32; diff --git a/crates/libm/src/math/tgamma.rs b/crates/libm/src/math/tgamma.rs index f8ccf669a..098f489bd 100644 --- a/crates/libm/src/math/tgamma.rs +++ b/crates/libm/src/math/tgamma.rs @@ -130,6 +130,8 @@ fn s(x: f64) -> f64 { return num / den; } +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn tgamma(mut x: f64) -> f64 { let u: u64 = x.to_bits(); let absx: f64; diff --git a/crates/libm/src/math/tgammaf.rs b/crates/libm/src/math/tgammaf.rs index a8f161f0c..43e7e3efb 100644 --- a/crates/libm/src/math/tgammaf.rs +++ b/crates/libm/src/math/tgammaf.rs @@ -1,5 +1,7 @@ use super::tgamma; +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn tgammaf(x: f32) -> f32 { tgamma(x as f64) as f32 } From 1eed20b230fa6ad67bbbaca0f7671b53c26987db Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:36:50 +0200 Subject: [PATCH 26/43] Deny missing no_panic and inline attrs --- crates/libm-analyze/src/lib.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index dd1abdbc1..8fe161d91 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -211,22 +211,26 @@ fn get_functions(files: &[syn::File]) -> Vec { syn_to_str!(generics.clone()) )); } - // FIXME: we can do better here, but right now, we should - // error if inline and no_panic are not used, which is the - // case if the public API has no attributes. - // - // We might also want to check other attributes as well. if attrs.is_empty() { - let e2 = e; err!(format!( - "missing `#[inline]` and `#[no_panic]` attributes {}", - attrs - .iter() - .map(|a| syn_to_str!(a)) - .collect::>() - .join(",") + "missing `#[inline]` and `#[no_panic]` attributes" )); - e = e2; + } else { + let attrs = attrs + .iter() + .map(|a| syn_to_str!(a)) + .collect::>() + .join(","); + if !attrs.contains("inline") { + err!(format!( + "missing `#[inline]` attribute" + )); + } + if !attrs.contains("no_panic") { + err!(format!( + "missing `#[no_panic]` attributes" + )); + } } // Validate and parse output parameters and function arguments: match output { From a11a6fecd5e9d8ef799d5a5c524192f09cfe224b Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:37:01 +0200 Subject: [PATCH 27/43] Add remaining no_panic and inline attrs --- crates/libm/src/math/acosh.rs | 2 ++ crates/libm/src/math/acoshf.rs | 2 ++ crates/libm/src/math/asinh.rs | 2 ++ crates/libm/src/math/asinhf.rs | 2 ++ crates/libm/src/math/atanh.rs | 2 ++ crates/libm/src/math/atanhf.rs | 2 ++ crates/libm/src/math/copysign.rs | 2 ++ crates/libm/src/math/copysignf.rs | 2 ++ crates/libm/src/math/erf.rs | 4 ++++ crates/libm/src/math/erff.rs | 4 ++++ 10 files changed, 24 insertions(+) diff --git a/crates/libm/src/math/acosh.rs b/crates/libm/src/math/acosh.rs index ac7a5f1c6..80c3ab8a9 100644 --- a/crates/libm/src/math/acosh.rs +++ b/crates/libm/src/math/acosh.rs @@ -7,6 +7,8 @@ const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa3 /// Calculates the inverse hyperbolic cosine of `x`. /// Is defined as `log(x + sqrt(x*x-1))`. /// `x` must be a number greater than or equal to 1. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn acosh(x: f64) -> f64 { let u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; diff --git a/crates/libm/src/math/acoshf.rs b/crates/libm/src/math/acoshf.rs index 0879e1edb..cf9d13a3b 100644 --- a/crates/libm/src/math/acoshf.rs +++ b/crates/libm/src/math/acoshf.rs @@ -7,6 +7,8 @@ const LN2: f32 = 0.693147180559945309417232121458176568; /// Calculates the inverse hyperbolic cosine of `x`. /// Is defined as `log(x + sqrt(x*x-1))`. /// `x` must be a number greater than or equal to 1. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn acoshf(x: f32) -> f32 { let u = x.to_bits(); let a = u & 0x7fffffff; diff --git a/crates/libm/src/math/asinh.rs b/crates/libm/src/math/asinh.rs index 14295357a..0fc4ebf91 100644 --- a/crates/libm/src/math/asinh.rs +++ b/crates/libm/src/math/asinh.rs @@ -7,6 +7,8 @@ const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa3 /// /// Calculates the inverse hyperbolic sine of `x`. /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn asinh(mut x: f64) -> f64 { let mut u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; diff --git a/crates/libm/src/math/asinhf.rs b/crates/libm/src/math/asinhf.rs index e22a29132..c71728887 100644 --- a/crates/libm/src/math/asinhf.rs +++ b/crates/libm/src/math/asinhf.rs @@ -7,6 +7,8 @@ const LN2: f32 = 0.693147180559945309417232121458176568; /// /// Calculates the inverse hyperbolic sine of `x`. /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn asinhf(mut x: f32) -> f32 { let u = x.to_bits(); let i = u & 0x7fffffff; diff --git a/crates/libm/src/math/atanh.rs b/crates/libm/src/math/atanh.rs index 79a989c42..01a3ace3c 100644 --- a/crates/libm/src/math/atanh.rs +++ b/crates/libm/src/math/atanh.rs @@ -5,6 +5,8 @@ use super::log1p; /// /// Calculates the inverse hyperbolic tangent of `x`. /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn atanh(x: f64) -> f64 { let u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; diff --git a/crates/libm/src/math/atanhf.rs b/crates/libm/src/math/atanhf.rs index 7b2f34d97..f8ac4291f 100644 --- a/crates/libm/src/math/atanhf.rs +++ b/crates/libm/src/math/atanhf.rs @@ -5,6 +5,8 @@ use super::log1pf; /// /// Calculates the inverse hyperbolic tangent of `x`. /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn atanhf(mut x: f32) -> f32 { let mut u = x.to_bits(); let sign = (u >> 31) != 0; diff --git a/crates/libm/src/math/copysign.rs b/crates/libm/src/math/copysign.rs index 1527fb6ea..57408048e 100644 --- a/crates/libm/src/math/copysign.rs +++ b/crates/libm/src/math/copysign.rs @@ -2,6 +2,8 @@ /// /// Constructs a number with the magnitude (absolute value) of its /// first argument, `x`, and the sign of its second argument, `y`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn copysign(x: f64, y: f64) -> f64 { let mut ux = x.to_bits(); let uy = y.to_bits(); diff --git a/crates/libm/src/math/copysignf.rs b/crates/libm/src/math/copysignf.rs index 35148561a..687862285 100644 --- a/crates/libm/src/math/copysignf.rs +++ b/crates/libm/src/math/copysignf.rs @@ -2,6 +2,8 @@ /// /// Constructs a number with the magnitude (absolute value) of its /// first argument, `x`, and the sign of its second argument, `y`. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn copysignf(x: f32, y: f32) -> f32 { let mut ux = x.to_bits(); let uy = y.to_bits(); diff --git a/crates/libm/src/math/erf.rs b/crates/libm/src/math/erf.rs index a2c617d34..01b7710eb 100644 --- a/crates/libm/src/math/erf.rs +++ b/crates/libm/src/math/erf.rs @@ -219,6 +219,8 @@ fn erfc2(ix: u32, mut x: f64) -> f64 { /// Calculates an approximation to the “error function”, which estimates /// the probability that an observation will fall within x standard /// deviations of the mean (assuming a normal distribution). +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn erf(x: f64) -> f64 { let r: f64; let s: f64; @@ -268,6 +270,8 @@ pub fn erf(x: f64) -> f64 { /// Is `1 - erf(x)`. Is computed directly, so that you can use it to avoid /// the loss of precision that would result from subtracting /// large probabilities (on large `x`) from 1. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn erfc(x: f64) -> f64 { let r: f64; let s: f64; diff --git a/crates/libm/src/math/erff.rs b/crates/libm/src/math/erff.rs index 384052293..f6c14a5e0 100644 --- a/crates/libm/src/math/erff.rs +++ b/crates/libm/src/math/erff.rs @@ -130,6 +130,8 @@ fn erfc2(mut ix: u32, mut x: f32) -> f32 { /// Calculates an approximation to the “error function”, which estimates /// the probability that an observation will fall within x standard /// deviations of the mean (assuming a normal distribution). +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn erff(x: f32) -> f32 { let r: f32; let s: f32; @@ -179,6 +181,8 @@ pub fn erff(x: f32) -> f32 { /// Is `1 - erf(x)`. Is computed directly, so that you can use it to avoid /// the loss of precision that would result from subtracting /// large probabilities (on large `x`) from 1. +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn erfcf(x: f32) -> f32 { let r: f32; let s: f32; From a27bd1073b8e998d80ceeafcb06e799043c9cd4c Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:47:09 +0200 Subject: [PATCH 28/43] Make all public APIs extern C to enforce nounwind and C API compat --- crates/libm-analyze/src/lib.rs | 12 +++--------- crates/libm-test/build.rs | 4 +--- crates/libm-test/src/lib.rs | 7 +++++-- crates/libm-test/tests/exhaustive32.rs | 2 +- crates/libm-test/tests/system_libm.rs | 2 +- crates/libm/src/math/acos.rs | 2 +- crates/libm/src/math/acosf.rs | 2 +- crates/libm/src/math/acosh.rs | 2 +- crates/libm/src/math/acoshf.rs | 2 +- crates/libm/src/math/asin.rs | 2 +- crates/libm/src/math/asinf.rs | 2 +- crates/libm/src/math/asinh.rs | 2 +- crates/libm/src/math/asinhf.rs | 2 +- crates/libm/src/math/atan.rs | 2 +- crates/libm/src/math/atan2.rs | 2 +- crates/libm/src/math/atan2f.rs | 2 +- crates/libm/src/math/atanf.rs | 2 +- crates/libm/src/math/atanh.rs | 2 +- crates/libm/src/math/atanhf.rs | 2 +- crates/libm/src/math/cbrt.rs | 2 +- crates/libm/src/math/cbrtf.rs | 2 +- crates/libm/src/math/ceil.rs | 2 +- crates/libm/src/math/ceilf.rs | 2 +- crates/libm/src/math/copysign.rs | 2 +- crates/libm/src/math/copysignf.rs | 2 +- crates/libm/src/math/cos.rs | 2 +- crates/libm/src/math/cosf.rs | 2 +- crates/libm/src/math/cosh.rs | 2 +- crates/libm/src/math/coshf.rs | 2 +- crates/libm/src/math/erf.rs | 4 ++-- crates/libm/src/math/erff.rs | 4 ++-- crates/libm/src/math/exp.rs | 2 +- crates/libm/src/math/exp10.rs | 2 +- crates/libm/src/math/exp10f.rs | 2 +- crates/libm/src/math/exp2.rs | 2 +- crates/libm/src/math/exp2f.rs | 2 +- crates/libm/src/math/expf.rs | 2 +- crates/libm/src/math/expm1.rs | 2 +- crates/libm/src/math/expm1f.rs | 2 +- crates/libm/src/math/fabs.rs | 2 +- crates/libm/src/math/fabsf.rs | 2 +- crates/libm/src/math/fdim.rs | 2 +- crates/libm/src/math/fdimf.rs | 2 +- crates/libm/src/math/floor.rs | 2 +- crates/libm/src/math/floorf.rs | 2 +- crates/libm/src/math/fma.rs | 2 +- crates/libm/src/math/fmaf.rs | 2 +- crates/libm/src/math/fmax.rs | 2 +- crates/libm/src/math/fmaxf.rs | 2 +- crates/libm/src/math/fmin.rs | 2 +- crates/libm/src/math/fminf.rs | 2 +- crates/libm/src/math/fmod.rs | 2 +- crates/libm/src/math/fmodf.rs | 2 +- crates/libm/src/math/frexp.rs | 2 +- crates/libm/src/math/frexpf.rs | 2 +- crates/libm/src/math/hypot.rs | 2 +- crates/libm/src/math/hypotf.rs | 2 +- crates/libm/src/math/ilogb.rs | 2 +- crates/libm/src/math/ilogbf.rs | 2 +- crates/libm/src/math/j0.rs | 4 ++-- crates/libm/src/math/j0f.rs | 4 ++-- crates/libm/src/math/j1.rs | 4 ++-- crates/libm/src/math/j1f.rs | 4 ++-- crates/libm/src/math/jn.rs | 4 ++-- crates/libm/src/math/jnf.rs | 4 ++-- crates/libm/src/math/ldexp.rs | 2 +- crates/libm/src/math/ldexpf.rs | 2 +- crates/libm/src/math/lgamma.rs | 2 +- crates/libm/src/math/lgamma_r.rs | 2 +- crates/libm/src/math/lgammaf.rs | 2 +- crates/libm/src/math/lgammaf_r.rs | 2 +- crates/libm/src/math/log.rs | 2 +- crates/libm/src/math/log10.rs | 2 +- crates/libm/src/math/log10f.rs | 2 +- crates/libm/src/math/log1p.rs | 2 +- crates/libm/src/math/log1pf.rs | 2 +- crates/libm/src/math/log2.rs | 2 +- crates/libm/src/math/log2f.rs | 2 +- crates/libm/src/math/logf.rs | 2 +- crates/libm/src/math/modf.rs | 2 +- crates/libm/src/math/modff.rs | 2 +- crates/libm/src/math/pow.rs | 2 +- crates/libm/src/math/powf.rs | 2 +- crates/libm/src/math/remainder.rs | 2 +- crates/libm/src/math/remainderf.rs | 2 +- crates/libm/src/math/remquo.rs | 2 +- crates/libm/src/math/remquof.rs | 2 +- crates/libm/src/math/round.rs | 2 +- crates/libm/src/math/roundf.rs | 2 +- crates/libm/src/math/scalbn.rs | 2 +- crates/libm/src/math/scalbnf.rs | 2 +- crates/libm/src/math/sin.rs | 2 +- crates/libm/src/math/sincos.rs | 2 +- crates/libm/src/math/sincosf.rs | 2 +- crates/libm/src/math/sinf.rs | 2 +- crates/libm/src/math/sinh.rs | 2 +- crates/libm/src/math/sinhf.rs | 2 +- crates/libm/src/math/sqrt.rs | 2 +- crates/libm/src/math/sqrtf.rs | 2 +- crates/libm/src/math/tan.rs | 2 +- crates/libm/src/math/tanf.rs | 2 +- crates/libm/src/math/tanh.rs | 2 +- crates/libm/src/math/tanhf.rs | 2 +- crates/libm/src/math/tgamma.rs | 2 +- crates/libm/src/math/tgammaf.rs | 2 +- crates/libm/src/math/trunc.rs | 2 +- crates/libm/src/math/truncf.rs | 2 +- 107 files changed, 121 insertions(+), 126 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 8fe161d91..7d417d6fc 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -212,9 +212,7 @@ fn get_functions(files: &[syn::File]) -> Vec { )); } if attrs.is_empty() { - err!(format!( - "missing `#[inline]` and `#[no_panic]` attributes" - )); + err!(format!("missing `#[inline]` and `#[no_panic]` attributes")); } else { let attrs = attrs .iter() @@ -222,14 +220,10 @@ fn get_functions(files: &[syn::File]) -> Vec { .collect::>() .join(","); if !attrs.contains("inline") { - err!(format!( - "missing `#[inline]` attribute" - )); + err!(format!("missing `#[inline]` attribute")); } if !attrs.contains("no_panic") { - err!(format!( - "missing `#[no_panic]` attributes" - )); + err!(format!("missing `#[no_panic]` attributes")); } } // Validate and parse output parameters and function arguments: diff --git a/crates/libm-test/build.rs b/crates/libm-test/build.rs index 057985f2b..345e60378 100644 --- a/crates/libm-test/build.rs +++ b/crates/libm-test/build.rs @@ -13,9 +13,7 @@ fn main() { if profile == "release" || opt_level > 0 { match target.as_str() { - "x86_64-unknown-linux-gnu" | - "x86_64-apple-darwin" | - "x86_64-pc-windows-msvc" => { + "x86_64-unknown-linux-gnu" | "x86_64-apple-darwin" | "x86_64-pc-windows-msvc" => { println!("cargo:rustc-cfg=exhaustive32"); } _ => (), diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index adbbf1c81..990bec4a6 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -112,9 +112,12 @@ macro_rules! assert_approx_eq { if !$crate::WithinUlps::within_ulps($result, $expected, $ulps) { let f = format!( "{}{:?} returns = {:?} != {:?} (expected)", - stringify!($id), $arg, $result, $expected + stringify!($id), + $arg, + $result, + $expected ); panic!(f); } - } + }; } diff --git a/crates/libm-test/tests/exhaustive32.rs b/crates/libm-test/tests/exhaustive32.rs index 0c160a0d5..5083ca7d7 100644 --- a/crates/libm-test/tests/exhaustive32.rs +++ b/crates/libm-test/tests/exhaustive32.rs @@ -1,7 +1,7 @@ //! Exhaustively test unary APIs taking 32-bit wide arguments. #![cfg(test)] #![cfg(exhaustive32)] -use libm_test::{assert_approx_eq}; +use libm_test::assert_approx_eq; macro_rules! exhaustive32 { // Skip those parts of the API that are not diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 7d196df4b..eaa4b8529 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -2,7 +2,7 @@ #![cfg(test)] #![cfg(feature = "system_libm")] -use libm_test::{adjust_input, Call, assert_approx_eq}; +use libm_test::{adjust_input, assert_approx_eq, Call}; // Number of tests to generate for each function const NTESTS: usize = 500; diff --git a/crates/libm/src/math/acos.rs b/crates/libm/src/math/acos.rs index d5e1f6865..2d98a9948 100644 --- a/crates/libm/src/math/acos.rs +++ b/crates/libm/src/math/acos.rs @@ -62,7 +62,7 @@ fn r(z: f64) -> f64 { /// Returns values in radians, in the range of 0 to pi. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn acos(x: f64) -> f64 { +pub extern "C" fn acos(x: f64) -> f64 { let x1p_120f = f64::from_bits(0x3870000000000000); // 0x1p-120 === 2 ^ -120 let z: f64; let w: f64; diff --git a/crates/libm/src/math/acosf.rs b/crates/libm/src/math/acosf.rs index d0598e811..2aab1673c 100644 --- a/crates/libm/src/math/acosf.rs +++ b/crates/libm/src/math/acosf.rs @@ -36,7 +36,7 @@ fn r(z: f32) -> f32 { /// Returns values in radians, in the range of 0 to pi. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn acosf(x: f32) -> f32 { +pub extern "C" fn acosf(x: f32) -> f32 { let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) let z: f32; diff --git a/crates/libm/src/math/acosh.rs b/crates/libm/src/math/acosh.rs index 80c3ab8a9..344dd8700 100644 --- a/crates/libm/src/math/acosh.rs +++ b/crates/libm/src/math/acosh.rs @@ -9,7 +9,7 @@ const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa3 /// `x` must be a number greater than or equal to 1. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn acosh(x: f64) -> f64 { +pub extern "C" fn acosh(x: f64) -> f64 { let u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; diff --git a/crates/libm/src/math/acoshf.rs b/crates/libm/src/math/acoshf.rs index cf9d13a3b..bc6af68f8 100644 --- a/crates/libm/src/math/acoshf.rs +++ b/crates/libm/src/math/acoshf.rs @@ -9,7 +9,7 @@ const LN2: f32 = 0.693147180559945309417232121458176568; /// `x` must be a number greater than or equal to 1. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn acoshf(x: f32) -> f32 { +pub extern "C" fn acoshf(x: f32) -> f32 { let u = x.to_bits(); let a = u & 0x7fffffff; diff --git a/crates/libm/src/math/asin.rs b/crates/libm/src/math/asin.rs index 774475e51..31bbcde46 100644 --- a/crates/libm/src/math/asin.rs +++ b/crates/libm/src/math/asin.rs @@ -69,7 +69,7 @@ fn comp_r(z: f64) -> f64 { /// Returns values in radians, in the range of -pi/2 to pi/2. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn asin(mut x: f64) -> f64 { +pub extern "C" fn asin(mut x: f64) -> f64 { let z: f64; let r: f64; let s: f64; diff --git a/crates/libm/src/math/asinf.rs b/crates/libm/src/math/asinf.rs index ce0f4a997..b0bb0b4c4 100644 --- a/crates/libm/src/math/asinf.rs +++ b/crates/libm/src/math/asinf.rs @@ -38,7 +38,7 @@ fn r(z: f32) -> f32 { /// Returns values in radians, in the range of -pi/2 to pi/2. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn asinf(mut x: f32) -> f32 { +pub extern "C" fn asinf(mut x: f32) -> f32 { let x1p_120 = f64::from_bits(0x3870000000000000); // 0x1p-120 === 2 ^ (-120) let hx = x.to_bits(); diff --git a/crates/libm/src/math/asinh.rs b/crates/libm/src/math/asinh.rs index 0fc4ebf91..7c0f84f04 100644 --- a/crates/libm/src/math/asinh.rs +++ b/crates/libm/src/math/asinh.rs @@ -9,7 +9,7 @@ const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa3 /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn asinh(mut x: f64) -> f64 { +pub extern "C" fn asinh(mut x: f64) -> f64 { let mut u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; let sign = (u >> 63) != 0; diff --git a/crates/libm/src/math/asinhf.rs b/crates/libm/src/math/asinhf.rs index c71728887..6072d3751 100644 --- a/crates/libm/src/math/asinhf.rs +++ b/crates/libm/src/math/asinhf.rs @@ -9,7 +9,7 @@ const LN2: f32 = 0.693147180559945309417232121458176568; /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn asinhf(mut x: f32) -> f32 { +pub extern "C" fn asinhf(mut x: f32) -> f32 { let u = x.to_bits(); let i = u & 0x7fffffff; let sign = (u >> 31) != 0; diff --git a/crates/libm/src/math/atan.rs b/crates/libm/src/math/atan.rs index f79fc0eb0..57244146a 100644 --- a/crates/libm/src/math/atan.rs +++ b/crates/libm/src/math/atan.rs @@ -66,7 +66,7 @@ const AT: [f64; 11] = [ /// Returns a value in radians, in the range of -pi/2 to pi/2. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atan(x: f64) -> f64 { +pub extern "C" fn atan(x: f64) -> f64 { let mut x = x; let mut ix = (x.to_bits() >> 32) as u32; let sign = ix >> 31; diff --git a/crates/libm/src/math/atan2.rs b/crates/libm/src/math/atan2.rs index a303dcc60..feef73ea0 100644 --- a/crates/libm/src/math/atan2.rs +++ b/crates/libm/src/math/atan2.rs @@ -50,7 +50,7 @@ const PI_LO: f64 = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ /// Returns a value in radians, in the range of -pi to pi. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atan2(y: f64, x: f64) -> f64 { +pub extern "C" fn atan2(y: f64, x: f64) -> f64 { if x.is_nan() || y.is_nan() { return x + y; } diff --git a/crates/libm/src/math/atan2f.rs b/crates/libm/src/math/atan2f.rs index 7bbe5f1d4..e5b169d39 100644 --- a/crates/libm/src/math/atan2f.rs +++ b/crates/libm/src/math/atan2f.rs @@ -26,7 +26,7 @@ const PI_LO: f32 = -8.7422776573e-08; /* 0xb3bbbd2e */ /// Returns a value in radians, in the range of -pi to pi. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atan2f(y: f32, x: f32) -> f32 { +pub extern "C" fn atan2f(y: f32, x: f32) -> f32 { if x.is_nan() || y.is_nan() { return x + y; } diff --git a/crates/libm/src/math/atanf.rs b/crates/libm/src/math/atanf.rs index 363e11d64..e9250b468 100644 --- a/crates/libm/src/math/atanf.rs +++ b/crates/libm/src/math/atanf.rs @@ -43,7 +43,7 @@ const A_T: [f32; 5] = [ /// Returns a value in radians, in the range of -pi/2 to pi/2. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atanf(mut x: f32) -> f32 { +pub extern "C" fn atanf(mut x: f32) -> f32 { let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) let z: f32; diff --git a/crates/libm/src/math/atanh.rs b/crates/libm/src/math/atanh.rs index 01a3ace3c..58d3c1eb2 100644 --- a/crates/libm/src/math/atanh.rs +++ b/crates/libm/src/math/atanh.rs @@ -7,7 +7,7 @@ use super::log1p; /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atanh(x: f64) -> f64 { +pub extern "C" fn atanh(x: f64) -> f64 { let u = x.to_bits(); let e = ((u >> 52) as usize) & 0x7ff; let sign = (u >> 63) != 0; diff --git a/crates/libm/src/math/atanhf.rs b/crates/libm/src/math/atanhf.rs index f8ac4291f..5e66e4196 100644 --- a/crates/libm/src/math/atanhf.rs +++ b/crates/libm/src/math/atanhf.rs @@ -7,7 +7,7 @@ use super::log1pf; /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn atanhf(mut x: f32) -> f32 { +pub extern "C" fn atanhf(mut x: f32) -> f32 { let mut u = x.to_bits(); let sign = (u >> 31) != 0; diff --git a/crates/libm/src/math/cbrt.rs b/crates/libm/src/math/cbrt.rs index 04469b159..3f5253175 100644 --- a/crates/libm/src/math/cbrt.rs +++ b/crates/libm/src/math/cbrt.rs @@ -32,7 +32,7 @@ const P4: f64 = 0.145996192886612446982; /* 0x3fc2b000, 0xd4e4edd7 */ /// Computes the cube root of the argument. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn cbrt(x: f64) -> f64 { +pub extern "C" fn cbrt(x: f64) -> f64 { let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 let mut ui: u64 = x.to_bits(); diff --git a/crates/libm/src/math/cbrtf.rs b/crates/libm/src/math/cbrtf.rs index 6e589c099..a129ef9bc 100644 --- a/crates/libm/src/math/cbrtf.rs +++ b/crates/libm/src/math/cbrtf.rs @@ -27,7 +27,7 @@ const B2: u32 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ /// Computes the cube root of the argument. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn cbrtf(x: f32) -> f32 { +pub extern "C" fn cbrtf(x: f32) -> f32 { let x1p24 = f32::from_bits(0x4b800000); // 0x1p24f === 2 ^ 24 let mut r: f64; diff --git a/crates/libm/src/math/ceil.rs b/crates/libm/src/math/ceil.rs index 4b964c769..a263968c8 100644 --- a/crates/libm/src/math/ceil.rs +++ b/crates/libm/src/math/ceil.rs @@ -7,7 +7,7 @@ const TOINT: f64 = 1. / f64::EPSILON; /// Finds the nearest integer greater than or equal to `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ceil(x: f64) -> f64 { +pub extern "C" fn ceil(x: f64) -> f64 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f64.ceil` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/ceilf.rs b/crates/libm/src/math/ceilf.rs index 8ab4e48f8..82924a0fe 100644 --- a/crates/libm/src/math/ceilf.rs +++ b/crates/libm/src/math/ceilf.rs @@ -5,7 +5,7 @@ use core::f32; /// Finds the nearest integer greater than or equal to `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ceilf(x: f32) -> f32 { +pub extern "C" fn ceilf(x: f32) -> f32 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f32.ceil` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/copysign.rs b/crates/libm/src/math/copysign.rs index 57408048e..fe0a020b6 100644 --- a/crates/libm/src/math/copysign.rs +++ b/crates/libm/src/math/copysign.rs @@ -4,7 +4,7 @@ /// first argument, `x`, and the sign of its second argument, `y`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn copysign(x: f64, y: f64) -> f64 { +pub extern "C" fn copysign(x: f64, y: f64) -> f64 { let mut ux = x.to_bits(); let uy = y.to_bits(); ux &= (!0) >> 1; diff --git a/crates/libm/src/math/copysignf.rs b/crates/libm/src/math/copysignf.rs index 687862285..1ecafc204 100644 --- a/crates/libm/src/math/copysignf.rs +++ b/crates/libm/src/math/copysignf.rs @@ -4,7 +4,7 @@ /// first argument, `x`, and the sign of its second argument, `y`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn copysignf(x: f32, y: f32) -> f32 { +pub extern "C" fn copysignf(x: f32, y: f32) -> f32 { let mut ux = x.to_bits(); let uy = y.to_bits(); ux &= 0x7fffffff; diff --git a/crates/libm/src/math/cos.rs b/crates/libm/src/math/cos.rs index fe5a89919..4eeb271ec 100644 --- a/crates/libm/src/math/cos.rs +++ b/crates/libm/src/math/cos.rs @@ -43,7 +43,7 @@ use super::{k_cos, k_sin, rem_pio2}; // #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn cos(x: f64) -> f64 { +pub extern "C" fn cos(x: f64) -> f64 { let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; /* |x| ~< pi/4 */ diff --git a/crates/libm/src/math/cosf.rs b/crates/libm/src/math/cosf.rs index 48d76c8ee..fc912948b 100644 --- a/crates/libm/src/math/cosf.rs +++ b/crates/libm/src/math/cosf.rs @@ -26,7 +26,7 @@ const C4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn cosf(x: f32) -> f32 { +pub extern "C" fn cosf(x: f32) -> f32 { let x64 = x as f64; let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 diff --git a/crates/libm/src/math/cosh.rs b/crates/libm/src/math/cosh.rs index bac875566..fb7d28198 100644 --- a/crates/libm/src/math/cosh.rs +++ b/crates/libm/src/math/cosh.rs @@ -9,7 +9,7 @@ use super::k_expo2; /// Angles are specified in radians. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn cosh(mut x: f64) -> f64 { +pub extern "C" fn cosh(mut x: f64) -> f64 { /* |x| */ let mut ix = x.to_bits(); ix &= 0x7fffffffffffffff; diff --git a/crates/libm/src/math/coshf.rs b/crates/libm/src/math/coshf.rs index bf99e42f0..f27fbd46c 100644 --- a/crates/libm/src/math/coshf.rs +++ b/crates/libm/src/math/coshf.rs @@ -9,7 +9,7 @@ use super::k_expo2f; /// Angles are specified in radians. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn coshf(mut x: f32) -> f32 { +pub extern "C" fn coshf(mut x: f32) -> f32 { let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 /* |x| */ diff --git a/crates/libm/src/math/erf.rs b/crates/libm/src/math/erf.rs index 01b7710eb..a3bf71096 100644 --- a/crates/libm/src/math/erf.rs +++ b/crates/libm/src/math/erf.rs @@ -221,7 +221,7 @@ fn erfc2(ix: u32, mut x: f64) -> f64 { /// deviations of the mean (assuming a normal distribution). #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn erf(x: f64) -> f64 { +pub extern "C" fn erf(x: f64) -> f64 { let r: f64; let s: f64; let z: f64; @@ -272,7 +272,7 @@ pub fn erf(x: f64) -> f64 { /// large probabilities (on large `x`) from 1. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn erfc(x: f64) -> f64 { +pub extern "C" fn erfc(x: f64) -> f64 { let r: f64; let s: f64; let z: f64; diff --git a/crates/libm/src/math/erff.rs b/crates/libm/src/math/erff.rs index f6c14a5e0..cfb53b4e0 100644 --- a/crates/libm/src/math/erff.rs +++ b/crates/libm/src/math/erff.rs @@ -132,7 +132,7 @@ fn erfc2(mut ix: u32, mut x: f32) -> f32 { /// deviations of the mean (assuming a normal distribution). #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn erff(x: f32) -> f32 { +pub extern "C" fn erff(x: f32) -> f32 { let r: f32; let s: f32; let z: f32; @@ -183,7 +183,7 @@ pub fn erff(x: f32) -> f32 { /// large probabilities (on large `x`) from 1. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn erfcf(x: f32) -> f32 { +pub extern "C" fn erfcf(x: f32) -> f32 { let r: f32; let s: f32; let z: f32; diff --git a/crates/libm/src/math/exp.rs b/crates/libm/src/math/exp.rs index 5465b5693..633d4bda6 100644 --- a/crates/libm/src/math/exp.rs +++ b/crates/libm/src/math/exp.rs @@ -83,7 +83,7 @@ const P5: f64 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn exp(mut x: f64) -> f64 { +pub extern "C" fn exp(mut x: f64) -> f64 { let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023 let x1p_149 = f64::from_bits(0x36a0000000000000); // 0x1p-149 === 2 ^ -149 diff --git a/crates/libm/src/math/exp10.rs b/crates/libm/src/math/exp10.rs index 4b73fa083..99f65bf6f 100644 --- a/crates/libm/src/math/exp10.rs +++ b/crates/libm/src/math/exp10.rs @@ -8,7 +8,7 @@ const P10: &[f64] = &[ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn exp10(x: f64) -> f64 { +pub extern "C" fn exp10(x: f64) -> f64 { let (mut y, n) = modf(x); let u: u64 = n.to_bits(); /* fabs(n) < 16 without raising invalid on nan */ diff --git a/crates/libm/src/math/exp10f.rs b/crates/libm/src/math/exp10f.rs index 5b3533571..f73cc2e22 100644 --- a/crates/libm/src/math/exp10f.rs +++ b/crates/libm/src/math/exp10f.rs @@ -8,7 +8,7 @@ const P10: &[f32] = &[ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn exp10f(x: f32) -> f32 { +pub extern "C" fn exp10f(x: f32) -> f32 { let (mut y, n) = modff(x); let u = n.to_bits(); /* fabsf(n) < 8 without raising invalid on nan */ diff --git a/crates/libm/src/math/exp2.rs b/crates/libm/src/math/exp2.rs index 96f230ef1..d6959be82 100644 --- a/crates/libm/src/math/exp2.rs +++ b/crates/libm/src/math/exp2.rs @@ -324,7 +324,7 @@ static TBL: [u64; TBLSIZE * 2] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn exp2(mut x: f64) -> f64 { +pub extern "C" fn exp2(mut x: f64) -> f64 { let redux = f64::from_bits(0x4338000000000000) / TBLSIZE as f64; let p1 = f64::from_bits(0x3fe62e42fefa39ef); let p2 = f64::from_bits(0x3fcebfbdff82c575); diff --git a/crates/libm/src/math/exp2f.rs b/crates/libm/src/math/exp2f.rs index 12c9e76a4..2249bb6a9 100644 --- a/crates/libm/src/math/exp2f.rs +++ b/crates/libm/src/math/exp2f.rs @@ -75,7 +75,7 @@ static EXP2FT: [u64; TBLSIZE] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn exp2f(mut x: f32) -> f32 { +pub extern "C" fn exp2f(mut x: f32) -> f32 { let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; let p1 = f32::from_bits(0x3f317218); let p2 = f32::from_bits(0x3e75fdf0); diff --git a/crates/libm/src/math/expf.rs b/crates/libm/src/math/expf.rs index 09323ec8d..b9d185be7 100644 --- a/crates/libm/src/math/expf.rs +++ b/crates/libm/src/math/expf.rs @@ -32,7 +32,7 @@ const P2: f32 = -2.7667332906e-3; /* -0xb55215.0p-32 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn expf(mut x: f32) -> f32 { +pub extern "C" fn expf(mut x: f32) -> f32 { let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 /*original 0x1p-149f ??????????? */ let mut hx = x.to_bits(); diff --git a/crates/libm/src/math/expm1.rs b/crates/libm/src/math/expm1.rs index a5f5e4338..9e7dd0e15 100644 --- a/crates/libm/src/math/expm1.rs +++ b/crates/libm/src/math/expm1.rs @@ -32,7 +32,7 @@ const Q5: f64 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */ /// where using `exp(x)-1` would lose many significant digits. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn expm1(mut x: f64) -> f64 { +pub extern "C" fn expm1(mut x: f64) -> f64 { let hi: f64; let lo: f64; let k: i32; diff --git a/crates/libm/src/math/expm1f.rs b/crates/libm/src/math/expm1f.rs index 9bb223448..6117b6a06 100644 --- a/crates/libm/src/math/expm1f.rs +++ b/crates/libm/src/math/expm1f.rs @@ -34,7 +34,7 @@ const Q2: f32 = 1.5807170421e-3; /* 0xcf3010.0p-33 */ /// where using `exp(x)-1` would lose many significant digits. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn expm1f(mut x: f32) -> f32 { +pub extern "C" fn expm1f(mut x: f32) -> f32 { let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 let mut hx = x.to_bits(); diff --git a/crates/libm/src/math/fabs.rs b/crates/libm/src/math/fabs.rs index a218cb514..e7bc34dd9 100644 --- a/crates/libm/src/math/fabs.rs +++ b/crates/libm/src/math/fabs.rs @@ -5,7 +5,7 @@ use core::u64; /// by direct manipulation of the bit representation of `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fabs(x: f64) -> f64 { +pub extern "C" fn fabs(x: f64) -> f64 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f64.abs` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/fabsf.rs b/crates/libm/src/math/fabsf.rs index a2fb611d3..35b109d46 100644 --- a/crates/libm/src/math/fabsf.rs +++ b/crates/libm/src/math/fabsf.rs @@ -3,7 +3,7 @@ /// by direct manipulation of the bit representation of `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fabsf(x: f32) -> f32 { +pub extern "C" fn fabsf(x: f32) -> f32 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f32.abs` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/fdim.rs b/crates/libm/src/math/fdim.rs index 06edc9960..555fb93c4 100644 --- a/crates/libm/src/math/fdim.rs +++ b/crates/libm/src/math/fdim.rs @@ -10,7 +10,7 @@ use core::f64; /// A range error may occur. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fdim(x: f64, y: f64) -> f64 { +pub extern "C" fn fdim(x: f64, y: f64) -> f64 { if x.is_nan() { x } else if y.is_nan() { diff --git a/crates/libm/src/math/fdimf.rs b/crates/libm/src/math/fdimf.rs index f1ad5896b..1e001cbab 100644 --- a/crates/libm/src/math/fdimf.rs +++ b/crates/libm/src/math/fdimf.rs @@ -10,7 +10,7 @@ use core::f32; /// A range error may occur. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fdimf(x: f32, y: f32) -> f32 { +pub extern "C" fn fdimf(x: f32, y: f32) -> f32 { if x.is_nan() { x } else if y.is_nan() { diff --git a/crates/libm/src/math/floor.rs b/crates/libm/src/math/floor.rs index 5be314949..96125cc2a 100644 --- a/crates/libm/src/math/floor.rs +++ b/crates/libm/src/math/floor.rs @@ -7,7 +7,7 @@ const TOINT: f64 = 1. / f64::EPSILON; /// Finds the nearest integer less than or equal to `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn floor(x: f64) -> f64 { +pub extern "C" fn floor(x: f64) -> f64 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f64.floor` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/floorf.rs b/crates/libm/src/math/floorf.rs index 82000dfdb..b22c760df 100644 --- a/crates/libm/src/math/floorf.rs +++ b/crates/libm/src/math/floorf.rs @@ -5,7 +5,7 @@ use core::f32; /// Finds the nearest integer less than or equal to `x`. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn floorf(x: f32) -> f32 { +pub extern "C" fn floorf(x: f32) -> f32 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f32.floor` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/fma.rs b/crates/libm/src/math/fma.rs index 07d90f8b7..c5d61391c 100644 --- a/crates/libm/src/math/fma.rs +++ b/crates/libm/src/math/fma.rs @@ -55,7 +55,7 @@ fn mul(x: u64, y: u64) -> (u64, u64) { /// according to the rounding mode characterized by the value of FLT_ROUNDS. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fma(x: f64, y: f64, z: f64) -> f64 { +pub extern "C" fn fma(x: f64, y: f64, z: f64) -> f64 { let x1p63: f64 = f64::from_bits(0x43e0000000000000); // 0x1p63 === 2 ^ 63 let x0_ffffff8p_63 = f64::from_bits(0x3bfffffff0000000); // 0x0.ffffff8p-63 diff --git a/crates/libm/src/math/fmaf.rs b/crates/libm/src/math/fmaf.rs index e77e0fa4a..a6609d918 100644 --- a/crates/libm/src/math/fmaf.rs +++ b/crates/libm/src/math/fmaf.rs @@ -48,7 +48,7 @@ use super::fenv::{ /// according to the rounding mode characterized by the value of FLT_ROUNDS. #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmaf(x: f32, y: f32, mut z: f32) -> f32 { +pub extern "C" fn fmaf(x: f32, y: f32, mut z: f32) -> f32 { let xy: f64; let mut result: f64; let mut ui: u64; diff --git a/crates/libm/src/math/fmax.rs b/crates/libm/src/math/fmax.rs index 22016d11c..9a7de6aa1 100644 --- a/crates/libm/src/math/fmax.rs +++ b/crates/libm/src/math/fmax.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmax(x: f64, y: f64) -> f64 { +pub extern "C" fn fmax(x: f64, y: f64) -> f64 { // IEEE754 says: maxNum(x, y) is the canonicalized number y if x < y, x if y < x, the // canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it // is either x or y, canonicalized (this means results might differ among implementations). diff --git a/crates/libm/src/math/fmaxf.rs b/crates/libm/src/math/fmaxf.rs index a883fdaef..84aded4e5 100644 --- a/crates/libm/src/math/fmaxf.rs +++ b/crates/libm/src/math/fmaxf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmaxf(x: f32, y: f32) -> f32 { +pub extern "C" fn fmaxf(x: f32, y: f32) -> f32 { // IEEE754 says: maxNum(x, y) is the canonicalized number y if x < y, x if y < x, the // canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it // is either x or y, canonicalized (this means results might differ among implementations). diff --git a/crates/libm/src/math/fmin.rs b/crates/libm/src/math/fmin.rs index d1ccc3a46..537143bc2 100644 --- a/crates/libm/src/math/fmin.rs +++ b/crates/libm/src/math/fmin.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmin(x: f64, y: f64) -> f64 { +pub extern "C" fn fmin(x: f64, y: f64) -> f64 { // IEEE754 says: minNum(x, y) is the canonicalized number x if x < y, y if y < x, the // canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it // is either x or y, canonicalized (this means results might differ among implementations). diff --git a/crates/libm/src/math/fminf.rs b/crates/libm/src/math/fminf.rs index 43ec97cb5..1cef90e5b 100644 --- a/crates/libm/src/math/fminf.rs +++ b/crates/libm/src/math/fminf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fminf(x: f32, y: f32) -> f32 { +pub extern "C" fn fminf(x: f32, y: f32) -> f32 { // IEEE754 says: minNum(x, y) is the canonicalized number x if x < y, y if y < x, the // canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it // is either x or y, canonicalized (this means results might differ among implementations). diff --git a/crates/libm/src/math/fmod.rs b/crates/libm/src/math/fmod.rs index 2cdd8a9ba..cb0c6cf5a 100644 --- a/crates/libm/src/math/fmod.rs +++ b/crates/libm/src/math/fmod.rs @@ -2,7 +2,7 @@ use core::u64; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmod(x: f64, y: f64) -> f64 { +pub extern "C" fn fmod(x: f64, y: f64) -> f64 { let mut uxi = x.to_bits(); let mut uyi = y.to_bits(); let mut ex = (uxi >> 52 & 0x7ff) as i64; diff --git a/crates/libm/src/math/fmodf.rs b/crates/libm/src/math/fmodf.rs index 3e6779a93..25cabe1d4 100644 --- a/crates/libm/src/math/fmodf.rs +++ b/crates/libm/src/math/fmodf.rs @@ -3,7 +3,7 @@ use core::u32; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmodf(x: f32, y: f32) -> f32 { +pub extern "C" fn fmodf(x: f32, y: f32) -> f32 { let mut uxi = x.to_bits(); let mut uyi = y.to_bits(); let mut ex = (uxi >> 23 & 0xff) as i32; diff --git a/crates/libm/src/math/frexp.rs b/crates/libm/src/math/frexp.rs index 92fd5ec39..676efa28b 100644 --- a/crates/libm/src/math/frexp.rs +++ b/crates/libm/src/math/frexp.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn frexp(x: f64) -> (f64, i32) { +pub extern "C" fn frexp(x: f64) -> (f64, i32) { let mut y = x.to_bits(); let ee = ((y >> 52) & 0x7ff) as i32; diff --git a/crates/libm/src/math/frexpf.rs b/crates/libm/src/math/frexpf.rs index a93544d61..2d63f6646 100644 --- a/crates/libm/src/math/frexpf.rs +++ b/crates/libm/src/math/frexpf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn frexpf(x: f32) -> (f32, i32) { +pub extern "C" fn frexpf(x: f32) -> (f32, i32) { let mut y = x.to_bits(); let ee: i32 = ((y >> 23) & 0xff) as i32; diff --git a/crates/libm/src/math/hypot.rs b/crates/libm/src/math/hypot.rs index e53baf539..d77eac945 100644 --- a/crates/libm/src/math/hypot.rs +++ b/crates/libm/src/math/hypot.rs @@ -20,7 +20,7 @@ fn sq(x: f64) -> (f64, f64) { #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn hypot(mut x: f64, mut y: f64) -> f64 { +pub extern "C" fn hypot(mut x: f64, mut y: f64) -> f64 { let x1p700 = f64::from_bits(0x6bb0000000000000); // 0x1p700 === 2 ^ 700 let x1p_700 = f64::from_bits(0x1430000000000000); // 0x1p-700 === 2 ^ -700 diff --git a/crates/libm/src/math/hypotf.rs b/crates/libm/src/math/hypotf.rs index 4636b8f1d..a15735cac 100644 --- a/crates/libm/src/math/hypotf.rs +++ b/crates/libm/src/math/hypotf.rs @@ -4,7 +4,7 @@ use super::sqrtf; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn hypotf(mut x: f32, mut y: f32) -> f32 { +pub extern "C" fn hypotf(mut x: f32, mut y: f32) -> f32 { let x1p90 = f32::from_bits(0x6c800000); // 0x1p90f === 2 ^ 90 let x1p_90 = f32::from_bits(0x12800000); // 0x1p-90f === 2 ^ -90 diff --git a/crates/libm/src/math/ilogb.rs b/crates/libm/src/math/ilogb.rs index 21e5445ca..3c2fed309 100644 --- a/crates/libm/src/math/ilogb.rs +++ b/crates/libm/src/math/ilogb.rs @@ -3,7 +3,7 @@ const FP_ILOGB0: i32 = FP_ILOGBNAN; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ilogb(x: f64) -> i32 { +pub extern "C" fn ilogb(x: f64) -> i32 { let mut i: u64 = x.to_bits(); let e = ((i >> 52) & 0x7ff) as i32; diff --git a/crates/libm/src/math/ilogbf.rs b/crates/libm/src/math/ilogbf.rs index 4d8c6b562..09230007c 100644 --- a/crates/libm/src/math/ilogbf.rs +++ b/crates/libm/src/math/ilogbf.rs @@ -3,7 +3,7 @@ const FP_ILOGB0: i32 = FP_ILOGBNAN; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ilogbf(x: f32) -> i32 { +pub extern "C" fn ilogbf(x: f32) -> i32 { let mut i = x.to_bits(); let e = ((i >> 23) & 0xff) as i32; diff --git a/crates/libm/src/math/j0.rs b/crates/libm/src/math/j0.rs index 7103729c1..516d0048c 100644 --- a/crates/libm/src/math/j0.rs +++ b/crates/libm/src/math/j0.rs @@ -111,7 +111,7 @@ const S04: f64 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn j0(mut x: f64) -> f64 { +pub extern "C" fn j0(mut x: f64) -> f64 { let z: f64; let r: f64; let s: f64; @@ -166,7 +166,7 @@ const V04: f64 = 4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn y0(x: f64) -> f64 { +pub extern "C" fn y0(x: f64) -> f64 { let z: f64; let u: f64; let v: f64; diff --git a/crates/libm/src/math/j0f.rs b/crates/libm/src/math/j0f.rs index b98a4c436..a93fbf9b8 100644 --- a/crates/libm/src/math/j0f.rs +++ b/crates/libm/src/math/j0f.rs @@ -64,7 +64,7 @@ const S04: f32 = 1.1661400734e-09; /* 0x30a045e8 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn j0f(mut x: f32) -> f32 { +pub extern "C" fn j0f(mut x: f32) -> f32 { let z: f32; let r: f32; let s: f32; @@ -110,7 +110,7 @@ const V03: f32 = 2.5915085189e-07; /* 0x348b216c */ const V04: f32 = 4.4111031494e-10; /* 0x2ff280c2 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn y0f(x: f32) -> f32 { +pub extern "C" fn y0f(x: f32) -> f32 { let z: f32; let u: f32; let v: f32; diff --git a/crates/libm/src/math/j1.rs b/crates/libm/src/math/j1.rs index 8b6352a10..23a01e3ad 100644 --- a/crates/libm/src/math/j1.rs +++ b/crates/libm/src/math/j1.rs @@ -115,7 +115,7 @@ const S05: f64 = 1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn j1(x: f64) -> f64 { +pub extern "C" fn j1(x: f64) -> f64 { let mut z: f64; let r: f64; let s: f64; @@ -162,7 +162,7 @@ const V0: [f64; 5] = [ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn y1(x: f64) -> f64 { +pub extern "C" fn y1(x: f64) -> f64 { let z: f64; let u: f64; let v: f64; diff --git a/crates/libm/src/math/j1f.rs b/crates/libm/src/math/j1f.rs index 90e84f065..377c1cd3d 100644 --- a/crates/libm/src/math/j1f.rs +++ b/crates/libm/src/math/j1f.rs @@ -65,7 +65,7 @@ const S05: f32 = 1.2354227016e-11; /* 0x2d59567e */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn j1f(x: f32) -> f32 { +pub extern "C" fn j1f(x: f32) -> f32 { let mut z: f32; let r: f32; let s: f32; @@ -111,7 +111,7 @@ const V0: [f32; 5] = [ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn y1f(x: f32) -> f32 { +pub extern "C" fn y1f(x: f32) -> f32 { let z: f32; let u: f32; let v: f32; diff --git a/crates/libm/src/math/jn.rs b/crates/libm/src/math/jn.rs index 900da69d5..682b868e9 100644 --- a/crates/libm/src/math/jn.rs +++ b/crates/libm/src/math/jn.rs @@ -40,7 +40,7 @@ const INVSQRTPI: f64 = 5.64189583547756279280e-01; /* 0x3FE20DD7, 0x50429B6D */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn jn(n: i32, mut x: f64) -> f64 { +pub extern "C" fn jn(n: i32, mut x: f64) -> f64 { let mut ix: u32; let lx: u32; let nm1: i32; @@ -255,7 +255,7 @@ pub fn jn(n: i32, mut x: f64) -> f64 { #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn yn(n: i32, x: f64) -> f64 { +pub extern "C" fn yn(n: i32, x: f64) -> f64 { let mut ix: u32; let lx: u32; let mut ib: u32; diff --git a/crates/libm/src/math/jnf.rs b/crates/libm/src/math/jnf.rs index e81cc987d..efb4a1df9 100644 --- a/crates/libm/src/math/jnf.rs +++ b/crates/libm/src/math/jnf.rs @@ -17,7 +17,7 @@ use super::{fabsf, j0f, j1f, logf, y0f, y1f}; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn jnf(n: i32, mut x: f32) -> f32 { +pub extern "C" fn jnf(n: i32, mut x: f32) -> f32 { let mut ix: u32; let mut nm1: i32; let mut sign: bool; @@ -199,7 +199,7 @@ pub fn jnf(n: i32, mut x: f32) -> f32 { #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ynf(n: i32, x: f32) -> f32 { +pub extern "C" fn ynf(n: i32, x: f32) -> f32 { let mut ix: u32; let mut ib: u32; let nm1: i32; diff --git a/crates/libm/src/math/ldexp.rs b/crates/libm/src/math/ldexp.rs index 780ddfc11..70dabbc54 100644 --- a/crates/libm/src/math/ldexp.rs +++ b/crates/libm/src/math/ldexp.rs @@ -1,5 +1,5 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ldexp(x: f64, n: i32) -> f64 { +pub extern "C" fn ldexp(x: f64, n: i32) -> f64 { super::scalbn(x, n) } diff --git a/crates/libm/src/math/ldexpf.rs b/crates/libm/src/math/ldexpf.rs index 70935a002..bb4902670 100644 --- a/crates/libm/src/math/ldexpf.rs +++ b/crates/libm/src/math/ldexpf.rs @@ -1,5 +1,5 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ldexpf(x: f32, n: i32) -> f32 { +pub extern "C" fn ldexpf(x: f32, n: i32) -> f32 { super::scalbnf(x, n) } diff --git a/crates/libm/src/math/lgamma.rs b/crates/libm/src/math/lgamma.rs index 3f04ada29..d1bfa8dfb 100644 --- a/crates/libm/src/math/lgamma.rs +++ b/crates/libm/src/math/lgamma.rs @@ -2,6 +2,6 @@ use super::lgamma_r; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn lgamma(x: f64) -> f64 { +pub extern "C" fn lgamma(x: f64) -> f64 { lgamma_r(x).0 } diff --git a/crates/libm/src/math/lgamma_r.rs b/crates/libm/src/math/lgamma_r.rs index f1ab295d0..8d9bfcc85 100644 --- a/crates/libm/src/math/lgamma_r.rs +++ b/crates/libm/src/math/lgamma_r.rs @@ -166,7 +166,7 @@ fn sin_pi(mut x: f64) -> f64 { #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn lgamma_r(mut x: f64) -> (f64, i32) { +pub extern "C" fn lgamma_r(mut x: f64) -> (f64, i32) { let u: u64 = x.to_bits(); let mut t: f64; let y: f64; diff --git a/crates/libm/src/math/lgammaf.rs b/crates/libm/src/math/lgammaf.rs index d97be1a94..2eabb59d6 100644 --- a/crates/libm/src/math/lgammaf.rs +++ b/crates/libm/src/math/lgammaf.rs @@ -2,6 +2,6 @@ use super::lgammaf_r; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn lgammaf(x: f32) -> f32 { +pub extern "C" fn lgammaf(x: f32) -> f32 { lgammaf_r(x).0 } diff --git a/crates/libm/src/math/lgammaf_r.rs b/crates/libm/src/math/lgammaf_r.rs index e7af0da3d..416cb54ed 100644 --- a/crates/libm/src/math/lgammaf_r.rs +++ b/crates/libm/src/math/lgammaf_r.rs @@ -100,7 +100,7 @@ fn sin_pi(mut x: f32) -> f32 { } #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn lgammaf_r(mut x: f32) -> (f32, i32) { +pub extern "C" fn lgammaf_r(mut x: f32) -> (f32, i32) { let u = x.to_bits(); let mut t: f32; let y: f32; diff --git a/crates/libm/src/math/log.rs b/crates/libm/src/math/log.rs index 4126e413b..51cf810a0 100644 --- a/crates/libm/src/math/log.rs +++ b/crates/libm/src/math/log.rs @@ -72,7 +72,7 @@ const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log(mut x: f64) -> f64 { +pub extern "C" fn log(mut x: f64) -> f64 { let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 let mut ui = x.to_bits(); diff --git a/crates/libm/src/math/log10.rs b/crates/libm/src/math/log10.rs index c99696040..e1caec64b 100644 --- a/crates/libm/src/math/log10.rs +++ b/crates/libm/src/math/log10.rs @@ -33,7 +33,7 @@ const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log10(mut x: f64) -> f64 { +pub extern "C" fn log10(mut x: f64) -> f64 { let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 let mut ui: u64 = x.to_bits(); diff --git a/crates/libm/src/math/log10f.rs b/crates/libm/src/math/log10f.rs index 9845cda5d..e97ecf2a5 100644 --- a/crates/libm/src/math/log10f.rs +++ b/crates/libm/src/math/log10f.rs @@ -27,7 +27,7 @@ const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log10f(mut x: f32) -> f32 { +pub extern "C" fn log10f(mut x: f32) -> f32 { let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 let mut ui: u32 = x.to_bits(); diff --git a/crates/libm/src/math/log1p.rs b/crates/libm/src/math/log1p.rs index cd7045ac9..e16e11f1c 100644 --- a/crates/libm/src/math/log1p.rs +++ b/crates/libm/src/math/log1p.rs @@ -67,7 +67,7 @@ const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log1p(x: f64) -> f64 { +pub extern "C" fn log1p(x: f64) -> f64 { let mut ui: u64 = x.to_bits(); let hfsq: f64; let mut f: f64 = 0.; diff --git a/crates/libm/src/math/log1pf.rs b/crates/libm/src/math/log1pf.rs index 8e9651357..717295b90 100644 --- a/crates/libm/src/math/log1pf.rs +++ b/crates/libm/src/math/log1pf.rs @@ -22,7 +22,7 @@ const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log1pf(x: f32) -> f32 { +pub extern "C" fn log1pf(x: f32) -> f32 { let mut ui: u32 = x.to_bits(); let hfsq: f32; let mut f: f32 = 0.; diff --git a/crates/libm/src/math/log2.rs b/crates/libm/src/math/log2.rs index a3d43e55c..8b304855c 100644 --- a/crates/libm/src/math/log2.rs +++ b/crates/libm/src/math/log2.rs @@ -31,7 +31,7 @@ const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log2(mut x: f64) -> f64 { +pub extern "C" fn log2(mut x: f64) -> f64 { let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 let mut ui: u64 = x.to_bits(); diff --git a/crates/libm/src/math/log2f.rs b/crates/libm/src/math/log2f.rs index 53a37e503..cf4ebed2a 100644 --- a/crates/libm/src/math/log2f.rs +++ b/crates/libm/src/math/log2f.rs @@ -25,7 +25,7 @@ const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn log2f(mut x: f32) -> f32 { +pub extern "C" fn log2f(mut x: f32) -> f32 { let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 let mut ui: u32 = x.to_bits(); diff --git a/crates/libm/src/math/logf.rs b/crates/libm/src/math/logf.rs index 95195601c..6a0a9f7a6 100644 --- a/crates/libm/src/math/logf.rs +++ b/crates/libm/src/math/logf.rs @@ -23,7 +23,7 @@ const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn logf(mut x: f32) -> f32 { +pub extern "C" fn logf(mut x: f32) -> f32 { let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 let mut ix = x.to_bits(); diff --git a/crates/libm/src/math/modf.rs b/crates/libm/src/math/modf.rs index 6b345fc03..e564c46a6 100644 --- a/crates/libm/src/math/modf.rs +++ b/crates/libm/src/math/modf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn modf(x: f64) -> (f64, f64) { +pub extern "C" fn modf(x: f64) -> (f64, f64) { let rv2: f64; let mut u = x.to_bits(); let mask: u64; diff --git a/crates/libm/src/math/modff.rs b/crates/libm/src/math/modff.rs index f6e5f3806..f79b87fe2 100644 --- a/crates/libm/src/math/modff.rs +++ b/crates/libm/src/math/modff.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn modff(x: f32) -> (f32, f32) { +pub extern "C" fn modff(x: f32) -> (f32, f32) { let rv2: f32; let mut u: u32 = x.to_bits(); let mask: u32; diff --git a/crates/libm/src/math/pow.rs b/crates/libm/src/math/pow.rs index dca70f99b..362a62442 100644 --- a/crates/libm/src/math/pow.rs +++ b/crates/libm/src/math/pow.rs @@ -91,7 +91,7 @@ const IVLN2_L: f64 = 1.92596299112661746887e-08; /* 0x3e54ae0b_f85ddf44 =1/ln2 t #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn pow(x: f64, y: f64) -> f64 { +pub extern "C" fn pow(x: f64, y: f64) -> f64 { let t1: f64; let t2: f64; diff --git a/crates/libm/src/math/powf.rs b/crates/libm/src/math/powf.rs index 015bade86..45ab1e4de 100644 --- a/crates/libm/src/math/powf.rs +++ b/crates/libm/src/math/powf.rs @@ -45,7 +45,7 @@ const IVLN2_L: f32 = 7.0526075433e-06; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn powf(x: f32, y: f32) -> f32 { +pub extern "C" fn powf(x: f32, y: f32) -> f32 { let mut z: f32; let mut ax: f32; let z_h: f32; diff --git a/crates/libm/src/math/remainder.rs b/crates/libm/src/math/remainder.rs index 7ce895004..57fd5aa38 100644 --- a/crates/libm/src/math/remainder.rs +++ b/crates/libm/src/math/remainder.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn remainder(x: f64, y: f64) -> f64 { +pub extern "C" fn remainder(x: f64, y: f64) -> f64 { let (result, _) = super::remquo(x, y); result } diff --git a/crates/libm/src/math/remainderf.rs b/crates/libm/src/math/remainderf.rs index 8b2aa5aab..3f5f92700 100644 --- a/crates/libm/src/math/remainderf.rs +++ b/crates/libm/src/math/remainderf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn remainderf(x: f32, y: f32) -> f32 { +pub extern "C" fn remainderf(x: f32, y: f32) -> f32 { let (result, _) = super::remquof(x, y); result } diff --git a/crates/libm/src/math/remquo.rs b/crates/libm/src/math/remquo.rs index 176765b5d..480216baf 100644 --- a/crates/libm/src/math/remquo.rs +++ b/crates/libm/src/math/remquo.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { +pub extern "C" fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { let ux: u64 = x.to_bits(); let mut uy: u64 = y.to_bits(); let mut ex = ((ux >> 52) & 0x7ff) as i32; diff --git a/crates/libm/src/math/remquof.rs b/crates/libm/src/math/remquof.rs index 66ac72bd6..b2b732a96 100644 --- a/crates/libm/src/math/remquof.rs +++ b/crates/libm/src/math/remquof.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn remquof(mut x: f32, mut y: f32) -> (f32, i32) { +pub extern "C" fn remquof(mut x: f32, mut y: f32) -> (f32, i32) { let ux: u32 = x.to_bits(); let mut uy: u32 = y.to_bits(); let mut ex = ((ux >> 23) & 0xff) as i32; diff --git a/crates/libm/src/math/round.rs b/crates/libm/src/math/round.rs index 13049fb25..d467adb0e 100644 --- a/crates/libm/src/math/round.rs +++ b/crates/libm/src/math/round.rs @@ -4,7 +4,7 @@ const TOINT: f64 = 1.0 / f64::EPSILON; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn round(mut x: f64) -> f64 { +pub extern "C" fn round(mut x: f64) -> f64 { let i = x.to_bits(); let e: u64 = i >> 52 & 0x7ff; let mut y: f64; diff --git a/crates/libm/src/math/roundf.rs b/crates/libm/src/math/roundf.rs index d6f15e58d..cb03ac7b5 100644 --- a/crates/libm/src/math/roundf.rs +++ b/crates/libm/src/math/roundf.rs @@ -4,7 +4,7 @@ const TOINT: f32 = 1.0 / f32::EPSILON; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn roundf(mut x: f32) -> f32 { +pub extern "C" fn roundf(mut x: f32) -> f32 { let i = x.to_bits(); let e: u32 = i >> 23 & 0xff; let mut y: f32; diff --git a/crates/libm/src/math/scalbn.rs b/crates/libm/src/math/scalbn.rs index d8c8409ac..03072f934 100644 --- a/crates/libm/src/math/scalbn.rs +++ b/crates/libm/src/math/scalbn.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn scalbn(x: f64, mut n: i32) -> f64 { +pub extern "C" fn scalbn(x: f64, mut n: i32) -> f64 { let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023 let x1p53 = f64::from_bits(0x4340000000000000); // 0x1p53 === 2 ^ 53 let x1p_1022 = f64::from_bits(0x0010000000000000); // 0x1p-1022 === 2 ^ (-1022) diff --git a/crates/libm/src/math/scalbnf.rs b/crates/libm/src/math/scalbnf.rs index 4e9771175..b11af2544 100644 --- a/crates/libm/src/math/scalbnf.rs +++ b/crates/libm/src/math/scalbnf.rs @@ -1,6 +1,6 @@ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn scalbnf(mut x: f32, mut n: i32) -> f32 { +pub extern "C" fn scalbnf(mut x: f32, mut n: i32) -> f32 { let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 let x1p24 = f32::from_bits(0x4b800000); // 0x1p24f === 2 ^ 24 diff --git a/crates/libm/src/math/sin.rs b/crates/libm/src/math/sin.rs index b73074416..8af0f0263 100644 --- a/crates/libm/src/math/sin.rs +++ b/crates/libm/src/math/sin.rs @@ -42,7 +42,7 @@ use super::{k_cos, k_sin, rem_pio2}; // TRIG(x) returns trig(x) nearly rounded #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sin(x: f64) -> f64 { +pub extern "C" fn sin(x: f64) -> f64 { let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120 /* High word of x. */ diff --git a/crates/libm/src/math/sincos.rs b/crates/libm/src/math/sincos.rs index 755d5a71f..c06ce7db0 100644 --- a/crates/libm/src/math/sincos.rs +++ b/crates/libm/src/math/sincos.rs @@ -14,7 +14,7 @@ use super::{get_high_word, k_cos, k_sin, rem_pio2}; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sincos(x: f64) -> (f64, f64) { +pub extern "C" fn sincos(x: f64) -> (f64, f64) { let s: f64; let c: f64; let mut ix: u32; diff --git a/crates/libm/src/math/sincosf.rs b/crates/libm/src/math/sincosf.rs index 96a07c8a6..7d92dc10d 100644 --- a/crates/libm/src/math/sincosf.rs +++ b/crates/libm/src/math/sincosf.rs @@ -25,7 +25,7 @@ const S4PIO2: f32 = 4.0 * PI_2; /* 0x401921FB, 0x54442D18 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sincosf(x: f32) -> (f32, f32) { +pub extern "C" fn sincosf(x: f32) -> (f32, f32) { let s: f32; let c: f32; let mut ix: u32; diff --git a/crates/libm/src/math/sinf.rs b/crates/libm/src/math/sinf.rs index 0c31099ed..9b91ebb5a 100644 --- a/crates/libm/src/math/sinf.rs +++ b/crates/libm/src/math/sinf.rs @@ -26,7 +26,7 @@ const S4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sinf(x: f32) -> f32 { +pub extern "C" fn sinf(x: f32) -> f32 { let x64 = x as f64; let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 diff --git a/crates/libm/src/math/sinh.rs b/crates/libm/src/math/sinh.rs index d36de66c1..0c09f32d2 100644 --- a/crates/libm/src/math/sinh.rs +++ b/crates/libm/src/math/sinh.rs @@ -6,7 +6,7 @@ use super::{expm1, expo2}; // #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sinh(x: f64) -> f64 { +pub extern "C" fn sinh(x: f64) -> f64 { // union {double f; uint64_t i;} u = {.f = x}; // uint32_t w; // double t, h, absx; diff --git a/crates/libm/src/math/sinhf.rs b/crates/libm/src/math/sinhf.rs index fd0b2bfc8..b553c6b7b 100644 --- a/crates/libm/src/math/sinhf.rs +++ b/crates/libm/src/math/sinhf.rs @@ -3,7 +3,7 @@ use super::k_expo2f; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sinhf(x: f32) -> f32 { +pub extern "C" fn sinhf(x: f32) -> f32 { let mut h = 0.5f32; let mut ix = x.to_bits(); if (ix >> 31) != 0 { diff --git a/crates/libm/src/math/sqrt.rs b/crates/libm/src/math/sqrt.rs index 7894cfc5c..e71fcde9c 100644 --- a/crates/libm/src/math/sqrt.rs +++ b/crates/libm/src/math/sqrt.rs @@ -83,7 +83,7 @@ const TINY: f64 = 1.0e-300; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sqrt(x: f64) -> f64 { +pub extern "C" fn sqrt(x: f64) -> f64 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f64.sqrt` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/sqrtf.rs b/crates/libm/src/math/sqrtf.rs index fa853e857..c6e978ccc 100644 --- a/crates/libm/src/math/sqrtf.rs +++ b/crates/libm/src/math/sqrtf.rs @@ -17,7 +17,7 @@ const TINY: f32 = 1.0e-30; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sqrtf(x: f32) -> f32 { +pub extern "C" fn sqrtf(x: f32) -> f32 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f32.sqrt` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/tan.rs b/crates/libm/src/math/tan.rs index e5c94cbb1..eac452070 100644 --- a/crates/libm/src/math/tan.rs +++ b/crates/libm/src/math/tan.rs @@ -41,7 +41,7 @@ use super::{k_tan, rem_pio2}; // TRIG(x) returns trig(x) nearly rounded #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tan(x: f64) -> f64 { +pub extern "C" fn tan(x: f64) -> f64 { let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; diff --git a/crates/libm/src/math/tanf.rs b/crates/libm/src/math/tanf.rs index c286cdeb4..1e593b116 100644 --- a/crates/libm/src/math/tanf.rs +++ b/crates/libm/src/math/tanf.rs @@ -26,7 +26,7 @@ const T4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tanf(x: f32) -> f32 { +pub extern "C" fn tanf(x: f32) -> f32 { let x64 = x as f64; let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 diff --git a/crates/libm/src/math/tanh.rs b/crates/libm/src/math/tanh.rs index 75d695cf7..4cad2a998 100644 --- a/crates/libm/src/math/tanh.rs +++ b/crates/libm/src/math/tanh.rs @@ -6,7 +6,7 @@ use super::expm1; */ #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tanh(mut x: f64) -> f64 { +pub extern "C" fn tanh(mut x: f64) -> f64 { let mut uf: f64 = x; let mut ui: u64 = f64::to_bits(uf); diff --git a/crates/libm/src/math/tanhf.rs b/crates/libm/src/math/tanhf.rs index ac4657b5a..ca051af4c 100644 --- a/crates/libm/src/math/tanhf.rs +++ b/crates/libm/src/math/tanhf.rs @@ -2,7 +2,7 @@ use super::expm1f; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tanhf(mut x: f32) -> f32 { +pub extern "C" fn tanhf(mut x: f32) -> f32 { /* x = |x| */ let mut ix = x.to_bits(); let sign = (ix >> 31) != 0; diff --git a/crates/libm/src/math/tgamma.rs b/crates/libm/src/math/tgamma.rs index 098f489bd..0e0de0ab0 100644 --- a/crates/libm/src/math/tgamma.rs +++ b/crates/libm/src/math/tgamma.rs @@ -132,7 +132,7 @@ fn s(x: f64) -> f64 { #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tgamma(mut x: f64) -> f64 { +pub extern "C" fn tgamma(mut x: f64) -> f64 { let u: u64 = x.to_bits(); let absx: f64; let mut y: f64; diff --git a/crates/libm/src/math/tgammaf.rs b/crates/libm/src/math/tgammaf.rs index 43e7e3efb..cc9529290 100644 --- a/crates/libm/src/math/tgammaf.rs +++ b/crates/libm/src/math/tgammaf.rs @@ -2,6 +2,6 @@ use super::tgamma; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn tgammaf(x: f32) -> f32 { +pub extern "C" fn tgammaf(x: f32) -> f32 { tgamma(x as f64) as f32 } diff --git a/crates/libm/src/math/trunc.rs b/crates/libm/src/math/trunc.rs index b626cd9eb..0d8387a74 100644 --- a/crates/libm/src/math/trunc.rs +++ b/crates/libm/src/math/trunc.rs @@ -2,7 +2,7 @@ use core::f64; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn trunc(x: f64) -> f64 { +pub extern "C" fn trunc(x: f64) -> f64 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f64.trunc` native instruction, so we can leverage this for both code size // and speed. diff --git a/crates/libm/src/math/truncf.rs b/crates/libm/src/math/truncf.rs index 6d8a9e742..5c3646741 100644 --- a/crates/libm/src/math/truncf.rs +++ b/crates/libm/src/math/truncf.rs @@ -2,7 +2,7 @@ use core::f32; #[inline] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn truncf(x: f32) -> f32 { +pub extern "C" fn truncf(x: f32) -> f32 { // On wasm32 we know that LLVM's intrinsic will compile to an optimized // `f32.trunc` native instruction, so we can leverage this for both code size // and speed. From 7b214c86ef6f9977ec49ed22e447a607fcec001f Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:48:13 +0200 Subject: [PATCH 29/43] Put exhaustive tests behind feature test --- crates/libm-test/Cargo.toml | 3 ++- crates/libm-test/tests/exhaustive32.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/libm-test/Cargo.toml b/crates/libm-test/Cargo.toml index 4cb40b6a2..89f117455 100644 --- a/crates/libm-test/Cargo.toml +++ b/crates/libm-test/Cargo.toml @@ -13,4 +13,5 @@ rand = "0.7" default = [] checked = ["libm/checked"] stable = ["libm/stable"] -system_libm = [] \ No newline at end of file +system_libm = [] +exhaustive = [] \ No newline at end of file diff --git a/crates/libm-test/tests/exhaustive32.rs b/crates/libm-test/tests/exhaustive32.rs index 5083ca7d7..429bd6125 100644 --- a/crates/libm-test/tests/exhaustive32.rs +++ b/crates/libm-test/tests/exhaustive32.rs @@ -1,6 +1,6 @@ //! Exhaustively test unary APIs taking 32-bit wide arguments. #![cfg(test)] -#![cfg(exhaustive32)] +#![cfg(all(exhaustive32, feature = "exhaustive"))] use libm_test::assert_approx_eq; macro_rules! exhaustive32 { From c8eeb45f663d8dd85a4b7f3d62cff7eb7e34bd1b Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:49:12 +0200 Subject: [PATCH 30/43] Always build exhaustive tests on CI --- ci/run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/run.sh b/ci/run.sh index 209704721..616cafe62 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -20,6 +20,7 @@ $CMD --release $CMD --features 'stable' $CMD --release --features 'stable' +cargo build -p libm-test --features 'exhaustive' if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then export TARGET=x86_64-unknown-linux-musl From 4eb8ed4a67cfd5708a528fc67d0a55132f29eb24 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:53:06 +0200 Subject: [PATCH 31/43] Enforce extern C in libm-analyze --- crates/libm-analyze/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 7d417d6fc..29d3e23e9 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -151,11 +151,7 @@ fn get_functions(files: &[syn::File]) -> Vec { // If the function signature isn't extern "C", we aren't ABI compatible // with libm. if !fn_sig.c_abi { - // FIXME: we should error here, but right that would break everything, - // so we disable erroring. - let e2 = e; err!("not `extern \"C\"`"); - e = e2; } // Right now there are no const fn functions. We might add them // in the future, and at that point, we should tune this here. From 1a49d702227b47dfc8147f56dcb8db88483fafc9 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 03:58:07 +0200 Subject: [PATCH 32/43] Really build the exhaustive tests on CI --- ci/run.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/run.sh b/ci/run.sh index 616cafe62..a9c2792c7 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -20,7 +20,8 @@ $CMD --release $CMD --features 'stable' $CMD --release --features 'stable' -cargo build -p libm-test --features 'exhaustive' + +cargo build --release -p libm-test --features 'exhaustive' if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then export TARGET=x86_64-unknown-linux-musl From b10d776e4cf591a9c17cabdcef55c685f52c1fc4 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 04:09:49 +0200 Subject: [PATCH 33/43] Remove workarounds due to lack of extern C APIs --- crates/libm-bench/benches/bench.rs | 11 +++-------- crates/libm-test/tests/system_libm.rs | 9 ++------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/crates/libm-bench/benches/bench.rs b/crates/libm-bench/benches/bench.rs index 2ca565244..d24e91c91 100644 --- a/crates/libm-bench/benches/bench.rs +++ b/crates/libm-bench/benches/bench.rs @@ -22,13 +22,8 @@ macro_rules! bench_fn { #[bench] #[allow(unused_mut)] pub fn $id(bh: &mut Bencher) { - // Type of the system libm fn: - type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; - - // FIXME: extern "C" wrapper - extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { - libm::$id($($arg_ids),*) - } + type FnTy + = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; // Generate a tuple of arguments containing random values: let mut rng = rand::thread_rng(); @@ -36,7 +31,7 @@ macro_rules! bench_fn { adjust_input!(fn: $id, input: x); - bh.iter(|| test::black_box(x).call(libm_fn as FnTy)) + bh.iter(|| test::black_box(x).call(libm::$id as FnTy)) } }; } diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index eaa4b8529..ea1a84e20 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -80,12 +80,7 @@ macro_rules! system_libm { type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; - // FIXME: extern "C" wrapper over our libm functions - // Shouldn't be needed once they are all extern "C" - extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty { - libm::$id($($arg_ids),*) - } - extern "C" { + extern "C" { // The system's libm function: fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; } @@ -98,7 +93,7 @@ macro_rules! system_libm { // correct_input!(fn: $id, input: args); adjust_input!(fn: $id, input: args); - let result = args.call(libm_fn as FnTy); + let result = args.call(libm::$id as FnTy); let expected = args.call($id as FnTy); assert_approx_eq!( result == expected, From 480ebc13a8ec9a4c3284004c366b970a09149d3a Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 07:42:55 +0200 Subject: [PATCH 34/43] Use inclusive range for testing all f32 bit patterns --- crates/libm-test/tests/exhaustive32.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/libm-test/tests/exhaustive32.rs b/crates/libm-test/tests/exhaustive32.rs index 429bd6125..cccffa0d4 100644 --- a/crates/libm-test/tests/exhaustive32.rs +++ b/crates/libm-test/tests/exhaustive32.rs @@ -60,7 +60,7 @@ macro_rules! exhaustive32 { fn $id($arg_id: f32) -> $ret_ty; } - for i in 0..u32::max_value() { + for i in 0..=u32::max_value() { let arg: f32 = unsafe { std::mem::transmute(i) }; let result = libm::$id(arg); let expected = unsafe { $id(arg) }; From b849b08ccb7c14724e57c38b123176aeea9cc756 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 11:00:01 +0200 Subject: [PATCH 35/43] Add nextafter and nextafterf --- crates/libm/src/math/mod.rs | 5 ++++ crates/libm/src/math/nextafter.rs | 38 ++++++++++++++++++++++++++++++ crates/libm/src/math/nextafterf.rs | 38 ++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 crates/libm/src/math/nextafter.rs create mode 100644 crates/libm/src/math/nextafterf.rs diff --git a/crates/libm/src/math/mod.rs b/crates/libm/src/math/mod.rs index 48b400a92..d352ed1ce 100644 --- a/crates/libm/src/math/mod.rs +++ b/crates/libm/src/math/mod.rs @@ -1,5 +1,6 @@ macro_rules! force_eval { ($e:expr) => { + #[allow(unused_unsafe)] unsafe { ::core::ptr::read_volatile(&$e); } @@ -146,6 +147,8 @@ mod log2f; mod logf; mod modf; mod modff; +mod nextafter; +mod nextafterf; mod pow; mod powf; mod remainder; @@ -258,6 +261,8 @@ pub use self::log2f::log2f; pub use self::logf::logf; pub use self::modf::modf; pub use self::modff::modff; +pub use self::nextafter::nextafter; +pub use self::nextafterf::nextafterf; pub use self::pow::pow; pub use self::powf::powf; pub use self::remainder::remainder; diff --git a/crates/libm/src/math/nextafter.rs b/crates/libm/src/math/nextafter.rs new file mode 100644 index 000000000..99c0db63a --- /dev/null +++ b/crates/libm/src/math/nextafter.rs @@ -0,0 +1,38 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub extern "C" fn nextafter(x: f64, y: f64) -> f64 { + if x.is_nan() || y.is_nan() { + return x + y; + } + + let mut ux_i = x.to_bits(); + let uy_i = y.to_bits(); + if ux_i == uy_i { + return y; + } + + let ax = ux_i & !1_u64 / 2; + let ay = uy_i & !1_u64 / 2; + if ax == 0 { + if ay == 0 { + return y; + } + ux_i = (uy_i & 1_u64 << 63) | 1; + } else if ax > ay || ((ux_i ^ uy_i) & 1_u64 << 63) != 0 { + ux_i -= 1; + } else { + ux_i += 1; + } + + let e = ux_i.wrapping_shr(52 & 0x7ff); + // raise overflow if ux.f is infinite and x is finite + if e == 0x7ff { + force_eval!(x + x); + } + let ux_f = f64::from_bits(ux_i); + // raise underflow if ux.f is subnormal or zero + if e == 0 { + force_eval!(x * x + ux_f * ux_f); + } + ux_f +} diff --git a/crates/libm/src/math/nextafterf.rs b/crates/libm/src/math/nextafterf.rs new file mode 100644 index 000000000..9d0be54b1 --- /dev/null +++ b/crates/libm/src/math/nextafterf.rs @@ -0,0 +1,38 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub extern "C" fn nextafterf(x: f32, y: f32) -> f32 { + if x.is_nan() || y.is_nan() { + return x + y; + } + + let mut ux_i = x.to_bits(); + let uy_i = y.to_bits(); + if ux_i == uy_i { + return y; + } + + let ax = ux_i & 0x7fff_ffff_u32; + let ay = uy_i & 0x7fff_ffff_u32; + if ax == 0 { + if ay == 0 { + return y; + } + ux_i = (uy_i & 0x8000_0000_u32) | 1; + } else if ax > ay || ((ux_i ^ uy_i) & 0x8000_0000_u32) != 0 { + ux_i -= 1; + } else { + ux_i += 1; + } + + let e = ux_i.wrapping_shr(0x7f80_0000_u32); + // raise overflow if ux_f is infinite and x is finite + if e == 0x7f80_0000_u32 { + force_eval!(x + x); + } + let ux_f = f32::from_bits(ux_i); + // raise underflow if ux_f is subnormal or zero + if e == 0 { + force_eval!(x * x + ux_f * ux_f); + } + ux_f +} From 308daa30276aab49ffbef09ca21dcb38f5e54fe0 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 11:29:38 +0200 Subject: [PATCH 36/43] Add nexttoward and signbit --- crates/libm/src/math/mod.rs | 6 ++++ crates/libm/src/math/nexttoward.rs | 43 ++++++++++++++++++++++++++++ crates/libm/src/math/nexttowardf.rs | 44 +++++++++++++++++++++++++++++ crates/libm/src/math/signbit.rs | 5 ++++ crates/libm/src/math/signbitf.rs | 5 ++++ 5 files changed, 103 insertions(+) create mode 100644 crates/libm/src/math/nexttoward.rs create mode 100644 crates/libm/src/math/nexttowardf.rs create mode 100644 crates/libm/src/math/signbit.rs create mode 100644 crates/libm/src/math/signbitf.rs diff --git a/crates/libm/src/math/mod.rs b/crates/libm/src/math/mod.rs index d352ed1ce..5c78717f0 100644 --- a/crates/libm/src/math/mod.rs +++ b/crates/libm/src/math/mod.rs @@ -149,6 +149,8 @@ mod modf; mod modff; mod nextafter; mod nextafterf; +mod nexttoward; +mod nexttowardf; mod pow; mod powf; mod remainder; @@ -159,6 +161,8 @@ mod round; mod roundf; mod scalbn; mod scalbnf; +mod signbit; +mod signbitf; mod sin; mod sincos; mod sincosf; @@ -263,6 +267,8 @@ pub use self::modf::modf; pub use self::modff::modff; pub use self::nextafter::nextafter; pub use self::nextafterf::nextafterf; +pub use self::nexttoward::nexttoward; +pub use self::nexttowardf::nexttowardf; pub use self::pow::pow; pub use self::powf::powf; pub use self::remainder::remainder; diff --git a/crates/libm/src/math/nexttoward.rs b/crates/libm/src/math/nexttoward.rs new file mode 100644 index 000000000..6d1a447c1 --- /dev/null +++ b/crates/libm/src/math/nexttoward.rs @@ -0,0 +1,43 @@ +use super::signbit::signbit; + +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub extern "C" fn nexttoward(x: f64, y: /*long double*/ f64) -> f64 { + let mut ux_i = x.to_bits(); + if x.is_nan() || y.is_nan() { + return x + y; + } + + if x == y { + return y; + } + if x == 0.0 { + ux_i = 1; + if signbit(y) != 0 { + ux_i |= 1_u64 << 63; + } + } else if x < y { + if signbit(x) != 0 { + ux_i -= 1; + } else { + ux_i += 1; + } + } else { + if signbit(x) != 0 { + ux_i += 1; + } else { + ux_i -= 1; + } + } + let e = ux_i >> 52 & 0x7ff; + // raise overflow if ux.f is infinite and x is finite + if e == 0x7ff { + force_eval!(x + x); + } + // raise underflow if ux.f is subnormal or zero + let ux_f = f64::from_bits(ux_i); + if e == 0 { + force_eval!(x * x + ux_f * ux_f); + } + ux_f +} diff --git a/crates/libm/src/math/nexttowardf.rs b/crates/libm/src/math/nexttowardf.rs new file mode 100644 index 000000000..9d9b7b797 --- /dev/null +++ b/crates/libm/src/math/nexttowardf.rs @@ -0,0 +1,44 @@ +use super::signbit::signbit; +use super::signbitf::signbitf; + +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub extern "C" fn nexttowardf(x: f32, y: f64) -> f32 { + let mut ux_i = x.to_bits(); + if x.is_nan() || y.is_nan() { + return (x as f64 + y) as f32; + } + + if x == y as f32 { + return y as f32; + } + if x == 0.0 { + ux_i = 1; + if signbit(y) != 0 { + ux_i |= 0x8000_0000_u32; + } + } else if x < (y as f32) { + if signbitf(x) != 0 { + ux_i -= 1; + } else { + ux_i += 1; + } + } else { + if signbitf(x) != 0 { + ux_i += 1; + } else { + ux_i -= 1; + } + } + let e = ux_i.wrapping_shr(0x7f80_0000_u32); + // raise overflow if ux.f is infinite and x is finite + if e == 0x7f80_0000_u32 { + force_eval!(x + x); + } + // raise underflow if ux.f is subnormal or zero + let ux_f = f32::from_bits(ux_i); + if e == 0 { + force_eval!(x * x + ux_f * ux_f); + } + ux_f +} diff --git a/crates/libm/src/math/signbit.rs b/crates/libm/src/math/signbit.rs new file mode 100644 index 000000000..d6df207b2 --- /dev/null +++ b/crates/libm/src/math/signbit.rs @@ -0,0 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub(crate) extern "C" fn signbit(x: f64) -> i32 { + (x.to_bits() >> 63) as i32 +} diff --git a/crates/libm/src/math/signbitf.rs b/crates/libm/src/math/signbitf.rs new file mode 100644 index 000000000..0d43c6725 --- /dev/null +++ b/crates/libm/src/math/signbitf.rs @@ -0,0 +1,5 @@ +#[inline] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub(crate) extern "C" fn signbitf(x: f32) -> i32 { + (x.to_bits() >> 31) as i32 +} From 7ef6fc2ad3af1f2fb57e5e6216b6f5a16668a2f1 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 12:33:34 +0200 Subject: [PATCH 37/43] Fix segfaults in FMA for some inputs --- crates/libm-test/tests/unit.rs | 16 ++++++++++++++++ crates/libm/src/math/fma.rs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/libm-test/tests/unit.rs b/crates/libm-test/tests/unit.rs index ed5d95199..6db51c118 100644 --- a/crates/libm-test/tests/unit.rs +++ b/crates/libm-test/tests/unit.rs @@ -344,3 +344,19 @@ fn j1f_2488() { fn y1f_2002() { assert_eq!(y1f(2.0000002_f32), -0.10703229_f32); } + +#[test] +fn fma_segfault_bug() { + // An attempt to substract with overflow was causing a segfault + // on FMA for these inputs: + assert_eq!( + fma( + -0.0000000000000002220446049250313, + -0.0000000000000002220446049250313, + -0.0000000000000002220446049250313 + ), + -0.00000000000000022204460492503126 + ); + + assert_eq!(fma(-0.992, -0.992, -0.992), -0.00793599999988632); +} diff --git a/crates/libm/src/math/fma.rs b/crates/libm/src/math/fma.rs index c5d61391c..01d4faaa6 100644 --- a/crates/libm/src/math/fma.rs +++ b/crates/libm/src/math/fma.rs @@ -126,8 +126,8 @@ pub extern "C" fn fma(x: f64, y: f64, z: f64) -> f64 { } else { /* r -= z */ let t = rlo; - rlo -= zlo; - rhi = rhi - zhi - (t < rlo) as u64; + rlo = rlo.wrapping_sub(zlo); + rhi = rhi.wrapping_sub(zhi.wrapping_sub((t < rlo) as u64)); if (rhi >> 63) != 0 { rlo = (-(rlo as i64)) as u64; rhi = (-(rhi as i64)) as u64 - (rlo != 0) as u64; From 41b817d5f67b291b34a3f25e05222f7175f8ba25 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 12:36:05 +0200 Subject: [PATCH 38/43] Refactor random tests into lib; be more exhaustive at boundaries. --- crates/libm-bench/benches/bench.rs | 7 +- crates/libm-test/Cargo.toml | 8 +- crates/libm-test/src/lib.rs | 156 +++++++++++++++++++++++--- crates/libm-test/tests/system_libm.rs | 63 +++-------- crates/libm/src/lib.rs | 2 +- 5 files changed, 171 insertions(+), 65 deletions(-) diff --git a/crates/libm-bench/benches/bench.rs b/crates/libm-bench/benches/bench.rs index d24e91c91..a380a49da 100644 --- a/crates/libm-bench/benches/bench.rs +++ b/crates/libm-bench/benches/bench.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use libm_test::{adjust_input, Call}; +use libm_test::{get_api_kind, ApiKind, Call}; use rand::Rng; use test::Bencher; @@ -29,7 +29,10 @@ macro_rules! bench_fn { let mut rng = rand::thread_rng(); let mut x: ( $($arg_tys,)+ ) = ( $(rng.gen::<$arg_tys>(),)+ ); - adjust_input!(fn: $id, input: x); + if let ApiKind::Jx = get_api_kind!(fn: $id) { + let ptr = &mut x as *mut _ as *mut i32; + unsafe { ptr.write(ptr.read() & 0xffff) }; + } bh.iter(|| test::black_box(x).call(libm::$id as FnTy)) } diff --git a/crates/libm-test/Cargo.toml b/crates/libm-test/Cargo.toml index 89f117455..46691669a 100644 --- a/crates/libm-test/Cargo.toml +++ b/crates/libm-test/Cargo.toml @@ -4,14 +4,16 @@ version = "0.1.0" authors = ["Gonzalo Brito Gadeschi "] edition = "2018" -[dev-dependencies] +[dependencies] +rand = "0.7" libm = { path = "../libm", default-features = false } + +[dev-dependencies] libm-analyze = { path = "../libm-analyze", default-features = true } -rand = "0.7" [features] default = [] checked = ["libm/checked"] stable = ["libm/stable"] system_libm = [] -exhaustive = [] \ No newline at end of file +exhaustive = [] diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 990bec4a6..5d089aac5 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -77,6 +77,7 @@ impl_call!((f64) -> f64: x: x.0); impl_call!((f64) -> i32: x: x.0); impl_call!((f32) -> i32: x: x.0); impl_call!((f32, f32) -> f32: x: x.0, x.1); +impl_call!((f32, f64) -> f32: x: x.0, x.1); impl_call!((f64, f64) -> f64: x: x.0, x.1); impl_call!((f64, i32) -> f64: x: x.0, x.1); impl_call!((f32, i32) -> f32: x: x.0, x.1); @@ -85,23 +86,53 @@ impl_call!((i32, f32) -> f32: x: x.0, x.1); impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); -// Adjust the input of a function. +pub trait TupleVec { + type Output; + fn get(&self, i: usize) -> Self::Output; +} + +macro_rules! impl_tuple_vec { + (($($arg_tys:ty),*): $self_:ident: $($xs:expr),*) => { + impl TupleVec for ($(Vec<$arg_tys>,)+) { + type Output = ($($arg_tys,)+); + fn get(&self, i: usize) -> Self::Output { + let $self_ = self; + ($($xs[i],)*) + } + } + }; +} + +impl_tuple_vec!((f32): x: x.0); +impl_tuple_vec!((f64): x: x.0); +impl_tuple_vec!((f32, f32): x: x.0, x.1); +impl_tuple_vec!((f32, f64): x: x.0, x.1); +impl_tuple_vec!((f64, f64): x: x.0, x.1); +impl_tuple_vec!((f64, i32): x: x.0, x.1); +impl_tuple_vec!((f32, i32): x: x.0, x.1); +impl_tuple_vec!((i32, f64): x: x.0, x.1); +impl_tuple_vec!((i32, f32): x: x.0, x.1); +impl_tuple_vec!((f32, f32, f32): x: x.0, x.1, x.2); +impl_tuple_vec!((f64, f64, f64): x: x.0, x.1, x.2); + +/// Kind of LibmApi - used to handle generating tests +/// for some functions slightly differently. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ApiKind { + Jx, + Other, +} + #[macro_export] -macro_rules! adjust_input { - (fn: j1, input: $arg:ident) => { - adjust_input!(adjust: $arg) +macro_rules! get_api_kind { + (fn: j1) => { + $crate::ApiKind::Jx }; - (fn: jn, input: $arg:ident) => { - adjust_input!(adjust: $arg) + (fn: jn) => { + $crate::ApiKind::Jx }; - (fn: $id:ident, input: $args:ident) => {}; - (adjust: $arg:ident) => { - // First argument to these functions are a number of - // iterations and passing large random numbers takes forever - // to execute, so check if their higher bits are set and - // zero them: - let p = &mut $arg as *mut _ as *mut i32; - unsafe { p.write(p.read() & 0xffff) } + (fn: $id:ident) => { + $crate::ApiKind::Other }; } @@ -121,3 +152,100 @@ macro_rules! assert_approx_eq { } }; } + +pub trait Toward: Sized { + fn toward(self, other: Self, len: usize) -> Vec; +} + +macro_rules! impl_toward_f { + ($float_ty:ident, $toward_fn:path) => { + impl Toward for $float_ty { + fn toward(self, other: Self, len: usize) -> Vec { + let mut vec = Vec::with_capacity(len); + let mut current = self; + vec.push(self); + for _ in 0..=len { + current = $toward_fn(current, other as _); + vec.push(self); + if current.to_bits() == other.to_bits() { + break; + } + } + vec + } + } + }; +} +impl_toward_f!(f32, libm::nextafterf); +impl_toward_f!(f64, libm::nextafter); + +pub trait RandSeq: Sized { + fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec; +} + +macro_rules! impl_rand_seq_f { + ($float_ty:ident) => { + impl RandSeq for $float_ty { + fn rand_seq(rng: &mut R, _api_kind: ApiKind, len: usize) -> Vec { + use std::$float_ty::*; + let mut vec = Vec::with_capacity(len); + + // These inputs are always tested + const BOUNDS: [$float_ty; 9] = [ + NAN, + INFINITY, + NEG_INFINITY, + EPSILON, + -EPSILON, + MAX, + MIN, + MIN_POSITIVE, + -MIN_POSITIVE, + ]; + vec.extend(&BOUNDS); + // A range around the inputs is also always tested: + const NSTEPS: usize = 1_000; + vec.extend(INFINITY.toward(0., NSTEPS)); + vec.extend(NEG_INFINITY.toward(0., NSTEPS)); + vec.extend((0. as $float_ty).toward(MIN_POSITIVE, NSTEPS)); + vec.extend((0. as $float_ty).toward(-MIN_POSITIVE, NSTEPS)); + + for i in 0..=NSTEPS { + let dx = 2. / NSTEPS as $float_ty; + let next = (-1. as $float_ty) + (i as $float_ty) * dx; + vec.push(next); + } + + // ~NSTEPS * 4 + assert!(len > 2 * 4 * NSTEPS, "len {} !> {}", len, 2 * 4 * NSTEPS); + let current_len = vec.len(); + let remaining_len = len.checked_sub(current_len).unwrap(); + + for _ in 0..remaining_len { + let n = rng.gen::<$float_ty>(); + vec.push(n); + } + assert_eq!(vec.len(), len); + vec + } + } + }; +} + +impl_rand_seq_f!(f32); +impl_rand_seq_f!(f64); + +impl RandSeq for i32 { + fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec { + let mut v = Vec::with_capacity(len); + for _ in 0..len { + let mut r = rng.gen::(); + if let ApiKind::Jx = api_kind { + r &= 0xffff; + } + v.push(r); + } + assert_eq!(v.len(), len); + v + } +} diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index ea1a84e20..b872e3d2e 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -2,10 +2,10 @@ #![cfg(test)] #![cfg(feature = "system_libm")] -use libm_test::{adjust_input, assert_approx_eq, Call}; +use libm_test::{assert_approx_eq, get_api_kind, Call, RandSeq, TupleVec}; // Number of tests to generate for each function -const NTESTS: usize = 500; +const NTESTS: usize = 10_000; const ULP_TOL: usize = 4; @@ -74,25 +74,26 @@ macro_rules! system_libm { #[allow(unused)] fn $id() { use crate::Call; - let mut rng = rand::thread_rng(); - for _ in 0..NTESTS { - // Type of the system libm fn: - type FnTy - = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + // Type of the system libm fn: + type FnTy + = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; + extern "C" { + // The system's libm function: + fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; + } - extern "C" { - // The system's libm function: - fn $id($($arg_ids: $arg_tys),*) -> $ret_ty; - } + let mut rng = rand::thread_rng(); - // Generate a tuple of arguments containing random values: - let mut args: ( $($arg_tys,)+ ) - = ( $(<$arg_tys as Rand>::gen(&mut rng),)+ ); + // Depending on the type of API, different ranges of values might be + // allowed or interesting to test: + let api_kind = get_api_kind!(fn: $id); - // Some APIs need their inputs to be "adjusted" (see macro): - // correct_input!(fn: $id, input: args); - adjust_input!(fn: $id, input: args); + // Generate a tuple of arguments containing random values: + let mut args: ( $(Vec<$arg_tys>,)+ ) + = ( $(<$arg_tys as RandSeq>::rand_seq(&mut rng, api_kind, NTESTS),)+ ); + for i in 0..NTESTS { + let args: ( $($arg_tys,)+ ) = args.get(i); let result = args.call(libm::$id as FnTy); let expected = args.call($id as FnTy); assert_approx_eq!( @@ -105,31 +106,3 @@ macro_rules! system_libm { } libm_analyze::for_each_api!(system_libm); - -// We need to be able to generate random numbers for the types involved. -// -// Rand does this well, but we want to also test some other specific values. -trait Rand { - fn gen(rng: &mut rand::rngs::ThreadRng) -> Self; -} - -macro_rules! impl_rand { - ($id:ident: [$($e:expr),*]) => { - impl Rand for $id { - fn gen(r: &mut rand::rngs::ThreadRng) -> Self { - use rand::{Rng, seq::SliceRandom}; - // 1/20 probability of picking a non-random value - if r.gen_range(0, 20) < 1 { - *[$($e),*].choose(r).unwrap() - } else { - r.gen::<$id>() - } - } - } - } -} - -// Some interesting random values -impl_rand!(f32: [std::f32::NAN, std::f32::INFINITY, std::f32::NEG_INFINITY]); -impl_rand!(f64: [std::f64::NAN, std::f64::INFINITY, std::f64::NEG_INFINITY]); -impl_rand!(i32: [i32::max_value(), 0_i32, i32::min_value()]); diff --git a/crates/libm/src/lib.rs b/crates/libm/src/lib.rs index 527fcb4f9..d78339cb5 100644 --- a/crates/libm/src/lib.rs +++ b/crates/libm/src/lib.rs @@ -1,6 +1,6 @@ //! libm in pure Rust #![deny(warnings)] -#![no_std] +//#![no_std] #![cfg_attr( all(target_arch = "wasm32", not(feature = "stable")), feature(core_intrinsics) From ae3ab7477433ab14f8f2be09e19246e978570c79 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 12:41:36 +0200 Subject: [PATCH 39/43] Use approx_eq in unit tests --- crates/libm-test/src/lib.rs | 5 +++++ crates/libm-test/tests/unit.rs | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 5d089aac5..9d6adb74f 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -151,6 +151,11 @@ macro_rules! assert_approx_eq { panic!(f); } }; + ($result:expr, $expected:expr, ulp: $ulps:expr) => { + if !$crate::WithinUlps::within_ulps($result, $expected, $ulps) { + panic!("{:?} != {:?}", $result, $expected); + } + } } pub trait Toward: Sized { diff --git a/crates/libm-test/tests/unit.rs b/crates/libm-test/tests/unit.rs index 6db51c118..2915b23da 100644 --- a/crates/libm-test/tests/unit.rs +++ b/crates/libm-test/tests/unit.rs @@ -1,6 +1,8 @@ #![cfg(test)] use libm::*; +use libm_test::assert_approx_eq; + #[test] fn remquo_q_overflow() { // 0xc000000000000001, 0x04c0000000000004 @@ -349,14 +351,19 @@ fn y1f_2002() { fn fma_segfault_bug() { // An attempt to substract with overflow was causing a segfault // on FMA for these inputs: - assert_eq!( + assert_approx_eq!( fma( -0.0000000000000002220446049250313, -0.0000000000000002220446049250313, -0.0000000000000002220446049250313 ), - -0.00000000000000022204460492503126 + -0.00000000000000022204460492503126, + ulp: 0 ); - assert_eq!(fma(-0.992, -0.992, -0.992), -0.00793599999988632); + assert_approx_eq!( + fma(-0.992, -0.992, -0.992), + -0.00793599999988632, + ulp: 0 + ); } From f81edcc81cf2a03faac4d17fd6aa0d27e6e567af Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 13:04:23 +0200 Subject: [PATCH 40/43] Silence clippy warnings for libm and add clippy to CI --- azure-pipelines.yml | 6 +++++- crates/libm-analyze/src/lib.rs | 6 +++--- crates/libm-test/src/lib.rs | 35 +++++++++++++++++++++++++-------- crates/libm/src/lib.rs | 2 +- crates/libm/src/math/exp2.rs | 2 +- crates/libm/src/math/mod.rs | 36 ++++++++++++++++++++++++++++++++++ 6 files changed, 73 insertions(+), 14 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 49d31094c..257ece7d1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -54,7 +54,7 @@ jobs: variables: TOOLCHAIN: nightly - - job: rustfmt + - job: tools pool: vmImage: ubuntu-16.04 steps: @@ -63,6 +63,10 @@ jobs: displayName: "install rustfmt" - bash: cargo fmt --all -- --check displayName: "check formatting" + - bash: rustup component add clippy + displayName: "install clippy" + - bash: cargo clippy --all -- -D clippy::pedantic + displayName: "check clippy" - job: compiler_builtins_works pool: diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 29d3e23e9..831c3b8dd 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -208,7 +208,7 @@ fn get_functions(files: &[syn::File]) -> Vec { )); } if attrs.is_empty() { - err!(format!("missing `#[inline]` and `#[no_panic]` attributes")); + err!("missing `#[inline]` and `#[no_panic]` attributes"); } else { let attrs = attrs .iter() @@ -216,10 +216,10 @@ fn get_functions(files: &[syn::File]) -> Vec { .collect::>() .join(","); if !attrs.contains("inline") { - err!(format!("missing `#[inline]` attribute")); + err!("missing `#[inline]` attribute"); } if !attrs.contains("no_panic") { - err!(format!("missing `#[no_panic]` attributes")); + err!("missing `#[no_panic]` attributes"); } } // Validate and parse output parameters and function arguments: diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index 9d6adb74f..f6d1b3616 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -115,7 +115,7 @@ impl_tuple_vec!((i32, f32): x: x.0, x.1); impl_tuple_vec!((f32, f32, f32): x: x.0, x.1, x.2); impl_tuple_vec!((f64, f64, f64): x: x.0, x.1, x.2); -/// Kind of LibmApi - used to handle generating tests +/// Kind of libm API - used to handle generating tests /// for some functions slightly differently. #[derive(Copy, Clone, Debug, PartialEq)] pub enum ApiKind { @@ -155,7 +155,7 @@ macro_rules! assert_approx_eq { if !$crate::WithinUlps::within_ulps($result, $expected, $ulps) { panic!("{:?} != {:?}", $result, $expected); } - } + }; } pub trait Toward: Sized { @@ -190,8 +190,10 @@ pub trait RandSeq: Sized { macro_rules! impl_rand_seq_f { ($float_ty:ident) => { + #[allow(clippy::use_self)] impl RandSeq for $float_ty { fn rand_seq(rng: &mut R, _api_kind: ApiKind, len: usize) -> Vec { + use rand::seq::SliceRandom; use std::$float_ty::*; let mut vec = Vec::with_capacity(len); @@ -212,12 +214,12 @@ macro_rules! impl_rand_seq_f { const NSTEPS: usize = 1_000; vec.extend(INFINITY.toward(0., NSTEPS)); vec.extend(NEG_INFINITY.toward(0., NSTEPS)); - vec.extend((0. as $float_ty).toward(MIN_POSITIVE, NSTEPS)); - vec.extend((0. as $float_ty).toward(-MIN_POSITIVE, NSTEPS)); + vec.extend((0. as Self).toward(MIN_POSITIVE, NSTEPS)); + vec.extend((0. as Self).toward(-MIN_POSITIVE, NSTEPS)); for i in 0..=NSTEPS { - let dx = 2. / NSTEPS as $float_ty; - let next = (-1. as $float_ty) + (i as $float_ty) * dx; + let dx = 2. / NSTEPS as Self; + let next = (-1. as Self) + (i as Self) * dx; vec.push(next); } @@ -227,10 +229,18 @@ macro_rules! impl_rand_seq_f { let remaining_len = len.checked_sub(current_len).unwrap(); for _ in 0..remaining_len { - let n = rng.gen::<$float_ty>(); + let n = rng.gen::(); vec.push(n); } assert_eq!(vec.len(), len); + + // Duplicate the vector, randomly shuffle it, and + // concatenate it. Otherwise for n-ary functions + // all vectors might have the same values. But + // testing with the same values is also worth doing. + let mut vec2 = vec.clone(); + vec2.shuffle(rng); + vec.extend(vec2); vec } } @@ -242,15 +252,24 @@ impl_rand_seq_f!(f64); impl RandSeq for i32 { fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec { + use rand::seq::SliceRandom; let mut v = Vec::with_capacity(len); for _ in 0..len { - let mut r = rng.gen::(); + let mut r = rng.gen::(); if let ApiKind::Jx = api_kind { r &= 0xffff; } v.push(r); } assert_eq!(v.len(), len); + + // Duplicate the vector, randomly shuffle it, and + // concatenate it. Otherwise for n-ary functions + // all vectors might have the same values. But + // testing with the same values is also worth doing. + let mut v2 = v.clone(); + v2.shuffle(rng); + v.extend(v2); v } } diff --git a/crates/libm/src/lib.rs b/crates/libm/src/lib.rs index d78339cb5..527fcb4f9 100644 --- a/crates/libm/src/lib.rs +++ b/crates/libm/src/lib.rs @@ -1,6 +1,6 @@ //! libm in pure Rust #![deny(warnings)] -//#![no_std] +#![no_std] #![cfg_attr( all(target_arch = "wasm32", not(feature = "stable")), feature(core_intrinsics) diff --git a/crates/libm/src/math/exp2.rs b/crates/libm/src/math/exp2.rs index d6959be82..7c7436dac 100644 --- a/crates/libm/src/math/exp2.rs +++ b/crates/libm/src/math/exp2.rs @@ -28,7 +28,7 @@ use super::scalbn; const TBLSIZE: usize = 256; -#[cfg_attr(rustfmt, rustfmt_skip)] +#[rustfmt::skip] static TBL: [u64; TBLSIZE * 2] = [ // exp2(z + eps) eps 0x3fe6a09e667f3d5d, 0x3d39880000000000, diff --git a/crates/libm/src/math/mod.rs b/crates/libm/src/math/mod.rs index 5c78717f0..5e692d9d2 100644 --- a/crates/libm/src/math/mod.rs +++ b/crates/libm/src/math/mod.rs @@ -1,3 +1,39 @@ +#![allow( + clippy::many_single_char_names, + clippy::similar_names, + clippy::unreadable_literal, + clippy::unseparated_literal_suffix, + clippy::doc_markdown, + clippy::needless_return, + clippy::if_not_else, + clippy::int_plus_one, + clippy::large_digit_groups, + clippy::items_after_statements, + clippy::collapsible_if, + clippy::mixed_case_hex_literals, + clippy::precedence, + clippy::suspicious_else_formatting, + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::cast_possible_wrap, + clippy::excessive_precision, + clippy::range_minus_one, + clippy::cognitive_complexity, + clippy::eq_op, + clippy::shadow_unrelated, + clippy::needless_range_loop, + clippy::float_cmp, + clippy::assign_op_pattern, + clippy::zero_divided_by_zero, + clippy::misrefactored_assign_op, + clippy::if_same_then_else, + clippy::verbose_bit_mask, + clippy::replace_consts, + clippy::used_underscore_binding, +)] + macro_rules! force_eval { ($e:expr) => { #[allow(unused_unsafe)] From f9f59e0b69106d78747a195591f0b1003763414d Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 15:48:21 +0200 Subject: [PATCH 41/43] Refactor libm-test; add tests specifics for each type of API --- crates/libm-analyze/src/lib.rs | 66 +++++- crates/libm-analyze/tests/analyze.rs | 1 + crates/libm-analyze/tests/analyze_opt.rs | 14 ++ crates/libm-bench/benches/bench.rs | 14 +- crates/libm-test/src/api_kind.rs | 120 +++++++++++ crates/libm-test/src/call_fn.rs | 38 ++++ crates/libm-test/src/float_rng.rs | 49 +++++ crates/libm-test/src/lib.rs | 259 ++--------------------- crates/libm-test/src/rand_seq.rs | 191 +++++++++++++++++ crates/libm-test/src/tuple_vec.rs | 31 +++ crates/libm-test/src/within_ulps.rs | 50 +++++ crates/libm-test/tests/exhaustive32.rs | 50 +---- crates/libm-test/tests/system_libm.rs | 76 ++----- 13 files changed, 594 insertions(+), 365 deletions(-) create mode 100644 crates/libm-analyze/tests/analyze_opt.rs create mode 100644 crates/libm-test/src/api_kind.rs create mode 100644 crates/libm-test/src/call_fn.rs create mode 100644 crates/libm-test/src/float_rng.rs create mode 100644 crates/libm-test/src/rand_seq.rs create mode 100644 crates/libm-test/src/tuple_vec.rs create mode 100644 crates/libm-test/src/within_ulps.rs diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 831c3b8dd..69286633b 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -5,6 +5,7 @@ extern crate proc_macro; use self::proc_macro::TokenStream; use quote::quote; +use std::collections::HashSet; use syn::parse_macro_input; /// `input` contains a single identifier, corresponding to a user-defined macro. @@ -13,22 +14,26 @@ use syn::parse_macro_input; /// See tests/analyze or below for the API. #[proc_macro] pub fn for_each_api(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as Input); let files = get_libm_files(); - let functions = get_functions(&files); - let input = parse_macro_input!(input as syn::Ident); + let functions = get_functions(&files, &input.ignored); let mut tokens = proc_macro2::TokenStream::new(); + let input_macro = input.macro_id; for function in functions { let id = function.ident; + let api_kind = function.api_kind; let ret_ty = function.ret_ty; let arg_tys = function.arg_tys; let arg_ids = get_arg_ids(arg_tys.len()); let t = quote! { - #input! { + #input_macro! { id: #id; + api_kind: #api_kind; arg_tys: #(#arg_tys),*; arg_ids: #(#arg_ids),*; ret_ty: #ret_ty; } + }; tokens.extend(t); } @@ -78,6 +83,7 @@ fn get_libm_files() -> Vec { /// Function signature that will be expanded for the user macro. struct FnSig { ident: syn::Ident, + api_kind: syn::Ident, c_abi: bool, ret_ty: Option, arg_tys: Vec, @@ -101,7 +107,7 @@ macro_rules! syn_to_str { /// Extracts all public functions from the libm files while /// doing some sanity checks on the function signatures. -fn get_functions(files: &[syn::File]) -> Vec { +fn get_functions(files: &[syn::File], ignored: &Option>) -> Vec { let mut error = false; let mut functions = Vec::new(); // Traverse all files matching function items @@ -122,10 +128,17 @@ fn get_functions(files: &[syn::File]) -> Vec { // Build a function signature while doing some sanity checks let mut fn_sig = FnSig { ident: ident.clone(), + api_kind: to_api_kind(ident.clone()), c_abi: false, arg_tys: Vec::new(), ret_ty: None, }; + // Skip ignored functions: + if let Some(ignored) = ignored { + if ignored.contains(&fn_sig.name()) { + continue; + } + } macro_rules! err { ($msg:expr) => {{ #[cfg(feature = "analyze")] @@ -300,3 +313,48 @@ fn get_arg_ids(len: usize) -> Vec { } ids } + +/// Returns the ApiKind enum variant for this function +fn to_api_kind(id: syn::Ident) -> syn::Ident { + let name = syn_to_str!(id); + let first = name.chars().nth(0).unwrap(); + let first_upper = first.to_uppercase().nth(0).unwrap(); + let name = name.replacen(first, &first_upper.to_string(), 1); + syn::Ident::new(&name, proc_macro2::Span::call_site()) +} + +#[derive(Debug)] +struct Input { + macro_id: syn::Ident, + ignored: Option>, +} + +impl syn::parse::Parse for Input { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + let macro_id: syn::Ident = input.parse()?; + let lookahead = input.lookahead1(); + if lookahead.peek(syn::token::Paren) { + let _paren_token = syn::parenthesized!(content in input); + let ignored: syn::Lit = content.parse::()?; + if let syn::Lit::Str(c) = ignored { + let s = c.value(); + let mut hash_set = HashSet::::new(); + for i in s.split(",") { + hash_set.insert(i.to_string()); + } + Ok(Input { + macro_id: macro_id, + ignored: Some(hash_set), + }) + } else { + Err(lookahead.error()) + } + } else { + Ok(Input { + macro_id: macro_id, + ignored: None, + }) + } + } +} diff --git a/crates/libm-analyze/tests/analyze.rs b/crates/libm-analyze/tests/analyze.rs index cee3c39bd..0b1ba83b3 100644 --- a/crates/libm-analyze/tests/analyze.rs +++ b/crates/libm-analyze/tests/analyze.rs @@ -4,6 +4,7 @@ macro_rules! nop { ( id: $id:ident; + api_kind: $api_kind:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; ret_ty: $ret_ty:ty; diff --git a/crates/libm-analyze/tests/analyze_opt.rs b/crates/libm-analyze/tests/analyze_opt.rs new file mode 100644 index 000000000..c5a2598eb --- /dev/null +++ b/crates/libm-analyze/tests/analyze_opt.rs @@ -0,0 +1,14 @@ +//! Tests that the proc-macro accepts macros with +//! the following pattern: + +macro_rules! nop { + ( + id: $id:ident; + api_kind: $api_kind:ident; + arg_tys: $($arg_tys:ty),*; + arg_ids: $($arg_ids:ident),*; + ret_ty: $ret_ty:ty; + ) => {}; +} + +libm_analyze::for_each_api!(nop("j1f,jn")); diff --git a/crates/libm-bench/benches/bench.rs b/crates/libm-bench/benches/bench.rs index a380a49da..17c2bb33a 100644 --- a/crates/libm-bench/benches/bench.rs +++ b/crates/libm-bench/benches/bench.rs @@ -1,20 +1,14 @@ #![feature(test)] extern crate test; -use libm_test::{get_api_kind, ApiKind, Call}; +use libm_test::{ApiKind, CallFn}; use rand::Rng; use test::Bencher; macro_rules! bench_fn { - // FIXME: jnf benches never terminate - ( - id: jnf; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; ( id: $id:ident; + api_kind: $api_kind:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; ret_ty: $ret_ty:ty; @@ -29,7 +23,7 @@ macro_rules! bench_fn { let mut rng = rand::thread_rng(); let mut x: ( $($arg_tys,)+ ) = ( $(rng.gen::<$arg_tys>(),)+ ); - if let ApiKind::Jx = get_api_kind!(fn: $id) { + if let ApiKind::Jn = ApiKind::$api_kind { let ptr = &mut x as *mut _ as *mut i32; unsafe { ptr.write(ptr.read() & 0xffff) }; } @@ -39,4 +33,4 @@ macro_rules! bench_fn { }; } -libm_analyze::for_each_api!(bench_fn); +libm_analyze::for_each_api!(bench_fn(/*ignore:*/ "jnf")); diff --git a/crates/libm-test/src/api_kind.rs b/crates/libm-test/src/api_kind.rs new file mode 100644 index 000000000..d1ca5a1c6 --- /dev/null +++ b/crates/libm-test/src/api_kind.rs @@ -0,0 +1,120 @@ +//! An enum with one variant per API + +// FIXME: this should be auto-generated + +/// Kind of libm API - used to handle generating tests +/// for some functions slightly differently. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ApiKind { + Acos, + Acosf, + Acosh, + Acoshf, + Asin, + Asinf, + Asinh, + Asinhf, + Atan, + Atan2, + Atan2f, + Atanf, + Atanh, + Atanhf, + Cbrt, + Cbrtf, + Ceil, + Ceilf, + Copysign, + Copysignf, + Cos, + Cosf, + Cosh, + Coshf, + Erf, + Erfc, + Erfcf, + Erff, + Exp, + Exp10, + Exp10f, + Exp2, + Exp2f, + Expf, + Expm1, + Expm1f, + Fabs, + Fabsf, + Fdim, + Fdimf, + Floor, + Floorf, + Fma, + Fmaf, + Fmax, + Fmaxf, + Fmin, + Fminf, + Fmod, + Fmodf, + Hypot, + Hypotf, + Ilogb, + Ilogbf, + J0, + J0f, + J1, + J1f, + Jn, + Ldexp, + Ldexpf, + Lgamma, + Lgammaf, + Log, + Log10, + Log10f, + Log1p, + Log1pf, + Log2, + Log2f, + Logf, + Modf, + Modff, + Nextafter, + Nextafterf, + Nexttoward, + Nexttowardf, + Pow, + Powf, + Remainder, + Remainderf, + Rint, + Rintf, + Round, + Roundf, + Scalbn, + Scalbnf, + Sin, + SinCos, + SinCosPi, + SinCosPif, + SinCosf, + Sinf, + Sinh, + Sinhf, + Sqrt, + Sqrtf, + Tan, + Tanf, + Tanh, + Tanhf, + Tgamma, + Tgammaf, + Trunc, + Truncf, + Y0, + Y0f, + Y1, + Y1f, + Yn, + Ynf, +} diff --git a/crates/libm-test/src/call_fn.rs b/crates/libm-test/src/call_fn.rs new file mode 100644 index 000000000..0d879ac30 --- /dev/null +++ b/crates/libm-test/src/call_fn.rs @@ -0,0 +1,38 @@ +//! Trait to do function dispatch based on tuples: + +/// This implements function dispatch for tuples of arguments used in the tests +/// above, so that we can: (f32, 32).call(fn(f32, f32) -> f32) generically. +/// +/// We need the input parameter F to support dispatching, e.g., (f32,f32) with +/// functions that return both f32 or i32. Those are two different types, so we +/// need to be parametric over them. +pub trait CallFn { + type Ret; + fn call(self, f: F) -> Self::Ret; +} + +macro_rules! impl_call { + (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { + impl CallFn $ret_ty> for ($($arg_tys,)+) { + type Ret = $ret_ty; + fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { + let $self_ = self; + unsafe { f($($xs),*) } + } + } + }; +} + +impl_call!((f32) -> f32: x: x.0); +impl_call!((f64) -> f64: x: x.0); +impl_call!((f64) -> i32: x: x.0); +impl_call!((f32) -> i32: x: x.0); +impl_call!((f32, f32) -> f32: x: x.0, x.1); +impl_call!((f32, f64) -> f32: x: x.0, x.1); +impl_call!((f64, f64) -> f64: x: x.0, x.1); +impl_call!((f64, i32) -> f64: x: x.0, x.1); +impl_call!((f32, i32) -> f32: x: x.0, x.1); +impl_call!((i32, f64) -> f64: x: x.0, x.1); +impl_call!((i32, f32) -> f32: x: x.0, x.1); +impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); +impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); diff --git a/crates/libm-test/src/float_rng.rs b/crates/libm-test/src/float_rng.rs new file mode 100644 index 000000000..07ea8ecdf --- /dev/null +++ b/crates/libm-test/src/float_rng.rs @@ -0,0 +1,49 @@ +//! Traits for generating ranges of floating-point values + +pub trait Toward: Sized { + /// Generates `len` minimal incremental steps from self to other. + /// + /// Other is often never reached. + fn toward(self, other: Self, len: usize) -> Vec; +} + +pub trait Distribute: Sized { + /// Distributes `len` values in range `[self, other]`. + fn distribute(self, other: Self, len: usize) -> Vec; +} + +macro_rules! impl_f { + ($float_ty:ident, $toward_fn:path) => { + impl Toward for $float_ty { + fn toward(self, other: Self, len: usize) -> Vec { + let mut vec = Vec::with_capacity(len); + let mut current = self; + vec.push(self); + for _ in 0..=len { + current = $toward_fn(current, other as _); + vec.push(self); + if current.to_bits() == other.to_bits() { + break; + } + } + vec + } + } + impl Distribute for $float_ty { + fn distribute(self, other: Self, mut len: usize) -> Vec { + let mut vec = Vec::with_capacity(len + 1); + vec.push(self); + // Bresenham's alg: + let mut x = self; + while len > 0 { + x += (other - self) / (len as Self); + len -= 1; + vec.push(x); + } + vec + } + } + }; +} +impl_f!(f32, libm::nextafterf); +impl_f!(f64, libm::nextafter); diff --git a/crates/libm-test/src/lib.rs b/crates/libm-test/src/lib.rs index f6d1b3616..692579bb2 100644 --- a/crates/libm-test/src/lib.rs +++ b/crates/libm-test/src/lib.rs @@ -1,141 +1,24 @@ //! Testing utilities required by most tests. -pub trait WithinUlps { - /// Returns true if two numbers are closer than `ulp_tol` to each other. - fn within_ulps(self, other: Self, ulp_tol: usize) -> bool; -} - -// Stamp the impls for floats: -macro_rules! impl_within_ulps_f { - ($f_ty:ty, $i_ty:ty) => { - impl WithinUlps for $f_ty { - #[allow(clippy::cast_possible_truncation)] - fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { - let x = self; - if x.is_nan() != y.is_nan() { - // one is nan but the other is not - return false; - } - if x.is_nan() && y.is_nan() { - return true; - } - if x.is_infinite() != y.is_infinite() { - // one is inf but the other is not - return false; - } - - let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; - let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; - if (xi < 0) != (yi < 0) { - // different sign, e.g., -0.0 != +0.0: - return false; - } - let ulps = (xi - yi).abs(); - ulps <= ulp_tol as _ - } - } - }; -} - -impl_within_ulps_f!(f32, i32); -impl_within_ulps_f!(f64, i64); - -impl WithinUlps for i32 { - #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] - fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { - let x = self; - let ulps = (x - y).abs(); - ulps <= ulp_tol as _ - } -} - -/// This implements function dispatch for tuples of arguments used in the tests -/// above, so that we can: (f32, 32).call(fn(f32, f32) -> f32) generically. -/// -/// We need the input parameter F to support dispatching, e.g., (f32,f32) with -/// functions that return both f32 or i32. Those are two different types, so we -/// need to be parametric over them. -pub trait Call { - type Ret; - fn call(self, f: F) -> Self::Ret; -} - -macro_rules! impl_call { - (($($arg_tys:ty),*) -> $ret_ty:ty: $self_:ident: $($xs:expr),*) => { - impl Call $ret_ty> for ($($arg_tys,)+) { - type Ret = $ret_ty; - fn call(self, f: unsafe extern "C" fn($($arg_tys),*) -> $ret_ty) -> Self::Ret { - let $self_ = self; - unsafe { f($($xs),*) } - } - } - }; -} +mod within_ulps; +pub use self::within_ulps::WithinUlps; -impl_call!((f32) -> f32: x: x.0); -impl_call!((f64) -> f64: x: x.0); -impl_call!((f64) -> i32: x: x.0); -impl_call!((f32) -> i32: x: x.0); -impl_call!((f32, f32) -> f32: x: x.0, x.1); -impl_call!((f32, f64) -> f32: x: x.0, x.1); -impl_call!((f64, f64) -> f64: x: x.0, x.1); -impl_call!((f64, i32) -> f64: x: x.0, x.1); -impl_call!((f32, i32) -> f32: x: x.0, x.1); -impl_call!((i32, f64) -> f64: x: x.0, x.1); -impl_call!((i32, f32) -> f32: x: x.0, x.1); -impl_call!((f32, f32, f32) -> f32: x: x.0, x.1, x.2); -impl_call!((f64, f64, f64) -> f64: x: x.0, x.1, x.2); +mod call_fn; +pub use self::call_fn::CallFn; -pub trait TupleVec { - type Output; - fn get(&self, i: usize) -> Self::Output; -} - -macro_rules! impl_tuple_vec { - (($($arg_tys:ty),*): $self_:ident: $($xs:expr),*) => { - impl TupleVec for ($(Vec<$arg_tys>,)+) { - type Output = ($($arg_tys,)+); - fn get(&self, i: usize) -> Self::Output { - let $self_ = self; - ($($xs[i],)*) - } - } - }; -} +mod tuple_vec; +pub use self::tuple_vec::TupleVec; -impl_tuple_vec!((f32): x: x.0); -impl_tuple_vec!((f64): x: x.0); -impl_tuple_vec!((f32, f32): x: x.0, x.1); -impl_tuple_vec!((f32, f64): x: x.0, x.1); -impl_tuple_vec!((f64, f64): x: x.0, x.1); -impl_tuple_vec!((f64, i32): x: x.0, x.1); -impl_tuple_vec!((f32, i32): x: x.0, x.1); -impl_tuple_vec!((i32, f64): x: x.0, x.1); -impl_tuple_vec!((i32, f32): x: x.0, x.1); -impl_tuple_vec!((f32, f32, f32): x: x.0, x.1, x.2); -impl_tuple_vec!((f64, f64, f64): x: x.0, x.1, x.2); +mod api_kind; +pub use self::api_kind::ApiKind; -/// Kind of libm API - used to handle generating tests -/// for some functions slightly differently. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum ApiKind { - Jx, - Other, -} +mod float_rng; +pub use self::float_rng::{Distribute, Toward}; -#[macro_export] -macro_rules! get_api_kind { - (fn: j1) => { - $crate::ApiKind::Jx - }; - (fn: jn) => { - $crate::ApiKind::Jx - }; - (fn: $id:ident) => { - $crate::ApiKind::Other - }; -} +mod rand_seq; +pub use self::rand_seq::RandSeq; +/// Asserts that two values are approximately equal up-to ULP tolerance #[macro_export] macro_rules! assert_approx_eq { ($result:ident == $expected:ident, @@ -157,119 +40,3 @@ macro_rules! assert_approx_eq { } }; } - -pub trait Toward: Sized { - fn toward(self, other: Self, len: usize) -> Vec; -} - -macro_rules! impl_toward_f { - ($float_ty:ident, $toward_fn:path) => { - impl Toward for $float_ty { - fn toward(self, other: Self, len: usize) -> Vec { - let mut vec = Vec::with_capacity(len); - let mut current = self; - vec.push(self); - for _ in 0..=len { - current = $toward_fn(current, other as _); - vec.push(self); - if current.to_bits() == other.to_bits() { - break; - } - } - vec - } - } - }; -} -impl_toward_f!(f32, libm::nextafterf); -impl_toward_f!(f64, libm::nextafter); - -pub trait RandSeq: Sized { - fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec; -} - -macro_rules! impl_rand_seq_f { - ($float_ty:ident) => { - #[allow(clippy::use_self)] - impl RandSeq for $float_ty { - fn rand_seq(rng: &mut R, _api_kind: ApiKind, len: usize) -> Vec { - use rand::seq::SliceRandom; - use std::$float_ty::*; - let mut vec = Vec::with_capacity(len); - - // These inputs are always tested - const BOUNDS: [$float_ty; 9] = [ - NAN, - INFINITY, - NEG_INFINITY, - EPSILON, - -EPSILON, - MAX, - MIN, - MIN_POSITIVE, - -MIN_POSITIVE, - ]; - vec.extend(&BOUNDS); - // A range around the inputs is also always tested: - const NSTEPS: usize = 1_000; - vec.extend(INFINITY.toward(0., NSTEPS)); - vec.extend(NEG_INFINITY.toward(0., NSTEPS)); - vec.extend((0. as Self).toward(MIN_POSITIVE, NSTEPS)); - vec.extend((0. as Self).toward(-MIN_POSITIVE, NSTEPS)); - - for i in 0..=NSTEPS { - let dx = 2. / NSTEPS as Self; - let next = (-1. as Self) + (i as Self) * dx; - vec.push(next); - } - - // ~NSTEPS * 4 - assert!(len > 2 * 4 * NSTEPS, "len {} !> {}", len, 2 * 4 * NSTEPS); - let current_len = vec.len(); - let remaining_len = len.checked_sub(current_len).unwrap(); - - for _ in 0..remaining_len { - let n = rng.gen::(); - vec.push(n); - } - assert_eq!(vec.len(), len); - - // Duplicate the vector, randomly shuffle it, and - // concatenate it. Otherwise for n-ary functions - // all vectors might have the same values. But - // testing with the same values is also worth doing. - let mut vec2 = vec.clone(); - vec2.shuffle(rng); - vec.extend(vec2); - vec - } - } - }; -} - -impl_rand_seq_f!(f32); -impl_rand_seq_f!(f64); - -impl RandSeq for i32 { - fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec { - use rand::seq::SliceRandom; - let mut v = Vec::with_capacity(len); - for _ in 0..len { - let mut r = rng.gen::(); - if let ApiKind::Jx = api_kind { - r &= 0xffff; - } - v.push(r); - } - assert_eq!(v.len(), len); - - // Duplicate the vector, randomly shuffle it, and - // concatenate it. Otherwise for n-ary functions - // all vectors might have the same values. But - // testing with the same values is also worth doing. - let mut v2 = v.clone(); - v2.shuffle(rng); - v.extend(v2); - v - } -} diff --git a/crates/libm-test/src/rand_seq.rs b/crates/libm-test/src/rand_seq.rs new file mode 100644 index 000000000..ab89b298f --- /dev/null +++ b/crates/libm-test/src/rand_seq.rs @@ -0,0 +1,191 @@ +//! Trait for generating rand sequence testing relevant values for each API + +use crate::ApiKind; + +pub trait RandSeq: Sized { + /// Generates a sequence containing exactly `len` values computed from the RNG `rng` + /// according to the `api_kind` + fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec; +} + +macro_rules! impl_rand_seq_f { + ($float_ty:ident) => { + #[allow(clippy::use_self)] + impl RandSeq for $float_ty { + fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec { + use crate::{Distribute, Toward}; + use rand::seq::SliceRandom; + use std::$float_ty::*; + + let mut vec = Vec::with_capacity(len); + let double = std::mem::size_of::() == 8; + + // These inputs are always tested + const BOUNDS: [$float_ty; 9] = [ + NAN, + INFINITY, + NEG_INFINITY, + EPSILON, + -EPSILON, + MAX, + MIN, + MIN_POSITIVE, + -MIN_POSITIVE, + ]; + vec.extend(&BOUNDS); + + // The domain close to these inputs is also tested: + const LIMIT_NSTEPS: usize = 10_001; + vec.extend(INFINITY.toward(0., LIMIT_NSTEPS)); + vec.extend(NEG_INFINITY.toward(0., LIMIT_NSTEPS)); + vec.extend((0. as Self).toward(MIN_POSITIVE, LIMIT_NSTEPS)); + vec.extend((0. as Self).toward(-MIN_POSITIVE, LIMIT_NSTEPS)); + vec.extend((-1. as Self).distribute(1. as Self, 200_001)); + + // These domains are extended with tests specific to each + // type of API: + { + macro_rules! dist { + ($from:expr, $to:expr, $steps:expr) => { + vec.extend(($from as Self).distribute($to as Self, $steps)); + }; + } + use crate::ApiKind::*; + match api_kind { + Sin | Cos | Tan => { + if double { + dist!(1e-300, 1e+8, 200_001); + } else { + dist!(1e-30, 1e+8, 200_001); + } + } + SinCos | SinCosPi => { + dist!(-1e-14, 1e+14, 200_001); + } + Log | Log2 | Log10 | Log1p => { + if double { + dist!(-1e-300, 1e+14, 200_001); + } else { + dist!(-1e-30, 1e+14, 200_001); + } + } + + Exp | Exp2 | Exp10 | Expm1 => { + dist!(-1000., 1000., 200_001); + } + Pow => { + dist!(-100., 100., 200_001); + } + Cbrt => { + dist!(1e-14, 1e+14, 100_001); + dist!(-1e-14, -1e+14, 100_001); + } + Hypot => { + dist!(-1e-7, 1e7, 200_001); + } + Atan => { + dist!(1e-3, 1e+7, 100_001); + dist!(1e-2, 1e+8, 100_001); + dist!(1e-3, 1e+7, 100_001); + dist!(-1e-2, 1e+8, 100_001); + } + Atan2 => { + dist!(-10, 10, 200_001); + } + Sinh | Cosh | Tanh | Asinh | Atanh => { + if double { + dist!(-700, 700, 200_001); + } else { + dist!(-88, 88, 200_001) + } + } + + Acosh => { + if double { + dist!(1, 700, 200_001); + } else { + dist!(1, 88, 200_001); + } + } + Lgamma => { + dist!(-5000, 5000, 200_001); + } + Tgamma => { + dist!(-10, 10, 200_001); + } + Erf => { + dist!(-100, 100, 200_001); + } + Erfc => { + dist!(-1, 100, 200_001); + } + Fabs => { + dist!(-100.5, 100.5, 200_001); + } + Copysign | Fmax | Fmin | Fmod | Nextafter => { + dist!(-1e+10, 1e+10, 200_001); + } + Modf => { + dist!(-1e+14, 1e+14, 200_001); + } + Trunc | Floor | Ceil | Round | Rint => { + dist!(-100, 100, 800); + } + _ => (), + } + } + + // ~NSTEPS * 4 + let current_len = vec.len(); + assert!(len > current_len); + let remaining_len = len.checked_sub(current_len).unwrap(); + + for _ in 0..remaining_len { + let n = rng.gen::(); + vec.push(n); + } + assert_eq!(vec.len(), len); + + // Duplicate the vector, randomly shuffle it, and + // concatenate it. Otherwise for n-ary functions + // all vectors might have the same values. But + // testing with the same values is also worth doing. + let mut vec2 = vec.clone(); + vec2.shuffle(rng); + vec.extend(vec2); + vec + } + } + }; +} + +impl_rand_seq_f!(f32); +impl_rand_seq_f!(f64); + +impl RandSeq for i32 { + fn rand_seq(rng: &mut R, api_kind: ApiKind, len: usize) -> Vec { + use rand::seq::SliceRandom; + + let mut v = Vec::with_capacity(len); + for _ in 0..len { + let mut r = rng.gen::(); + if let ApiKind::Jn = api_kind { + // The integer argument of these APIs is a number of iterations. + // Computational cost blows up if we pass huge values, so zero + // their lower bits. + r &= 0xffff; + } + v.push(r); + } + assert_eq!(v.len(), len); + + // Duplicate the vector, randomly shuffle it, and + // concatenate it. Otherwise for n-ary functions + // all vectors might have the same values. But + // testing with the same values is also worth doing. + let mut v2 = v.clone(); + v2.shuffle(rng); + v.extend(v2); + v + } +} diff --git a/crates/libm-test/src/tuple_vec.rs b/crates/libm-test/src/tuple_vec.rs new file mode 100644 index 000000000..dbfaeecce --- /dev/null +++ b/crates/libm-test/src/tuple_vec.rs @@ -0,0 +1,31 @@ +//! Trait for tuples of vectors + +/// Access to tuple elements of a tuple of vectors +pub trait TupleVec { + type Output; + fn get(&self, i: usize) -> Self::Output; +} + +macro_rules! impl_tuple_vec { + (($($arg_tys:ty),*): $self_:ident: $($xs:expr),*) => { + impl TupleVec for ($(Vec<$arg_tys>,)+) { + type Output = ($($arg_tys,)+); + fn get(&self, i: usize) -> Self::Output { + let $self_ = self; + ($($xs[i],)*) + } + } + }; +} + +impl_tuple_vec!((f32): x: x.0); +impl_tuple_vec!((f64): x: x.0); +impl_tuple_vec!((f32, f32): x: x.0, x.1); +impl_tuple_vec!((f32, f64): x: x.0, x.1); +impl_tuple_vec!((f64, f64): x: x.0, x.1); +impl_tuple_vec!((f64, i32): x: x.0, x.1); +impl_tuple_vec!((f32, i32): x: x.0, x.1); +impl_tuple_vec!((i32, f64): x: x.0, x.1); +impl_tuple_vec!((i32, f32): x: x.0, x.1); +impl_tuple_vec!((f32, f32, f32): x: x.0, x.1, x.2); +impl_tuple_vec!((f64, f64, f64): x: x.0, x.1, x.2); diff --git a/crates/libm-test/src/within_ulps.rs b/crates/libm-test/src/within_ulps.rs new file mode 100644 index 000000000..88761852e --- /dev/null +++ b/crates/libm-test/src/within_ulps.rs @@ -0,0 +1,50 @@ +//! Floating-point comparison with ULP tolerance + +pub trait WithinUlps { + /// Returns true if two numbers are closer than `ulp_tol` to each other. + fn within_ulps(self, other: Self, ulp_tol: usize) -> bool; +} + +// Stamp the impls for floats: +macro_rules! impl_within_ulps_f { + ($f_ty:ty, $i_ty:ty) => { + impl WithinUlps for $f_ty { + #[allow(clippy::cast_possible_truncation)] + fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { + let x = self; + if x.is_nan() != y.is_nan() { + // one is nan but the other is not + return false; + } + if x.is_nan() && y.is_nan() { + return true; + } + if x.is_infinite() != y.is_infinite() { + // one is inf but the other is not + return false; + } + + let xi: $i_ty = unsafe { core::intrinsics::transmute(x) }; + let yi: $i_ty = unsafe { core::intrinsics::transmute(y) }; + if (xi < 0) != (yi < 0) { + // different sign, e.g., -0.0 != +0.0: + return false; + } + let ulps = (xi - yi).abs(); + ulps <= ulp_tol as _ + } + } + }; +} + +impl_within_ulps_f!(f32, i32); +impl_within_ulps_f!(f64, i64); + +impl WithinUlps for i32 { + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] + fn within_ulps(self, y: Self, ulp_tol: usize) -> bool { + let x = self; + let ulps = (x - y).abs(); + ulps <= ulp_tol as _ + } +} diff --git a/crates/libm-test/tests/exhaustive32.rs b/crates/libm-test/tests/exhaustive32.rs index cccffa0d4..28bb7b43a 100644 --- a/crates/libm-test/tests/exhaustive32.rs +++ b/crates/libm-test/tests/exhaustive32.rs @@ -4,50 +4,9 @@ use libm_test::assert_approx_eq; macro_rules! exhaustive32 { - // Skip those parts of the API that are not - // exposed by the system libm library: - // - // FIXME: maybe we can skip them as part of libm-analyze? - ( - id: j0f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: j1f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: y0f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: y1f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: exp10f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - // Too expensive: - ( - id: jn; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - // match only unary functions taking f32: ( id: $id:ident; + api_kind: $api_kind:ident; arg_tys: f32; arg_ids: $arg_id:ident; ret_ty: $ret_ty:ty; @@ -73,10 +32,15 @@ macro_rules! exhaustive32 { }; ( id: $id:ident; + api_kind: $api_kind:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; ret_ty: $ret_ty:ty; ) => {}; } -libm_analyze::for_each_api!(exhaustive32); +libm_analyze::for_each_api!(exhaustive32( + // jn is to expensive - the others have weird API: + /*ignored:*/ + "j0f,j1f,y0f,y1f,exp10f,jn" +)); diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index b872e3d2e..3153e1c3a 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -2,70 +2,18 @@ #![cfg(test)] #![cfg(feature = "system_libm")] -use libm_test::{assert_approx_eq, get_api_kind, Call, RandSeq, TupleVec}; +use libm_test::{assert_approx_eq, CallFn, RandSeq, TupleVec}; // Number of tests to generate for each function -const NTESTS: usize = 10_000; +const NTESTS: usize = 1_000_000; const ULP_TOL: usize = 4; macro_rules! system_libm { - // Skip those parts of the API that are not - // exposed by the system libm library: - // - // FIXME: maybe we can skip them as part of libm-analyze? - ( - id: j0f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: j1f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: jnf; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: y0f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: y1f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: ynf; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: exp10; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - ( - id: exp10f; - arg_tys: $($arg_tys:ty),*; - arg_ids: $($arg_ids:ident),*; - ret_ty: $ret_ty:ty; - ) => {}; - // Generate random tests for all others: ( id: $id:ident; + api_kind: $api_kind:ident; arg_tys: $($arg_tys:ty),*; arg_ids: $($arg_ids:ident),*; ret_ty: $ret_ty:ty; @@ -73,7 +21,6 @@ macro_rules! system_libm { #[test] #[allow(unused)] fn $id() { - use crate::Call; // Type of the system libm fn: type FnTy = unsafe extern "C" fn ($($arg_ids: $arg_tys),*) -> $ret_ty; @@ -84,13 +31,14 @@ macro_rules! system_libm { let mut rng = rand::thread_rng(); - // Depending on the type of API, different ranges of values might be - // allowed or interesting to test: - let api_kind = get_api_kind!(fn: $id); - // Generate a tuple of arguments containing random values: let mut args: ( $(Vec<$arg_tys>,)+ ) - = ( $(<$arg_tys as RandSeq>::rand_seq(&mut rng, api_kind, NTESTS),)+ ); + = ( $( + <$arg_tys as RandSeq>::rand_seq( + &mut rng, libm_test::ApiKind::$api_kind, NTESTS + ), + )+ + ); for i in 0..NTESTS { let args: ( $($arg_tys,)+ ) = args.get(i); @@ -105,4 +53,8 @@ macro_rules! system_libm { } } -libm_analyze::for_each_api!(system_libm); +libm_analyze::for_each_api!(system_libm( + // Not exposed by the system's musl + /*ignored:*/ + "j0f,j1f,jnf,y0f,y1f,ynf,exp10,exp10f" +)); From e0fe7169837e2f0f5436e7ece488f393bc29e2ca Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 16:06:41 +0200 Subject: [PATCH 42/43] Fix clippy nits --- crates/libm-analyze/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/libm-analyze/src/lib.rs b/crates/libm-analyze/src/lib.rs index 69286633b..be0aefd82 100644 --- a/crates/libm-analyze/src/lib.rs +++ b/crates/libm-analyze/src/lib.rs @@ -314,7 +314,7 @@ fn get_arg_ids(len: usize) -> Vec { ids } -/// Returns the ApiKind enum variant for this function +/// Returns the `ApiKind` enum variant for this function fn to_api_kind(id: syn::Ident) -> syn::Ident { let name = syn_to_str!(id); let first = name.chars().nth(0).unwrap(); @@ -340,19 +340,19 @@ impl syn::parse::Parse for Input { if let syn::Lit::Str(c) = ignored { let s = c.value(); let mut hash_set = HashSet::::new(); - for i in s.split(",") { + for i in s.split(',') { hash_set.insert(i.to_string()); } - Ok(Input { - macro_id: macro_id, + Ok(Self { + macro_id, ignored: Some(hash_set), }) } else { Err(lookahead.error()) } } else { - Ok(Input { - macro_id: macro_id, + Ok(Self { + macro_id, ignored: None, }) } From 6e323e91026b5e6b5d55704b0bd6522dccb2ae7a Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 5 Jul 2019 16:56:23 +0200 Subject: [PATCH 43/43] Remove nexttoward/f, signbit/f, and disable fma system tests - failure --- crates/libm-test/tests/system_libm.rs | 5 +-- crates/libm/src/math/mod.rs | 6 ---- crates/libm/src/math/nexttoward.rs | 43 -------------------------- crates/libm/src/math/nexttowardf.rs | 44 --------------------------- crates/libm/src/math/signbit.rs | 5 --- crates/libm/src/math/signbitf.rs | 5 --- 6 files changed, 3 insertions(+), 105 deletions(-) delete mode 100644 crates/libm/src/math/nexttoward.rs delete mode 100644 crates/libm/src/math/nexttowardf.rs delete mode 100644 crates/libm/src/math/signbit.rs delete mode 100644 crates/libm/src/math/signbitf.rs diff --git a/crates/libm-test/tests/system_libm.rs b/crates/libm-test/tests/system_libm.rs index 3153e1c3a..e98d577b7 100644 --- a/crates/libm-test/tests/system_libm.rs +++ b/crates/libm-test/tests/system_libm.rs @@ -54,7 +54,8 @@ macro_rules! system_libm { } libm_analyze::for_each_api!(system_libm( - // Not exposed by the system's musl + // FIXME: Some are not exposed by the system's musl, + // others are incorrect. FMA is broken. /*ignored:*/ - "j0f,j1f,jnf,y0f,y1f,ynf,exp10,exp10f" + "j0f,j1f,jnf,y0f,y1f,ynf,exp10,exp10f,fma" )); diff --git a/crates/libm/src/math/mod.rs b/crates/libm/src/math/mod.rs index 5e692d9d2..87424f8ce 100644 --- a/crates/libm/src/math/mod.rs +++ b/crates/libm/src/math/mod.rs @@ -185,8 +185,6 @@ mod modf; mod modff; mod nextafter; mod nextafterf; -mod nexttoward; -mod nexttowardf; mod pow; mod powf; mod remainder; @@ -197,8 +195,6 @@ mod round; mod roundf; mod scalbn; mod scalbnf; -mod signbit; -mod signbitf; mod sin; mod sincos; mod sincosf; @@ -303,8 +299,6 @@ pub use self::modf::modf; pub use self::modff::modff; pub use self::nextafter::nextafter; pub use self::nextafterf::nextafterf; -pub use self::nexttoward::nexttoward; -pub use self::nexttowardf::nexttowardf; pub use self::pow::pow; pub use self::powf::powf; pub use self::remainder::remainder; diff --git a/crates/libm/src/math/nexttoward.rs b/crates/libm/src/math/nexttoward.rs deleted file mode 100644 index 6d1a447c1..000000000 --- a/crates/libm/src/math/nexttoward.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::signbit::signbit; - -#[inline] -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub extern "C" fn nexttoward(x: f64, y: /*long double*/ f64) -> f64 { - let mut ux_i = x.to_bits(); - if x.is_nan() || y.is_nan() { - return x + y; - } - - if x == y { - return y; - } - if x == 0.0 { - ux_i = 1; - if signbit(y) != 0 { - ux_i |= 1_u64 << 63; - } - } else if x < y { - if signbit(x) != 0 { - ux_i -= 1; - } else { - ux_i += 1; - } - } else { - if signbit(x) != 0 { - ux_i += 1; - } else { - ux_i -= 1; - } - } - let e = ux_i >> 52 & 0x7ff; - // raise overflow if ux.f is infinite and x is finite - if e == 0x7ff { - force_eval!(x + x); - } - // raise underflow if ux.f is subnormal or zero - let ux_f = f64::from_bits(ux_i); - if e == 0 { - force_eval!(x * x + ux_f * ux_f); - } - ux_f -} diff --git a/crates/libm/src/math/nexttowardf.rs b/crates/libm/src/math/nexttowardf.rs deleted file mode 100644 index 9d9b7b797..000000000 --- a/crates/libm/src/math/nexttowardf.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::signbit::signbit; -use super::signbitf::signbitf; - -#[inline] -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub extern "C" fn nexttowardf(x: f32, y: f64) -> f32 { - let mut ux_i = x.to_bits(); - if x.is_nan() || y.is_nan() { - return (x as f64 + y) as f32; - } - - if x == y as f32 { - return y as f32; - } - if x == 0.0 { - ux_i = 1; - if signbit(y) != 0 { - ux_i |= 0x8000_0000_u32; - } - } else if x < (y as f32) { - if signbitf(x) != 0 { - ux_i -= 1; - } else { - ux_i += 1; - } - } else { - if signbitf(x) != 0 { - ux_i += 1; - } else { - ux_i -= 1; - } - } - let e = ux_i.wrapping_shr(0x7f80_0000_u32); - // raise overflow if ux.f is infinite and x is finite - if e == 0x7f80_0000_u32 { - force_eval!(x + x); - } - // raise underflow if ux.f is subnormal or zero - let ux_f = f32::from_bits(ux_i); - if e == 0 { - force_eval!(x * x + ux_f * ux_f); - } - ux_f -} diff --git a/crates/libm/src/math/signbit.rs b/crates/libm/src/math/signbit.rs deleted file mode 100644 index d6df207b2..000000000 --- a/crates/libm/src/math/signbit.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[inline] -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub(crate) extern "C" fn signbit(x: f64) -> i32 { - (x.to_bits() >> 63) as i32 -} diff --git a/crates/libm/src/math/signbitf.rs b/crates/libm/src/math/signbitf.rs deleted file mode 100644 index 0d43c6725..000000000 --- a/crates/libm/src/math/signbitf.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[inline] -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub(crate) extern "C" fn signbitf(x: f32) -> i32 { - (x.to_bits() >> 31) as i32 -}