Eigen  5.0.1
 
Loading...
Searching...
No Matches
PacketMath.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10#ifndef EIGEN_PACKET_MATH_SME_H
11#define EIGEN_PACKET_MATH_SME_H
12
13// IWYU pragma: private
14#include "../../InternalHeaderCheck.h"
15
16namespace Eigen {
17namespace internal {
18
19// ---------------------------------------------------------------------------
20// Streaming-mode packet layer of the SME backend.
21//
22// The evaluators keep NEON packets; the streaming SVE registers and the ZA array
23// are reached only from the product kernels, through the operations below, which
24// cannot be the packet_traits type or p* specializations:
25// - a streaming vector (svfloat32_t, ...) is sizeless -- no sizeof, not a
26// member or array element type, lane count known at run time -- so neither
27// unpacket_traits<>::size nor PacketBlock can describe it;
28// - code touching one must carry __arm_streaming (or _compatible), which is
29// part of the function type: a p* specialization cannot add it, and an
30// evaluator cannot call it without a mode switch, nor at all where the
31// target has no non-streaming SVE (-mcpu=apple-m4);
32// - streaming mode runs scalar FP 30-80x slower and NEON not at all on Apple M4.
33// So these are __arm_streaming overloads on the streaming vector types: Eigen's
34// names and argument order where a counterpart exists (pset1, ploadu, pstoreu,
35// padd, pmul, pnegate, pmadd, pnmadd, predux), the arithmetic and memory
36// operations taking the SVE governing predicate first; the SVE name with Eigen's
37// p prefix where none does (pget, pcreate, pld2, pst2, puzp1/2, pzip1/2, psplice); an sme_
38// prefix for the ZA tile operations; and the predicates and lane counts as
39// members of sme_packet_traits. Eigen's templates of the same names drop out of
40// overload resolution, by arity or by substitution failure on the empty
41// unpacket_traits below.
42// ---------------------------------------------------------------------------
43
44// ZA tile count at a given element width: 4 ZA.S tiles, 8 ZA.D tiles. Architectural rather than
45// feature-dependent, so it stays outside sme_packet_traits, which has no double specialization
46// without FEAT_SME_F64F64.
47template <typename RealScalar>
48struct sme_tile_count;
49template <>
50struct sme_tile_count<float> {
51 static constexpr int value = 4;
52};
53template <>
54struct sme_tile_count<double> {
55 static constexpr int value = 8;
56};
57
58// Clang 23 and later never inline a private-ZA function (one with neither a shared-ZA attribute nor
59// __arm_agnostic("sme_za_state")) into a caller with ZA state, always_inline or not: the call stays
60// out of line behind a TPIDR2 lazy-save setup. So every function reachable from __arm_inout("za") or
61// __arm_new("za") code needs a shared-ZA attribute or EIGEN_SME_ZA_AGNOSTIC, whether or not it uses
62// intrinsics; agnostic rather than shared because private-ZA streaming entry points such as
63// pack_direct call the same helpers. GCC and Clang before 20 lack the keyword and inline these anyway.
64#if EIGEN_COMP_CLANG
65#if !__is_identifier(__arm_agnostic)
66#define EIGEN_SME_ZA_AGNOSTIC __arm_agnostic("sme_za_state")
67#endif
68#endif
69#ifndef EIGEN_SME_ZA_AGNOSTIC
70#define EIGEN_SME_ZA_AGNOSTIC
71#endif
72
73// Scalar -> streaming vector, its two- and four-vector tuples, and the predicates of its element
74// width; size() is the lane count of one streaming vector, a runtime value. whilelt takes int64_t:
75// svwhilelt_b* is overloaded on the four fixed-width types only, so Index -- `long` where int64_t
76// is `long long` -- matches none of them exactly; every bound the kernels pass is a non-negative
77// Index.
78template <typename Scalar>
79struct sme_packet_traits {};
80
81template <>
82struct sme_packet_traits<float> {
83 using type = svfloat32_t;
84 using type_x2 = svfloat32x2_t;
85 using type_x4 = svfloat32x4_t;
86 static EIGEN_ALWAYS_INLINE int size() __arm_streaming_compatible EIGEN_SME_ZA_AGNOSTIC {
87 return static_cast<int>(svcntsw());
88 }
89 static EIGEN_ALWAYS_INLINE svbool_t ptrue() __arm_streaming EIGEN_SME_ZA_AGNOSTIC { return svptrue_b32(); }
90 static EIGEN_ALWAYS_INLINE svcount_t ptrue_c() __arm_streaming EIGEN_SME_ZA_AGNOSTIC { return svptrue_c32(); }
91 static EIGEN_ALWAYS_INLINE svbool_t whilelt(int64_t begin, int64_t end) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
92 return svwhilelt_b32(begin, end);
93 }
94 static EIGEN_ALWAYS_INLINE svcount_t whilelt_c4(int64_t begin, int64_t end) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
95 return svwhilelt_c32_s64(begin, end, 4);
96 }
97};
98
99#ifdef EIGEN_VECTORIZE_SME_F64F64
100template <>
101struct sme_packet_traits<double> {
102 using type = svfloat64_t;
103 using type_x2 = svfloat64x2_t;
104 using type_x4 = svfloat64x4_t;
105 static EIGEN_ALWAYS_INLINE int size() __arm_streaming_compatible EIGEN_SME_ZA_AGNOSTIC {
106 return static_cast<int>(svcntsd());
107 }
108 static EIGEN_ALWAYS_INLINE svbool_t ptrue() __arm_streaming EIGEN_SME_ZA_AGNOSTIC { return svptrue_b64(); }
109 static EIGEN_ALWAYS_INLINE svcount_t ptrue_c() __arm_streaming EIGEN_SME_ZA_AGNOSTIC { return svptrue_c64(); }
110 static EIGEN_ALWAYS_INLINE svbool_t whilelt(int64_t begin, int64_t end) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
111 return svwhilelt_b64(begin, end);
112 }
113 static EIGEN_ALWAYS_INLINE svcount_t whilelt_c4(int64_t begin, int64_t end) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
114 return svwhilelt_c64_s64(begin, end, 4);
115 }
116};
117#endif
118
119// Streaming vector -> scalar. The primary template has no members, so pset1's parameter type
120// below fails substitution for every other packet type and Eigen's own pset1 is selected there.
121template <typename Packet>
122struct sme_unpacket_traits {};
123template <>
124struct sme_unpacket_traits<svfloat32_t> {
125 using type = float;
126 static EIGEN_ALWAYS_INLINE svfloat32_t dup(float from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
127 return svdup_f32(from);
128 }
129};
130#ifdef EIGEN_VECTORIZE_SME_F64F64
131template <>
132struct sme_unpacket_traits<svfloat64_t> {
133 using type = double;
134 static EIGEN_ALWAYS_INLINE svfloat64_t dup(double from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
135 return svdup_f64(from);
136 }
137};
138#endif
139
140// A streaming vector is neither a packet nor a scalar, so unpacket_traits has no members for it: a
141// signature naming unpacket_traits<Packet>::type, such as Eigen's pset1, fails substitution, and a
142// generic operation called without its predicate (predux(v), pfirst(v)) does not compile, where
143// mapping the vector to itself would make it a one-lane "scalar" and resolve those to the identity.
144// The primary template cannot serve: it applies sizeof, which a sizeless type does not have.
145template <>
146struct unpacket_traits<svfloat32_t> {};
147#ifdef EIGEN_VECTORIZE_SME_F64F64
148template <>
149struct unpacket_traits<svfloat64_t> {};
150#endif
151
152// An overload of Eigen's pset1 rather than a specialization (see the file comment); the two never
153// compete, since each fails substitution on the other's vector types.
154template <typename Packet>
155EIGEN_ALWAYS_INLINE Packet
156pset1(typename sme_unpacket_traits<Packet>::type from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
157 return sme_unpacket_traits<Packet>::dup(from);
158}
159
160// Every operation takes its governing predicate, as the ACLE intrinsics do: the kernels keep the
161// predicate of the enclosing loop live, and an all-true one materialized per operation instead
162// costs a PTRUE and a P register inside the ZA store loops (48 PTRUEs and 10 predicate spills
163// against 10 and none in the float GEMM kernel). The vector type follows the scalar pointer or the
164// vector arguments, so one template per operation covers every element width through the
165// type-generic ACLE overloads. SVE loads have no alignment requirement, hence only the unaligned
166// spellings; _x2/_x4 move two and four consecutive vectors under one SME2 predicate-as-counter.
167template <typename Scalar>
168EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type ploadu(
169 svbool_t pg, const Scalar* from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
170 return svld1(pg, from);
171}
172template <typename Scalar>
173EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type_x2 ploadu_x2(
174 svcount_t pn, const Scalar* from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
175 return svld1_x2(pn, from);
176}
177template <typename Scalar>
178EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type_x4 ploadu_x4(
179 svcount_t pn, const Scalar* from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
180 return svld1_x4(pn, from);
181}
182template <typename Scalar>
183EIGEN_ALWAYS_INLINE void pstoreu(svbool_t pg, Scalar* to,
184 typename sme_packet_traits<Scalar>::type from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
185 svst1(pg, to, from);
186}
187template <typename Scalar>
188EIGEN_ALWAYS_INLINE void pstoreu_x2(
189 svcount_t pn, Scalar* to, typename sme_packet_traits<Scalar>::type_x2 from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
190 svst1(pn, to, from);
191}
192template <typename Scalar>
193EIGEN_ALWAYS_INLINE void pstoreu_x4(
194 svcount_t pn, Scalar* to, typename sme_packet_traits<Scalar>::type_x4 from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
195 svst1(pn, to, from);
196}
197// Two-element structure load and store (LD2/ST2): pld2 splits interleaved pairs into the even and odd
198// lanes, the real and imaginary parts of a complex array, and pst2 interleaves them back.
199template <typename Scalar>
200EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type_x2 pld2(
201 svbool_t pg, const Scalar* from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
202 return svld2(pg, from);
203}
204template <typename Scalar>
205EIGEN_ALWAYS_INLINE void pst2(svbool_t pg, Scalar* to,
206 typename sme_packet_traits<Scalar>::type_x2 from) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
207 svst2(pg, to, from);
208}
209
210// Arithmetic in Eigen's argument order after the predicate: pmadd(pg, a, b, c) = a * b + c and
211// pnmadd(pg, a, b, c) = c - a * b, each one fused operation. Inactive lanes are unspecified (the
212// _x forms); every consumer stores through a predicate of its own.
213template <typename Packet>
214EIGEN_ALWAYS_INLINE Packet padd(svbool_t pg, Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
215 return svadd_x(pg, a, b);
216}
217template <typename Packet>
218EIGEN_ALWAYS_INLINE Packet pmul(svbool_t pg, Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
219 return svmul_x(pg, a, b);
220}
221template <typename Packet>
222EIGEN_ALWAYS_INLINE Packet pnegate(svbool_t pg, Packet a) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
223 return svneg_x(pg, a);
224}
225template <typename Packet>
226EIGEN_ALWAYS_INLINE Packet pmadd(svbool_t pg, Packet a, Packet b, Packet c) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
227 return svmla_x(pg, c, a, b);
228}
229template <typename Packet>
230EIGEN_ALWAYS_INLINE Packet pnmadd(svbool_t pg, Packet a, Packet b, Packet c) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
231 return svmls_x(pg, c, a, b);
232}
233// Merging form: inactive lanes keep c, in the one FMLA that a select after the _x form does not fold to.
234template <typename Packet>
235EIGEN_ALWAYS_INLINE Packet pmadd_m(svbool_t pg, Packet a, Packet b, Packet c) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
236 return svmla_m(pg, c, a, b);
237}
238// Sum of the active lanes.
239template <typename Packet>
240EIGEN_ALWAYS_INLINE typename sme_unpacket_traits<Packet>::type predux(svbool_t pg,
241 Packet a) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
242 return svaddv(pg, a);
243}
244
245// Vector tuples: the lane of a tuple is an instruction immediate, hence a template parameter.
246template <int Lane>
247EIGEN_ALWAYS_INLINE svfloat32_t pget(svfloat32x2_t v) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
248 return svget2_f32(v, Lane);
249}
250template <int Lane>
251EIGEN_ALWAYS_INLINE svfloat32_t pget(svfloat32x4_t v) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
252 return svget4_f32(v, Lane);
253}
254#ifdef EIGEN_VECTORIZE_SME_F64F64
255template <int Lane>
256EIGEN_ALWAYS_INLINE svfloat64_t pget(svfloat64x2_t v) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
257 return svget2_f64(v, Lane);
258}
259template <int Lane>
260EIGEN_ALWAYS_INLINE svfloat64_t pget(svfloat64x4_t v) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
261 return svget4_f64(v, Lane);
262}
263#endif
264template <typename Packet>
265EIGEN_ALWAYS_INLINE auto pcreate(Packet a, Packet b) EIGEN_SME_ZA_AGNOSTIC __arm_streaming
266 -> decltype(svcreate2(a, b)) {
267 return svcreate2(a, b);
268}
269template <typename Packet>
270EIGEN_ALWAYS_INLINE auto pcreate(Packet a, Packet b, Packet c, Packet d) EIGEN_SME_ZA_AGNOSTIC __arm_streaming
271 -> decltype(svcreate4(a, b, c, d)) {
272 return svcreate4(a, b, c, d);
273}
274
275// Permutes: puzp1/puzp2 gather the even/odd lanes of the concatenation (a, b), pzip1/pzip2
276// interleave its low/high halves, psplice(pg, a, b) is the active lanes of a followed by the leading
277// lanes of b.
278template <typename Packet>
279EIGEN_ALWAYS_INLINE Packet puzp1(Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
280 return svuzp1(a, b);
281}
282template <typename Packet>
283EIGEN_ALWAYS_INLINE Packet puzp2(Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
284 return svuzp2(a, b);
285}
286template <typename Packet>
287EIGEN_ALWAYS_INLINE Packet pzip1(Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
288 return svzip1(a, b);
289}
290template <typename Packet>
291EIGEN_ALWAYS_INLINE Packet pzip2(Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
292 return svzip2(a, b);
293}
294template <typename Packet>
295EIGEN_ALWAYS_INLINE Packet psplice(svbool_t pg, Packet a, Packet b) __arm_streaming EIGEN_SME_ZA_AGNOSTIC {
296 return svsplice(pg, a, b);
297}
298
299// Entering and leaving streaming mode sets FPSR's cumulative exception flags (Arm DDI0616, RMHTLZ), also when the OS
300// resumes a thread in streaming mode, and FP arithmetic into ZA raises none. So a call into streaming code keeps the
301// caller's flags and reports none of its own.
302struct sme_fpsr_guard {
303 EIGEN_ALWAYS_INLINE sme_fpsr_guard() { asm volatile("mrs %0, fpsr" : "=r"(value) : : "memory"); }
304 EIGEN_ALWAYS_INLINE ~sme_fpsr_guard() { asm volatile("msr fpsr, %0" : : "r"(value) : "memory"); }
305 sme_fpsr_guard(const sme_fpsr_guard&) = delete;
306 sme_fpsr_guard& operator=(const sme_fpsr_guard&) = delete;
307
308 private:
309 std::uint64_t value;
310};
311
312// min() usable from streaming functions (numext::mini lacks the __arm_streaming_compatible attribute).
313template <typename T>
314EIGEN_ALWAYS_INLINE T sme_min(T a, T b) __arm_streaming_compatible EIGEN_SME_ZA_AGNOSTIC {
315 return a < b ? a : b;
316}
317
318// Offset a pointer by n elements without forming the pointer value: an access whose predicate is
319// empty makes no memory reference, but computing an address more than one past the end of the
320// object is undefined regardless, so the second-vector accesses of the kernels reach their address
321// through uintptr_t.
322template <typename T>
323EIGEN_ALWAYS_INLINE T* sme_offset(T* p, Index n) __arm_streaming_compatible EIGEN_SME_ZA_AGNOSTIC {
324 return reinterpret_cast<T*>(uintptr_t(p) + ptrdiff_t(n) * sizeof(T));
325}
326
327// ZA tile access. A tile number is an instruction immediate, hence a template parameter; a slice
328// number is a register operand and stays a value. The element width follows the vector or pointer
329// argument, or is given as the scalar type where there is neither (sme_za_read).
330template <int Tile>
331EIGEN_ALWAYS_INLINE void sme_ld1_hor_za(uint32_t slice, svbool_t pg, const float* p) __arm_streaming __arm_inout("za") {
332 svld1_hor_za32(Tile, slice, pg, p);
333}
334template <int Tile>
335EIGEN_ALWAYS_INLINE svfloat32_t sme_read_hor_za(svfloat32_t zero, svbool_t pg,
336 uint32_t slice) __arm_streaming __arm_in("za") {
337 return svread_hor_za32_f32_m(zero, pg, Tile, slice);
338}
339template <int Tile>
340EIGEN_ALWAYS_INLINE svfloat32_t sme_read_ver_za(svfloat32_t zero, svbool_t pg,
341 uint32_t slice) __arm_streaming __arm_in("za") {
342 return svread_ver_za32_f32_m(zero, pg, Tile, slice);
343}
344// Four slices at once (SME2 MOVA ... vg4).
345template <int Tile>
346EIGEN_ALWAYS_INLINE void sme_write_hor_za_vg4(uint32_t slice, svfloat32_t a, svfloat32_t b, svfloat32_t c,
347 svfloat32_t d) __arm_streaming __arm_inout("za") {
348 svwrite_hor_za32_f32_vg4(Tile, slice, svcreate4_f32(a, b, c, d));
349}
350template <int Tile>
351EIGEN_ALWAYS_INLINE void sme_write_ver_za_vg4(uint32_t slice, svfloat32x4_t v) __arm_streaming __arm_inout("za") {
352 svwrite_ver_za32_f32_vg4(Tile, slice, v);
353}
354// Outer product accumulate (FMOPA) and subtract (FMOPS) into a tile.
355template <int Tile>
356EIGEN_ALWAYS_INLINE void sme_mopa(svbool_t pm, svbool_t pn, svfloat32_t a,
357 svfloat32_t b) __arm_streaming __arm_inout("za") {
358 svmopa_za32_f32_m(Tile, pm, pn, a, b);
359}
360template <int Tile>
361EIGEN_ALWAYS_INLINE void sme_mops(svbool_t pm, svbool_t pn, svfloat32_t a,
362 svfloat32_t b) __arm_streaming __arm_inout("za") {
363 svmops_za32_f32_m(Tile, pm, pn, a, b);
364}
365// SME2 multi-vector FMLA into four ZA array vectors, the vector-length accumulators of the vector
366// kernels; four independent groups hide the accumulator latency.
367EIGEN_ALWAYS_INLINE void sme_madd_za_vg1x4(uint32_t slice, svfloat32x4_t x,
368 svfloat32_t y) __arm_streaming __arm_inout("za") {
369 svmla_single_za32_f32_vg1x4(slice, x, y);
370}
371EIGEN_ALWAYS_INLINE void sme_madd_za_vg1x4(uint32_t slice, svfloat32x4_t x,
372 svfloat32x4_t y) __arm_streaming __arm_inout("za") {
373 svmla_za32_f32_vg1x4(slice, x, y);
374}
375EIGEN_ALWAYS_INLINE void sme_write_za_vg1x4(uint32_t slice, svfloat32x4_t x) __arm_streaming __arm_inout("za") {
376 svwrite_za32_f32_vg1x4(slice, x);
377}
378
379#ifdef EIGEN_VECTORIZE_SME_F64F64
380template <int Tile>
381EIGEN_ALWAYS_INLINE void sme_ld1_hor_za(uint32_t slice, svbool_t pg,
382 const double* p) __arm_streaming __arm_inout("za") {
383 svld1_hor_za64(Tile, slice, pg, p);
384}
385template <int Tile>
386EIGEN_ALWAYS_INLINE svfloat64_t sme_read_hor_za(svfloat64_t zero, svbool_t pg,
387 uint32_t slice) __arm_streaming __arm_in("za") {
388 return svread_hor_za64_f64_m(zero, pg, Tile, slice);
389}
390template <int Tile>
391EIGEN_ALWAYS_INLINE svfloat64_t sme_read_ver_za(svfloat64_t zero, svbool_t pg,
392 uint32_t slice) __arm_streaming __arm_in("za") {
393 return svread_ver_za64_f64_m(zero, pg, Tile, slice);
394}
395template <int Tile>
396EIGEN_ALWAYS_INLINE void sme_write_hor_za_vg4(uint32_t slice, svfloat64_t a, svfloat64_t b, svfloat64_t c,
397 svfloat64_t d) __arm_streaming __arm_inout("za") {
398 svwrite_hor_za64_f64_vg4(Tile, slice, svcreate4_f64(a, b, c, d));
399}
400template <int Tile>
401EIGEN_ALWAYS_INLINE void sme_write_ver_za_vg4(uint32_t slice, svfloat64x4_t v) __arm_streaming __arm_inout("za") {
402 svwrite_ver_za64_f64_vg4(Tile, slice, v);
403}
404template <int Tile>
405EIGEN_ALWAYS_INLINE void sme_mopa(svbool_t pm, svbool_t pn, svfloat64_t a,
406 svfloat64_t b) __arm_streaming __arm_inout("za") {
407 svmopa_za64_f64_m(Tile, pm, pn, a, b);
408}
409template <int Tile>
410EIGEN_ALWAYS_INLINE void sme_mops(svbool_t pm, svbool_t pn, svfloat64_t a,
411 svfloat64_t b) __arm_streaming __arm_inout("za") {
412 svmops_za64_f64_m(Tile, pm, pn, a, b);
413}
414EIGEN_ALWAYS_INLINE void sme_madd_za_vg1x4(uint32_t slice, svfloat64x4_t x,
415 svfloat64_t y) __arm_streaming __arm_inout("za") {
416 svmla_single_za64_f64_vg1x4(slice, x, y);
417}
418EIGEN_ALWAYS_INLINE void sme_madd_za_vg1x4(uint32_t slice, svfloat64x4_t x,
419 svfloat64x4_t y) __arm_streaming __arm_inout("za") {
420 svmla_za64_f64_vg1x4(slice, x, y);
421}
422EIGEN_ALWAYS_INLINE void sme_write_za_vg1x4(uint32_t slice, svfloat64x4_t x) __arm_streaming __arm_inout("za") {
423 svwrite_za64_f64_vg1x4(slice, x);
424}
425#endif // EIGEN_VECTORIZE_SME_F64F64
426
427// The two reads with no width-carrying argument: four vertical slices of a tile, and four ZA array
428// vectors.
429template <typename Scalar>
430struct sme_za_read;
431template <>
432struct sme_za_read<float> {
433 template <int Tile>
434 static EIGEN_ALWAYS_INLINE svfloat32x4_t ver_vg4(uint32_t slice) __arm_streaming __arm_in("za") {
435 return svread_ver_za32_f32_vg4(Tile, slice);
436 }
437 static EIGEN_ALWAYS_INLINE svfloat32x4_t vg1x4(uint32_t slice) __arm_streaming __arm_in("za") {
438 return svread_za32_f32_vg1x4(slice);
439 }
440};
441#ifdef EIGEN_VECTORIZE_SME_F64F64
442template <>
443struct sme_za_read<double> {
444 template <int Tile>
445 static EIGEN_ALWAYS_INLINE svfloat64x4_t ver_vg4(uint32_t slice) __arm_streaming __arm_in("za") {
446 return svread_ver_za64_f64_vg4(Tile, slice);
447 }
448 static EIGEN_ALWAYS_INLINE svfloat64x4_t vg1x4(uint32_t slice) __arm_streaming __arm_in("za") {
449 return svread_za64_f64_vg1x4(slice);
450 }
451};
452#endif
453template <int Tile, typename Scalar>
454EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type_x4 sme_read_ver_za_vg4(
455 uint32_t slice) __arm_streaming __arm_in("za") {
456 return sme_za_read<Scalar>::template ver_vg4<Tile>(slice);
457}
458template <typename Scalar>
459EIGEN_ALWAYS_INLINE typename sme_packet_traits<Scalar>::type_x4 sme_read_za_vg1x4(
460 uint32_t slice) __arm_streaming __arm_in("za") {
461 return sme_za_read<Scalar>::vg1x4(slice);
462}
463
464// Outer-product accumulate with a compile-time sign: the complex kernel's four real products differ
465// only in whether they add or subtract into the tile.
466template <int Tile, bool Subtract, typename Packet>
467EIGEN_ALWAYS_INLINE void sme_mopa_signed(svbool_t pm, svbool_t pn, Packet a,
468 Packet b) __arm_streaming __arm_inout("za") {
469 EIGEN_IF_CONSTEXPR (Subtract) {
470 sme_mops<Tile>(pm, pn, a, b);
471 } else {
472 sme_mopa<Tile>(pm, pn, a, b);
473 }
474}
475
476} // namespace internal
477} // namespace Eigen
478
479#endif // EIGEN_PACKET_MATH_SME_H