Skip to content

Instantly share code, notes, and snippets.

@thomcc
Created September 11, 2019 00:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomcc/565da163836ac8e22b49507e25102724 to your computer and use it in GitHub Desktop.
Save thomcc/565da163836ac8e22b49507e25102724 to your computer and use it in GitHub Desktop.
/// ```
/// # #[allow(dead_code)] fn code_requiring_fma_and_avx() {}
/// # #[allow(dead_code)] fn code_requiring_sse41() {}
/// # #[allow(dead_code)] fn code_requiring_sse2() {}
/// # #[allow(dead_code)] fn fallback_code() {}
/// # fn main() {
/// let thing = t3m::simd_match! {
/// // Comma separate required target features to require both.
/// "fma", "avx" => code_requiring_fma_and_avx(),
/// "sse4.1" => code_requiring_sse41(),
/// "sse2" => code_requiring_sse2(),
/// // If a default case is ommitted, we'll emit a
/// // compile error when compiling for a platform
/// // that doesn't match something above.
/// _ => fallback_code(),
/// };
/// # let _ = thing;
/// # }
/// ```
/// Note that (like match) these must be in MOST to LEAST specific. Specifying
/// `sse` before `avx` will mean the AVX one will never be used! I'd like to fix
/// this, but am unsure how.
#[macro_export(local_inner_macros)]
macro_rules! simd_match {
($($feat:literal),+ => $it:expr, $($rest:tt)*) => {{
// Create a dummy variable that the bodies initialize.
// and allows the match arms to be exprs without rustc
// complaining (even fabricating a block inside the macro
// hits an error)
let _simd_match_val;
simd_match! {
@__scan;
_simd_match_val;
();
$($feat),+ => $it,
$($rest)*
};
_simd_match_val
}};
(@__scan;
$target:ident ;
($($nots:meta),* $(,)?) ;
$($feat:literal),+ => $block:expr,
$($rest:tt)*
) => {
simd_match!{
@__scan;
$target;
(all($(target_feature = $feat),+), $($nots),*);
$($rest)*
};
#[cfg(all(
not(any($($nots),*)),
$(target_feature = $feat),+
))] { $target = $block; }
};
// hacky duplicated case to make trailing comma optional here...
// can't use $(,)? before a $($rest:tt)*, and can't leave
// the comma out or we'd have an `expr` followed by a tt, which
// is invalid.
(@__scan;
$target:ident ;
($($nots:meta),* $(,)?) ;
$($feat:literal),+ => $block:expr
) => {
// add the comma and recurse.
simd_match!{
@__scan;
$target ;
(all($(target_feature = $feat),+), $($nots),*) ;
$($feat),+ => $block,
}
};
(@__scan; $target:ident; ($($nots:meta),* $(,)?) ; $(,)*) => {
#[cfg(not(any($($nots),*)))] {
compile_error!(
concat!(
"Unhandled case in simd_match! Expected one of the following cfg's to match: ",
stringify!($($nots),+)
)
);
}
};
(@__scan;
$target:ident;
($($nots:meta),* $(,)?) ;
_ => $fallback:expr $(,)?
) => {
#[cfg(not(any($($nots),*)))] { $target = $fallback; };
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment