From a3363785ea5e496e8dd925abe611169a6c11cbab Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 28 Dec 2024 09:52:19 +0000 Subject: [PATCH 1/3] Add domain and edge case tests to musl This provides an increase in test coverage on platforms that cannot test against MPFR. --- .../libm/crates/libm-test/src/lib.rs | 4 +- .../libm/crates/libm-test/src/op.rs | 2 + .../libm-test/tests/compare_built_musl.rs | 97 ++++++++++++++++++- 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/library/compiler-builtins/libm/crates/libm-test/src/lib.rs b/library/compiler-builtins/libm/crates/libm-test/src/lib.rs index a940db1d2066..251114a0dcb9 100644 --- a/library/compiler-builtins/libm/crates/libm-test/src/lib.rs +++ b/library/compiler-builtins/libm/crates/libm-test/src/lib.rs @@ -23,7 +23,9 @@ use std::time::SystemTime; pub use f8_impl::f8; pub use libm::support::{Float, Int, IntTy, MinInt}; pub use num::{FloatExt, logspace}; -pub use op::{BaseName, FloatTy, Identifier, MathOp, OpCFn, OpFTy, OpRustFn, OpRustRet, Ty}; +pub use op::{ + BaseName, FloatTy, Identifier, MathOp, OpCFn, OpCRet, OpFTy, OpRustFn, OpRustRet, Ty, +}; pub use precision::{MaybeOverride, SpecialCase, default_ulp}; use run_cfg::EXTENSIVE_MAX_ITERATIONS; pub use run_cfg::{CheckBasis, CheckCtx, EXTENSIVE_ENV, GeneratorKind, skip_extensive_test}; diff --git a/library/compiler-builtins/libm/crates/libm-test/src/op.rs b/library/compiler-builtins/libm/crates/libm-test/src/op.rs index ee61eb0b8406..8329d3424c32 100644 --- a/library/compiler-builtins/libm/crates/libm-test/src/op.rs +++ b/library/compiler-builtins/libm/crates/libm-test/src/op.rs @@ -96,6 +96,8 @@ pub type OpFTy = ::FTy; pub type OpITy = <::FTy as Float>::Int; /// Access the associated `CFn` type from an op (helper to avoid ambiguous associated types). pub type OpCFn = ::CFn; +/// Access the associated `CRet` type from an op (helper to avoid ambiguous associated types). +pub type OpCRet = ::CRet; /// Access the associated `RustFn` type from an op (helper to avoid ambiguous associated types). pub type OpRustFn = ::RustFn; /// Access the associated `RustRet` type from an op (helper to avoid ambiguous associated types). diff --git a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs index 3e11d322acd7..b91d7f9f50d9 100644 --- a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs +++ b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs @@ -9,8 +9,9 @@ // There are some targets we can't build musl for #![cfg(feature = "build-musl")] -use libm_test::gen::random; +use libm_test::domain::HasDomain; use libm_test::gen::random::RandomInput; +use libm_test::gen::{domain_logspace, edge_cases, random}; use libm_test::{CheckBasis, CheckCtx, CheckOutput, MathOp, TupleCall}; macro_rules! musl_rand_tests { @@ -53,3 +54,97 @@ libm_macros::for_each_function! { [exp10, exp10f, exp2, exp2f, rint] ], } + +/// Test against musl with generators from a domain. +macro_rules! musl_domain_tests { + ( + fn_name: $fn_name:ident, + attrs: [$($attr:meta),*], + ) => { + paste::paste! { + #[test] + $(#[$attr])* + fn [< musl_edge_case_ $fn_name >]() { + type Op = libm_test::op::$fn_name::Routine; + domain_test_runner::( + edge_cases::get_test_cases::, + musl_math_sys::$fn_name, + ); + } + + #[test] + $(#[$attr])* + fn [< musl_logspace_ $fn_name >]() { + type Op = libm_test::op::$fn_name::Routine; + domain_test_runner::( + domain_logspace::get_test_cases::, + musl_math_sys::$fn_name, + ); + } + } + }; +} + +/// Test a single routine against domaine-aware inputs. +fn domain_test_runner(gen: impl FnOnce(&CheckCtx) -> I, musl_fn: Op::CFn) +where + Op: MathOp, + Op: HasDomain, + I: Iterator, +{ + let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Musl); + let cases = gen(&ctx); + + for input in cases { + let musl_res = input.call(musl_fn); + let crate_res = input.call(Op::ROUTINE); + + crate_res.validate(musl_res, input, &ctx).unwrap(); + } +} + +libm_macros::for_each_function! { + callback: musl_domain_tests, + attributes: [], + skip: [ + // Functions with multiple inputs + atan2, + atan2f, + copysign, + copysignf, + copysignf16, + copysignf128, + fdim, + fdimf, + fma, + fmaf, + fmax, + fmaxf, + fmin, + fminf, + fmod, + fmodf, + hypot, + hypotf, + jn, + jnf, + ldexp, + ldexpf, + nextafter, + nextafterf, + pow, + powf, + remainder, + remainderf, + remquo, + remquof, + scalbn, + scalbnf, + yn, + ynf, + + // Not provided by musl + fabsf16, + fabsf128, + ], +} From 4b014bff40c0fffe97fd64f854c152c0d33b4973 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 6 Jan 2025 09:26:27 +0000 Subject: [PATCH 2/3] Add an override for failing ceil/floor tests on i586 --- .../libm/crates/libm-test/src/precision.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs index f8c3a7b8fcf3..817ea0fae644 100644 --- a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs @@ -270,6 +270,16 @@ impl MaybeOverride<(f64,)> for SpecialCase { return XFAIL; } + if (ctx.fn_ident == Identifier::Ceil || ctx.fn_ident == Identifier::Floor) + && expected.eq_repr(F::NEG_ZERO) + && actual.eq_repr(F::ZERO) + && cfg!(x86_no_sse) + { + // FIXME: the x87 implementations do not keep the distinction between -0.0 and 0.0. + // See https://github.com/rust-lang/libm/pull/404#issuecomment-2572399955 + return XFAIL; + } + maybe_check_nan_bits(actual, expected, ctx) } From 449d41a24fffd282b088c4b2967e5c3e9899189c Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 6 Jan 2025 09:46:16 +0000 Subject: [PATCH 3/3] Loosen precision on i586 based on new tests --- .../libm/crates/libm-test/src/precision.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs index 817ea0fae644..1a66a430c50a 100644 --- a/library/compiler-builtins/libm/crates/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm/crates/libm-test/src/precision.rs @@ -106,7 +106,11 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { match ctx.fn_ident { Id::Asinh => ulp = 3, Id::Asinhf => ulp = 3, + Id::Exp10 | Id::Exp10f => ulp = 1_000_000, + Id::Exp2 | Id::Exp2f => ulp = 10_000_000, + Id::Fmaf => ulp = 1, Id::Log1p | Id::Log1pf => ulp = 2, + Id::Rint => ulp = 100_000, Id::Round => ulp = 1, Id::Tan => ulp = 2, _ => (), @@ -271,15 +275,23 @@ impl MaybeOverride<(f64,)> for SpecialCase { } if (ctx.fn_ident == Identifier::Ceil || ctx.fn_ident == Identifier::Floor) + && cfg!(x86_no_sse) && expected.eq_repr(F::NEG_ZERO) && actual.eq_repr(F::ZERO) - && cfg!(x86_no_sse) { // FIXME: the x87 implementations do not keep the distinction between -0.0 and 0.0. // See https://github.com/rust-lang/libm/pull/404#issuecomment-2572399955 return XFAIL; } + if (ctx.fn_ident == Identifier::Exp10 || ctx.fn_ident == Identifier::Exp2) + && cfg!(x86_no_sse) + { + // FIXME: i586 has very imprecise results with ULP > u32::MAX for these + // operations so we can't reasonably provide a limit. + return XFAIL; + } + maybe_check_nan_bits(actual, expected, ctx) }