And what would prevent that from being possible?
That function would just dispatch to the Add function implemented for T. If T implements Add, then nothing can fail.
f32x8 would only be available if some target feature is enabled, and it might conditionally have different Add implementations (or none) depending on the target features available.
The only error you could get from your code is a front-end compiler error saying Add is not implemented for T.
A per function target feature always tries to emit the code. If the code cannot be emitted, you will get a backend error. You would need to wrap per function target features in conditional compilation blocks:
#[cfg(target_architecture("x86"))] {
#[target_feature("AVX")]
fn foo() { ... } // code for foo is always generated on x86
}
instead of requiring … unsafe { ... }
Intrinsics will always be unsafe. That is possible with a higher level library that abstracts the intrinsics away and provides fallback implementations (this is exactly what the simd crate currently does).
I think that you seem to be trying to reduce the need for #[cfg(target_feature(...))] to try to rely as much on #[target_feature]. One is a compile-time flag, the other is just a function attribute, that says, generate this function with this code.
If you want to generate multiple functions with #[target_feature] that do the same thing for different feature sets, you can either:
- give them different names (Rust doesn’t have overloading), and wrap them in some other function without the attribute that dispatches to the proper one depending on run-time detection or using
#[cfg(target_feature(...))] at compile-time
- give them the same name but only compile one of them (by using
#[cfg(target_feature))])
The same would be true for impls, when you write:
#[target_feature("AVX")]
impl Add for f32x8 { ... }
what this is saying is always generate the code for Add for f32x8, but the whole thing should be wrapped in an #[cfg] anyways because doing so is not always possible (e.g. ARM). In particular you might want to offer an f32x8-like abstraction that also works when AVX is not available, but you cannot have two impls for the same type, so adding the following below the previous impl does not work:
#[target_feature("SSE4.2")]
impl Add for f32x8 { ... }
The only ways you could make it work are the same as for function, be sure there is only one impl of Add being generated for f32x8, by either using conditional compilation, or an impl of Add that is independent of the target feature and uses a layer of indirection. For example at compile time:
// independent of target features
impl Add for f32x8 {
// compile-time switch
fn foo() {
if #[cfg(target_feature("AVX"))] {
// do this using AVX
} else {
// fallback impl, can be plain Rust, works for x86 and ARM
}
}
// run-time switch:
fn bar() {
if #[cfg(architecture("X86"))] {
if cpuid.is("AVX") {
f32x8_add_AVX_bar_impl(); //< written with #[target_feature]
} else {
f32x8_add_fallback_bar_impl();
}
} else {
// do something for ARM
}
}