Skip to content

fix: handle GCC 15 MVE load signatures in Zephyr builds #218

Description

@evanchen11

Summary

GCC 15 fails to compile MVE kernels in a Zephyr build because its
pragma-generated vldrwq_s32 intrinsics retain the compiler-native
long int pointer type while Zephyr exposes int32_t as int.

This was reproduced with:

  • Arm GNU Toolchain GCC 15.2.1
  • Zephyr SDK GCC 14.3.0 as the working comparison
  • Zephyr apollo510_evb
  • MVE integer and floating-point support enabled

The same sources compile normally with GCC 15 outside Zephyr.

Root cause

Zephyr force-includes zephyr_stdint.h, which changes:

#define __INT32_TYPE__ int

Consequently, int32_t * is int * in the Zephyr translation unit.

GCC 14's arm_mve.h declares the intrinsic textually:

#define vldrwq_s32(base) __arm_vldrwq_s32(base)
__arm_vldrwq_s32(int32_t const *base)

That declaration follows Zephyr's visible int32_t, so the call sites
compile.

GCC 15 changed arm_mve.h to pragma-generated intrinsics:

#pragma GCC arm "arm_mve.h" false

The generated declarations retain these compiler-native signatures:

vldrwq_s32(const long int *)
vldrwq_z_s32(const long int *, mve_pred16_t)

Calls using int32_t * therefore fail with
-Wincompatible-pointer-types. This is a compiler error without Zephyr
promoting the diagnostic through -Werror.

The full application build produced 66 diagnostics across 20 source
files, all limited to vldrwq_s32 and vldrwq_z_s32.

Suggested compatibility fix

Add a narrowly gated workaround immediately after including
arm_mve.h in Include/Internal/arm_nn_compiler.h:

#if defined(__ZEPHYR__) && defined(__GNUC__) && \
    !defined(__clang__) && (__GNUC__ >= 15)
#define vldrwq_s32(base) \
    vldrwq_s32((const long int *)(base))
#define vldrwq_z_s32(base, p) \
    vldrwq_z_s32((const long int *)(base), (p))
#endif

Macro recursion suppression leaves the inner token referring to GCC's
intrinsic. The guard prevents any change in non-Zephyr builds, where
GCC's native int32_t already matches the intrinsic.

On AArch32 MVE, int and long int are both 32-bit and have identical
alignment. The cast does not alter the address or generated load:
representative output remains a single vldrw.32.

This should be treated as a GCC 15/Zephyr compatibility shim. The
underlying long-term fix is for GCC's generated signature to agree with
the translation unit's visible int32_t, or for Zephyr to revise its
compiler type override.

Validation

With the suggested workaround:

  • The complete application builds with Arm GNU GCC 15.2.1.
  • The complete application still builds with Zephyr SDK GCC 14.3.0.
  • All previously failing MVE kernels compile.
  • A representative GCC 15 kernel also compiles with strict aliasing
    enabled.

Both application builds used effective -Ofast. This avoids conflating
the issue with the separate GCC 14 internal compiler error seen in some
F16 convolution files at module-local -O2 or -O3.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions