Skip to content

[ARM] MLAS: portable machine-code SVE elementwise kernels, FEXPA exp - #31145

Draft
mirounga wants to merge 1 commit into
microsoft:mainfrom
mirounga:rs_sve_eltwise
Draft

[ARM] MLAS: portable machine-code SVE elementwise kernels, FEXPA exp#31145
mirounga wants to merge 1 commit into
microsoft:mainfrom
mirounga:rs_sve_eltwise

Conversation

@mirounga

Copy link
Copy Markdown
Contributor

Rework the SVE elementwise kernels (f32: Erf, Logistic, Exp, SumExp, Softmax, LogSoftmax, ReduceMaximum, ReduceMinimumMaximum; fp16: Erf, Gelu, Tanh) so they no longer require an SVE-capable compiler or a Linux-only build, and speed them up while doing so.

Structure: each kernel becomes a relocation-free *Impl function that receives its constants through table pointers (no literal pools, no global data references), fronted by a plain C++ dispatch layer (sve/elementwise_sve_dispatch.cpp) that owns the public entry points and fastpaths. The production kernels ship as portable machine code in the style of Arm's KleidiAI library (aarch64/elementwise_sve_asm.S: raw instruction words via KAI_ASM_INST, which is GAS ".inst" on Linux/macOS and armasm64 "DCD" on Windows via aarch64/kai_asm_macros.h, macro set adopted verbatim from KleidiAI). The SVE intrinsics translation units remain in-tree as the reference implementation and regeneration source, selectable with cmake option onnxruntime_SVE_ELEMENTWISE_ASM=OFF.

The exp path uses the SVE FEXPA instruction (base SVE, no extension required) for the SumExp/exp evaluation, with the input clamped at -88.0f: below -127*ln2 the FEXPA index underflows into the exponent field and would produce NaN.

Two kernels gain platform-dispatch coverage on AArch64/SVE that previously existed only on AMD64/RISCV64: ComputeExpF32Kernel and ReduceMinimumMaximumF32Kernel (MLAS_PLATFORM fields moved to a shared guard; call sites in compute.cpp/quantize.cpp extended with MLAS_USE_SVE).

Windows enablement: runtime detection via
PF_ARM_SVE_INSTRUCTIONS_AVAILABLE (SDK-#ifdef-guarded, Windows 11 24H2+), sources wired through the existing cl /P + armasm64 pipeline, the fp16 kernel dispatch un-gated from !_WIN32 (the NEON fp16 fallbacks remain POSIX-only; on Windows without SVE the routines stay null and callers use their scalar fallbacks). Structurally complete; awaiting validation on Windows-on-ARM SVE hardware.

Measured on Cortex-X925/A725 (SVE VL=128), big-core pinned, 104 median cells, versus the SVE intrinsics kernels currently shipping (real-time medians, lower is better):

ComputeSoftmaxInplace (32 cells) 0.772x (1.30x speedup)
GeluErf fused/unfused (36 cells) 0.795-0.797x
Silu (36 cells) 0.927-0.930x
All 104 cells 0.832x (1.20x speedup)

Correctness: full onnxruntime_mlas_test with the machine-code kernels linked: 33213 passed, 0 failed. The code is fully position-independent and vector-length agnostic (runtime cntw/cnth + whilelo loops).

Description

Motivation and Context

Rework the SVE elementwise kernels (f32: Erf, Logistic, Exp, SumExp,
Softmax, LogSoftmax, ReduceMaximum, ReduceMinimumMaximum; fp16: Erf, Gelu,
Tanh) so they no longer require an SVE-capable compiler or a Linux-only
build, and speed them up while doing so.

Structure: each kernel becomes a relocation-free *Impl function that
receives its constants through table pointers (no literal pools, no global
data references), fronted by a plain C++ dispatch layer
(sve/elementwise_sve_dispatch.cpp) that owns the public entry points and
fastpaths. The production kernels ship as portable machine code in the
style of Arm's KleidiAI library (aarch64/elementwise_sve_asm.S: raw
instruction words via KAI_ASM_INST, which is GAS ".inst" on Linux/macOS
and armasm64 "DCD" on Windows via aarch64/kai_asm_macros.h, macro set
adopted verbatim from KleidiAI). The SVE intrinsics translation units
remain in-tree as the reference implementation and regeneration source,
selectable with cmake option onnxruntime_SVE_ELEMENTWISE_ASM=OFF.

The exp path uses the SVE FEXPA instruction (base SVE, no extension
required) for the SumExp/exp evaluation, with the input clamped at -88.0f:
below -127*ln2 the FEXPA index underflows into the exponent field and
would produce NaN.

Two kernels gain platform-dispatch coverage on AArch64/SVE that previously
existed only on AMD64/RISCV64: ComputeExpF32Kernel and
ReduceMinimumMaximumF32Kernel (MLAS_PLATFORM fields moved to a shared
guard; call sites in compute.cpp/quantize.cpp extended with MLAS_USE_SVE).

Windows enablement: runtime detection via
PF_ARM_SVE_INSTRUCTIONS_AVAILABLE (SDK-#ifdef-guarded, Windows 11 24H2+),
sources wired through the existing cl /P + armasm64 pipeline, the fp16
kernel dispatch un-gated from !_WIN32 (the NEON fp16 fallbacks remain
POSIX-only; on Windows without SVE the routines stay null and callers use
their scalar fallbacks). Structurally complete; awaiting validation on
Windows-on-ARM SVE hardware.

Measured on Cortex-X925/A725 (SVE VL=128), big-core pinned, 104 median
cells, versus the SVE intrinsics kernels currently shipping (real-time
medians, lower is better):

  ComputeSoftmaxInplace (32 cells)      0.772x  (1.30x speedup)
  GeluErf fused/unfused (36 cells)      0.795-0.797x
  Silu (36 cells)                       0.927-0.930x
  All 104 cells                         0.832x  (1.20x speedup)

Correctness: full onnxruntime_mlas_test with the machine-code kernels
linked: 33213 passed, 0 failed. The code is fully position-independent
and vector-length agnostic (runtime cntw/cnth + whilelo loops).
@mirounga
mirounga marked this pull request as draft July 29, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant