Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit f5d5cf8

Browse files
authored
Merge pull request #384 from tgross35/api-list
Use `rustdoc` JSON for API list, add functions that were missing
2 parents 766e21c + 780519d commit f5d5cf8

File tree

14 files changed

+326
-88
lines changed

14 files changed

+326
-88
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ jobs:
9696
run: ./ci/download-musl.sh
9797
shell: bash
9898

99+
- name: Verify API list
100+
if: matrix.os == 'ubuntu-24.04'
101+
run: python3 etc/update-api-list.py --check
102+
99103
# Non-linux tests just use our raw script
100104
- name: Run locally
101105
if: matrix.os != 'ubuntu-24.04' || contains(matrix.target, 'wasm')

ci/run-docker.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ run() {
2828
docker run \
2929
--rm \
3030
--user "$(id -u):$(id -g)" \
31+
-e CI \
3132
-e RUSTFLAGS \
3233
-e CARGO_HOME=/cargo \
3334
-e CARGO_TARGET_DIR=/target \

crates/libm-macros/src/shared.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
1111
None,
1212
&[
1313
"acosf", "acoshf", "asinf", "asinhf", "atanf", "atanhf", "cbrtf", "ceilf", "cosf",
14-
"coshf", "erff", "exp10f", "exp2f", "expf", "expm1f", "fabsf", "floorf", "j0f", "j1f",
15-
"lgammaf", "log10f", "log1pf", "log2f", "logf", "rintf", "roundf", "sinf", "sinhf",
16-
"sqrtf", "tanf", "tanhf", "tgammaf", "truncf",
14+
"coshf", "erff", "erfcf", "exp10f", "exp2f", "expf", "expm1f", "fabsf", "floorf",
15+
"j0f", "j1f", "lgammaf", "log10f", "log1pf", "log2f", "logf", "rintf", "roundf",
16+
"sinf", "sinhf", "sqrtf", "tanf", "tanhf", "tgammaf", "truncf", "y0f", "y1f",
1717
],
1818
),
1919
(
@@ -23,9 +23,9 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
2323
None,
2424
&[
2525
"acos", "acosh", "asin", "asinh", "atan", "atanh", "cbrt", "ceil", "cos", "cosh",
26-
"erf", "exp10", "exp2", "exp", "expm1", "fabs", "floor", "j0", "j1", "lgamma", "log10",
27-
"log1p", "log2", "log", "rint", "round", "sin", "sinh", "sqrt", "tan", "tanh",
28-
"tgamma", "trunc",
26+
"erf", "erfc", "exp10", "exp2", "exp", "expm1", "fabs", "floor", "j0", "j1", "lgamma",
27+
"log10", "log1p", "log2", "log", "rint", "round", "sin", "sinh", "sqrt", "tan", "tanh",
28+
"tgamma", "trunc", "y0", "y1",
2929
],
3030
),
3131
(
@@ -97,14 +97,14 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
9797
FloatTy::F32,
9898
Signature { args: &[Ty::I32, Ty::F32], returns: &[Ty::F32] },
9999
None,
100-
&["jnf"],
100+
&["jnf", "ynf"],
101101
),
102102
(
103103
// `(i32, f64) -> f64`
104104
FloatTy::F64,
105105
Signature { args: &[Ty::I32, Ty::F64], returns: &[Ty::F64] },
106106
None,
107-
&["jn"],
107+
&["jn", "yn"],
108108
),
109109
(
110110
// `(f32, i32) -> f32`

crates/libm-test/build.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,8 @@
1-
use std::fmt::Write;
2-
use std::fs;
3-
41
#[path = "../../configure.rs"]
52
mod configure;
63
use configure::Config;
74

85
fn main() {
96
let cfg = Config::from_env();
10-
11-
list_all_tests(&cfg);
12-
137
configure::emit_test_config(&cfg);
148
}
15-
16-
/// Create a list of all source files in an array. This can be used for making sure that
17-
/// all functions are tested or otherwise covered in some way.
18-
// FIXME: it would probably be better to use rustdoc JSON output to get public functions.
19-
fn list_all_tests(cfg: &Config) {
20-
let math_src = cfg.manifest_dir.join("../../src/math");
21-
22-
let mut files = fs::read_dir(math_src)
23-
.unwrap()
24-
.map(|f| f.unwrap().path())
25-
.filter(|entry| entry.is_file())
26-
.map(|f| f.file_stem().unwrap().to_str().unwrap().to_owned())
27-
.collect::<Vec<_>>();
28-
files.sort();
29-
30-
let mut s = "pub const ALL_FUNCTIONS: &[&str] = &[".to_owned();
31-
for f in files {
32-
if f == "mod" {
33-
// skip mod.rs
34-
continue;
35-
}
36-
write!(s, "\"{f}\",").unwrap();
37-
}
38-
write!(s, "];").unwrap();
39-
40-
let outfile = cfg.out_dir.join("all_files.rs");
41-
fs::write(outfile, s).unwrap();
42-
}

crates/libm-test/src/domain.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl_has_domain! {
147147
cos => TRIG;
148148
cosh => UNBOUNDED;
149149
erf => UNBOUNDED;
150+
erfc => UNBOUNDED;
150151
exp => UNBOUNDED;
151152
exp10 => UNBOUNDED;
152153
exp2 => UNBOUNDED;
@@ -173,6 +174,8 @@ impl_has_domain! {
173174
tanh => UNBOUNDED;
174175
tgamma => GAMMA;
175176
trunc => UNBOUNDED;
177+
y0 => UNBOUNDED;
178+
y1 => UNBOUNDED;
176179
}
177180

178181
/* Manual implementations, these functions don't follow `foo`->`foof` naming */

crates/libm-test/src/gen/random.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ pub fn get_test_cases<RustArgs>(ctx: &CheckCtx) -> impl Iterator<Item = RustArgs
110110
where
111111
CachedInput: GenerateInput<RustArgs>,
112112
{
113-
let inputs = if ctx.base_name == BaseName::Jn { &TEST_CASES_JN } else { &TEST_CASES };
113+
let inputs = if ctx.base_name == BaseName::Jn || ctx.base_name == BaseName::Yn {
114+
&TEST_CASES_JN
115+
} else {
116+
&TEST_CASES
117+
};
114118
inputs.get_cases()
115119
}

crates/libm-test/src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ pub use test_traits::{CheckOutput, GenerateInput, Hex, TupleCall};
2323
/// propagate.
2424
pub type TestResult<T = (), E = anyhow::Error> = Result<T, E>;
2525

26-
// List of all files present in libm's source
27-
include!(concat!(env!("OUT_DIR"), "/all_files.rs"));
28-
2926
/// True if `EMULATED` is set and nonempty. Used to determine how many iterations to run.
3027
pub const fn emulated() -> bool {
3128
match option_env!("EMULATED") {
@@ -34,3 +31,12 @@ pub const fn emulated() -> bool {
3431
Some(_) => true,
3532
}
3633
}
34+
35+
/// True if `CI` is set and nonempty.
36+
pub const fn ci() -> bool {
37+
match option_env!("CI") {
38+
Some(s) if s.is_empty() => false,
39+
None => false,
40+
Some(_) => true,
41+
}
42+
}

crates/libm-test/src/mpfloat.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ libm_macros::for_each_function! {
130130
fabsf, ceilf, copysignf, floorf, rintf, roundf, truncf,
131131
fmod, fmodf, frexp, frexpf, ilogb, ilogbf, jn, jnf, ldexp, ldexpf,
132132
lgamma_r, lgammaf_r, modf, modff, nextafter, nextafterf, pow,powf,
133-
remquo, remquof, scalbn, scalbnf, sincos, sincosf,
133+
remquo, remquof, scalbn, scalbnf, sincos, sincosf, yn, ynf,
134134
],
135135
fn_extra: match MACRO_FN_NAME {
136136
// Remap function names that are different between mpfr and libm
@@ -266,6 +266,21 @@ macro_rules! impl_op_for_ty {
266266
)
267267
}
268268
}
269+
270+
impl MpOp for crate::op::[<yn $suffix>]::Routine {
271+
type MpTy = (i32, MpFloat);
272+
273+
fn new_mp() -> Self::MpTy {
274+
(0, new_mpfloat::<Self::FTy>())
275+
}
276+
277+
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
278+
this.0 = input.0;
279+
this.1.assign(input.1);
280+
let ord = this.1.yn_round(this.0, Nearest);
281+
prep_retval::<Self::FTy>(&mut this.1, ord)
282+
}
283+
}
269284
}
270285
};
271286
}

