Choosing between fixed-width NEON and length-agnostic SVE for portable AArch64 SIMD
0 reputation · 26 Nov 2020, 17:18 UTC
0 reputation · 26 Nov 2020, 17:18 UTC
Goal: create a single AArch64 binary that runs on every ARMv8‑A CPU, using NEON when SVE is absent and automatically exploiting longer vectors on SVE‑capable cores without maintaining separate builds.
Constraints: GCC and Clang’s auto‑vectorizer may choose NEON, SVE, or a mixture depending on -march, -mtune, optimization level, data alignment, or OpenMP SIMD clauses, and there is no portable pragma or attribute that forces a specific SIMD backend across both compilers. SVE’s vector length is unknown at compile time, so code must rely on runtime capability checks (glibc HWCAP_SVE/HWCAP_SVE2 or the auxiliary vector) and length‑agnostic loops. On heterogeneous big.LITTLE systems SVE may be present only on some cores, requiring per‑CPU feature detection before launching SVE work.
35275 reputation · 26 Nov 2020, 18:44 UTC
Use -march=armv8-a without +sve to restrict code generation to NEON/FP in both GCC and Clang; no portable per-function attribute forces NEON across both compilers. For SVE with NEON fallback, isolate SVE kernels in translation units compiled with -march=armv8-a+sve or +sve2, guard entry with getauxval(AT_HWCAP) & HWCAP_SVE, and select implementations with GCC ifunc resolvers (Clang support is limited). Choose a single fat binary when distribution simplicity matters and resolver behavior is verified; choose separate NEON/SVE packages when SVE code is large, per-microarchitecture -mtune is needed, or toolchain ifunc support is uneven.
-march=armv8-a.-march=armv8-a+sve or +sve2 and write length-agnostic loops via <arm_sve.h> intrinsics or auto-vectorization.simd clauses, -mtune, optimization level, and alignment influence but do not guarantee NEON or SVE selection.getauxval(AT_HWCAP) for HWCAP_SVE/HWCAP_SVE2. On heterogeneous big.LITTLE systems, this may reflect only the boot CPU; check /sys/devices/system/cpu/cpu*/features before pinning SVE work.ifunc resolvers can select SVE or NEON implementations at load time based on HWCAP_SVE. Clang ifunc support on AArch64 is more limited; verify linker behavior in your toolchain.ifunc resolver or explicit HWCAP guard is required to avoid SIGILL.-march=armv8-a does not guarantee every vectorizable loop becomes NEON, and -march=...+sve does not guarantee SVE for every loop. Inspect assembly with objdump -d to confirm.ifunc resolvers reduces distribution complexity, but resolver testing on both SVE-capable and SVE-less hardware (or QEMU -cpu without +sve) is essential.-mtune without bloating the NEON binary.-march=armv8-a -O2 -ftree-vectorize. Verify NEON-only instructions via objdump -d and confirm no SVE mnemonics such as whilelt or predicate-register loads.-march=armv8-a+sve -O2 (or +sve2 for SVE2). Use <arm_sve.h> intrinsics for length-agnostic loops; avoid hard-coded VL assumptions.ifunc resolver for each SVE entry point:
extern void kernel_neon(void);
extern void kernel_sve(void);
void *resolve_kernel(void) {
unsigned long hwcap = getauxval(AT_HWCAP);
return (hwcap & HWCAP_SVE) ? (void*)kernel_sve : (void*)kernel_neon;
}
void kernel(void) __attribute__((ifunc("resolve_kernel")));
SIGILL and correct fallback./sys/devices/system/cpu/cpu*/features for the sve flag and set thread affinity accordingly.-mtune, or Clang ifunc is required but unsupported.Which compiler (GCC version, Clang version) and linker (ld.bfd, ld.gold, lld) are you using for the final link? Clang ifunc support on AArch64 varies by version and linker; this determines whether a single fat binary is viable or separate packages are required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.