macro_rules! unsafe_ifunc { ( $memchrty:ident, $memchrfind:ident, $fnty:ty, $retty:ty, $hay_start:ident, $hay_end:ident, $($needle:ident),+ ) => { ... }; }
Expand description
Provides a way to run a memchr-like function while amortizing the cost of runtime CPU feature detection.
This works by loading a function pointer from an atomic global. Initially, this global is set to a function that does CPU feature detection. For example, if AVX2 is enabled, then the AVX2 implementation is used. Otherwise, at least on x86_64, the SSE2 implementation is used. (And in some niche cases, if SSE2 isn’t available, then the architecture independent fallback implementation is used.)
After the first call to this function, the atomic global is replaced with the specific AVX2, SSE2 or fallback routine chosen. Subsequent calls then will directly call the chosen routine instead of needing to go through the CPU feature detection branching again.
This particular macro is specifically written to provide the implementation of functions with the following signature:
fn memchr(needle1: u8, start: *const u8, end: *const u8) -> Option<usize>;
Where you can also have memchr2
and memchr3
, but with needle2
and
needle3
, respectively. The start
and end
parameters correspond to the
start and end of the haystack, respectively.
We use raw pointers here instead of the more obvious haystack: &[u8]
so
that the function is compatible with our lower level iterator logic that
operates on raw pointers. We use this macro to implement “raw” memchr
routines with the signature above, and then define memchr routines using
regular slices on top of them.
Note that we use #[cfg(target_feature = "sse2")]
below even though
it shouldn’t be strictly necessary because without it, it seems to
cause the compiler to blow up. I guess it can’t handle a function
pointer being created with a sse target feature? Dunno. See the
build-for-x86-64-but-non-sse-target
CI job if you want to experiment with
this.
§Safety
Primarily callers must that $fnty
is a correct function pointer type and
not something else.
Callers must also ensure that $memchrty::$memchrfind
corresponds to a
routine that returns a valid function pointer when a match is found. That
is, a pointer that is >= start
and < end
.
Callers must also ensure that the $hay_start
and $hay_end
identifiers
correspond to valid pointers.