Module simd::cortex_m
std::simd, ARM Cortex-M convenience layer
Unlike the other five per-ISA headers in std/simd/, Cortex-M support is partial and this header documents exactly where the line is, verified directly against this compiler's output (not assumed):
* Cortex-M55 / Cortex-M85 (Armv8.1-M, MVE/"Helium", target attribute
'+mve'): vec<T,N> arithmetic lowers to real MVE vector instructions
(q-register vadd.i32/vldrw.u32/vstrw.32, ...) — verified via
`--target thumbv8.1m.main-none-eabi` + `llc -mattr=+mve`. The
f32x4/i32x4/i16x8/i8x16 aliases below are meaningful and native
(one 128-bit MVE q-register) on these cores.
* Cortex-M4 / Cortex-M7 (Armv7E-M, DSP extension, target attribute
'+dsp'): the DSP extension's packed-SIMD instructions (SADD16,
SMLAD, ...) operate on 2x16-bit or 4x8-bit lanes packed into a
single 32-bit *scalar* register, not a real vector register file.
LLVM does not auto-vectorize generic vec<T,N> IR into these
instructions the way it does for MVE/NEON/SSE/RVV/SIMD128 — a
`vec<short,2>` add on `thumbv7em` lowers to two independent 32-bit
`add`s, not a single `sadd16` (confirmed by inspecting compiler
output), so there is no vec<T,N>-based type alias for DSP below.
Instead, the dsp_* functions declared below wrap compiler builtins
(__arm_dsp_*, see CodeGen.cpp) that map 1:1 onto the real
instructions — verified against real llc output on
'-mtriple=thumbv7em-none-eabi -mcpu=cortex-m4' (qadd/sadd16/smlad/
usad8/ssat all emit the single named instruction, not a libcall).
* Cortex-M0 / M0+ / M3 / M23 (no DSP, no MVE): vec<T,N> still compiles
(verified: whole stdlib compiles clean for `thumbv6m-none-eabi
--freestanding`) and is fully correct, but every lane operation is
scalarized — there is no SIMD hardware to target at all on these
cores, so vec<T,N> here is purely a portable-code convenience, not
a performance feature.
Type aliases
| mve_f32x4 | Cortex-M55/M85 (MVE, '+mve'): native 128-bit q-register operations. |
| mve_i32x4 | |
| mve_i16x8 | |
| mve_i8x16 |
Functions
| dsp_qadd | Each 'int'/'unsigned int' operand packs 2x16-bit or 4x8-bit lanes into one 32-bit word (lane 0 in the low bits); packing/unpacking individual lanes is the caller's job (shifts, or reading two adjacent short/char values as one int through a union or pointer cast). Requires an ARM target — using any of these while compiling for a non-ARM target is a compile error at the call site (enforced by the underlying _arm_dsp* builtin). |
| dsp_qsub | saturating 32-bit sub |
| dsp_qadd16 | saturating 2x16-bit add |
| dsp_qadd8 | saturating 4x8-bit add |
| dsp_qsub16 | saturating 2x16-bit sub |
| dsp_qsub8 | saturating 4x8-bit sub |
| dsp_sadd16 | wrapping 2x16-bit add |
| dsp_sadd8 | wrapping 4x8-bit add |
| dsp_ssub16 | wrapping 2x16-bit sub |
| dsp_ssub8 | wrapping 4x8-bit sub |
| dsp_uqadd16 | |
| dsp_uqadd8 | |
| dsp_uqsub16 | |
| dsp_uqsub8 | |
| dsp_smlad | dual 16x16 multiply-accumulate |
| dsp_smladx | exchanged (cross) lanes |
| dsp_smlsd | dual 16x16 multiply-subtract |
| dsp_smlsdx | |
| dsp_smuad | dual 16x16 multiply, summed |
| dsp_smuadx | |
| dsp_smusd | dual 16x16 multiply, difference |
| dsp_smusdx | |
| dsp_usad8 | sum of |byte diffs|, 4 lanes |
| dsp_usada8 | |
| dsp_sxtab16 | sign-extend b's two bytes, add to a's halfwords |
| dsp_uxtab16 |