libm-macros: Start tracking which functions are public

It would be nice to reuse some of the macro structure for internal
functions, like `rem_pio2`. To facilitate this, add a `public` field and
make it available in the macro's API.
This commit is contained in:
Trevor Gross 2025-04-23 06:46:31 +00:00 committed by Trevor Gross
parent b6db36061e
commit 1dd39e27f0
4 changed files with 226 additions and 175 deletions

View file

@ -12,7 +12,7 @@ use syn::visit_mut::VisitMut;
use syn::{Ident, ItemEnum};
const KNOWN_TYPES: &[&str] = &[
"FTy", "CFn", "CArgs", "CRet", "RustFn", "RustArgs", "RustRet",
"FTy", "CFn", "CArgs", "CRet", "RustFn", "RustArgs", "RustRet", "public",
];
/// Populate an enum with a variant representing function. Names are in upper camel case.
@ -80,6 +80,8 @@ pub fn base_name_enum(attributes: pm::TokenStream, tokens: pm::TokenStream) -> p
/// RustArgs: $RustArgs:ty,
/// // The Rust version's return type (e.g. `(f32, f32)`)
/// RustRet: $RustRet:ty,
/// // True if this is part of `libm`'s public API
/// public: $public:expr,
/// // Attributes for the current function, if any
/// attrs: [$($attr:meta),*],
/// // Extra tokens passed directly (if any)
@ -329,6 +331,7 @@ fn expand(input: StructuredInput, fn_list: &[&MathOpInfo]) -> syn::Result<pm2::T
let c_ret = &func.c_sig.returns;
let rust_args = &func.rust_sig.args;
let rust_ret = &func.rust_sig.returns;
let public = func.public;
let mut ty_fields = Vec::new();
for ty in &input.emit_types {
@ -340,6 +343,7 @@ fn expand(input: StructuredInput, fn_list: &[&MathOpInfo]) -> syn::Result<pm2::T
"RustFn" => quote! { RustFn: fn( #(#rust_args),* ,) -> ( #(#rust_ret),* ), },
"RustArgs" => quote! { RustArgs: ( #(#rust_args),* ,), },
"RustRet" => quote! { RustRet: ( #(#rust_ret),* ), },
"public" => quote! { public: #public, },
_ => unreachable!("checked in validation"),
};
ty_fields.push(field);

View file

@ -3,16 +3,26 @@
use std::fmt;
use std::sync::LazyLock;
const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])] = &[
(
struct NestedOp {
float_ty: FloatTy,
rust_sig: Signature,
c_sig: Option<Signature>,
fn_list: &'static [&'static str],
public: bool,
}
/// We need a flat list to work with most of the time, but define things as a more convenient
/// nested list.
const ALL_OPERATIONS_NESTED: &[NestedOp] = &[
NestedOp {
// `fn(f16) -> f16`
FloatTy::F16,
Signature {
float_ty: FloatTy::F16,
rust_sig: Signature {
args: &[Ty::F16],
returns: &[Ty::F16],
},
None,
&[
c_sig: None,
fn_list: &[
"ceilf16",
"fabsf16",
"floorf16",
@ -22,16 +32,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"sqrtf16",
"truncf16",
],
),
(
public: true,
},
NestedOp {
// `fn(f32) -> f32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32],
returns: &[Ty::F32],
},
None,
&[
c_sig: None,
fn_list: &[
"acosf",
"acoshf",
"asinf",
@ -70,16 +81,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"y0f",
"y1f",
],
),
(
public: true,
},
NestedOp {
// `(f64) -> f64`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64],
returns: &[Ty::F64],
},
None,
&[
c_sig: None,
fn_list: &[
"acos",
"acosh",
"asin",
@ -118,16 +130,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"y0",
"y1",
],
),
(
public: true,
},
NestedOp {
// `fn(f128) -> f128`
FloatTy::F128,
Signature {
float_ty: FloatTy::F128,
rust_sig: Signature {
args: &[Ty::F128],
returns: &[Ty::F128],
},
None,
&[
c_sig: None,
fn_list: &[
"ceilf128",
"fabsf128",
"floorf128",
@ -137,16 +150,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"sqrtf128",
"truncf128",
],
),
(
public: true,
},
NestedOp {
// `(f16, f16) -> f16`
FloatTy::F16,
Signature {
float_ty: FloatTy::F16,
rust_sig: Signature {
args: &[Ty::F16, Ty::F16],
returns: &[Ty::F16],
},
None,
&[
c_sig: None,
fn_list: &[
"copysignf16",
"fdimf16",
"fmaxf16",
@ -157,16 +171,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"fminimumf16",
"fmodf16",
],
),
(
public: true,
},
NestedOp {
// `(f32, f32) -> f32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32, Ty::F32],
returns: &[Ty::F32],
},
None,
&[
c_sig: None,
fn_list: &[
"atan2f",
"copysignf",
"fdimf",
@ -182,16 +197,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"powf",
"remainderf",
],
),
(
public: true,
},
NestedOp {
// `(f64, f64) -> f64`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64, Ty::F64],
returns: &[Ty::F64],
},
None,
&[
c_sig: None,
fn_list: &[
"atan2",
"copysign",
"fdim",
@ -207,16 +223,17 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"pow",
"remainder",
],
),
(
public: true,
},
NestedOp {
// `(f128, f128) -> f128`
FloatTy::F128,
Signature {
float_ty: FloatTy::F128,
rust_sig: Signature {
args: &[Ty::F128, Ty::F128],
returns: &[Ty::F128],
},
None,
&[
c_sig: None,
fn_list: &[
"copysignf128",
"fdimf128",
"fmaxf128",
@ -227,221 +244,241 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"fminimumf128",
"fmodf128",
],
),
(
public: true,
},
NestedOp {
// `(f32, f32, f32) -> f32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32, Ty::F32, Ty::F32],
returns: &[Ty::F32],
},
None,
&["fmaf"],
),
(
c_sig: None,
fn_list: &["fmaf"],
public: true,
},
NestedOp {
// `(f64, f64, f64) -> f64`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64, Ty::F64, Ty::F64],
returns: &[Ty::F64],
},
None,
&["fma"],
),
(
c_sig: None,
fn_list: &["fma"],
public: true,
},
NestedOp {
// `(f128, f128, f128) -> f128`
FloatTy::F128,
Signature {
float_ty: FloatTy::F128,
rust_sig: Signature {
args: &[Ty::F128, Ty::F128, Ty::F128],
returns: &[Ty::F128],
},
None,
&["fmaf128"],
),
(
c_sig: None,
fn_list: &["fmaf128"],
public: true,
},
NestedOp {
// `(f32) -> i32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32],
returns: &[Ty::I32],
},
None,
&["ilogbf"],
),
(
c_sig: None,
fn_list: &["ilogbf"],
public: true,
},
NestedOp {
// `(f64) -> i32`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64],
returns: &[Ty::I32],
},
None,
&["ilogb"],
),
(
c_sig: None,
fn_list: &["ilogb"],
public: true,
},
NestedOp {
// `(i32, f32) -> f32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::I32, Ty::F32],
returns: &[Ty::F32],
},
None,
&["jnf", "ynf"],
),
(
c_sig: None,
fn_list: &["jnf", "ynf"],
public: true,
},
NestedOp {
// `(i32, f64) -> f64`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::I32, Ty::F64],
returns: &[Ty::F64],
},
None,
&["jn", "yn"],
),
(
c_sig: None,
fn_list: &["jn", "yn"],
public: true,
},
NestedOp {
// `(f16, i32) -> f16`
FloatTy::F16,
Signature {
float_ty: FloatTy::F16,
rust_sig: Signature {
args: &[Ty::F16, Ty::I32],
returns: &[Ty::F16],
},
None,
&["ldexpf16", "scalbnf16"],
),
(
c_sig: None,
fn_list: &["ldexpf16", "scalbnf16"],
public: true,
},
NestedOp {
// `(f32, i32) -> f32`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32, Ty::I32],
returns: &[Ty::F32],
},
None,
&["ldexpf", "scalbnf"],
),
(
c_sig: None,
fn_list: &["ldexpf", "scalbnf"],
public: true,
},
NestedOp {
// `(f64, i64) -> f64`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64, Ty::I32],
returns: &[Ty::F64],
},
None,
&["ldexp", "scalbn"],
),
(
c_sig: None,
fn_list: &["ldexp", "scalbn"],
public: true,
},
NestedOp {
// `(f128, i32) -> f128`
FloatTy::F128,
Signature {
float_ty: FloatTy::F128,
rust_sig: Signature {
args: &[Ty::F128, Ty::I32],
returns: &[Ty::F128],
},
None,
&["ldexpf128", "scalbnf128"],
),
(
c_sig: None,
fn_list: &["ldexpf128", "scalbnf128"],
public: true,
},
NestedOp {
// `(f32, &mut f32) -> f32` as `(f32) -> (f32, f32)`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32],
returns: &[Ty::F32, Ty::F32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F32, Ty::MutF32],
returns: &[Ty::F32],
}),
&["modff"],
),
(
fn_list: &["modff"],
public: true,
},
NestedOp {
// `(f64, &mut f64) -> f64` as `(f64) -> (f64, f64)`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64],
returns: &[Ty::F64, Ty::F64],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F64, Ty::MutF64],
returns: &[Ty::F64],
}),
&["modf"],
),
(
fn_list: &["modf"],
public: true,
},
NestedOp {
// `(f32, &mut c_int) -> f32` as `(f32) -> (f32, i32)`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32],
returns: &[Ty::F32, Ty::I32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F32, Ty::MutCInt],
returns: &[Ty::F32],
}),
&["frexpf", "lgammaf_r"],
),
(
fn_list: &["frexpf", "lgammaf_r"],
public: true,
},
NestedOp {
// `(f64, &mut c_int) -> f64` as `(f64) -> (f64, i32)`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64],
returns: &[Ty::F64, Ty::I32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F64, Ty::MutCInt],
returns: &[Ty::F64],
}),
&["frexp", "lgamma_r"],
),
(
fn_list: &["frexp", "lgamma_r"],
public: true,
},
NestedOp {
// `(f32, f32, &mut c_int) -> f32` as `(f32, f32) -> (f32, i32)`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32, Ty::F32],
returns: &[Ty::F32, Ty::I32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F32, Ty::F32, Ty::MutCInt],
returns: &[Ty::F32],
}),
&["remquof"],
),
(
fn_list: &["remquof"],
public: true,
},
NestedOp {
// `(f64, f64, &mut c_int) -> f64` as `(f64, f64) -> (f64, i32)`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64, Ty::F64],
returns: &[Ty::F64, Ty::I32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F64, Ty::F64, Ty::MutCInt],
returns: &[Ty::F64],
}),
&["remquo"],
),
(
fn_list: &["remquo"],
public: true,
},
NestedOp {
// `(f32, &mut f32, &mut f32)` as `(f32) -> (f32, f32)`
FloatTy::F32,
Signature {
float_ty: FloatTy::F32,
rust_sig: Signature {
args: &[Ty::F32],
returns: &[Ty::F32, Ty::F32],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F32, Ty::MutF32, Ty::MutF32],
returns: &[],
}),
&["sincosf"],
),
(
fn_list: &["sincosf"],
public: true,
},
NestedOp {
// `(f64, &mut f64, &mut f64)` as `(f64) -> (f64, f64)`
FloatTy::F64,
Signature {
float_ty: FloatTy::F64,
rust_sig: Signature {
args: &[Ty::F64],
returns: &[Ty::F64, Ty::F64],
},
Some(Signature {
c_sig: Some(Signature {
args: &[Ty::F64, Ty::MutF64, Ty::MutF64],
returns: &[],
}),
&["sincos"],
),
fn_list: &["sincos"],
public: true,
},
];
/// A type used in a function signature.
@ -520,27 +557,31 @@ pub struct MathOpInfo {
pub c_sig: Signature,
/// Function signature for Rust implementations
pub rust_sig: Signature,
/// True if part of libm's public API
pub public: bool,
}
/// A flat representation of `ALL_FUNCTIONS`.
pub static ALL_OPERATIONS: LazyLock<Vec<MathOpInfo>> = LazyLock::new(|| {
let mut ret = Vec::new();
for (base_fty, rust_sig, c_sig, names) in ALL_OPERATIONS_NESTED {
for name in *names {
for op in ALL_OPERATIONS_NESTED {
let fn_names = op.fn_list;
for name in fn_names {
let api = MathOpInfo {
name,
float_ty: *base_fty,
rust_sig: rust_sig.clone(),
c_sig: c_sig.clone().unwrap_or_else(|| rust_sig.clone()),
float_ty: op.float_ty,
rust_sig: op.rust_sig.clone(),
c_sig: op.c_sig.clone().unwrap_or_else(|| op.rust_sig.clone()),
public: op.public,
};
ret.push(api);
}
if !names.is_sorted() {
let mut sorted = (*names).to_owned();
if !fn_names.is_sorted() {
let mut sorted = (*fn_names).to_owned();
sorted.sort_unstable();
panic!("names list is not sorted: {names:?}\nExpected: {sorted:?}");
panic!("names list is not sorted: {fn_names:?}\nExpected: {sorted:?}");
}
}

View file

@ -13,6 +13,7 @@ macro_rules! basic {
RustFn: $RustFn:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
public: $public:expr,
attrs: [$($attr:meta),*],
extra: [$($extra_tt:tt)*],
fn_extra: $fn_extra:expr,
@ -25,6 +26,7 @@ macro_rules! basic {
type RustFnTy = $RustFn;
type RustArgsTy = $RustArgs;
type RustRetTy = $RustRet;
const PUBLIC: bool = $public;
const A: &[&str] = &[$($extra_tt)*];
fn foo(a: f32) -> f32 {
$fn_extra(a)

View file

@ -90,6 +90,9 @@ pub trait MathOp {
/// The function in `libm` which can be called.
const ROUTINE: Self::RustFn;
/// Whether or not the function is part of libm public API.
const PUBLIC: bool;
}
/// Access the associated `FTy` type from an op (helper to avoid ambiguous associated types).
@ -107,7 +110,7 @@ pub type OpRustArgs<Op> = <Op as MathOp>::RustArgs;
/// Access the associated `RustRet` type from an op (helper to avoid ambiguous associated types).
pub type OpRustRet<Op> = <Op as MathOp>::RustRet;
macro_rules! do_thing {
macro_rules! create_op_modules {
// Matcher for unary functions
(
fn_name: $fn_name:ident,
@ -118,8 +121,8 @@ macro_rules! do_thing {
RustFn: $RustFn:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
public: $public:expr,
attrs: [$($attr:meta),*],
) => {
paste::paste! {
$(#[$attr])*
@ -138,6 +141,7 @@ macro_rules! do_thing {
const IDENTIFIER: Identifier = Identifier::[< $fn_name:camel >];
const ROUTINE: Self::RustFn = libm::$fn_name;
const PUBLIC: bool = $public;
}
}
@ -146,6 +150,6 @@ macro_rules! do_thing {
}
libm_macros::for_each_function! {
callback: do_thing,
callback: create_op_modules,
emit_types: all,
}