crates/libm-test/src/precision.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 {
2626
// Overrides that apply to either basis
2727
// FMA is expected to be infinite precision.
2828
(_, Id::Fma | Id::Fmaf) => 0,
29-
(_, Id::J0 | Id::J0f | Id::J1 | Id::J1f) => {
30-
// Results seem very target-dependent
31-
if cfg!(target_arch = "x86_64") { 4000 } else { 800_000 }
32-
}
33-
(_, Id::Jn | Id::Jnf) => 1000,
29+
(_, Id::J0 | Id::J0f | Id::J1 | Id::J1f | Id::Y0 | Id::Y0f | Id::Y1 | Id::Y1f) => 800_000,
30+
(_, Id::Jn | Id::Jnf | Id::Yn | Id::Ynf) => 1000,
31+
(_, Id::Erfc | Id::Erfcf) => 4,
3432

3533
// Overrides for musl
3634
#[cfg(x86_no_sse)]
@@ -297,7 +295,7 @@ impl MaybeOverride<(i32, f32)> for SpecialCase {
297295
(Musl, _) => bessel_prec_dropoff(input, ulp, ctx),
298296

299297
// We return +0.0, MPFR returns -0.0
300-
(Mpfr, BaseName::Jn)
298+
(Mpfr, BaseName::Jn | BaseName::Yn)
301299
if input.1 == f32::NEG_INFINITY && actual == F::ZERO && expected == F::ZERO =>
302300
{
303301
XFAIL
@@ -319,7 +317,7 @@ impl MaybeOverride<(i32, f64)> for SpecialCase {
319317
(Musl, _) => bessel_prec_dropoff(input, ulp, ctx),
320318

321319
// We return +0.0, MPFR returns -0.0
322-
(Mpfr, BaseName::Jn)
320+
(Mpfr, BaseName::Jn | BaseName::Yn)
323321
if input.1 == f64::NEG_INFINITY && actual == F::ZERO && expected == F::ZERO =>
324322
{
325323
XFAIL
@@ -336,7 +334,7 @@ fn bessel_prec_dropoff<F: Float>(
336334
ulp: &mut u32,
337335
ctx: &CheckCtx,
338336
) -> Option<TestResult> {
339-
if ctx.base_name == BaseName::Jn {
337+
if ctx.base_name == BaseName::Jn || ctx.base_name == BaseName::Yn {
340338
if input.0 > 4000 {
341339
return XFAIL;
342340
} else if input.0 > 2000 {
Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
11
//! Ensure that `for_each_function!` isn't missing any symbols.
22
3-
/// Files in `src/` that do not export a testable symbol.
4-
const ALLOWED_SKIPS: &[&str] = &[
5-
// Not a generic test function
6-
"fenv",
7-
// Nonpublic functions
8-
"expo2",
9-
"k_cos",
10-
"k_cosf",
11-
"k_expo2",
12-
"k_expo2f",
13-
"k_sin",
14-
"k_sinf",
15-
"k_tan",
16-
"k_tanf",
17-
"rem_pio2",
18-
"rem_pio2_large",
19-
"rem_pio2f",
20-
];
3+
use std::collections::HashSet;
4+
use std::env;
5+
use std::path::Path;
6+
use std::process::Command;
217

228
macro_rules! callback {
239
(
2410
fn_name: $name:ident,
25-
extra: [$push_to:ident],
11+
extra: [$set:ident],
2612
) => {
27-
$push_to.push(stringify!($name));
13+
let name = stringify!($name);
14+
let new = $set.insert(name);
15+
assert!(new, "duplicate function `{name}` in `ALL_OPERATIONS`");
2816
};
2917
}
3018

3119
#[test]
3220
fn test_for_each_function_all_included() {
33-
let mut included = Vec::new();
34-
let mut missing = Vec::new();
21+
let all_functions: HashSet<_> = include_str!("../../../etc/function-list.txt")
22+
.lines()
23+
.filter(|line| !line.starts_with("#"))
24+
.collect();
25+
26+
let mut tested = HashSet::new();
3527

3628
libm_macros::for_each_function! {
3729
callback: callback,
38-
extra: [included],
30+
extra: [tested],
3931
};
4032

41-
for f in libm_test::ALL_FUNCTIONS {
42-
if !included.contains(f) && !ALLOWED_SKIPS.contains(f) {
43-
missing.push(f)
44-
}
45-
}
46-
47-
if !missing.is_empty() {
33+
let untested = all_functions.difference(&tested);
34+
if untested.clone().next().is_some() {
4835
panic!(
49-
"missing tests for the following: {missing:#?} \
36+
"missing tests for the following: {untested:#?} \
5037
\nmake sure any new functions are entered in \
51-
`ALL_FUNCTIONS` (in `libm-macros`)."
38+
`ALL_OPERATIONS` (in `libm-macros`)."
5239
);
5340
}
41+
assert_eq!(all_functions, tested);
42+
}
43+
44+
#[test]
45+
fn ensure_list_updated() {
46+
if libm_test::ci() {
47+
// Most CI tests run in Docker where we don't have Python or Rustdoc, so it's easiest
48+
// to just run the python file directly when it is available.
49+
eprintln!("skipping test; CI runs the python file directly");
50+
return;
51+
}
52+
53+
let res = Command::new("python3")
54+
.arg(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../etc/update-api-list.py"))
55+
.arg("--check")
56+
.status()
57+
.unwrap();
58+
59+
assert!(res.success(), "May need to run `./etc/update-api-list.py`");
5460
}

crates/libm-test/tests/multiprecision.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ libm_macros::for_each_function! {
4848
attributes: [
4949
// Also an assertion failure on i686: at `MPFR_ASSERTN (! mpfr_erangeflag_p ())`
5050
#[ignore = "large values are infeasible in MPFR"]
51-
[jn, jnf],
51+
[jn, jnf, yn, ynf],
5252
],
5353
skip: [
5454
// FIXME: MPFR tests needed
@@ -157,6 +157,8 @@ libm_macros::for_each_function! {
157157
remquof,
158158
scalbn,
159159
scalbnf,
160+
yn,
161+
ynf,
160162

161163
// FIXME: MPFR tests needed
162164
frexp,

crates/musl-math-sys/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,5 +282,6 @@ functions! {
282282
musl_y0f: y0f(a: f32) -> f32;
283283
musl_y1: y1(a: f64) -> f64;
284284
musl_y1f: y1f(a: f32) -> f32;
285+
musl_yn: yn(a: c_int, b: f64) -> f64;
285286
musl_ynf: ynf(a: c_int, b: f32) -> f32;
286287
}

0 commit comments

Comments
 (0)