npsimd 0.3.0

An ergonomic library for architecture-specific vectorization.
Documentation
//! Utility macros for low-level intrinsics.

/// Assert a `const` expression.
macro_rules! const_assert {
    ($cond:expr) => {
        // Asserts are "overly complex" for a const expr.
        const fn assert(cond: bool) {
            assert!(cond);
        }

        const { assert($cond) }
    };
}

/// A macro to call a function with an array of parameters.
macro_rules! impl_array_vector {
    (
        $maker:tt ( $( ($arrays:tt [ $indices:tt ] $($cast:tt)* ), )* ),
        $offset:expr, $($offsets:expr,)*
    ) => {
        impl_array_vector!(
            $maker (
                $( ($arrays [ $indices ] $($cast)* ), )*
                $( ($arrays [ ($offset + $indices) ] $($cast)* ), )*
            ),
            $($offsets,)*
        )
    };

    (
        $maker:tt ( $( $value:expr, )* ),
        $offset:expr, $($offsets:expr,)*
    ) => {
        impl_array_vector!(
            $maker ( $( $value, )* $( $value, )* ),
            $($offsets,)*
        )
    };

    ($maker:tt ( $( $value:expr, )* ),) => {
        $maker ( $( $value, )* )
    };
}

/// Construct a documentation comment for Intel intrinsic equivalents.
macro_rules! document_intel_equivs {
    ($head:literal, $($tail:literal),+) => {
        concat!(
            "\n\nIntel Equivalents:",
            document_intel_equivs!(0: $head, $($tail,)+)
        )
    };

    ($equiv:literal $(,)?) => {
        concat!("\n\nIntel Equivalent: [`", $equiv, "`]")
    };

    (0: $head:literal, $($tail:literal,)+) => {
        concat!(
            " [`", $head, "`],",
            document_intel_equivs!(0: $($tail,)+)
        )
    };

    (0: $last:literal,) => {
        concat!(" [`", $last, "`].",)
    };
}

/// Define an arbitrary vector method.
macro_rules! defn_simd {
    {
        $(#[doc = $doc:literal])*
        $(#[intrinsic_for($inst:literal)])?
        $(#[intel_equivalents($($intel:literal),+ $(,)?)])?
        $(#[cfg($cfg:meta)])?
        $vis:vis fn $name:ident
        $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
        $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
        ($($param_name:ident: $param_type:ty),* $(,)?)
        $(-> $rett:ty)?
        $body:block
    } => {
        $(#[doc = $doc])*
        $(#[doc = concat!("\n\nInstruction: `", $inst, "`.")])?
        $(#[doc = document_intel_equivs!($($intel),+)])?
        $(#[doc(alias = $inst)])?
        $(#[doc(alias($($intel),+))])?
        $(#[cfg(any(doc, $cfg))])?
        $vis fn $name
        <
            $($($imm_name: Imm<$imm_type>,)*)?
            $($(const $const_name: $const_type,)*)?
        >
        (&self, $($param_name: $param_type),*)
        $(-> $rett)? $body
    };
}

/// Define a vector method with a body.
macro_rules! defn_simd_body {
    ($target:literal, {
        $(#[$($attr:tt)*])*
        $vis:vis fn $name:ident
        $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
        $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
        ($($param_name:ident: $param_type:ty),* $(,)?)
        $(-> $rett:ty)?
        $body:block
    }) => {
        defn_simd! {
            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*)
            $(-> $rett)? {
                #[inline]
                #[target_feature(enable = $target)]
                unsafe fn inner<
                    $($($imm_name: Imm<$imm_type>,)*)?
                    $($(const $const_name: $const_type,)*)?
                >($($param_name: $param_type),*)
                $(-> $rett)? $body

                unsafe {
                    inner::<
                        $($($imm_name,)*)?
                        $($($const_name,)*)?
                    >($($param_name),*)
                }
            }
        }
    };
}

/// Define vector methods with manual implementations.
macro_rules! defn_simd_manual {
    ($target:literal, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
            $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
            ($($param_name:ident: $param_type:ty),* $(,)?)
            $(-> $rett:ty)?
            $body:block
        )*
    }) => {
        $(defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*) $(-> $rett)?
            $body
        });)*
    };

    ($target:literal, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
            $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
            ($($param_name:ident: $param_type:ty),* $(,)?)
            $(-> $rett:ty)?
            = $func:path;
        )*
    }) => {
        $(defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*) $(-> $rett)?
            { $func($($param_name),*) }
        });)*
    };
}

