I'm not a Rust guy, so can someone explain to me why SIMD intrinsics are "unsafe"? They don't seem unsafe in the way that, I dunno, raw memory accesses are unsafe.
1. Using instructions that don't exist on a given CPU will cause the program to crash.
2. `core::arch` exists so that stable Rust programs could make use of SIMD instructions for performance (in particular, `regex` crate). They are very low level, and there is a lot of those, so whether any given SIMD intrinsic is safe or not wasn't considered as those functions exist for safe SIMD abstractions to use - which is advantageous, as Rust cannot really do breaking changes, while a library could easily release a new version without breaking the users (which will still use the old version).
> Using instructions that don't exist on a given CPU will cause the program to crash.
But surely this also applies to a Rust program that only uses safe code but it's compiled with the right compiler switches that is allowed to use SIMD instructions?
Sure would be nice to have a way to mark a block as "compile for arches x, y" and have the binary check on entry which you have and select a different code path. This way you could do it only for your hot sections and not need to have the entire binary duplicated.
AFAIK the main way to manage this right now is throwing the optimized stuff into a plugin and then loading it at runtime— but that ends up having huge implications on your project structure, source layout, etc.
By default Rust builds binaries that will run on any processor for the given target. If you want it to generate instructions that are processor-specific--thereby reducing the portability of the binary--you must pass an explicit compiler flag. Clang exhibits the same behavior with its -march and -mcpu flags, and I would assume GCC does as well.
Is important to note what is the POV of "unsafe". Is not unsafe for you or for your setup.
Is unsafe from the COMPILER ie: it can't PROVE is safe!
Static analysis can only decide a subset of the possible "safe" interactions. In the time of COMPILING. Rust can't decide if use of SIMD is safe AT COMPILE TIME because at RUNTIME maybe the cpu don't have it!
Why can't it just run dynamic feature detection to see if it needs to load e.g. a SISD shim version of the function which is exactly the same except the intrinsic is replaced with a generic implementation?
Or at least the other way around: if a function is declared with dynamic feature detection and a default path exists why can't it be declared safe by the compiler?
The thing is that is not yet in-build in the lang. Let the crates sort some problems before commit it in the lang is like part of the philosophy of rust.
For example, rust change the hash implementation after certain crate prove it worthy.
I get that; obviously if you use an intrinsic that reads from memory it's a raw load. However, surely there's a great big pile of them that are really just different ways of doing things to a (slightly bigger than usual) Plain Old Data structure?
I think it's partly architecture compatibility. The SIMD functions exposed by Rust at the moment are all CPU-architecture-specific, but Rust code is generally expected to work seamlessly cross-platform.
I believe there are plans to expose higher-level functionality as a safe interface at a later date (the API design work just hasn't been done yet). For now you can get this functionality as a 3rd-party crate https://github.com/AdamNiederer/faster
In addition to other answers: SIMD in rust is currently exposed via a very low-level API. But in the future, someone will probably build a safe API on top of it.
OK, thanks for the clarifications. I think it's kind of daft to lump in "might not run on your particular processor" with "unsafe" - these seem like very different concepts - but it's at least consistent.
Inline assembly is possibly one of the most unsafe features available in any HLL. What happens when your inline assembly isn't valid on the target system? Undefined behavior. What happens when your inline assembly does operations that are outside the model of the language? Undefined behavior.
Surely if you control the whole toolchain, you can stick flags into the binaries/libraries and fail at link/load time.
The fact that default C ABI prefers to work without a condom here doesn't mean that Rust has to! This is one of the most avoidable forms of undefined behavior I'm aware of.
Eventually the program counter will advance to the unsafe inline assembly. At this point there are no guarantees. There is no enforced ABI. The inline assembly can do anything. This is not a compile or link time problem. This is a runtime problem.
I just meant that a binary or library could mark itself (say) "If you execute me, I will run AVX2" upon using this kind of inline asm. Static linking to create a bigger library or program transfers the mark. Eventually you will either dynamically link or load a library or attempt to run it on the actual computer where it's meant to run; at this point, you fail gracefully.
I know this isn't a 100% panacea, as some programs will have multiple bits of inline asm for different system and some sort of better or worse way of determining at run-time what to run. However, I've written plenty of programs where it pretty much says on the tin: "you must have AVX2 to run this".
All up this seems like a pretty trivial problem compared to the problem of 'lack-of-safety' in general, and I'm not thrilled to see SIMD intrinsics being put in the "OMG So Dangerous" box as if they are a bunch of wild pointer ops (except, of course, in the case where they actually are - e.g. scatter/gather)...
Non-portable, of course, I get.