AFAICT, there is basically only one option: if monomorphization leads to a function either passing a SIMD type as an argument or receiving one as a return value, it must be compiled with the appropriate target feature. Anything else seems like it would lead to either monomorphization-time errors or surprising behavior.
Here are two suggestions to mitigate the unfortunate implicitness:
-
pre-monomorphization, it is a compile error to pass a SIMD type or get one as a return value, unless you are in a function that is compiled with the appropriate target feature. This limits the implicit target_feature propagation to generic functions, and doesn't introduce any post-monomorphization errors.
-
post-monomorphization, it is a warning to call a monomorphized function that got an implicit target feature, unless you are calling from a function with that target feature (implicit or not). There would be an attribute to silence this warning, since it's legitimate to do this after a runtime CPU feature check.
The implication of the first rule for @burntsushi's examples: in
#[inline(always)]
#[target_feature = "+avx2"]
fn testfoo(x: i64, y: i64, shift: i32) -> i64 {
let a = i64x4(x, x, y, y);
_mm256_slli_epi64(a, shift).0
}
it would be a compile-time error to leave off target_feature, regardless of whether inline
is specified. In
#[target_feature = "+avx2"]
fn testfoo(x: i64, y: i64, shift: i32) -> i64x4 {
let a = i64x4(x, x, y, y);
_mm256_slli_epi64(a, shift)
}
it would be a compile-time error to call testfoo
from a function without an appropriate target_feature (avx or avx2).