/// Define vector methods with a shared implementation.
macro_rules! defn_simd_shared {
    ($target:literal, $body:block, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
            $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
            ($($param_name:ident: $param_type:ty),* $(,)?)
            $(-> $rett:ty)?;
        )*
    }) => {
        $(defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*) $(-> $rett)?
            $body
        });)*
    };

    ($target:literal,
     fn $param_tvars:tt -> $rett_tvar:ident $body:block,
     {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
            $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
            ($($param_name:ident: $param_type:ty),* $(,)?)
            -> $rett:ty;
        )*
    }) => {
        $(defn_simd_shared!(0: $target, {
            fn $param_tvars -> $rett_tvar $body

            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*)
            -> $rett;
        });)*
    };

    (0: $target:literal, {
        fn
        ($($param_tvar:ident),* $(,)?)
        $(-> $rett_tvar:ident)?
        $body:block

        $(#[$($attr:tt)*])*
        $vis:vis fn $name:ident
        $([$($imm_name:ident: $imm_type:ty),* $(,)?])?
        $(<$($const_name:ident: $const_type:ty),* $(,)?>)?
        ($($param_name:ident: $param_type:ty),* $(,)?)
        -> $rett:ty;
    }) => {
        defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name
            $([$($imm_name: $imm_type),*])?
            $(<$($const_name: $const_type),*>)?
            ($($param_name: $param_type),*)
            -> $rett {
                $(
                    #[allow(dead_code)]
                    type $rett_tvar = $rett;
                )?

                $(
                    #[allow(dead_code)]
                    type $param_tvar = $param_type;
                )*

                $body
            }
        });
    };
}

/// Define vector construction from pairs of sub-vectors.
macro_rules! defn_simd_set_pair {
    ($target:literal, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            (value: [$elem:ty; 2])
            -> $rett:ty;
        )*
    }) => {
        $(defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name(value: [$elem; 2]) -> $rett {
                simd_shuffle(value[0], value[1], const {
                    simd_slice_indices::<$rett>(0)
                })
            }
        });)*
    };
}

/// Declare an LLVM intrinsic.
macro_rules! decl_llvm_func {
    (
        $intrinsic:literal as $name:ident
        ($($param_name:ident: $param_type:ty),* $(,)?)
        $(-> $rett:ty)?
    ) => {
        // The SIMD types are not considered FFI-safe.
        #[allow(improper_ctypes)]
        // Some intrinsics are used with different types.
        #[allow(clashing_extern_declarations)]
        extern "C" {
            #[link_name = $intrinsic]
            fn $name($($param_name: $param_type),*) $(-> $rett)?;
        }
    };
}

/// Define SIMD intrinsics with LLVM-provided implementations.
macro_rules! defn_simd_llvm {
    ($target:literal, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            ($($param_name:ident: $param_type:ty),* $(,)?)
            $(-> $rett:ty)?
            = $intrinsic:literal;
        )*
    }) => {
        $(defn_simd! {
            $(#[$($attr)*])* $vis fn $name
            ($($param_name: $param_type),*) $(-> $rett)? {
                decl_llvm_func!(
                    $intrinsic as
                    inner($($param_name: $param_type),*)
                    $(-> $rett)?
                );

                unsafe { inner($($param_name),*) }
            }
        })*
    };

    ($target:literal, {
        $(
            $(#[$($attr:tt)*])*
            $vis:vis fn $name:ident
            ($($param_name:ident: $param_type:ty),* $(,)?)
            $(-> $rett:ty)?
            = $intrinsic:literal as
            $impl:ident($($impl_pname:ident: $impl_ptype:ty),*)
            $(-> $impl_rett:ty)?
            => $body:block;
        )*
    }) => {
        $(defn_simd_body!($target, {
            $(#[$($attr)*])* $vis fn $name
            ($($param_name: $param_type),*) $(-> $rett)? {
                decl_llvm_func!(
                    $intrinsic as
                    $impl($($impl_pname: $impl_ptype),*)
                    $(-> $impl_rett)?
                );

                $body
            }
        });)*
    };
}