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

Commit 412595b

Browse files
committed
Only run system tests on x86_64-unknown-linux-musl for now
1 parent 38b1828 commit 412595b

File tree

4 files changed

+154
-31
lines changed

4 files changed

+154
-31
lines changed

ci/run.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
#!/usr/bin/env sh
22

3+
if [ -z "$1" ]; then
4+
echo "This script takes the $TARGET triple as its argument"
5+
exit 1
6+
fi
7+
38
set -ex
9+
410
TARGET=$1
511

6-
CMD="cargo test --all --no-default-features --target $TARGET"
12+
CMD="cargo test \
13+
--manifest-path=crates/libm-test/Cargo.toml --all \
14+
--no-default-features \
15+
--target $TARGET "
716

817
$CMD
918
$CMD --release
1019

1120
$CMD --features 'stable'
1221
$CMD --release --features 'stable'
1322

14-
$CMD --features 'stable checked musl-reference-tests'
15-
$CMD --release --features 'stable checked musl-reference-tests'
23+
if [ "$TARGET" = "x86_64-unknown-linux-gnu" ]; then
24+
export TARGET=x86_64-unknown-linux-musl
25+
26+
$CMD --features 'stable checked system_libm'
27+
$CMD --release --features 'stable checked system_libm'
28+
fi
29+

crates/libm-analyze/src/lib.rs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ use self::proc_macro::TokenStream;
55
use quote::quote;
66
use syn::parse_macro_input;
77

