Introduce a op module with struct representations of each routine
This contains: 1. Per-function and per-operation enums created by the proc macro 2. The `MathOp` trait which is implemented once per struct representing a function 3. Submodules for each function, each containing a `Routine` struct that implements `MathOp`
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
pub mod gen;
|
||||
#[cfg(feature = "test-multiprecision")]
|
||||
pub mod mpfloat;
|
||||
pub mod op;
|
||||
mod precision;
|
||||
mod test_traits;
|
||||
|
||||
pub use libm::support::{Float, Int};
|
||||
pub use op::{BaseName, MathOp, Name};
|
||||
pub use precision::{MaybeOverride, SpecialCase, multiprec_allowed_ulp, musl_allowed_ulp};
|
||||
pub use test_traits::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, Hex, TupleCall};
|
||||
|
||||
|
||||
111
library/compiler-builtins/libm/crates/libm-test/src/op.rs
Normal file
111
library/compiler-builtins/libm/crates/libm-test/src/op.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
//! Types representing individual functions.
|
||||
//!
|
||||
//! Each routine gets a module with its name, e.g. `mod sinf { /* ... */ }`. The module
|
||||
//! contains a unit struct `Routine` which implements `MathOp`.
|
||||
//!
|
||||
//! Basically everything could be called a "function" here, so we loosely use the following
|
||||
//! terminology:
|
||||
//!
|
||||
//! - "Function": the math operation that does not have an associated precision. E.g. `f(x) = e^x`,
|
||||
//! `f(x) = log(x)`.
|
||||
//! - "Routine": A code implementation of a math operation with a specific precision. E.g. `exp`,
|
||||
//! `expf`, `expl`, `log`, `logf`.
|
||||
//! - "Operation" / "Op": Something that relates a routine to a function or is otherwise higher
|
||||
//! level. `Op` is also used as the name for generic parameters since it is terse.
|
||||
|
||||
use crate::{CheckOutput, Float, TupleCall};
|
||||
|
||||
/// An enum representing each possible routine name.
|
||||
#[libm_macros::function_enum(BaseName)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Name {}
|
||||
|
||||
/// The name without any type specifier, e.g. `sin` and `sinf` both become `sin`.
|
||||
#[libm_macros::base_name_enum]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum BaseName {}
|
||||
|
||||
/// Attributes ascribed to a `libm` routine including signature, type information,
|
||||
/// and naming.
|
||||
pub trait MathOp {
|
||||
/// The float type used for this operation.
|
||||
type FTy: Float;
|
||||
|
||||
/// The function type representing the signature in a C library.
|
||||
type CFn: Copy;
|
||||
|
||||
/// Arguments passed to the C library function as a tuple. These may include `&mut` return
|
||||
/// values.
|
||||
type CArgs<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// The type returned by C implementations.
|
||||
type CRet;
|
||||
|
||||
/// The signature of the Rust function as a `fn(...) -> ...` type.
|
||||
type RustFn: Copy;
|
||||
|
||||
/// Arguments passed to the Rust library function as a tuple.
|
||||
///
|
||||
/// The required `TupleCall` bounds ensure this type can be passed either to the C function or
|
||||
/// to the Rust function.
|
||||
type RustArgs: Copy
|
||||
+ TupleCall<Self::RustFn, Output = Self::RustRet>
|
||||
+ TupleCall<Self::CFn, Output = Self::RustRet>;
|
||||
|
||||
/// Type returned from the Rust function.
|
||||
type RustRet: CheckOutput<Self::RustArgs>;
|
||||
|
||||
/// The name of this function, including suffix (e.g. `sin`, `sinf`).
|
||||
const NAME: Name;
|
||||
|
||||
/// The name as a string.
|
||||
const NAME_STR: &'static str = Self::NAME.as_str();
|
||||
|
||||
/// The name of the function excluding the type suffix, e.g. `sin` and `sinf` are both `sin`.
|
||||
const BASE_NAME: BaseName = Self::NAME.base_name();
|
||||
|
||||
/// The function in `libm` which can be called.
|
||||
const ROUTINE: Self::RustFn;
|
||||
}
|
||||
|
||||
macro_rules! do_thing {
|
||||
// Matcher for unary functions
|
||||
(
|
||||
fn_name: $fn_name:ident,
|
||||
FTy: $FTy:ty,
|
||||
CFn: $CFn:ty,
|
||||
CArgs: $CArgs:ty,
|
||||
CRet: $CRet:ty,
|
||||
RustFn: $RustFn:ty,
|
||||
RustArgs: $RustArgs:ty,
|
||||
RustRet: $RustRet:ty,
|
||||
) => {
|
||||
paste::paste! {
|
||||
pub mod $fn_name {
|
||||
use super::*;
|
||||
pub struct Routine;
|
||||
|
||||
impl MathOp for Routine {
|
||||
type FTy = $FTy;
|
||||
type CFn = for<'a> $CFn;
|
||||
type CArgs<'a> = $CArgs where Self: 'a;
|
||||
type CRet = $CRet;
|
||||
type RustFn = $RustFn;
|
||||
type RustArgs = $RustArgs;
|
||||
type RustRet = $RustRet;
|
||||
|
||||
const NAME: Name = Name::[< $fn_name:camel >];
|
||||
const ROUTINE: Self::RustFn = libm::$fn_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
libm_macros::for_each_function! {
|
||||
callback: do_thing,
|
||||
emit_types: all,
|
||||
}
|
||||
@@ -137,7 +137,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1, T2, T3> TupleCall<fn(T1, &mut T2, &mut T3)> for (T1,)
|
||||
impl<T1, T2, T3> TupleCall<for<'a> fn(T1, &'a mut T2, &'a mut T3)> for (T1,)
|
||||
where
|
||||
T1: fmt::Debug,
|
||||
T2: fmt::Debug + Default,
|
||||
@@ -145,7 +145,7 @@ where
|
||||
{
|
||||
type Output = (T2, T3);
|
||||
|
||||
fn call(self, f: fn(T1, &mut T2, &mut T3)) -> Self::Output {
|
||||
fn call(self, f: for<'a> fn(T1, &'a mut T2, &'a mut T3)) -> Self::Output {
|
||||
let mut t2 = T2::default();
|
||||
let mut t3 = T3::default();
|
||||
f(self.0, &mut t2, &mut t3);
|
||||
|
||||
Reference in New Issue
Block a user