Skip to content

Commit 59dfcdd

Browse files
allnesclaude
andauthored
[CPU][ARM] Guard ARM SVE kernel entry points (#36453)
## Cause - On AArch64 with GCC (cross-compile, gcc-10) + LTO, `-march=armv8-a+sve` target attributes from runtime-dispatched SVE clones leak into generic ARMv8-A baseline code. - GCC auto-vectorizes generic helpers (e.g. `std::vector<...>` resize in the SVE PagedAttention `AttentionExecutor::init`) with SVE instructions (`mov z0.b, #0` / `whilelo` / `st1d`). - On cores without SVE (e.g. Cortex-A72) these are illegal instructions → SIGILL at runtime (reached on a TBB worker thread). - `brgemm_kernel.cpp` ISA selection unconditionally fell back to `sve_128` without checking `mayiuse(...)`, so it built oneDNN kernels that emit SVE on cores without SVE. ## Solution - `precision_support`: add a single ARM ISA gate — `enum class ArmISA { ASIMD, SVE, DOTPROD, I8MM }` and `bool hasArmISASupport(ArmISA)` (ASIMD → always true baseline, SVE → `with_cpu_sve()`, DOTPROD/I8MM → `with_cpu_arm_*`). It consolidates the former `hasIntDotProductSupport()` / `hasInt8MMSupport()` helpers. Declared/defined for ARM targets only, so x86_64 / RISC-V are unaffected. - ACL executors: encapsulate the precondition common to every ACL executor in `aclCommonExecutorSupported()` (in `acl_utils.hpp`; today the ARMv8-A NEON/ASIMD baseline, extensible). Each ACL executor calls it in its `supports()` / `isSupported()` predicate (`VERIFY(..., UNSUPPORTED_ACL_COMMON_PRECONDITION)` or `if/return` + `DEBUG_LOG`), keeping its op-specific checks. ASIMD is always present on AArch64, so this never over-restricts. - `kleidiai_mm` / `fullyconnected` (KleidiAI path): query `hasArmISASupport` directly (ASIMD, plus DOTPROD/I8MM for ukernel selection). - `brgemm_kernel.cpp`: `getSupportedSveIsa(min_isa = sve_128)` returns the highest supported SVE ISA at or above `min_isa`, or `isa_undef` (then the caller throws) instead of the unconditional `sve_128` fallback. The main brgemm kernel uses the default `sve_128` floor; the `copy_a` / `copy_b` kernels pass `sve_256` (oneDNN only provides `sve_256`/`sve_512` for them — `create_brgemm_matmul_copy_*` asserts a superset of `sve_256`). - `paged_attn.cpp`: decline the ARM PagedAttention executor when the core has no SVE (`hasArmISASupport(ArmISA::SVE)`), in the baseline caller before `make_pa_executor` dispatches to the SVE clone, so the SVE-autovectorized `AttentionExecutor::init` is never reached. The node throws cleanly instead. - SVE codegen is unchanged: SVE-capable cores keep SVE paths; non-SVE cores decline them and fall back to ANY/NEON. ## Tickets - CVS-179098 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 45c62b7 commit 59dfcdd

25 files changed

Lines changed: 179 additions & 58 deletions

src/plugins/intel_cpu/src/nodes/executors/acl/acl_conv.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,13 @@ ACLConvolutionExecutor::ACLConvolutionExecutor(const ConvAttrs& attrs,
117117
}
118118

119119
bool ACLConvolutionExecutor::supports(const ConvConfig& config) {
120-
VERIFY(config.attrs.postOps.size() <= 1U, UNSUPPORTED_BY_EXECUTOR);
121-
122120
const auto& srcDesc = config.descs.at(ARG_SRC);
123121
const auto& weiDesc = config.descs.at(ARG_WEI);
124122
const auto& dstDesc = config.descs.at(ARG_DST);
125123

124+
VERIFY(aclSupported({srcDesc, weiDesc, dstDesc}), UNSUPPORTED_ACL_COMMON_PRECONDITION);
125+
VERIFY(config.attrs.postOps.size() <= 1U, UNSUPPORTED_BY_EXECUTOR);
126+
126127
// ACL GemmConv2d supports 4D activations and 4D weight only
127128
VERIFY(srcDesc->getShape().getRank() == 4 && weiDesc->getShape().getRank() == 4, UNSUPPORTED_BY_EXECUTOR);
128129
// isQuantized verifies whether src is u8/i8, weights is i8 and FQ is fused if dst is u8/i8

src/plugins/intel_cpu/src/nodes/executors/acl/acl_convert.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "cpu_memory.h"
2121
#include "memory_desc/cpu_memory_desc.h"
2222
#include "nodes/executors/convert.hpp"
23+
#include "nodes/executors/debug_messages.hpp"
2324
#include "openvino/core/type/element_type.hpp"
2425
#include "utils/debug_capabilities.h"
2526
#include "utils/general_utils.h"
@@ -98,8 +99,9 @@ void ACLConvertExecutor::exec(const std::vector<MemoryCPtr>& src, const std::vec
9899
}
99100

100101
bool ACLConvertExecutorBuilder::isSupported(const ConvertParams& convertParams,
101-
[[maybe_unused]] const MemoryDescPtr& srcDesc,
102-
[[maybe_unused]] const MemoryDescPtr& dstDesc) const {
102+
const MemoryDescPtr& srcDesc,
103+
const MemoryDescPtr& dstDesc) const {
104+
VERIFY(aclSupported({srcDesc, dstDesc}), UNSUPPORTED_ACL_COMMON_PRECONDITION);
103105
if (convertParams.srcPrc != convertParams.dstPrc) {
104106
if (none_of(convertParams.srcPrc,
105107
ov::element::i8,

src/plugins/intel_cpu/src/nodes/executors/acl/acl_deconv.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "cpu_memory.h"
2222
#include "memory_desc/cpu_memory_desc.h"
2323
#include "nodes/executors/acl/acl_utils.hpp"
24+
#include "nodes/executors/debug_messages.hpp"
2425
#include "nodes/executors/deconv.hpp"
2526
#include "nodes/executors/executor.hpp"
2627
#include "openvino/core/parallel.hpp"
@@ -234,6 +235,7 @@ void AclDeconvExecutor::exec(const std::vector<MemoryCPtr>& src,
234235
bool AclDeconvExecutorBuilder::customIsSupported(const DeconvAttrs& deconvAttrs,
235236
const std::vector<MemoryDescPtr>& srcDescs,
236237
const std::vector<MemoryDescPtr>& dstDescs) {
238+
VERIFY(aclSupported({srcDescs[0], dstDescs[0]}), UNSUPPORTED_ACL_COMMON_PRECONDITION);
237239
if (srcDescs[0]->getShape().getDims().size() != 4 || dstDescs[0]->getShape().getDims().size() != 4 ||
238240
srcDescs[1]->getShape().getDims().size() != 4) {
239241
DEBUG_LOG("AclDeconvExecutor only supports 4D tensors:",

src/plugins/intel_cpu/src/nodes/executors/acl/acl_eltwise.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ bool AclEltwiseExecutor::supports(const EltwiseConfig& config) {
8787
srcDescs[argId - ARG_SRC] = desc;
8888
}
8989

90+
if (!aclSupported({srcDescs[0], dstDescs[0]})) {
91+
DEBUG_LOG("ACL common preconditions are not met");
92+
return false;
93+
}
94+
9095
auto checkPrecision = [&srcDescs, &dstDescs](std::vector<ov::element::Type> srcVecPrc,
9196
ov::element::Type dstPrc) -> bool {
9297
for (size_t i = 0; i < srcDescs.size(); i++) {

src/plugins/intel_cpu/src/nodes/executors/acl/acl_fullyconnected.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ ACLFullyConnectedExecutor::ACLFullyConnectedExecutor(const FCAttrs& attrs,
7575
}
7676

7777
bool ACLFullyConnectedExecutor::supports(const FCConfig& config) {
78+
VERIFY(aclSupported({config.descs.at(ARG_SRC), config.descs.at(ARG_WEI), config.descs.at(ARG_DST)}),
79+
UNSUPPORTED_ACL_COMMON_PRECONDITION);
7880
VERIFY(any_of(srcType(config), ov::element::f16, ov::element::f32), UNSUPPORTED_SRC_PRECISIONS);
7981
VERIFY(any_of(weiType(config), ov::element::f16, ov::element::f32), UNSUPPORTED_WEI_PRECISIONS);
8082
VERIFY(postOpsNumbers(config) < 2, UNSUPPORTED_NUMBER_OF_POSTOPS);

src/plugins/intel_cpu/src/nodes/executors/acl/acl_interpolate.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "acl_utils.hpp"
1919
#include "cpu_memory.h"
2020
#include "memory_desc/cpu_memory_desc.h"
21+
#include "nodes/executors/debug_messages.hpp"
2122
#include "nodes/executors/interpolate.hpp"
2223
#include "openvino/core/except.hpp"
2324
#include "utils/debug_capabilities.h"
@@ -197,6 +198,7 @@ bool ov::intel_cpu::ACLInterpolateExecutorBuilder::isSupportedConfiguration(
197198
bool ov::intel_cpu::ACLInterpolateExecutorBuilder::isSupported(const ov::intel_cpu::InterpolateAttrs& interpolateAttrs,
198199
const std::vector<MemoryDescPtr>& srcDescs,
199200
const std::vector<MemoryDescPtr>& dstDescs) const {
201+
VERIFY(aclSupported({srcDescs[0], dstDescs[0]}), UNSUPPORTED_ACL_COMMON_PRECONDITION);
200202
if (srcDescs[0]->getShape().getDims().size() != 4U) {
201203
DEBUG_LOG("ACL Interpolate does not support src shape rank: ", srcDescs[0]->getShape().getDims().size());
202204
return false;

src/plugins/intel_cpu/src/nodes/executors/acl/acl_lowp_fullyconnected.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ ACLLowpFullyConnectedExecutor::ACLLowpFullyConnectedExecutor(const FCAttrs& attr
7878
}
7979

8080
bool ACLLowpFullyConnectedExecutor::supports(const FCConfig& config) {
81+
VERIFY(aclSupported({config.descs.at(ARG_SRC), config.descs.at(ARG_WEI), config.descs.at(ARG_DST)}),
82+
UNSUPPORTED_ACL_COMMON_PRECONDITION);
8183
VERIFY(any_of(srcType(config), ov::element::u8, ov::element::i8), UNSUPPORTED_SRC_PRECISIONS);
8284
VERIFY(weiType(config) == ov::element::i8, UNSUPPORTED_WEI_PRECISIONS);
8385
VERIFY(dstType(config) == ov::element::f32, UNSUPPORTED_DST_PRECISIONS);

src/plugins/intel_cpu/src/nodes/executors/acl/acl_mvn.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "cpu_memory.h"
1717
#include "memory_desc/cpu_memory_desc.h"
1818
#include "nodes/executors/acl/acl_utils.hpp"
19+
#include "nodes/executors/debug_messages.hpp"
1920
#include "nodes/executors/executor.hpp"
2021
#include "nodes/executors/mvn.hpp"
2122
#include "openvino/core/type/element_type.hpp"
@@ -101,6 +102,7 @@ void AclMVNExecutor::exec(const std::vector<MemoryCPtr>& src,
101102
bool AclMVNExecutorBuilder::isSupported(const MVNAttrs& mvnAttrs,
102103
const std::vector<MemoryDescPtr>& srcDescs,
103104
const std::vector<MemoryDescPtr>& dstDescs) const {
105+
VERIFY(aclSupported({srcDescs[0], dstDescs[0]}), UNSUPPORTED_ACL_COMMON_PRECONDITION);
104106
if ((srcDescs[0]->getPrecision() != ov::element::f32 && srcDescs[0]->getPrecision() != ov::element::f16) ||
105107
srcDescs[0]->getPrecision() != dstDescs[0]->getPrecision()) {
106108
DEBUG_LOG("NEMeanStdDevNormalizationLayer does not support precisions:",

src/plugins/intel_cpu/src/nodes/executors/acl/acl_pooling.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "acl_utils.hpp"
78
#include "arm_compute/runtime/NEON/NEFunctions.h"
89
#include "nodes/executors/pooling.hpp"
910
#include "utils/debug_capabilities.h"
@@ -53,6 +54,10 @@ class AclPoolingExecutorBuilder : public PoolingExecutorBuilder {
5354
[[nodiscard]] bool isSupported(const PoolingAttrs& poolingAttrs,
5455
const std::vector<MemoryDescPtr>& srcDescs,
5556
const std::vector<MemoryDescPtr>& dstDescs) const override {
57+
if (!aclSupported({srcDescs[0], dstDescs[0]})) {
58+
DEBUG_LOG("ACL common preconditions are not met");
59+
return false;
60+
}
5661
auto isSupportedPrecision = [](const ov::element::Type precision) {
5762
return any_of(precision, ov::element::f32, ov::element::f16, ov::element::u8, ov::element::i8);
5863
};

src/plugins/intel_cpu/src/nodes/executors/acl/acl_reduce.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class AclReduceExecutorBuilder : public ReduceExecutorBuilder {
4242
[[nodiscard]] bool isSupported(const ReduceAttrs& reduceAttrs,
4343
const std::vector<MemoryDescPtr>& srcDescs,
4444
const std::vector<MemoryDescPtr>& dstDescs) const override {
45+
if (!aclSupported({srcDescs[0], dstDescs[0]})) {
46+
DEBUG_LOG("ACL common preconditions are not met");
47+
return false;
48+
}
4549
if (reduceAttrs.operation == Algorithm::ReduceMean) {
4650
if (srcDescs[0]->getPrecision() != dstDescs[0]->getPrecision() ||
4751
(srcDescs[0]->getPrecision() != ov::element::f32 && srcDescs[0]->getPrecision() != ov::element::f16)) {

0 commit comments

Comments
 (0)