8+
/// `input` contains a single identifier, corresponding to a user-defined macro.
9+
/// This identifier is expanded for each libm public API.
10+
///
11+
/// See tests/analyze or below for the API.
812
#[proc_macro]
913
pub fn for_each_api(input: TokenStream) -> TokenStream {
1014
let files = get_libm_files();
@@ -30,14 +34,16 @@ pub fn for_each_api(input: TokenStream) -> TokenStream {
3034
}
3135

3236
/// Traverses the libm crate directory, parsing all .rs files
33-
fn get_libm_files() -> Vec<(syn::File, String)> {
37+
fn get_libm_files() -> Vec<syn::File> {
38+
// Find the directory of the libm crate:
3439
let root_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
3540
let libm_dir = root_dir
3641
.parent()
3742
.expect("couldn't access crates/ dir")
3843
.join("libm");
3944
let libm_src_dir = libm_dir.join("src");
4045

46+
// Traverse all Rust files, parsing them as `syn::File`
4147
let mut files = Vec::new();
4248
for entry in walkdir::WalkDir::new(libm_src_dir)
4349
.into_iter()
@@ -51,23 +57,25 @@ fn get_libm_files() -> Vec<(syn::File, String)> {
5157
.expect("can't format file path")
5258
.ends_with(".rs")
5359
{
60+
// If the path is a directory or not a ".rs" file => skip it.
5461
continue;
5562
}
5663

64+
// Read the file into a string, and parse it into an AST using syn.
5765
let mut file_string = String::new();
5866
std::fs::File::open(&file_path)
5967
.unwrap_or_else(|_| panic!("can't open file at path: {}", file_path.display()))
6068
.read_to_string(&mut file_string)
6169
.expect("failed to read file to string");
6270
let file = syn::parse_file(&file_string).expect("failed to parse");
63-
files.push((file, file_path.to_str().unwrap().to_string()));
71+
files.push(file);
6472
}
6573
files
6674
}
6775

76+
/// Function signature that will be expanded for the user macro.
6877
struct FnSig {
6978
ident: syn::Ident,
70-
unsafety: bool,
7179
c_abi: bool,
7280
ret_ty: Option<syn::Type>,
7381
arg_tys: Vec<syn::Type>,
@@ -89,11 +97,13 @@ macro_rules! syn_to_str {
8997
}};
9098
}
9199

92-
/// Extracts all public functions from the libm files.
93-
fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
100+
/// Extracts all public functions from the libm files while
101+
/// doing some sanity checks on the function signatures.
102+
fn get_functions(files: Vec<syn::File>) -> Vec<FnSig> {
94103
let mut error = false;
95104
let mut functions = Vec::new();
96-
for item in files.iter().flat_map(|f| f.0.items.iter()) {
105+
// Traverse all files matching function items
106+
for item in files.iter().flat_map(|f| f.items.iter()) {
97107
let mut e = false;
98108
match item {
99109
syn::Item::Fn(syn::ItemFn {
@@ -107,9 +117,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
107117
decl,
108118
block: _,
109119
}) => {
120+
// Build a function signature while doing some sanity checks
110121
let mut fn_sig = FnSig {
111122
ident: ident.clone(),
112-
unsafety: true,
113123
c_abi: false,
114124
arg_tys: Vec::new(),
115125
ret_ty: None,
@@ -136,14 +146,31 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
136146
fn_sig.c_abi = true;
137147
}
138148
}
149+
// If the function signature isn't extern "C", we aren't ABI compatible
150+
// with libm.
151+
if !fn_sig.c_abi {
152+
// FIXME: we should error here, but right that would break everything,
153+
// so we disable erroring.
154+
let e2 = e;
155+
err!("not `extern \"C\"`");
156+
e = e2;
157+
}
158+
// Right now no functions are const fn - they could be, but that
159+
// change should be explicit - so error if somebody tries.
139160
if let Some(_) = constness {
140161
err!("is const");
141162
}
163+
// No functions should be async fn
142164
if let Some(_) = asyncness {
143165
err!("is async");
144166
}
145-
if &None == unsafety {
146-
fn_sig.unsafety = false;
167+
// FIXME: Math functions shouldn't be unsafe. Some functions
168+
// that should take pointers use repr(Rust) tuples. When we fix
169+
// those, they should use references are not pointers.
170+
if let Some(_) = unsafety {
171+
let e2 = e;
172+
err!("is unsafe");
173+
e = e2;
147174
}
148175
let syn::FnDecl {
149176
fn_token: _,
@@ -154,6 +181,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
154181
output,
155182
} = (**decl).clone();
156183

184+
// Forbid generic parameters, lifetimes, and consts in public APIs:
157185
if variadic.is_some() {
158186
err!(format!(
159187
"contains variadic arguments \"{}\"",
@@ -178,7 +206,13 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
178206
syn_to_str!(generics.clone())
179207
));
180208
}
209+
// FIXME: we can do better here, but right now, we should
210+
// error if inline and no_panic are not used, which is the
211+
// case if the public API has no attributes.
212+
//
213+
// We might also want to check other attributes as well.
181214
if attrs.is_empty() {
215+
let e2 = e;
182216
err!(format!(
183217
"missing `#[inline]` and `#[no_panic]` attributes {}",
184218
attrs
@@ -187,13 +221,9 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
187221
.collect::<Vec<_>>()
188222
.join(",")
189223
));
190-
} // TODO: might want to check other attributes as well
191-
if !fn_sig.c_abi {
192-
// FIXME: do not disable test if this fails - otherwise no test passes
193-
let e2 = e;
194-
err!("not `extern \"C\"`");
195224
e = e2;
196225
}
226+
// Validate and parse output parameters and function arguments:
197227
match output {
198228
syn::ReturnType::Default => (),
199229
syn::ReturnType::Type(_, ref b) if valid_ty(&b) => {
@@ -212,6 +242,7 @@ fn get_functions(files: Vec<(syn::File, String)>) -> Vec<FnSig> {
212242
)),
213243
}
214244
}
245+
// If there was an error, we skip the function:
215246
if !e {
216247
functions.push(fn_sig);
217248
} else {
@@ -254,7 +285,8 @@ fn valid_ty(t: &syn::Type) -> bool {
254285
.ident
255286
.to_string();
256287
match s.as_str() {
257-
"i8" | "i16" | "i32" | "i64" | "isize" | "u8" | "u16" | "u32" | "u64" | "usize"
288+
| "i8" | "i16" | "i32" | "i64" | "isize"
289+
| "u8" | "u16" | "u32" | "u64" | "usize"
258290
| "f32" | "f64" => true,
259291
_ => false,
260292
}
@@ -263,6 +295,7 @@ fn valid_ty(t: &syn::Type) -> bool {
263295
}
264296
}
265297

298+
/// Returns a vector containing `len` identifiers.
266299
fn get_arg_ids(len: usize) -> Vec<syn::Ident> {
267300
let mut ids = Vec::new();
268301
for i in 0..len {

crates/libm-test/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ authors = ["Gonzalo Brito Gadeschi <[email protected]>"]
55
edition = "2018"
66

77
[dev-dependencies]
8-
libm = { path = "../libm" }
9-
libm-analyze = { path = "../libm-analyze", no-default-features = true }
8+
libm = { path = "../libm", default-features = false }
9+
libm-analyze = { path = "../libm-analyze", default-features = true }
1010
rand = "0.7"
1111

1212
[features]
1313
default = []
1414
checked = ["libm/checked"]
15+
stable = ["libm/stable"]
16+
system_libm = []

crates/libm-test/tests/system_libm.rs

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,65 @@
11
//! Compare the results of the `libm` implementation against the system's libm.
22
#![cfg(test)]
3-
3+
#![cfg(feature = "system_libm")]
44
// Number of tests to generate for each function
55
const NTESTS: usize = 500;
66

77
// FIXME: should be 1
8-
const ULP_TOL: usize = 3;
8+
const ULP_TOL: usize = 4;
99

1010
macro_rules! system_libm {
11+
// Skip those parts of the API that are not
12+
// exposed by the system libm library:
13+
(
14+
id: j0f;
15+
arg_tys: $($arg_tys:ty),*;
16+
arg_ids: $($arg_ids:ident),*;
17+
ret: $ret_ty:ty;
18+
) => {};
19+
(
20+
id: j1f;
21+
arg_tys: $($arg_tys:ty),*;
22+
arg_ids: $($arg_ids:ident),*;
23+
ret: $ret_ty:ty;
24+
) => {};
25+
(
26+
id: jnf;
27+
arg_tys: $($arg_tys:ty),*;
28+
arg_ids: $($arg_ids:ident),*;
29+
ret: $ret_ty:ty;
30+
) => {};
31+
(
32+
id: y0f;
33+
arg_tys: $($arg_tys:ty),*;
34+
arg_ids: $($arg_ids:ident),*;
35+
ret: $ret_ty:ty;
36+
) => {};
37+
(
38+
id: y1f;
39+
arg_tys: $($arg_tys:ty),*;
40+
arg_ids: $($arg_ids:ident),*;
41+
ret: $ret_ty:ty;
42+
) => {};
43+
(
44+
id: ynf;
45+
arg_tys: $($arg_tys:ty),*;
46+
arg_ids: $($arg_ids:ident),*;
47+
ret: $ret_ty:ty;
48+
) => {};
49+
(
50+
id: exp10;
51+
arg_tys: $($arg_tys:ty),*;
52+
arg_ids: $($arg_ids:ident),*;
53+
ret: $ret_ty:ty;
54+
) => {};
55+
(
56+
id: exp10f;
57+
arg_tys: $($arg_tys:ty),*;
58+
arg_ids: $($arg_ids:ident),*;
59+
ret: $ret_ty:ty;
60+
) => {};
61+
62+
// Generate random tests for all others:
1163
(
1264
id: $id:ident;
1365
arg_tys: $($arg_tys:ty),*;
@@ -20,17 +72,34 @@ macro_rules! system_libm {
2072
use crate::Call;
2173
let mut rng = rand::thread_rng();
2274
for _ in 0..NTESTS {
23-
let args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ );
24-
extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty {
75+
let mut args: ( $($arg_tys),+ ) = ( $(<$arg_tys as Rand>::gen(&mut rng)),+ );
76+
77+
match stringify!($id) {
78+
"j1" | "jn" => {
79+
// First argument to this function appears to be a number of
80+
// iterations, so passing in massive random numbers causes it to
81+
// take forever to execute, so make sure we're not running random
82+
// math code until the heat death of the universe.
83+
let p = &mut args as *mut _ as *mut i32;
84+
unsafe { p.write(p.read() & 0xffff) }
85+
},
86+
_ => (),
87+
}
88+
89+
unsafe extern "C" fn libm_fn($($arg_ids: $arg_tys),*) -> $ret_ty {
2590
libm::$id($($arg_ids),*)
2691
}
27-
let result = args.call(libm_fn);
92+
let result = <($($arg_tys),*) as Call<
93+
unsafe extern "C" fn($($arg_tys),*) -> $ret_ty
94+
>>::call(args, libm_fn);
2895
extern "C" {
2996
fn $id($($arg_ids: $arg_tys),*) -> $ret_ty;
3097
}
31-
let expected = args.call($id);
98+
let expected = <($($arg_tys),*) as Call<
99+
unsafe extern "C" fn($($arg_tys),*) -> $ret_ty
100+
>>::call(args, $id);
32101
if !result.eq(expected) {
33-
eprintln!("result = {} != {} (expected)", result, expected);
102+
eprintln!("result = {:?} != {:?} (expected)", result, expected);
34103
panic!();
35104
}
36105
}
@@ -58,14 +127,19 @@ macro_rules! impl_call {
58127
}
59128

60129
impl_call!((f32) -> f32: x: x);
61-
impl_call!((f32,f32) -> f32: x: x.0, x.1);
62-
impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2);
63130
impl_call!((f64) -> f64: x: x);
64-
impl_call!((f64,f64) -> f64: x: x.0, x.1);
65-
impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2);
131+
impl_call!((f64) -> i32: x: x);
132+
impl_call!((f32) -> i32: x: x);
66133

134+
impl_call!((f32,f32) -> f32: x: x.0, x.1);
135+
impl_call!((f64,f64) -> f64: x: x.0, x.1);
67136
impl_call!((f64, i32) -> f64: x: x.0, x.1);
68137
impl_call!((f32, i32) -> f32: x: x.0, x.1);
138+
impl_call!((i32, f64) -> f64: x: x.0, x.1);
139+
impl_call!((i32, f32) -> f32: x: x.0, x.1);
140+
141+
impl_call!((f32,f32,f32) -> f32: x: x.0, x.1, x.2);
142+
impl_call!((f64,f64,f64) -> f64: x: x.0, x.1, x.2);
69143

70144
trait Rand {
71145
fn gen(rng: &mut rand::rngs::ThreadRng) -> Self;

0 commit comments

Comments
 (0)