Module core::core_simd::intrinsics
source · 🔬This is a nightly-only experimental API. (
portable_simd
#86656)Expand description
This module contains the LLVM intrinsics bindings that provide the functionality for this crate.
The LLVM assembly language is documented here: https://llvm.org/docs/LangRef.html
A quick glossary of jargon that may appear in this module, mostly paraphrasing LLVM’s LangRef:
- poison: “undefined behavior as a value”. specifically, it is like uninit memory (such as padding bytes). it is “safe” to create poison, BUT poison MUST NOT be observed from safe code, as operations on poison return poison, like NaN. unlike NaN, which has defined comparisons, poison is neither true nor false, and LLVM may also convert it to undef (at which point it is both). so, it can’t be conditioned on, either.
- undef: “a value that is every value”. functionally like poison, insofar as Rust is concerned. poison may become this. note: this means that division by poison or undef is like division by zero, which means it inflicts…
- “UB”: poison and undef cover most of what people call “UB”. “UB” means this operation immediately invalidates the program:
LLVM is allowed to lower it to
ud2
or other opcodes that may cause an illegal instruction exception, and this is the “good end”. The “bad end” is that LLVM may reverse time to the moment control flow diverged on a path towards undefined behavior, and destroy the other branch, potentially deleting safe code and violating Rust’sunsafe
contract.
Note that according to LLVM, vectors are not arrays, but they are equivalent when stored to and loaded from memory.
Unless stated otherwise, all intrinsics for binary operations require SIMD vectors of equal types and lengths.
Functions
- add/fadd
- and
- getelementptr (without inbounds) equivalent to wrapping_offset
- follows Rust’s
T as U
semantics, including saturating float casts which amounts to the same assimd_cast
for many cases - fptoui/fptosi/uitofp/sitofp casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5 but the truncated value must fit in the target type or the result is poison. use
simd_as
instead for a cast that performs a saturating conversion. - equivalent to
T as U
semantics, specifically for pointers - udiv/sdiv/fdiv ints and uints: {s,u}div incur UB if division by zero occurs. ints: sdiv is UB for int::MIN / -1. floats: fdiv is never UB, but may create NaNs or infinities.
- expose a pointer as an address
- fabs
- convert an exposed address back to a pointer
- llvm.masked.gather like a loop of pointer reads val: vector of values to select if a lane is masked ptr: vector of pointers to read from mask: a “wide” mask of integers, selects as if simd_select(mask, read(ptr), val) note, the LLVM intrinsic accepts a mask vector of
<N x i1>
FIXME: review this if/when we fix up our mask story in general? - mul/fmul
- neg/fneg ints: ultimately becomes a call to cg_ssa’s BuilderMethods::neg. cg_llvm equates this to
simd_sub(Simd::splat(0), x)
. floats: LLVM’s fneg, which changes the floating point sign bit. Some arches have instructions for it. Rust panics for Neg::neg(int::MIN) due to overflow, but it is not UB in LLVM withoutnsw
. - or
- urem/srem/frem ints and uints: {s,u}rem incur UB if division by zero occurs. ints: srem is UB for int::MIN / -1. floats: frem is equivalent to libm::fmod in the “default” floating point environment, sans errno.
- llvm.masked.scatter like gather, but more spicy, as it writes instead of reads
- shl for (u)ints. poison if rhs >= lhs::BITS
- ints: ashr uints: lshr poison if rhs >= lhs::BITS
- sub/fsub
- xor