init
| -rw-r--r-- | Cargo.lock | 56 | ||||
| -rw-r--r-- | Cargo.toml | 18 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | README.md | 56 | ||||
| -rw-r--r-- | src/algebraic.rs | 63 | ||||
| -rw-r--r-- | src/fast.rs | 88 | ||||
| -rw-r--r-- | src/fast.safety.md | 4 | ||||
| -rw-r--r-- | src/lib.rs | 29 |
8 files changed, 335 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9fb0e37 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "lower" +version = "0.1.3" +dependencies = [ + "lower-macros", +] + +[[package]] +name = "lower-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff64bae8cfa89c8871e4b07d27629aa000978b341b98ba5fc1a0a55e2a79a53" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3e36934 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "lower" +version = "0.1.3" +authors = ["bend-n <[email protected]>"] +description = "desugar math where the compiler wont" +edition = "2021" +repository = "https://github.com/bend-n/lower-plus.git" +license = "MIT" + +[dependencies] +lower-macros = "0.1.0" + +[features] +default = ["modules"] +modules = [] + +[package.metadata.docs.rs] +all-features = true @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 bendn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a1389f --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# lower + +lowers expressions to their "desugared" form. + +e.g + +`a * b + c` => `(a.mul(b)).add(c)` + +note that it is _extremely pervasive_, so + +```rust +lower::math! { fn main() -> u8 { + const X: u8 = 31 * 2; + return 2 * X + 2; +} } +``` + +expands to + +```rust +fn main() -> u8 { + const X: u8 = 31.mul(2); + return (2.mul(X)).add(2); +} +``` + +it should work for most expressions. + +also implements some modules that let it work with some core intrinsics (`f*_fast`, `f*_algebraic`). (nightly only!) + +## why + +rust added an amazing feature called algebraic math (thanks orlp), allowing us to use `f*_algebraic`. however, dont you hate it when your + +```rs +fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] { + x.map(|[a, b, c]| a * b + c) // not optimized! cant use `(v)fmadd` +} +``` + +turns into + +```rs +fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] { + x.map(|[a, b, c]| core::intrinsics::fadd_algebraic(core::intrinsics::fmul_algebraic(a, b), c)) // readability in shambles +} +``` + +this crate allows you to + +```rs +fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] { + // wow! such readability! ultimate simd! + lower::algebraic! { x.map(|[a, b, c]| a * b + c) } +} +``` diff --git a/src/algebraic.rs b/src/algebraic.rs new file mode 100644 index 0000000..a7d6082 --- /dev/null +++ b/src/algebraic.rs @@ -0,0 +1,63 @@ +//! Provides the algebraic math trait and macro. +/// Trait for Algebraic-ly math'd floats. Try to use [`algebraic::math`](`math`). +pub trait Float: Copy { + fn add(self, other: Self) -> Self; + fn sub(self, other: Self) -> Self; + fn mul(self, other: Self) -> Self; + fn div(self, other: Self) -> Self; + fn rem(self, other: Self) -> Self; + + fn eq(self, other: Self) -> bool; + fn lt(self, other: Self) -> bool; + fn le(self, other: Self) -> bool; + fn ne(self, other: Self) -> bool; + fn ge(self, other: Self) -> bool; + fn gt(self, other: Self) -> bool; + + fn deref(&self) -> Self { + *self + } + fn neg(self) -> Self; +} +use std::intrinsics::*; +macro_rules! imp { + ($this:ty) => { +#[rustfmt::skip] +impl Float for $this { + fn add(self, other: Self) -> Self { fadd_algebraic(self, other) } + fn sub(self, other: Self) -> Self { fsub_algebraic(self, other) } + fn mul(self, other: Self) -> Self { fmul_algebraic(self, other) } + fn div(self, other: Self) -> Self { fdiv_algebraic(self, other) } + fn rem(self, other: Self) -> Self { frem_algebraic(self, other) } + + fn eq(self, other: Self) -> bool { self == other } + fn lt(self, other: Self) -> bool { self < other } + fn le(self, other: Self) -> bool { self <= other } + fn ne(self, other: Self) -> bool { self != other } + fn ge(self, other: Self) -> bool { self >= other } + fn gt(self, other: Self) -> bool { self > other } + + fn deref(&self) -> Self { *self } + fn neg(self) -> Self { -self } +} + }; +} +imp!(f32); +imp!(f64); + +/// Changes all the math to algebraic float math. See [`fadd_algebraic`]. +/// +/// This allows automatic vectorization of float math easier for the compiler, among other things. +/// If you intend to create a function in this, first import [`lower::algebraic::Float`](crate::algebraic::Float), use [`lower::math`](crate::math). +/// +/// This would lower `a + b + c + d` to `alge_add(alge_add(alge_add(a, b), c), d)` (which could be organized by the compiler into `(a + b) + (c + d)` if it chooses) +#[macro_export] +#[doc(hidden)] +macro_rules! algebraic { + ($($x:tt)+) => {{ + use $crate::algebraic::Float as _; + lower_macros::math! { $($x)+ } + }} +} +#[doc(inline)] +pub use algebraic as math; diff --git a/src/fast.rs b/src/fast.rs new file mode 100644 index 0000000..e375172 --- /dev/null +++ b/src/fast.rs @@ -0,0 +1,88 @@ +//! Provides the fast math trait and macro. See terms and conditions[^1]. +//! +//! [^1]: <https://simonbyrne.github.io/notes/fastmath> +#[allow(unused_imports)] +use std::f32::{INFINITY as INF, NAN}; + +macro_rules! s { + ($($x:tt)+) => { + #[doc = include_str!("fast.safety.md")] + $($x)+; + }; +} + +#[rustfmt::skip] +/// Trait for fast math floats. Try to use [`fast::math`](crate::fast::math). +/// These functions all assume that your float is NOT [`INF`] | [`NAN`]. +/// If so undefined behaviour is incurred. +pub unsafe trait Float: Copy { + s!(unsafe fn add(self, other: Self) -> Self); + s!(unsafe fn sub(self, other: Self) -> Self); + s!(unsafe fn mul(self, other: Self) -> Self); + s!(unsafe fn div(self, other: Self) -> Self); + s!(unsafe fn rem(self, other: Self) -> Self); + + s!(unsafe fn eq(self, other: Self) -> bool); + s!(unsafe fn lt(self, other: Self) -> bool); + s!(unsafe fn le(self, other: Self) -> bool); + s!(unsafe fn ne(self, other: Self) -> bool); + s!(unsafe fn ge(self, other: Self) -> bool); + s!(unsafe fn gt(self, other: Self) -> bool); + + fn deref(&self) -> Self { + *self + } + fn neg(self) -> Self; + + #[doc(hidden)] + unsafe fn finite(self, other: Self) -> Self; +} + +use std::intrinsics::*; +macro_rules! imp { + ($this:ty) => { +#[rustfmt::skip] +unsafe impl Float for $this { + #[inline(always)] + unsafe fn finite(self, other: Self) -> Self { + core::hint::assert_unchecked(self.is_finite()); + core::hint::assert_unchecked(other.is_finite()); + self + } + unsafe fn add(self, other: Self) -> Self { fadd_fast(self.finite(other), other) } + unsafe fn sub(self, other: Self) -> Self { fsub_fast(self.finite(other), other) } + unsafe fn mul(self, other: Self) -> Self { fmul_fast(self.finite(other), other) } + unsafe fn div(self, other: Self) -> Self { fdiv_fast(self.finite(other), other) } + unsafe fn rem(self, other: Self) -> Self { frem_fast(self.finite(other), other) } + + unsafe fn eq(self, other: Self) -> bool { (self.finite(other) + 0.0).to_bits() == (other + 0.0).to_bits() } + unsafe fn lt(self, other: Self) -> bool { self.finite(other) < other } + unsafe fn le(self, other: Self) -> bool { self.finite(other) <= other } + unsafe fn ne(self, other: Self) -> bool { self.finite(other) != other } + unsafe fn ge(self, other: Self) -> bool { self.finite(other) >= other } + unsafe fn gt(self, other: Self) -> bool { self.finite(other) > other } + + fn deref(&self) -> Self { *self } + fn neg(self) -> Self { -self } +} + }; +} +imp!(f32); +imp!(f64); + +/// Changes all the math to fast float math. See [`fadd_fast`]. +/// +/// This allows automatic vectorization of float math for the compiler, among other things. +/// If you intend to create a function in this, first import [`lower::fast::Float`](crate::fast::Float), use [`lower::math`](crate::math). +/// +/// This would lower `a * b + c` to `fadd(fmul(a, b), c)`. +#[macro_export] +#[doc(hidden)] +macro_rules! fast { + ($($x:tt)+) => {{ + use $crate::fast::Float as _; + lower_macros::math! { $($x)+ } + }} +} +#[doc(inline)] +pub use fast as math; diff --git a/src/fast.safety.md b/src/fast.safety.md new file mode 100644 index 0000000..115a9e2 --- /dev/null +++ b/src/fast.safety.md @@ -0,0 +1,4 @@ +# Safety + +MUSNT call this function with [`NAN`] | [`INF`]. +This function WILL assume the inputs are finite. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..73e8dce --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,29 @@ +//! a lil macro crate. +//! +//! provides a handy macro for converting `a + b` to `a.add(b)` for when you cant easily overload the `Add` trait, +//! and some traits that provide [`f*_algebraic`](algebraic::math) and [`f*_fast`](fast::math) implementations. (requires nightly) +#![allow(internal_features)] +#![cfg_attr( + feature = "modules", + feature(doc_auto_cfg, core_intrinsics, hint_assert_unchecked) +)] + +#[cfg(feature = "modules")] +pub mod algebraic; +#[cfg(feature = "modules")] +pub mod fast; + +/// Lower math to method calls. Only useful if you define the functions. +/// ``` +/// # use std::ops::*; +/// let [a, b, c] = [5i32, 6, 7]; +/// assert_eq!(lower::math! { a * *&b + -c }, a * *&b + -c); +/// // expands to +/// // a.mul((&b).deref()).add(c.neg()) +/// ``` +#[macro_export] +macro_rules! math { + ($($x:tt)+) => { + lower_macros::math! { $($x)+ } + }; +} |