I see, thanks for chiming in.
The plan is to add these tests later because rustc already has some infrastructure for doing this, and I don’t understand it. Thus far, I’ve been ad hoc looking at the generated Assembly.
If you ever get to it let me know. I did not understand those either and quickly hacked my own tiny system for it that just does a 1:1 comparison of the generated assembly. If you ever use this for the stdsimd crate let me know, I’ll be glad to dump mine and use what rustc uses.
Note that this is how Clang exposes vendor intrinsics. I am not taking a novel path here. This is well trodden ground.
Note that calling a function with __target__ in a CPU that doesn’t support its feature set seems to be undefined behavior (in LLVM, the assembler, and probably the CPU as well). See this comment on the RFC PR.
Basically, not even x86 guarantees the illegal instruction will be reached at all, nor that the decoder will detect that it is an illegal instruction, nor that the assembler didn’t insert other target specific code before the instruction that in a different CPU might lead to memory unsafety.
This really isn’t a question of whether or not a high level SIMD library should be unsafe or not, and I’m really confused as to why you’re focusing on that.
I thought that stdsimd was a safe wrapper for SIMD intrinsics but IIUC it is only a proposal for the low-level intrinsics that std should expose, such that high-level wrappers can be built.
Vendor intrinsics must be defined with #[target_feature] in order for anything to work at all.
Not all of them. Maybe this is common for SIMD, but for the bit manipulation instruction sets (TBM, ABM, BMI1, and BMI2) this is not required. Calling the llvm intrinsic directly, like here for rbit or here for pdep, is enough.
@alexcrichton I think after this explanation from @burntsushi I finally understand what you mean with making #[target_feature] unsafe would significantly impact our SIMD story. I think this is an issue, but I am still not convinced that this is a big / critical one. Here is why:
The purpose of putting the lowest-level-possible SIMD intrinsics in std:: is to allow SIMD libraries to be written in stable rust out of tree. If the std:: intrinsics for SIMD are unsafe, those writing a wrapper around them will need to deal with this unsafety. These intrinsics can be wrapped in “low-lever” libraries out of std:: that provide a safe API without introducing an extra cost.
So unless one is writing a “lowest-level-safe SIMD wrapper” one shouldn’t need to deal with this. Even those writing slightly higher-level should build on top of safe-low-level wrappers.
Also, we can always make these intrinsics safe in a backwards compatible way.
What we cannot do is make these intrinsics unsafe after we made them safe in a backwards compatible way.
I really hope that I am missing something, and that @alexcrichton or @burntsushi can convince me that either the impact to the ecosystem will be significant or that it is 100% safe to call these intrinsics on targets that do not support them. After talking with some LLVM-devs about how decoders on Intel CPUs cannot be relied at all to raise SIGILL exceptions and how the assembler might insert filling code before that happens anyways that might modify memory I still lean on the safest solution which is to make #[target_feature] unsafe.