1*5f39d1b3SJooyung Han // Copyright 2016 The gemmlowp Authors. All Rights Reserved.
2*5f39d1b3SJooyung Han //
3*5f39d1b3SJooyung Han // Licensed under the Apache License, Version 2.0 (the "License");
4*5f39d1b3SJooyung Han // you may not use this file except in compliance with the License.
5*5f39d1b3SJooyung Han // You may obtain a copy of the License at
6*5f39d1b3SJooyung Han //
7*5f39d1b3SJooyung Han // http://www.apache.org/licenses/LICENSE-2.0
8*5f39d1b3SJooyung Han //
9*5f39d1b3SJooyung Han // Unless required by applicable law or agreed to in writing, software
10*5f39d1b3SJooyung Han // distributed under the License is distributed on an "AS IS" BASIS,
11*5f39d1b3SJooyung Han // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5f39d1b3SJooyung Han // See the License for the specific language governing permissions and
13*5f39d1b3SJooyung Han // limitations under the License.
14*5f39d1b3SJooyung Han
15*5f39d1b3SJooyung Han // This is a standalone testbed and benchmark for gemmlowp-style GEMM kernels,
16*5f39d1b3SJooyung Han // either doing integer or float arithmetic.
17*5f39d1b3SJooyung Han // It verifies that a kernel produces correct results, then benchmarks it.
18*5f39d1b3SJooyung Han //
19*5f39d1b3SJooyung Han // Some benchmark results are recorded in this spreadsheet:
20*5f39d1b3SJooyung Han //
21*5f39d1b3SJooyung Han // https://docs.google.com/spreadsheets/d/1UPbzbp9rdsD6RXxOr5q6AZ0n1omgEknLYO2ogiw6Kqk/edit?usp=sharing
22*5f39d1b3SJooyung Han //
23*5f39d1b3SJooyung Han // This program is entirely self-contained, and can be compiled manually
24*5f39d1b3SJooyung Han // such as suggested in the command lines below.
25*5f39d1b3SJooyung Han // It currently supports only Android/ARM but would trivially generalize to
26*5f39d1b3SJooyung Han // other OSes (it's mostly standard POSIX) or architectures (each kernel
27*5f39d1b3SJooyung Han // targets a specific architecture, one may simply add more).
28*5f39d1b3SJooyung Han
29*5f39d1b3SJooyung Han /*
30*5f39d1b3SJooyung Han Build and run this benchmark on Android/ARM/32bit:
31*5f39d1b3SJooyung Han ~/android/toolchains/arm-linux-androideabi/bin/arm-linux-androideabi-clang++ \
32*5f39d1b3SJooyung Han -fPIE -pie -O3 --std=c++11 standalone/neon-gemm-kernel-benchmark.cc -o \
33*5f39d1b3SJooyung Han /tmp/benchmark -mfloat-abi=softfp -mfpu=neon-vfpv4 && adb push /tmp/benchmark \
34*5f39d1b3SJooyung Han /data/local/tmp && adb shell /data/local/tmp/benchmark
35*5f39d1b3SJooyung Han Build and run this benchmark on Android/ARM/64bit:
36*5f39d1b3SJooyung Han ~/android/toolchains/aarch64-linux-android/bin/aarch64-linux-android-clang++ \
37*5f39d1b3SJooyung Han -fPIE -static -O3 --std=c++11 standalone/neon-gemm-kernel-benchmark.cc -o \
38*5f39d1b3SJooyung Han /tmp/benchmark && adb push /tmp/benchmark /data/local/tmp && adb shell \
39*5f39d1b3SJooyung Han /data/local/tmp/benchmark
40*5f39d1b3SJooyung Han */
41*5f39d1b3SJooyung Han
42*5f39d1b3SJooyung Han // For big.LITTLE devices, use 'taskset' to select which cores to benchmark.
43*5f39d1b3SJooyung Han //
44*5f39d1b3SJooyung Han // The syntax is: taskset <mask> <commandline>
45*5f39d1b3SJooyung Han // where mask is a binary mask where each bit corresponds to a core,
46*5f39d1b3SJooyung Han // and low bits are little cores.
47*5f39d1b3SJooyung Han //
48*5f39d1b3SJooyung Han // Examples:
49*5f39d1b3SJooyung Han // Nexus 5X big cores: taskset 30
50*5f39d1b3SJooyung Han // Nexus 5X little cores: taskset 0f
51*5f39d1b3SJooyung Han // Pixel XL big cores: taskset 0c
52*5f39d1b3SJooyung Han // Pixel XL little cores: taskset 03
53*5f39d1b3SJooyung Han //
54*5f39d1b3SJooyung Han // Full example:
55*5f39d1b3SJooyung Han // adb shell taskset 0c /data/local/tmp/benchmark
56*5f39d1b3SJooyung Han
57*5f39d1b3SJooyung Han #include <sched.h>
58*5f39d1b3SJooyung Han #include <unistd.h>
59*5f39d1b3SJooyung Han
60*5f39d1b3SJooyung Han #include <algorithm>
61*5f39d1b3SJooyung Han #include <cassert>
62*5f39d1b3SJooyung Han #include <cstdint>
63*5f39d1b3SJooyung Han #include <cstdlib>
64*5f39d1b3SJooyung Han #include <cstring>
65*5f39d1b3SJooyung Han #include <iostream>
66*5f39d1b3SJooyung Han #include <random>
67*5f39d1b3SJooyung Han #include <type_traits>
68*5f39d1b3SJooyung Han
69*5f39d1b3SJooyung Han #if !defined(__arm__) && !defined(__aarch64__) && \
70*5f39d1b3SJooyung Han !(defined(__mips) && (__mips_isa_rev >= 5) && defined(__mips_msa))
71*5f39d1b3SJooyung Han #error This benchmark assumes ARM or MIPS (for intrinsics and inline assembly sections).
72*5f39d1b3SJooyung Han #endif
73*5f39d1b3SJooyung Han
74*5f39d1b3SJooyung Han #if defined(__arm__) || defined(__aarch64__)
75*5f39d1b3SJooyung Han #include <arm_neon.h>
76*5f39d1b3SJooyung Han #endif
77*5f39d1b3SJooyung Han
78*5f39d1b3SJooyung Han #if defined(__mips)
79*5f39d1b3SJooyung Han #include <msa.h>
80*5f39d1b3SJooyung Han
81*5f39d1b3SJooyung Han // Some convenience macros to hide differences between MIPS32 and MIPS64.
82*5f39d1b3SJooyung Han #ifdef __LP64__
83*5f39d1b3SJooyung Han #define GEMMLOWP_MIPS_XADDIU "daddiu"
84*5f39d1b3SJooyung Han #else
85*5f39d1b3SJooyung Han #define GEMMLOWP_MIPS_XADDIU "addiu"
86*5f39d1b3SJooyung Han #endif
87*5f39d1b3SJooyung Han #endif
88*5f39d1b3SJooyung Han
89*5f39d1b3SJooyung Han // Typically one wants to fit in L1 cache, and GEMM implementations
90*5f39d1b3SJooyung Han // are carefully optimized to tune their access patterns to that effect.
91*5f39d1b3SJooyung Han // Most devices have at least 16k of L1 cache. The Kraits have exactly 16k.
92*5f39d1b3SJooyung Han const int kDefaultCacheSizeK = 16;
93*5f39d1b3SJooyung Han
94*5f39d1b3SJooyung Han const int kCacheLineSize = 64;
95*5f39d1b3SJooyung Han
96*5f39d1b3SJooyung Han // These definitions are used for labels within assembly code. Required for
97*5f39d1b3SJooyung Han // iOS toolchain compatibility.
98*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_AFTER_LOOP "1"
99*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_LOOP "2"
100*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES "3"
101*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_STORE "4"
102*5f39d1b3SJooyung Han
103*5f39d1b3SJooyung Han // BEGIN code copied from gemmlowp/internal/kernel.h
104*5f39d1b3SJooyung Han
105*5f39d1b3SJooyung Han // Explanation of general gemmlowp terminology
106*5f39d1b3SJooyung Han // ===========================================
107*5f39d1b3SJooyung Han //
108*5f39d1b3SJooyung Han // We use the following abbreviations:
109*5f39d1b3SJooyung Han // LHS = "left-hand side"
110*5f39d1b3SJooyung Han // RHS = "right-hand side"
111*5f39d1b3SJooyung Han // Sometimes when referring to either LHS or RHS, we just say a "Side".
112*5f39d1b3SJooyung Han //
113*5f39d1b3SJooyung Han // In a matrix product of a MxK matrix times a KxN matrix,
114*5f39d1b3SJooyung Han // we call K the 'depth'. Note that M is the number of rows
115*5f39d1b3SJooyung Han // of the result (and of the LHS), and N is the number of columns
116*5f39d1b3SJooyung Han // of the result (and of the RHS).
117*5f39d1b3SJooyung Han //
118*5f39d1b3SJooyung Han // In each of the LHS and RHS matrices, we call 'width' the
119*5f39d1b3SJooyung Han // other dimension, besides the depth. So in the LHS, 'width'
120*5f39d1b3SJooyung Han // is the number of rows, while in the RHS, 'width' is the number
121*5f39d1b3SJooyung Han // of columns.
122*5f39d1b3SJooyung Han //
123*5f39d1b3SJooyung Han // So in the LHS MxK matrix, the depth is K and the width in M.
124*5f39d1b3SJooyung Han // And in the RHS KxN matrix, the depth is K and the width in N.
125*5f39d1b3SJooyung Han //
126*5f39d1b3SJooyung Han // This is illustrated in this picture:
127*5f39d1b3SJooyung Han //
128*5f39d1b3SJooyung Han // RHS width
129*5f39d1b3SJooyung Han // <----------------->
130*5f39d1b3SJooyung Han // +-----------------+ ^
131*5f39d1b3SJooyung Han // | RHS | | Depth
132*5f39d1b3SJooyung Han // +-----------------+ v
133*5f39d1b3SJooyung Han // ^ +--+ +-----------------+
134*5f39d1b3SJooyung Han // | |L | | |
135*5f39d1b3SJooyung Han // LHS width | |H | | Result |
136*5f39d1b3SJooyung Han // | |S | | |
137*5f39d1b3SJooyung Han // v +--+ +-----------------+
138*5f39d1b3SJooyung Han // <-->
139*5f39d1b3SJooyung Han // Depth
140*5f39d1b3SJooyung Han
141*5f39d1b3SJooyung Han // Explanation of gemmlowp kernel formats and "cells"
142*5f39d1b3SJooyung Han // ==================================================
143*5f39d1b3SJooyung Han //
144*5f39d1b3SJooyung Han // Kernels operate on small LHS and RHS blocks that fit in registers.
145*5f39d1b3SJooyung Han // These blocks are stored contiguously in memory, but not always
146*5f39d1b3SJooyung Han // in a traditional column-major or row-major order; instead,
147*5f39d1b3SJooyung Han // they consist of a number of sub-blocks, which we call "cells",
148*5f39d1b3SJooyung Han // that are stored in column-major or row-major order. However,
149*5f39d1b3SJooyung Han // what really matters to us is not so much rows vs columns, but
150*5f39d1b3SJooyung Han // rather width vs depth. So we refer to "width-major" and "depth-major"
151*5f39d1b3SJooyung Han // storage orders. In the LHS, width-major means row-major,
152*5f39d1b3SJooyung Han // while in the RHS, width-major means column-major.
153*5f39d1b3SJooyung Han // There is also a third possibility, "diagonal order",
154*5f39d1b3SJooyung Han // which is unused at the moment.
155*5f39d1b3SJooyung Han //
156*5f39d1b3SJooyung Han // We aim to treat both sides, LHS and RHS, on an equal footing,
157*5f39d1b3SJooyung Han // so we call them both 'sides'. A KernelFormat thus is just a pair
158*5f39d1b3SJooyung Han // of KernelSideFormat's, one for LHS and one for RHS; each KernelSideFormat
159*5f39d1b3SJooyung Han // contains a CellFormat and a number of cells; cells are only ever
160*5f39d1b3SJooyung Han // stacked in the width dimension, which means stacked vertically in the
161*5f39d1b3SJooyung Han // LHS and stacked horizondally in the RHS.
162*5f39d1b3SJooyung Han //
163*5f39d1b3SJooyung Han // Example
164*5f39d1b3SJooyung Han // =======
165*5f39d1b3SJooyung Han //
166*5f39d1b3SJooyung Han // Let's work out the data layout expected by a kernel having the
167*5f39d1b3SJooyung Han // following format (the struct names here are defined below in this file):
168*5f39d1b3SJooyung Han //
169*5f39d1b3SJooyung Han // KernelFormat<
170*5f39d1b3SJooyung Han // KernelSideFormat<CellFormat<3, 4>, 3>,
171*5f39d1b3SJooyung Han // KernelSideFormat<CellFormat<5, 4>, 2>
172*5f39d1b3SJooyung Han // >
173*5f39d1b3SJooyung Han //
174*5f39d1b3SJooyung Han // The LHS format, KernelSideFormat<CellFormat<3, 4>, 3>, means:
175*5f39d1b3SJooyung Han // 3 cells, each cell having dimensions (width=3, depth=4), laid out in
176*5f39d1b3SJooyung Han // DepthMajor order (the default value, see CellFormat). In the LHS,
177*5f39d1b3SJooyung Han // DepthMajor means column-major, so the LHS cells are of size 3x4 in
178*5f39d1b3SJooyung Han // column-major order, so the LHS layout is:
179*5f39d1b3SJooyung Han //
180*5f39d1b3SJooyung Han // 0 3 6 9
181*5f39d1b3SJooyung Han // 1 4 7 10
182*5f39d1b3SJooyung Han // 2 5 8 11
183*5f39d1b3SJooyung Han // 12 15 18 21
184*5f39d1b3SJooyung Han // 13 16 19 22
185*5f39d1b3SJooyung Han // 14 17 20 23
186*5f39d1b3SJooyung Han // 24 27 30 33
187*5f39d1b3SJooyung Han // 25 28 31 34
188*5f39d1b3SJooyung Han // 26 29 32 35
189*5f39d1b3SJooyung Han //
190*5f39d1b3SJooyung Han // The RHS format, KernelSideFormat<CellFormat<5, 4>, 2>, means:
191*5f39d1b3SJooyung Han // 2 cells each having dimensions (width=5, depth=4), laid out in
192*5f39d1b3SJooyung Han // DepthMajor order (the default value, see CellFormat). In the RHS,
193*5f39d1b3SJooyung Han // DepthMajor means row-major, so the RHS cells are of size 4x5 in
194*5f39d1b3SJooyung Han // row-major order, so the RHS layout is:
195*5f39d1b3SJooyung Han //
196*5f39d1b3SJooyung Han // 0 1 2 3 4 20 21 22 23 24
197*5f39d1b3SJooyung Han // 5 6 7 8 9 25 26 27 28 29
198*5f39d1b3SJooyung Han // 10 11 12 13 14 30 31 32 33 34
199*5f39d1b3SJooyung Han // 15 16 17 18 19 35 36 37 38 39
200*5f39d1b3SJooyung Han
201*5f39d1b3SJooyung Han // CellOrder enumerates the possible storage orders (=layouts) for
202*5f39d1b3SJooyung Han // a cell (see explanation above).
203*5f39d1b3SJooyung Han enum class CellOrder { DepthMajor, WidthMajor, Diagonal };
204*5f39d1b3SJooyung Han
205*5f39d1b3SJooyung Han // CellFormat describes how data is laid
206*5f39d1b3SJooyung Han // out in a cell. That is, a CellOrder together with actual dimensions.
207*5f39d1b3SJooyung Han template <int tWidth, int tDepth, CellOrder tOrder>
208*5f39d1b3SJooyung Han struct CellFormat {
209*5f39d1b3SJooyung Han static const int kWidth = tWidth;
210*5f39d1b3SJooyung Han static const int kDepth = tDepth;
211*5f39d1b3SJooyung Han static const CellOrder kOrder = tOrder;
212*5f39d1b3SJooyung Han
213*5f39d1b3SJooyung Han static const int kSize = kWidth * kDepth;
214*5f39d1b3SJooyung Han };
215*5f39d1b3SJooyung Han
216*5f39d1b3SJooyung Han // KernelSideFormat describes how data is laid out in a kernel side
217*5f39d1b3SJooyung Han // (i.e. LHS or RHS). That is, a CellFormat together with a number of
218*5f39d1b3SJooyung Han // cells. These cells are always stacked in the Width dimension.
219*5f39d1b3SJooyung Han // For example, in the LHS case, the Width dimension is the rows dimension,
220*5f39d1b3SJooyung Han // se we're saying that in the LHS, cells are stacked vertically.
221*5f39d1b3SJooyung Han // We never stack cells in the Depth dimension.
222*5f39d1b3SJooyung Han template <typename tCellFormat, int tCells>
223*5f39d1b3SJooyung Han struct KernelSideFormat {
224*5f39d1b3SJooyung Han typedef tCellFormat Cell;
225*5f39d1b3SJooyung Han static const int kCells = tCells;
226*5f39d1b3SJooyung Han static const int kWidth = kCells * Cell::kWidth;
227*5f39d1b3SJooyung Han static const int kDepth = Cell::kDepth;
228*5f39d1b3SJooyung Han };
229*5f39d1b3SJooyung Han
230*5f39d1b3SJooyung Han // KernelFormat describes fully the input data layout that a kernel expects.
231*5f39d1b3SJooyung Han // It consists of two KernelSideFormat's, one for LHS and one for RHS.
232*5f39d1b3SJooyung Han template <typename tLhs, typename tRhs>
233*5f39d1b3SJooyung Han struct KernelFormat {
234*5f39d1b3SJooyung Han typedef tLhs Lhs;
235*5f39d1b3SJooyung Han typedef tRhs Rhs;
236*5f39d1b3SJooyung Han
237*5f39d1b3SJooyung Han static_assert(Lhs::Cell::kDepth == Rhs::Cell::kDepth, "");
238*5f39d1b3SJooyung Han static const int kDepth = Lhs::Cell::kDepth;
239*5f39d1b3SJooyung Han static const int kRows = Lhs::Cell::kWidth * Lhs::kCells;
240*5f39d1b3SJooyung Han static const int kCols = Rhs::Cell::kWidth * Rhs::kCells;
241*5f39d1b3SJooyung Han };
242*5f39d1b3SJooyung Han
243*5f39d1b3SJooyung Han // KernelOperandRanges specifies the minimum and maximum values an operand can
244*5f39d1b3SJooyung Han // take. It consists of two ranges: one for the LHS and one for the RHS. The
245*5f39d1b3SJooyung Han // default values are the minimum and maximum values of the operand data type.
246*5f39d1b3SJooyung Han template <typename Kernel, typename OperandType = typename Kernel::OperandType>
247*5f39d1b3SJooyung Han struct KernelOperandRanges {
LhsMinKernelOperandRanges248*5f39d1b3SJooyung Han static OperandType LhsMin() {
249*5f39d1b3SJooyung Han return std::numeric_limits<OperandType>::lowest();
250*5f39d1b3SJooyung Han }
LhsMaxKernelOperandRanges251*5f39d1b3SJooyung Han static OperandType LhsMax() {
252*5f39d1b3SJooyung Han return std::numeric_limits<OperandType>::max();
253*5f39d1b3SJooyung Han }
RhsMinKernelOperandRanges254*5f39d1b3SJooyung Han static OperandType RhsMin() {
255*5f39d1b3SJooyung Han return std::numeric_limits<OperandType>::lowest();
256*5f39d1b3SJooyung Han }
RhsMaxKernelOperandRanges257*5f39d1b3SJooyung Han static OperandType RhsMax() {
258*5f39d1b3SJooyung Han return std::numeric_limits<OperandType>::max();
259*5f39d1b3SJooyung Han }
260*5f39d1b3SJooyung Han };
261*5f39d1b3SJooyung Han
262*5f39d1b3SJooyung Han template <typename Kernel>
263*5f39d1b3SJooyung Han struct KernelOperandRanges<Kernel, float> {
LhsMinKernelOperandRanges264*5f39d1b3SJooyung Han static float LhsMin() { return -100.f; }
LhsMaxKernelOperandRanges265*5f39d1b3SJooyung Han static float LhsMax() { return 100.f; }
RhsMinKernelOperandRanges266*5f39d1b3SJooyung Han static float RhsMin() { return -100.f; }
RhsMaxKernelOperandRanges267*5f39d1b3SJooyung Han static float RhsMax() { return 100.f; }
268*5f39d1b3SJooyung Han };
269*5f39d1b3SJooyung Han
270*5f39d1b3SJooyung Han #define SET_7BIT_RANGES(kernel) \
271*5f39d1b3SJooyung Han template <> \
272*5f39d1b3SJooyung Han struct KernelOperandRanges<kernel, std::int8_t> { \
273*5f39d1b3SJooyung Han static std::int8_t LhsMin() { return -63; } \
274*5f39d1b3SJooyung Han static std::int8_t LhsMax() { return 63; } \
275*5f39d1b3SJooyung Han static std::int8_t RhsMin() { return -64; } \
276*5f39d1b3SJooyung Han static std::int8_t RhsMax() { return 63; } \
277*5f39d1b3SJooyung Han };
278*5f39d1b3SJooyung Han
279*5f39d1b3SJooyung Han #define SET_425BIT_RANGES(kernel) \
280*5f39d1b3SJooyung Han template <> \
281*5f39d1b3SJooyung Han struct KernelOperandRanges<kernel, std::int8_t> { \
282*5f39d1b3SJooyung Han static std::int8_t LhsMin() { return -7; } \
283*5f39d1b3SJooyung Han static std::int8_t LhsMax() { return 7; } \
284*5f39d1b3SJooyung Han static std::int8_t RhsMin() { return -9; } \
285*5f39d1b3SJooyung Han static std::int8_t RhsMax() { return 9; } \
286*5f39d1b3SJooyung Han };
287*5f39d1b3SJooyung Han
CellOrderName(CellOrder o)288*5f39d1b3SJooyung Han inline const char* CellOrderName(CellOrder o) {
289*5f39d1b3SJooyung Han switch (o) {
290*5f39d1b3SJooyung Han case CellOrder::DepthMajor:
291*5f39d1b3SJooyung Han return "DepthMajor";
292*5f39d1b3SJooyung Han case CellOrder::WidthMajor:
293*5f39d1b3SJooyung Han return "WidthMajor";
294*5f39d1b3SJooyung Han case CellOrder::Diagonal:
295*5f39d1b3SJooyung Han return "Diagonal";
296*5f39d1b3SJooyung Han default:
297*5f39d1b3SJooyung Han assert(false);
298*5f39d1b3SJooyung Han return nullptr;
299*5f39d1b3SJooyung Han }
300*5f39d1b3SJooyung Han }
301*5f39d1b3SJooyung Han
302*5f39d1b3SJooyung Han // Returns the offset into a cell, at which a given coefficient is stored.
303*5f39d1b3SJooyung Han template <typename CellFormat>
OffsetIntoCell(int w,int d)304*5f39d1b3SJooyung Han inline int OffsetIntoCell(int w, int d) {
305*5f39d1b3SJooyung Han switch (CellFormat::kOrder) {
306*5f39d1b3SJooyung Han case CellOrder::DepthMajor:
307*5f39d1b3SJooyung Han return w + d * CellFormat::kWidth;
308*5f39d1b3SJooyung Han case CellOrder::WidthMajor:
309*5f39d1b3SJooyung Han return d + w * CellFormat::kDepth;
310*5f39d1b3SJooyung Han case CellOrder::Diagonal:
311*5f39d1b3SJooyung Han assert(CellFormat::kWidth == CellFormat::kDepth);
312*5f39d1b3SJooyung Han static const int size = CellFormat::kWidth;
313*5f39d1b3SJooyung Han return ((size + w - d) * size + d) % (size * size);
314*5f39d1b3SJooyung Han default:
315*5f39d1b3SJooyung Han assert(false);
316*5f39d1b3SJooyung Han return 0;
317*5f39d1b3SJooyung Han }
318*5f39d1b3SJooyung Han }
319*5f39d1b3SJooyung Han
320*5f39d1b3SJooyung Han // END code copied from gemmlowp/internal/kernel.h
321*5f39d1b3SJooyung Han
322*5f39d1b3SJooyung Han #ifdef __arm__
323*5f39d1b3SJooyung Han
324*5f39d1b3SJooyung Han // This is the current standard kernel in gemmlowp, see:
325*5f39d1b3SJooyung Han // https://github.com/google/gemmlowp/blob/b1e2a29ff866680028f3080efc244e10e8dd7f46/internal/kernel_neon.h#L33
326*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators {
327*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
328*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
329*5f39d1b3SJooyung Han typedef KernelFormat<
330*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 3>,
331*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 1> >
332*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Uint8Operands_Uint32Accumulators333*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
334*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
335*5f39d1b3SJooyung Han asm volatile(
336*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 2x4
337*5f39d1b3SJooyung Han "vld1.8 {d0}, [%[rhs_ptr]]!\n"
338*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x2 each
339*5f39d1b3SJooyung Han "vld1.8 {d2}, [%[lhs_ptr]]!\n"
340*5f39d1b3SJooyung Han "vld1.8 {d4}, [%[lhs_ptr]]!\n"
341*5f39d1b3SJooyung Han "vld1.8 {d6}, [%[lhs_ptr]]!\n"
342*5f39d1b3SJooyung Han // Load accumulators
343*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
344*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
345*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
346*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
347*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
348*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
349*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
350*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
351*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
352*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
353*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
354*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
355*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
356*5f39d1b3SJooyung Han
357*5f39d1b3SJooyung Han "subs %[depth], #2\n"
358*5f39d1b3SJooyung Han
359*5f39d1b3SJooyung Han "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
360*5f39d1b3SJooyung Han
361*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
362*5f39d1b3SJooyung Han ":\n"
363*5f39d1b3SJooyung Han // Overview of register layout:
364*5f39d1b3SJooyung Han //
365*5f39d1b3SJooyung Han // A 2x4 cell of Rhs is stored in 16bit in d0--d1 (q0).
366*5f39d1b3SJooyung Han // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in d2--d7
367*5f39d1b3SJooyung Han // (q1--q3).
368*5f39d1b3SJooyung Han // A 12x4 block of accumulators is stored in 32bit in q4--q15.
369*5f39d1b3SJooyung Han //
370*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
371*5f39d1b3SJooyung Han // |d0[0]|d0[1]|d0[2]|d0[3]|
372*5f39d1b3SJooyung Han // Rhs +-----+-----+-----+-----+
373*5f39d1b3SJooyung Han // |d1[0]|d1[1]|d1[2]|d1[3]|
374*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
375*5f39d1b3SJooyung Han //
376*5f39d1b3SJooyung Han // | | | | |
377*5f39d1b3SJooyung Han //
378*5f39d1b3SJooyung Han // Lhs | | | | |
379*5f39d1b3SJooyung Han //
380*5f39d1b3SJooyung Han // +--+--+ - - - - +-----+-----+-----+-----+
381*5f39d1b3SJooyung Han // |d2|d3| | q4 | q5 | q6 | q7 |
382*5f39d1b3SJooyung Han // |d2|d3| | q4 | q5 | q6 | q7 |
383*5f39d1b3SJooyung Han // |d2|d3| | q4 | q5 | q6 | q7 |
384*5f39d1b3SJooyung Han // |d2|d3| | q4 | q5 | q6 | q7 |
385*5f39d1b3SJooyung Han // +--+--+ - - - - +-----+-----+-----+-----+
386*5f39d1b3SJooyung Han // |d4|d5| | q8 | q9 | q10 | q11 |
387*5f39d1b3SJooyung Han // |d4|d5| | q8 | q9 | q10 | q11 |
388*5f39d1b3SJooyung Han // |d4|d5| | q8 | q9 | q10 | q11 |
389*5f39d1b3SJooyung Han // |d4|d5| | q8 | q9 | q10 | q11 |
390*5f39d1b3SJooyung Han // +--+--+ - - - - +-----+-----+-----+-----+
391*5f39d1b3SJooyung Han // |d6|d7| | q12 | q13 | q14 | q15 |
392*5f39d1b3SJooyung Han // |d6|d7| | q12 | q13 | q14 | q15 |
393*5f39d1b3SJooyung Han // |d6|d7| | q12 | q13 | q14 | q15 |
394*5f39d1b3SJooyung Han // |d6|d7| | q12 | q13 | q14 | q15 |
395*5f39d1b3SJooyung Han // +--+--+ - - - - +-----+-----+-----+-----+
396*5f39d1b3SJooyung Han //
397*5f39d1b3SJooyung Han // Accumulator
398*5f39d1b3SJooyung Han
399*5f39d1b3SJooyung Han // Expand Lhs/Rhs cells to 16 bit.
400*5f39d1b3SJooyung Han // Note: moving theses vmovls further down to allow for
401*5f39d1b3SJooyung Han // longer data pipelining helps a little on A57 but is
402*5f39d1b3SJooyung Han // harmful on A53 --- It looks as if A53 doesn't like
403*5f39d1b3SJooyung Han // interleaving vmovl's into the vmlal's.
404*5f39d1b3SJooyung Han "vmovl.u8 q0, d0\n"
405*5f39d1b3SJooyung Han "vmovl.u8 q1, d2\n"
406*5f39d1b3SJooyung Han "vmovl.u8 q2, d4\n"
407*5f39d1b3SJooyung Han "vmovl.u8 q3, d6\n"
408*5f39d1b3SJooyung Han
409*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 0
410*5f39d1b3SJooyung Han "vmlal.u16 q4, d2, d0[0]\n"
411*5f39d1b3SJooyung Han "vmlal.u16 q5, d2, d0[1]\n"
412*5f39d1b3SJooyung Han "vmlal.u16 q6, d2, d0[2]\n"
413*5f39d1b3SJooyung Han "vmlal.u16 q7, d2, d0[3]\n"
414*5f39d1b3SJooyung Han "vldr d2, [%[lhs_ptr]]\n"
415*5f39d1b3SJooyung Han "vmlal.u16 q8, d4, d0[0]\n"
416*5f39d1b3SJooyung Han "vmlal.u16 q9, d4, d0[1]\n"
417*5f39d1b3SJooyung Han "vmlal.u16 q10, d4, d0[2]\n"
418*5f39d1b3SJooyung Han "vmlal.u16 q11, d4, d0[3]\n"
419*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #8]\n"
420*5f39d1b3SJooyung Han "vmlal.u16 q12, d6, d0[0]\n"
421*5f39d1b3SJooyung Han "vmlal.u16 q13, d6, d0[1]\n"
422*5f39d1b3SJooyung Han "vmlal.u16 q14, d6, d0[2]\n"
423*5f39d1b3SJooyung Han "vmlal.u16 q15, d6, d0[3]\n"
424*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #16]\n"
425*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr]]\n"
426*5f39d1b3SJooyung Han
427*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 1
428*5f39d1b3SJooyung Han "vmlal.u16 q4, d3, d1[0]\n"
429*5f39d1b3SJooyung Han "vmlal.u16 q5, d3, d1[1]\n"
430*5f39d1b3SJooyung Han "add %[lhs_ptr], #24\n"
431*5f39d1b3SJooyung Han "vmlal.u16 q6, d3, d1[2]\n"
432*5f39d1b3SJooyung Han "vmlal.u16 q7, d3, d1[3]\n"
433*5f39d1b3SJooyung Han "add %[rhs_ptr], #8\n"
434*5f39d1b3SJooyung Han "vmlal.u16 q8, d5, d1[0]\n"
435*5f39d1b3SJooyung Han "vmlal.u16 q9, d5, d1[1]\n"
436*5f39d1b3SJooyung Han "subs %[depth], #2\n"
437*5f39d1b3SJooyung Han "vmlal.u16 q10, d5, d1[2]\n"
438*5f39d1b3SJooyung Han "vmlal.u16 q11, d5, d1[3]\n"
439*5f39d1b3SJooyung Han "vmlal.u16 q12, d7, d1[0]\n"
440*5f39d1b3SJooyung Han "vmlal.u16 q13, d7, d1[1]\n"
441*5f39d1b3SJooyung Han "vmlal.u16 q14, d7, d1[2]\n"
442*5f39d1b3SJooyung Han "vmlal.u16 q15, d7, d1[3]\n"
443*5f39d1b3SJooyung Han
444*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP "b\n"
445*5f39d1b3SJooyung Han
446*5f39d1b3SJooyung Han GEMMLOWP_LABEL_AFTER_LOOP
447*5f39d1b3SJooyung Han ":\n"
448*5f39d1b3SJooyung Han
449*5f39d1b3SJooyung Han // Expand Lhs/Rhs cells to 16 bit.
450*5f39d1b3SJooyung Han "vmovl.u8 q0, d0\n"
451*5f39d1b3SJooyung Han "vmovl.u8 q1, d2\n"
452*5f39d1b3SJooyung Han "vmovl.u8 q2, d4\n"
453*5f39d1b3SJooyung Han "vmovl.u8 q3, d6\n"
454*5f39d1b3SJooyung Han
455*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 0
456*5f39d1b3SJooyung Han "vmlal.u16 q4, d2, d0[0]\n"
457*5f39d1b3SJooyung Han "vmlal.u16 q5, d2, d0[1]\n"
458*5f39d1b3SJooyung Han "vmlal.u16 q6, d2, d0[2]\n"
459*5f39d1b3SJooyung Han "vmlal.u16 q7, d2, d0[3]\n"
460*5f39d1b3SJooyung Han "vmlal.u16 q8, d4, d0[0]\n"
461*5f39d1b3SJooyung Han "vmlal.u16 q9, d4, d0[1]\n"
462*5f39d1b3SJooyung Han "vmlal.u16 q10, d4, d0[2]\n"
463*5f39d1b3SJooyung Han "vmlal.u16 q11, d4, d0[3]\n"
464*5f39d1b3SJooyung Han "vmlal.u16 q12, d6, d0[0]\n"
465*5f39d1b3SJooyung Han "vmlal.u16 q13, d6, d0[1]\n"
466*5f39d1b3SJooyung Han "vmlal.u16 q14, d6, d0[2]\n"
467*5f39d1b3SJooyung Han "vmlal.u16 q15, d6, d0[3]\n"
468*5f39d1b3SJooyung Han
469*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 1
470*5f39d1b3SJooyung Han "vmlal.u16 q4, d3, d1[0]\n"
471*5f39d1b3SJooyung Han "vmlal.u16 q5, d3, d1[1]\n"
472*5f39d1b3SJooyung Han "vmlal.u16 q6, d3, d1[2]\n"
473*5f39d1b3SJooyung Han "vmlal.u16 q7, d3, d1[3]\n"
474*5f39d1b3SJooyung Han "vmlal.u16 q8, d5, d1[0]\n"
475*5f39d1b3SJooyung Han "vmlal.u16 q9, d5, d1[1]\n"
476*5f39d1b3SJooyung Han "vmlal.u16 q10, d5, d1[2]\n"
477*5f39d1b3SJooyung Han "vmlal.u16 q11, d5, d1[3]\n"
478*5f39d1b3SJooyung Han "vmlal.u16 q12, d7, d1[0]\n"
479*5f39d1b3SJooyung Han "vmlal.u16 q13, d7, d1[1]\n"
480*5f39d1b3SJooyung Han "vmlal.u16 q14, d7, d1[2]\n"
481*5f39d1b3SJooyung Han "vmlal.u16 q15, d7, d1[3]\n"
482*5f39d1b3SJooyung Han
483*5f39d1b3SJooyung Han // Store accumulators
484*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
485*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
486*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
487*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
488*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
489*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
490*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
491*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
492*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
493*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
494*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
495*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
496*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
497*5f39d1b3SJooyung Han : // outputs
498*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
499*5f39d1b3SJooyung Han [depth] "+r"(depth)
500*5f39d1b3SJooyung Han : // inputs
501*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
502*5f39d1b3SJooyung Han : // clobbers
503*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
504*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
505*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
506*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
507*5f39d1b3SJooyung Han }
508*5f39d1b3SJooyung Han };
509*5f39d1b3SJooyung Han
510*5f39d1b3SJooyung Han // This is Maciek Chociej's fast kernel not expanding operands,
511*5f39d1b3SJooyung Han // from gemmlowp/meta/. Search for
512*5f39d1b3SJooyung Han // mul_3x8_3x8_int32_lhsadd_rhsadd
513*5f39d1b3SJooyung Han // in this file:
514*5f39d1b3SJooyung Han // https://raw.githubusercontent.com/google/gemmlowp/e4b9d858b6637d5d0058bfa3d869d2b95864251b/meta/single_thread_gemm.h
515*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand {
516*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
517*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
518*5f39d1b3SJooyung Han typedef KernelFormat<
519*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<3, 8, CellOrder::WidthMajor>, 1>,
520*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<3, 8, CellOrder::WidthMajor>, 1> >
521*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand522*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
523*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
524*5f39d1b3SJooyung Han asm volatile(
525*5f39d1b3SJooyung Han // Clear aggregators.
526*5f39d1b3SJooyung Han "vmov.i32 q0, #0\n"
527*5f39d1b3SJooyung Han "vmov.i32 q1, #0\n"
528*5f39d1b3SJooyung Han "vmov.i32 q2, #0\n"
529*5f39d1b3SJooyung Han "vmov.i32 q3, q0\n"
530*5f39d1b3SJooyung Han "vmov.i32 q4, q1\n"
531*5f39d1b3SJooyung Han "vmov.i32 q5, q2\n"
532*5f39d1b3SJooyung Han "vmov.i32 q6, q3\n"
533*5f39d1b3SJooyung Han "vmov.i32 q7, q4\n"
534*5f39d1b3SJooyung Han "vmov.i32 q8, q5\n"
535*5f39d1b3SJooyung Han
536*5f39d1b3SJooyung Han // Loop head
537*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
538*5f39d1b3SJooyung Han ":\n"
539*5f39d1b3SJooyung Han
540*5f39d1b3SJooyung Han // Subtract counter.
541*5f39d1b3SJooyung Han "subs %[depth], %[depth], #8\n"
542*5f39d1b3SJooyung Han
543*5f39d1b3SJooyung Han "vld1.8 {d18, d19, d20}, [%[rhs_ptr]]!\n"
544*5f39d1b3SJooyung Han "vld1.8 {d21, d22, d23}, [%[lhs_ptr]]!\n"
545*5f39d1b3SJooyung Han "vmull.u8 q12, d18, d21\n"
546*5f39d1b3SJooyung Han "vmull.u8 q13, d18, d22\n"
547*5f39d1b3SJooyung Han "vmull.u8 q14, d18, d23\n"
548*5f39d1b3SJooyung Han "vmull.u8 q15, d19, d21\n"
549*5f39d1b3SJooyung Han "vpadal.u16 q0, q12\n"
550*5f39d1b3SJooyung Han "vpadal.u16 q1, q13\n"
551*5f39d1b3SJooyung Han "vpadal.u16 q2, q14\n"
552*5f39d1b3SJooyung Han "vpadal.u16 q3, q15\n"
553*5f39d1b3SJooyung Han "vmull.u8 q12, d19, d22\n"
554*5f39d1b3SJooyung Han "vmull.u8 q13, d19, d23\n"
555*5f39d1b3SJooyung Han "vmull.u8 q14, d20, d21\n"
556*5f39d1b3SJooyung Han "vmull.u8 q15, d20, d22\n"
557*5f39d1b3SJooyung Han "vmull.u8 q9, d20, d23\n"
558*5f39d1b3SJooyung Han "vpadal.u16 q4, q12\n"
559*5f39d1b3SJooyung Han "vpadal.u16 q5, q13\n"
560*5f39d1b3SJooyung Han "vpadal.u16 q6, q14\n"
561*5f39d1b3SJooyung Han "vpadal.u16 q7, q15\n"
562*5f39d1b3SJooyung Han "vpadal.u16 q8, q9\n"
563*5f39d1b3SJooyung Han
564*5f39d1b3SJooyung Han // Loop branch
565*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
566*5f39d1b3SJooyung Han "b\n"
567*5f39d1b3SJooyung Han
568*5f39d1b3SJooyung Han // Horizontal reduce aggregators, step 1
569*5f39d1b3SJooyung Han "vpadd.u32 d0, d0, d1\n"
570*5f39d1b3SJooyung Han "vpadd.u32 d2, d2, d3\n"
571*5f39d1b3SJooyung Han "vpadd.u32 d4, d4, d5\n"
572*5f39d1b3SJooyung Han "vpadd.u32 d6, d6, d7\n"
573*5f39d1b3SJooyung Han "vpadd.u32 d8, d8, d9\n"
574*5f39d1b3SJooyung Han "vpadd.u32 d10, d10, d11\n"
575*5f39d1b3SJooyung Han "vpadd.u32 d12, d12, d13\n"
576*5f39d1b3SJooyung Han "vpadd.u32 d14, d14, d15\n"
577*5f39d1b3SJooyung Han "vpadd.u32 d16, d16, d17\n"
578*5f39d1b3SJooyung Han
579*5f39d1b3SJooyung Han // Horizontal reduce aggregators, step 2
580*5f39d1b3SJooyung Han "vpadd.u32 d0, d0, d2\n"
581*5f39d1b3SJooyung Han "vpadd.u32 d1, d4, d4\n"
582*5f39d1b3SJooyung Han "vpadd.u32 d6, d6, d8\n"
583*5f39d1b3SJooyung Han "vpadd.u32 d7, d10, d10\n"
584*5f39d1b3SJooyung Han "vpadd.u32 d12, d12, d14\n"
585*5f39d1b3SJooyung Han "vpadd.u32 d13, d16, d16\n"
586*5f39d1b3SJooyung Han
587*5f39d1b3SJooyung Han // Load accumulators
588*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
589*5f39d1b3SJooyung Han "vld1.32 {d2}, [r0]!\n"
590*5f39d1b3SJooyung Han "vld1.32 {d3[0]}, [r0]!\n"
591*5f39d1b3SJooyung Han
592*5f39d1b3SJooyung Han "vld1.32 {d8}, [r0]!\n"
593*5f39d1b3SJooyung Han "vld1.32 {d9[0]}, [r0]!\n"
594*5f39d1b3SJooyung Han
595*5f39d1b3SJooyung Han "vld1.32 {d14}, [r0]!\n"
596*5f39d1b3SJooyung Han "vld1.32 {d15[0]}, [r0]!\n"
597*5f39d1b3SJooyung Han
598*5f39d1b3SJooyung Han // Accumulate
599*5f39d1b3SJooyung Han "vadd.s32 q0, q0, q1\n"
600*5f39d1b3SJooyung Han "vadd.s32 q3, q3, q4\n"
601*5f39d1b3SJooyung Han "vadd.s32 q6, q6, q7\n"
602*5f39d1b3SJooyung Han
603*5f39d1b3SJooyung Han // Store accumulators
604*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
605*5f39d1b3SJooyung Han "vst1.32 {d0}, [r0]!\n"
606*5f39d1b3SJooyung Han "vst1.32 {d1[0]}, [r0]!\n"
607*5f39d1b3SJooyung Han
608*5f39d1b3SJooyung Han "vst1.32 {d6}, [r0]!\n"
609*5f39d1b3SJooyung Han "vst1.32 {d7[0]}, [r0]!\n"
610*5f39d1b3SJooyung Han
611*5f39d1b3SJooyung Han "vst1.32 {d12}, [r0]!\n"
612*5f39d1b3SJooyung Han "vst1.32 {d13[0]}, [r0]!\n"
613*5f39d1b3SJooyung Han : // outputs
614*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
615*5f39d1b3SJooyung Han [depth] "+r"(depth)
616*5f39d1b3SJooyung Han : // inputs
617*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
618*5f39d1b3SJooyung Han : // clobbers
619*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
620*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
621*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
622*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
623*5f39d1b3SJooyung Han }
624*5f39d1b3SJooyung Han };
625*5f39d1b3SJooyung Han
626*5f39d1b3SJooyung Han // Fast kernel operating on int8 operands.
627*5f39d1b3SJooyung Han // It is assumed that one of the two int8 operands only takes values
628*5f39d1b3SJooyung Han // in [-127, 127], while the other may freely range in [-128, 127].
629*5f39d1b3SJooyung Han // The issue with both operands taking the value -128 is that:
630*5f39d1b3SJooyung Han // -128*-128 + -128*-128 == -32768 overflows int16.
631*5f39d1b3SJooyung Han // Every other expression a*b + c*d, for any int8 a,b,c,d, fits in int16
632*5f39d1b3SJooyung Han // range. That is the basic idea of this kernel.
633*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Int8Operands_AccumTwoWithin16Bits {
634*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
635*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
636*5f39d1b3SJooyung Han typedef KernelFormat<
637*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
638*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<2, 16, CellOrder::WidthMajor>, 1> >
639*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Int8Operands_AccumTwoWithin16Bits640*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
641*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
642*5f39d1b3SJooyung Han std::size_t start_depth = 123;
643*5f39d1b3SJooyung Han std::size_t run_depth = depth;
644*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
645*5f39d1b3SJooyung Han asm volatile(
646*5f39d1b3SJooyung Han
647*5f39d1b3SJooyung Han // Overview of register layout:
648*5f39d1b3SJooyung Han //
649*5f39d1b3SJooyung Han // A 2x16 block of Rhs is stored in 8 bit in d0--d3.
650*5f39d1b3SJooyung Han // A 4x16 block of Lhs is stored in 8 bit in d4--d7. That is only
651*5f39d1b3SJooyung Han // half of the register space required, so we loop over these registers
652*5f39d1b3SJooyung Han // twice. Only half of it, a 2x16 block, is stored in d4--d7 at
653*5f39d1b3SJooyung Han // any given time.
654*5f39d1b3SJooyung Han //
655*5f39d1b3SJooyung Han // A 4x2 block of accumulators is stored in q8--q15 (as 4x32 bit
656*5f39d1b3SJooyung Han // components which need to be horizontally-added at the end)
657*5f39d1b3SJooyung Han //
658*5f39d1b3SJooyung Han // The Lhs vectors are multiplied by the Rhs vectors with a widening
659*5f39d1b3SJooyung Han // multiply over the 8 first levels of depth, producing int16x8
660*5f39d1b3SJooyung Han // vectors of products for each position in the accumulator matrix.
661*5f39d1b3SJooyung Han // Here comes the special trick: since the operands are signed int8,
662*5f39d1b3SJooyung Han // their range being [ -2^7 , 2^7 ), their products are in range
663*5f39d1b3SJooyung Han // [ -2^14 , 2^14 - 1 ), meaning that we can add two such values
664*5f39d1b3SJooyung Han // without any risk of overflowing int16.
665*5f39d1b3SJooyung Han // We thus proceed with the 8 next levels of depth, multiplying
666*5f39d1b3SJooyung Han // again Lhs by Rhs, accumulating into this existing int16x8 vector.
667*5f39d1b3SJooyung Han //
668*5f39d1b3SJooyung Han // Only then, having processed 16 levels of depth, do we need to
669*5f39d1b3SJooyung Han // horizontally add these int16x8 accumulators into the final
670*5f39d1b3SJooyung Han // int32x4 accumulators.
671*5f39d1b3SJooyung Han //
672*5f39d1b3SJooyung Han // As we do not have enough registers to store all 16 int16x8
673*5f39d1b3SJooyung Han // temporary-16bit-accumulators, we have them cycle through q4--q7.
674*5f39d1b3SJooyung Han //
675*5f39d1b3SJooyung Han //
676*5f39d1b3SJooyung Han // Register layout (ignoring the q4--q7 temporary 16bit accumulators):
677*5f39d1b3SJooyung Han //
678*5f39d1b3SJooyung Han // +----+----+
679*5f39d1b3SJooyung Han // | d0 | d2 |
680*5f39d1b3SJooyung Han // | . | . |
681*5f39d1b3SJooyung Han // | . | . |
682*5f39d1b3SJooyung Han // | . | . |
683*5f39d1b3SJooyung Han // Rhs +----+----+
684*5f39d1b3SJooyung Han // | d1 | d3 |
685*5f39d1b3SJooyung Han // | . | . |
686*5f39d1b3SJooyung Han // | . | . |
687*5f39d1b3SJooyung Han // | . | . |
688*5f39d1b3SJooyung Han // +----+----+
689*5f39d1b3SJooyung Han //
690*5f39d1b3SJooyung Han // | | |
691*5f39d1b3SJooyung Han //
692*5f39d1b3SJooyung Han // Lhs | | |
693*5f39d1b3SJooyung Han //
694*5f39d1b3SJooyung Han // +--------+--------+ - - - - +----+----+
695*5f39d1b3SJooyung Han // | d4 ... | d5 ... | | q8 | q9 |
696*5f39d1b3SJooyung Han // | d6 ... | d7 ... | | q10| q11|
697*5f39d1b3SJooyung Han // | d4 ... | d5 ... | | q12| q13|
698*5f39d1b3SJooyung Han // | d6 ... | d7 ... | | q14| q15|
699*5f39d1b3SJooyung Han // +--------+--------+ - - - - +----+----+
700*5f39d1b3SJooyung Han //
701*5f39d1b3SJooyung Han // Accumulator
702*5f39d1b3SJooyung Han //
703*5f39d1b3SJooyung Han
704*5f39d1b3SJooyung Han // Clear accumulators, and, interleaved with it,
705*5f39d1b3SJooyung Han // initial loads of the first loop iteration,
706*5f39d1b3SJooyung Han // taken out of the loop so that in the loop itself we have
707*5f39d1b3SJooyung Han // optimal streaming of data from memory.
708*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr], #0]\n"
709*5f39d1b3SJooyung Han "vmov.i32 q8, #0\n"
710*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #0]\n"
711*5f39d1b3SJooyung Han "vmov.i32 q9, #0\n"
712*5f39d1b3SJooyung Han "vldr d2, [%[rhs_ptr], #16]\n"
713*5f39d1b3SJooyung Han "vmov.i32 q10, q8\n"
714*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #16]\n"
715*5f39d1b3SJooyung Han "vmov.i32 q11, q8\n"
716*5f39d1b3SJooyung Han "vldr d1, [%[rhs_ptr], #8]\n"
717*5f39d1b3SJooyung Han "vmov.i32 q12, q8\n"
718*5f39d1b3SJooyung Han "vldr d5, [%[lhs_ptr], #8]\n"
719*5f39d1b3SJooyung Han "vmov.i32 q13, q8\n"
720*5f39d1b3SJooyung Han "vldr d3, [%[rhs_ptr], #24]\n"
721*5f39d1b3SJooyung Han "vmov.i32 q14, q8\n"
722*5f39d1b3SJooyung Han "vldr d7, [%[lhs_ptr], #24]\n"
723*5f39d1b3SJooyung Han "vmov.i32 q15, q8\n"
724*5f39d1b3SJooyung Han
725*5f39d1b3SJooyung Han // General loop.
726*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
727*5f39d1b3SJooyung Han ":\n"
728*5f39d1b3SJooyung Han
729*5f39d1b3SJooyung Han // Multiply 8 first levels of depth.
730*5f39d1b3SJooyung Han "vmull.s8 q4, d0, d4\n"
731*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #32\n"
732*5f39d1b3SJooyung Han "vmull.s8 q5, d2, d4\n"
733*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #32]\n"
734*5f39d1b3SJooyung Han "vmull.s8 q6, d0, d6\n"
735*5f39d1b3SJooyung Han "vmull.s8 q7, d2, d6\n"
736*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #48]\n"
737*5f39d1b3SJooyung Han
738*5f39d1b3SJooyung Han // Multiply-accumulate second-half, again into the same
739*5f39d1b3SJooyung Han // 16bit local accumulator registers. This is where we
740*5f39d1b3SJooyung Han // take advantage of having int8 instead of uint8 and therefore
741*5f39d1b3SJooyung Han // being able to accumulate two products into int16.
742*5f39d1b3SJooyung Han "vmlal.s8 q4, d1, d5\n"
743*5f39d1b3SJooyung Han "vmlal.s8 q5, d3, d5\n"
744*5f39d1b3SJooyung Han "vldr d5, [%[lhs_ptr], #40]\n"
745*5f39d1b3SJooyung Han "vmlal.s8 q6, d1, d7\n"
746*5f39d1b3SJooyung Han "vmlal.s8 q7, d3, d7\n"
747*5f39d1b3SJooyung Han "vldr d7, [%[lhs_ptr], #56]\n"
748*5f39d1b3SJooyung Han
749*5f39d1b3SJooyung Han // Add pairwise, accumulate into 32-bit accumulators.
750*5f39d1b3SJooyung Han "vpadal.s16 q8, q4\n"
751*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #64\n"
752*5f39d1b3SJooyung Han "vpadal.s16 q9, q5\n"
753*5f39d1b3SJooyung Han "subs %[run_depth], %[run_depth], #16\n"
754*5f39d1b3SJooyung Han "vpadal.s16 q10, q6\n"
755*5f39d1b3SJooyung Han "vpadal.s16 q11, q7\n"
756*5f39d1b3SJooyung Han
757*5f39d1b3SJooyung Han "beq " GEMMLOWP_LABEL_AFTER_LOOP
758*5f39d1b3SJooyung Han "f\n"
759*5f39d1b3SJooyung Han
760*5f39d1b3SJooyung Han // Multiply first half.
761*5f39d1b3SJooyung Han "vmull.s8 q4, d0, d4\n"
762*5f39d1b3SJooyung Han "vmull.s8 q5, d2, d4\n"
763*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #0]\n"
764*5f39d1b3SJooyung Han "vmull.s8 q6, d0, d6\n"
765*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr], #0]\n"
766*5f39d1b3SJooyung Han "vmull.s8 q7, d2, d6\n"
767*5f39d1b3SJooyung Han "vldr d2, [%[rhs_ptr], #16]\n"
768*5f39d1b3SJooyung Han
769*5f39d1b3SJooyung Han // Multiply-accumulate second-half, again into the same
770*5f39d1b3SJooyung Han // 16bit local accumulator registers. This is where we
771*5f39d1b3SJooyung Han // take advantage of having int8 instead of uint8 and therefore
772*5f39d1b3SJooyung Han // being able to accumulate two products into int16.
773*5f39d1b3SJooyung Han "vmlal.s8 q4, d1, d5\n"
774*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #16]\n"
775*5f39d1b3SJooyung Han "vmlal.s8 q5, d3, d5\n"
776*5f39d1b3SJooyung Han "vldr d5, [%[lhs_ptr], #8]\n"
777*5f39d1b3SJooyung Han "vmlal.s8 q6, d1, d7\n"
778*5f39d1b3SJooyung Han "vldr d1, [%[rhs_ptr], #8]\n"
779*5f39d1b3SJooyung Han "vmlal.s8 q7, d3, d7\n"
780*5f39d1b3SJooyung Han "vldr d3, [%[rhs_ptr], #24]\n"
781*5f39d1b3SJooyung Han
782*5f39d1b3SJooyung Han // Add pairwise, accumulate into 32-bit accumulators.
783*5f39d1b3SJooyung Han "vpadal.s16 q12, q4\n"
784*5f39d1b3SJooyung Han "vldr d7, [%[lhs_ptr], #24]\n"
785*5f39d1b3SJooyung Han "vpadal.s16 q13, q5\n"
786*5f39d1b3SJooyung Han "vpadal.s16 q14, q6\n"
787*5f39d1b3SJooyung Han "vpadal.s16 q15, q7\n"
788*5f39d1b3SJooyung Han
789*5f39d1b3SJooyung Han "b " GEMMLOWP_LABEL_LOOP "b\n"
790*5f39d1b3SJooyung Han
791*5f39d1b3SJooyung Han GEMMLOWP_LABEL_AFTER_LOOP
792*5f39d1b3SJooyung Han ":\n"
793*5f39d1b3SJooyung Han
794*5f39d1b3SJooyung Han // Multiply first half.
795*5f39d1b3SJooyung Han "vmull.s8 q4, d0, d4\n"
796*5f39d1b3SJooyung Han "vmull.s8 q5, d2, d4\n"
797*5f39d1b3SJooyung Han "vmull.s8 q6, d0, d6\n"
798*5f39d1b3SJooyung Han "vmull.s8 q7, d2, d6\n"
799*5f39d1b3SJooyung Han
800*5f39d1b3SJooyung Han // Multiply-accumulate second-half, again into the same
801*5f39d1b3SJooyung Han // 16bit local accumulator registers. This is where we
802*5f39d1b3SJooyung Han // take advantage of having int8 instead of uint8 and therefore
803*5f39d1b3SJooyung Han // being able to accumulate two products into int16.
804*5f39d1b3SJooyung Han "vmlal.s8 q4, d1, d5\n"
805*5f39d1b3SJooyung Han "vmlal.s8 q5, d3, d5\n"
806*5f39d1b3SJooyung Han "vmlal.s8 q6, d1, d7\n"
807*5f39d1b3SJooyung Han "vmlal.s8 q7, d3, d7\n"
808*5f39d1b3SJooyung Han
809*5f39d1b3SJooyung Han // Add pairwise, accumulate into 32-bit accumulators.
810*5f39d1b3SJooyung Han "vpadal.s16 q12, q4\n"
811*5f39d1b3SJooyung Han "vpadal.s16 q13, q5\n"
812*5f39d1b3SJooyung Han "vpadal.s16 q14, q6\n"
813*5f39d1b3SJooyung Han "vpadal.s16 q15, q7\n"
814*5f39d1b3SJooyung Han "cmp %[start_depth], #0\n"
815*5f39d1b3SJooyung Han
816*5f39d1b3SJooyung Han // Reduce 32bit accumulators horizontally.
817*5f39d1b3SJooyung Han "vpadd.s32 d0, d16, d17\n"
818*5f39d1b3SJooyung Han "vpadd.s32 d1, d18, d19\n"
819*5f39d1b3SJooyung Han "vpadd.s32 d2, d20, d21\n"
820*5f39d1b3SJooyung Han "vpadd.s32 d3, d22, d23\n"
821*5f39d1b3SJooyung Han "vpadd.s32 d4, d24, d25\n"
822*5f39d1b3SJooyung Han "vpadd.s32 d5, d26, d27\n"
823*5f39d1b3SJooyung Han "vpadd.s32 d6, d28, d29\n"
824*5f39d1b3SJooyung Han "vpadd.s32 d7, d30, d31\n"
825*5f39d1b3SJooyung Han
826*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
827*5f39d1b3SJooyung Han "f\n"
828*5f39d1b3SJooyung Han
829*5f39d1b3SJooyung Han // Reduce 32bit accumulators horizontally, second pass
830*5f39d1b3SJooyung Han // (each pass adds pairwise. we need to add 4-wise).
831*5f39d1b3SJooyung Han "vpadd.s32 d8, d0, d2\n"
832*5f39d1b3SJooyung Han "vpadd.s32 d9, d4, d6\n"
833*5f39d1b3SJooyung Han "vpadd.s32 d10, d1, d3\n"
834*5f39d1b3SJooyung Han "vpadd.s32 d11, d5, d7\n"
835*5f39d1b3SJooyung Han
836*5f39d1b3SJooyung Han "b " GEMMLOWP_LABEL_STORE "f\n"
837*5f39d1b3SJooyung Han
838*5f39d1b3SJooyung Han GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
839*5f39d1b3SJooyung Han ":\n"
840*5f39d1b3SJooyung Han
841*5f39d1b3SJooyung Han // Reduce 32bit accumulators horizontally, second pass
842*5f39d1b3SJooyung Han // (each pass adds pairwise. we need to add 4-wise),
843*5f39d1b3SJooyung Han // and load destination values from memory.
844*5f39d1b3SJooyung Han "mov r0, %[dst_ptr]\n"
845*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
846*5f39d1b3SJooyung Han "vpadd.s32 d8, d0, d2\n"
847*5f39d1b3SJooyung Han "vpadd.s32 d9, d4, d6\n"
848*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]\n"
849*5f39d1b3SJooyung Han "vpadd.s32 d10, d1, d3\n"
850*5f39d1b3SJooyung Han "vpadd.s32 d11, d5, d7\n"
851*5f39d1b3SJooyung Han
852*5f39d1b3SJooyung Han // Add horizontally-reduced accumulators into
853*5f39d1b3SJooyung Han // the values loaded from memory
854*5f39d1b3SJooyung Han "vadd.s32 q4, q8, q4\n"
855*5f39d1b3SJooyung Han "vadd.s32 q5, q9, q5\n"
856*5f39d1b3SJooyung Han
857*5f39d1b3SJooyung Han GEMMLOWP_LABEL_STORE
858*5f39d1b3SJooyung Han ":\n"
859*5f39d1b3SJooyung Han // Store back into memory
860*5f39d1b3SJooyung Han "mov r0, %[dst_ptr]\n"
861*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
862*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]\n"
863*5f39d1b3SJooyung Han : // outputs
864*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
865*5f39d1b3SJooyung Han [dst_ptr] "+r"(dst_ptr), [run_depth] "+r"(run_depth)
866*5f39d1b3SJooyung Han : // inputs
867*5f39d1b3SJooyung Han [start_depth] "r"(start_depth)
868*5f39d1b3SJooyung Han : // clobbers
869*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
870*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
871*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
872*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
873*5f39d1b3SJooyung Han }
874*5f39d1b3SJooyung Han };
875*5f39d1b3SJooyung Han
876*5f39d1b3SJooyung Han // We don't actually use int32*int32 in production. This is just an
877*5f39d1b3SJooyung Han // experiment to help dissociate the effect of integer-vs-float, from the
878*5f39d1b3SJooyung Han // effect of operands width.
879*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Int32_WithScalar {
880*5f39d1b3SJooyung Han typedef std::int32_t OperandType;
881*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
882*5f39d1b3SJooyung Han typedef KernelFormat<
883*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
884*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
885*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Int32_WithScalar886*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
887*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
888*5f39d1b3SJooyung Han asm volatile(
889*5f39d1b3SJooyung Han // Load accumulators
890*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
891*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
892*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
893*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
894*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
895*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
896*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
897*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
898*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
899*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
900*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
901*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
902*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
903*5f39d1b3SJooyung Han
904*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
905*5f39d1b3SJooyung Han ":\n"
906*5f39d1b3SJooyung Han
907*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 1x4
908*5f39d1b3SJooyung Han "vld1.32 {d0, d1}, [%[rhs_ptr]]!\n"
909*5f39d1b3SJooyung Han
910*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
911*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
912*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
913*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
914*5f39d1b3SJooyung Han
915*5f39d1b3SJooyung Han // Multiply-accumulate
916*5f39d1b3SJooyung Han "vmla.s32 q4, q1, d0[0]\n"
917*5f39d1b3SJooyung Han "vmla.s32 q5, q1, d0[1]\n"
918*5f39d1b3SJooyung Han "vmla.s32 q6, q1, d1[0]\n"
919*5f39d1b3SJooyung Han "vmla.s32 q7, q1, d1[1]\n"
920*5f39d1b3SJooyung Han "vmla.s32 q8, q2, d0[0]\n"
921*5f39d1b3SJooyung Han "vmla.s32 q9, q2, d0[1]\n"
922*5f39d1b3SJooyung Han "vmla.s32 q10, q2, d1[0]\n"
923*5f39d1b3SJooyung Han "vmla.s32 q11, q2, d1[1]\n"
924*5f39d1b3SJooyung Han "vmla.s32 q12, q3, d0[0]\n"
925*5f39d1b3SJooyung Han "vmla.s32 q13, q3, d0[1]\n"
926*5f39d1b3SJooyung Han "vmla.s32 q14, q3, d1[0]\n"
927*5f39d1b3SJooyung Han "vmla.s32 q15, q3, d1[1]\n"
928*5f39d1b3SJooyung Han
929*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
930*5f39d1b3SJooyung Han // level of depth.
931*5f39d1b3SJooyung Han "subs %[depth], #1\n"
932*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
933*5f39d1b3SJooyung Han "b\n"
934*5f39d1b3SJooyung Han
935*5f39d1b3SJooyung Han // Store accumulators
936*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
937*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
938*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
939*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
940*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
941*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
942*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
943*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
944*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
945*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
946*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
947*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
948*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
949*5f39d1b3SJooyung Han : // outputs
950*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
951*5f39d1b3SJooyung Han [depth] "+r"(depth)
952*5f39d1b3SJooyung Han : // inputs
953*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
954*5f39d1b3SJooyung Han : // clobbers
955*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
956*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
957*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
958*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
959*5f39d1b3SJooyung Han }
960*5f39d1b3SJooyung Han };
961*5f39d1b3SJooyung Han
962*5f39d1b3SJooyung Han // Not very efficient kernel, just an experiment to see what we can do
963*5f39d1b3SJooyung Han // without using NEON multiply-with-scalar instructions.
964*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_MLA_WithVectorDuplicatingScalar {
965*5f39d1b3SJooyung Han typedef float OperandType;
966*5f39d1b3SJooyung Han typedef float AccumulatorType;
967*5f39d1b3SJooyung Han typedef KernelFormat<
968*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
969*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
970*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_MLA_WithVectorDuplicatingScalar971*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
972*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
973*5f39d1b3SJooyung Han asm volatile(
974*5f39d1b3SJooyung Han // Load accumulators
975*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
976*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
977*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
978*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
979*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
980*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
981*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
982*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
983*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
984*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
985*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
986*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
987*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
988*5f39d1b3SJooyung Han
989*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
990*5f39d1b3SJooyung Han ":\n"
991*5f39d1b3SJooyung Han
992*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
993*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
994*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
995*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
996*5f39d1b3SJooyung Han
997*5f39d1b3SJooyung Han // Multiply-accumulate
998*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
999*5f39d1b3SJooyung Han "vmla.f32 q4, q1, q0\n"
1000*5f39d1b3SJooyung Han "vmla.f32 q8, q2, q0\n"
1001*5f39d1b3SJooyung Han "vmla.f32 q12, q3, q0\n"
1002*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1003*5f39d1b3SJooyung Han "vmla.f32 q5, q1, q0\n"
1004*5f39d1b3SJooyung Han "vmla.f32 q9, q2, q0\n"
1005*5f39d1b3SJooyung Han "vmla.f32 q13, q3, q0\n"
1006*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1007*5f39d1b3SJooyung Han "vmla.f32 q6, q1, q0\n"
1008*5f39d1b3SJooyung Han "vmla.f32 q10, q2, q0\n"
1009*5f39d1b3SJooyung Han "vmla.f32 q14, q3, q0\n"
1010*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1011*5f39d1b3SJooyung Han "vmla.f32 q7, q1, q0\n"
1012*5f39d1b3SJooyung Han "vmla.f32 q11, q2, q0\n"
1013*5f39d1b3SJooyung Han "vmla.f32 q15, q3, q0\n"
1014*5f39d1b3SJooyung Han
1015*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
1016*5f39d1b3SJooyung Han // level of depth.
1017*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1018*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1019*5f39d1b3SJooyung Han "b\n"
1020*5f39d1b3SJooyung Han
1021*5f39d1b3SJooyung Han // Store accumulators
1022*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1023*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1024*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1025*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1026*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1027*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1028*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1029*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1030*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1031*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1032*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1033*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1034*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1035*5f39d1b3SJooyung Han : // outputs
1036*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1037*5f39d1b3SJooyung Han [depth] "+r"(depth)
1038*5f39d1b3SJooyung Han : // inputs
1039*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1040*5f39d1b3SJooyung Han : // clobbers
1041*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1042*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
1043*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
1044*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
1045*5f39d1b3SJooyung Han }
1046*5f39d1b3SJooyung Han };
1047*5f39d1b3SJooyung Han
1048*5f39d1b3SJooyung Han // Not very efficient kernel, just an experiment to see what we can do
1049*5f39d1b3SJooyung Han // without using NEON multiply-with-scalar instructions.
1050*5f39d1b3SJooyung Han // This variant is relevant as on ARMv7 FMA does not have a with-scalar variant.
1051*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_FMA_WithVectorDuplicatingScalar {
1052*5f39d1b3SJooyung Han typedef float OperandType;
1053*5f39d1b3SJooyung Han typedef float AccumulatorType;
1054*5f39d1b3SJooyung Han typedef KernelFormat<
1055*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
1056*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
1057*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_FMA_WithVectorDuplicatingScalar1058*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1059*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1060*5f39d1b3SJooyung Han asm volatile(
1061*5f39d1b3SJooyung Han // Load accumulators
1062*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1063*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1064*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1065*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1066*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1067*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1068*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1069*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1070*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1071*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1072*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1073*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1074*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1075*5f39d1b3SJooyung Han
1076*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1077*5f39d1b3SJooyung Han ":\n"
1078*5f39d1b3SJooyung Han
1079*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
1080*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
1081*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
1082*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
1083*5f39d1b3SJooyung Han
1084*5f39d1b3SJooyung Han // Multiply-accumulate
1085*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1086*5f39d1b3SJooyung Han "vfma.f32 q4, q1, q0\n"
1087*5f39d1b3SJooyung Han "vfma.f32 q8, q2, q0\n"
1088*5f39d1b3SJooyung Han "vfma.f32 q12, q3, q0\n"
1089*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1090*5f39d1b3SJooyung Han "vfma.f32 q5, q1, q0\n"
1091*5f39d1b3SJooyung Han "vfma.f32 q9, q2, q0\n"
1092*5f39d1b3SJooyung Han "vfma.f32 q13, q3, q0\n"
1093*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1094*5f39d1b3SJooyung Han "vfma.f32 q6, q1, q0\n"
1095*5f39d1b3SJooyung Han "vfma.f32 q10, q2, q0\n"
1096*5f39d1b3SJooyung Han "vfma.f32 q14, q3, q0\n"
1097*5f39d1b3SJooyung Han "vld1.32 {d0[], d1[]}, [%[rhs_ptr]]!\n"
1098*5f39d1b3SJooyung Han "vfma.f32 q7, q1, q0\n"
1099*5f39d1b3SJooyung Han "vfma.f32 q11, q2, q0\n"
1100*5f39d1b3SJooyung Han "vfma.f32 q15, q3, q0\n"
1101*5f39d1b3SJooyung Han
1102*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
1103*5f39d1b3SJooyung Han // level of depth.
1104*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1105*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1106*5f39d1b3SJooyung Han "b\n"
1107*5f39d1b3SJooyung Han
1108*5f39d1b3SJooyung Han // Store accumulators
1109*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1110*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1111*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1112*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1113*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1114*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1115*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1116*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1117*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1118*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1119*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1120*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1121*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1122*5f39d1b3SJooyung Han : // outputs
1123*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1124*5f39d1b3SJooyung Han [depth] "+r"(depth)
1125*5f39d1b3SJooyung Han : // inputs
1126*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1127*5f39d1b3SJooyung Han : // clobbers
1128*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1129*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
1130*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
1131*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
1132*5f39d1b3SJooyung Han }
1133*5f39d1b3SJooyung Han };
1134*5f39d1b3SJooyung Han
1135*5f39d1b3SJooyung Han // This is the "most natural" kernel, using NEON multiply-with-scalar
1136*5f39d1b3SJooyung Han // instructions.
1137*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_MLA_WithScalar {
1138*5f39d1b3SJooyung Han typedef float OperandType;
1139*5f39d1b3SJooyung Han typedef float AccumulatorType;
1140*5f39d1b3SJooyung Han typedef KernelFormat<
1141*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
1142*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
1143*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_MLA_WithScalar1144*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1145*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1146*5f39d1b3SJooyung Han asm volatile(
1147*5f39d1b3SJooyung Han // Load accumulators
1148*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1149*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1150*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1151*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1152*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1153*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1154*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1155*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1156*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1157*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1158*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1159*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1160*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1161*5f39d1b3SJooyung Han
1162*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1163*5f39d1b3SJooyung Han ":\n"
1164*5f39d1b3SJooyung Han
1165*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 1x4
1166*5f39d1b3SJooyung Han "vld1.32 {d0, d1}, [%[rhs_ptr]]!\n"
1167*5f39d1b3SJooyung Han
1168*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
1169*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
1170*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
1171*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
1172*5f39d1b3SJooyung Han
1173*5f39d1b3SJooyung Han // Multiply-accumulate
1174*5f39d1b3SJooyung Han "vmla.f32 q4, q1, d0[0]\n"
1175*5f39d1b3SJooyung Han "vmla.f32 q5, q1, d0[1]\n"
1176*5f39d1b3SJooyung Han "vmla.f32 q6, q1, d1[0]\n"
1177*5f39d1b3SJooyung Han "vmla.f32 q7, q1, d1[1]\n"
1178*5f39d1b3SJooyung Han "vmla.f32 q8, q2, d0[0]\n"
1179*5f39d1b3SJooyung Han "vmla.f32 q9, q2, d0[1]\n"
1180*5f39d1b3SJooyung Han "vmla.f32 q10, q2, d1[0]\n"
1181*5f39d1b3SJooyung Han "vmla.f32 q11, q2, d1[1]\n"
1182*5f39d1b3SJooyung Han "vmla.f32 q12, q3, d0[0]\n"
1183*5f39d1b3SJooyung Han "vmla.f32 q13, q3, d0[1]\n"
1184*5f39d1b3SJooyung Han "vmla.f32 q14, q3, d1[0]\n"
1185*5f39d1b3SJooyung Han "vmla.f32 q15, q3, d1[1]\n"
1186*5f39d1b3SJooyung Han
1187*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
1188*5f39d1b3SJooyung Han // level of depth.
1189*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1190*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1191*5f39d1b3SJooyung Han "b\n"
1192*5f39d1b3SJooyung Han
1193*5f39d1b3SJooyung Han // Store accumulators
1194*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1195*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1196*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1197*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1198*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1199*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1200*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1201*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1202*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1203*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1204*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1205*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1206*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1207*5f39d1b3SJooyung Han : // outputs
1208*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1209*5f39d1b3SJooyung Han [depth] "+r"(depth)
1210*5f39d1b3SJooyung Han : // inputs
1211*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1212*5f39d1b3SJooyung Han : // clobbers
1213*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1214*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
1215*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
1216*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
1217*5f39d1b3SJooyung Han }
1218*5f39d1b3SJooyung Han };
1219*5f39d1b3SJooyung Han
1220*5f39d1b3SJooyung Han // Faster kernel contributed by ARM in 64bit form
1221*5f39d1b3SJooyung Han // (see NEON_64bit_GEMM_Float32_WithScalar_A53) then ported to 32bit code.
1222*5f39d1b3SJooyung Han // Tuned for A53.
1223*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_WithScalar_A53 {
1224*5f39d1b3SJooyung Han typedef float OperandType;
1225*5f39d1b3SJooyung Han typedef float AccumulatorType;
1226*5f39d1b3SJooyung Han typedef KernelFormat<
1227*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
1228*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
1229*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_WithScalar_A531230*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1231*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1232*5f39d1b3SJooyung Han asm volatile(
1233*5f39d1b3SJooyung Han // Load accumulators
1234*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1235*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1236*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1237*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1238*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1239*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1240*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1241*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1242*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1243*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1244*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1245*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1246*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1247*5f39d1b3SJooyung Han
1248*5f39d1b3SJooyung Han // Overview of register layout:
1249*5f39d1b3SJooyung Han //
1250*5f39d1b3SJooyung Han // A 1x4 cell of Rhs is stored in d0--d1 (q0).
1251*5f39d1b3SJooyung Han // A 12x1 block of 3 4x1 cells Lhs is stored in d2--d7
1252*5f39d1b3SJooyung Han // (q1--q3).
1253*5f39d1b3SJooyung Han // A 12x4 block of accumulators is stored in q4--q15.
1254*5f39d1b3SJooyung Han //
1255*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
1256*5f39d1b3SJooyung Han // Rhs |d0[0]|d0[1]|d1[0]|d1[1]|
1257*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
1258*5f39d1b3SJooyung Han //
1259*5f39d1b3SJooyung Han // | | | | |
1260*5f39d1b3SJooyung Han //
1261*5f39d1b3SJooyung Han // Lhs | | | | |
1262*5f39d1b3SJooyung Han //
1263*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1264*5f39d1b3SJooyung Han // |d2| | q4 | q5 | q6 | q7 |
1265*5f39d1b3SJooyung Han // |d2| | q4 | q5 | q6 | q7 |
1266*5f39d1b3SJooyung Han // |d3| | q4 | q5 | q6 | q7 |
1267*5f39d1b3SJooyung Han // |d3| | q4 | q5 | q6 | q7 |
1268*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1269*5f39d1b3SJooyung Han // |d4| | q8 | q9 | q10 | q11 |
1270*5f39d1b3SJooyung Han // |d4| | q8 | q9 | q10 | q11 |
1271*5f39d1b3SJooyung Han // |d5| | q8 | q9 | q10 | q11 |
1272*5f39d1b3SJooyung Han // |d5| | q8 | q9 | q10 | q11 |
1273*5f39d1b3SJooyung Han // +--+ - - - - - - +-----+-----+-----+-----+
1274*5f39d1b3SJooyung Han // |d6| | q12 | q13 | q14 | q15 |
1275*5f39d1b3SJooyung Han // |d6| | q12 | q13 | q14 | q15 |
1276*5f39d1b3SJooyung Han // |d7| | q12 | q13 | q14 | q15 |
1277*5f39d1b3SJooyung Han // |d7| | q12 | q13 | q14 | q15 |
1278*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1279*5f39d1b3SJooyung Han //
1280*5f39d1b3SJooyung Han // Accumulator
1281*5f39d1b3SJooyung Han
1282*5f39d1b3SJooyung Han // Load Rhs cell
1283*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr]]\n"
1284*5f39d1b3SJooyung Han "ldr r2, [%[rhs_ptr], #8]\n"
1285*5f39d1b3SJooyung Han "ldr r3, [%[rhs_ptr], #12]\n"
1286*5f39d1b3SJooyung Han
1287*5f39d1b3SJooyung Han // Load 1st Lhs Cell
1288*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]\n"
1289*5f39d1b3SJooyung Han
1290*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1291*5f39d1b3SJooyung Han ":\n"
1292*5f39d1b3SJooyung Han
1293*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #16]\n" // Load 1st half of 2nd Lhs cell
1294*5f39d1b3SJooyung Han "vmov d1, r2, r3\n" // Prepare 2nd half of Rhs cell
1295*5f39d1b3SJooyung Han "vmla.f32 q4, q1, d0[0]\n" // Multiply 1st Lhs cell with column 0
1296*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #24]\n" // Load 2nd half of 2nd Lhs cell, part 1
1297*5f39d1b3SJooyung Han "vmla.f32 q5, q1, d0[1]\n" // Multiply 1st Lhs cell with column 1
1298*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #28]\n" // Load 2nd half of 2nd Lhs cell, part 2
1299*5f39d1b3SJooyung Han "vmla.f32 q6, q1, d1[0]\n" // Multiply 1st Lhs cell with column 2
1300*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1301*5f39d1b3SJooyung Han
1302*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #32]\n" // Load 1st half of 3rd Lhs cell
1303*5f39d1b3SJooyung Han "vmov d5, r2, r3\n" // Prepare 2nd half of 2nd Lhs cell
1304*5f39d1b3SJooyung Han "vmla.f32 q7, q1, d1[1]\n" // Multiply 1st Lhs cell with column 3
1305*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #40]\n" // Load 2nd half of 3rd Lhs cell, part 1
1306*5f39d1b3SJooyung Han "vmla.f32 q8, q2, d0[0]\n" // Multiply 2nd Lhs cell with column 0
1307*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #44]\n" // Load 2nd half of 3rd Lhs cell, part 2
1308*5f39d1b3SJooyung Han "vmla.f32 q9, q2, d0[1]\n" // Multiply 2nd Lhs cell with column 1
1309*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #16\n" // Move forward by 1 Rhs cell
1310*5f39d1b3SJooyung Han
1311*5f39d1b3SJooyung Han "vldr d2, [%[lhs_ptr], #48]\n" // Load 1st half of 1st Lhs cell of next
1312*5f39d1b3SJooyung Han // iteration
1313*5f39d1b3SJooyung Han "vmov d7, r2, r3\n" // Prepare 2nd half of 3rd Lhs cell
1314*5f39d1b3SJooyung Han "vmla.f32 q10, q2, d1[0]\n" // Multiply 2nd Lhs cell with column 2
1315*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #56]\n" // Load 2nd half of 1st Lhs cell of next
1316*5f39d1b3SJooyung Han // iter, part 1
1317*5f39d1b3SJooyung Han "vmla.f32 q12, q3, d0[0]\n" // Multiply 3rd Lhs cell with column 0
1318*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #60]\n" // Load 2nd half of 1st Lhs cell of next
1319*5f39d1b3SJooyung Han // iter, part 2
1320*5f39d1b3SJooyung Han "vmla.f32 q13, q3, d0[1]\n" // Multiply 3rd Lhs cell with column 1
1321*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #48\n" // Move forward by 3 Lhs cells
1322*5f39d1b3SJooyung Han
1323*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr]]\n" // Load 1st half of Rhs cell of next
1324*5f39d1b3SJooyung Han // iteration
1325*5f39d1b3SJooyung Han "vmov d3, r2, r3\n" // Prepare 2nd half of 1st Lhs cell of next
1326*5f39d1b3SJooyung Han // iteration
1327*5f39d1b3SJooyung Han "vmla.f32 q11, q2, d1[1]\n" // Multiply 2nd Lhs cell with column 3
1328*5f39d1b3SJooyung Han "ldr r2, [%[rhs_ptr], #8]\n" // Load 2nd half of Rhs cell of next
1329*5f39d1b3SJooyung Han // iteration, part 1
1330*5f39d1b3SJooyung Han "vmla.f32 q14, q3, d1[0]\n" // Multiply 3rd Lhs cell with column 2
1331*5f39d1b3SJooyung Han "ldr r3, [%[rhs_ptr], #12]\n" // Load 2nd half of Rhs cell of next
1332*5f39d1b3SJooyung Han // iteration, part 2
1333*5f39d1b3SJooyung Han "vmla.f32 q15, q3, d1[1]\n" // Multiply 3rd Lhs cell with column 3
1334*5f39d1b3SJooyung Han
1335*5f39d1b3SJooyung Han // Loop branch. This will dual issue in fmla cycle 3 of the 4th block.
1336*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1337*5f39d1b3SJooyung Han "b\n"
1338*5f39d1b3SJooyung Han
1339*5f39d1b3SJooyung Han // Store accumulators
1340*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1341*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1342*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1343*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1344*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1345*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1346*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1347*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1348*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1349*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1350*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1351*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1352*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1353*5f39d1b3SJooyung Han : // outputs
1354*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1355*5f39d1b3SJooyung Han [depth] "+r"(depth)
1356*5f39d1b3SJooyung Han : // inputs
1357*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1358*5f39d1b3SJooyung Han : // clobbers
1359*5f39d1b3SJooyung Han "cc", "memory", "r0", "r2", "r3", "d0", "d1", "d2", "d3", "d4", "d5",
1360*5f39d1b3SJooyung Han "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16",
1361*5f39d1b3SJooyung Han "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26",
1362*5f39d1b3SJooyung Han "d27", "d28", "d29", "d30", "d31");
1363*5f39d1b3SJooyung Han }
1364*5f39d1b3SJooyung Han };
1365*5f39d1b3SJooyung Han
1366*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_WithScalar_A53_depth2 {
1367*5f39d1b3SJooyung Han typedef float OperandType;
1368*5f39d1b3SJooyung Han typedef float AccumulatorType;
1369*5f39d1b3SJooyung Han typedef KernelFormat<
1370*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 3>,
1371*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 1> >
1372*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_WithScalar_A53_depth21373*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1374*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1375*5f39d1b3SJooyung Han asm volatile(
1376*5f39d1b3SJooyung Han // Load accumulators
1377*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1378*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1379*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1380*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1381*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1382*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1383*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1384*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1385*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1386*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1387*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1388*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1389*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1390*5f39d1b3SJooyung Han
1391*5f39d1b3SJooyung Han // Overview of register layout:
1392*5f39d1b3SJooyung Han //
1393*5f39d1b3SJooyung Han // A 1x4 cell of Rhs is stored in d0--d1 (q0).
1394*5f39d1b3SJooyung Han // A 12x1 block of 3 4x1 cells Lhs is stored in d2--d7
1395*5f39d1b3SJooyung Han // (q1--q3).
1396*5f39d1b3SJooyung Han // A 12x4 block of accumulators is stored in q4--q15.
1397*5f39d1b3SJooyung Han //
1398*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
1399*5f39d1b3SJooyung Han // Rhs |d0[0]|d0[1]|d1[0]|d1[1]|
1400*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
1401*5f39d1b3SJooyung Han //
1402*5f39d1b3SJooyung Han // | | | | |
1403*5f39d1b3SJooyung Han //
1404*5f39d1b3SJooyung Han // Lhs | | | | |
1405*5f39d1b3SJooyung Han //
1406*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1407*5f39d1b3SJooyung Han // |d2| | q4 | q5 | q6 | q7 |
1408*5f39d1b3SJooyung Han // |d2| | q4 | q5 | q6 | q7 |
1409*5f39d1b3SJooyung Han // |d3| | q4 | q5 | q6 | q7 |
1410*5f39d1b3SJooyung Han // |d3| | q4 | q5 | q6 | q7 |
1411*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1412*5f39d1b3SJooyung Han // |d4| | q8 | q9 | q10 | q11 |
1413*5f39d1b3SJooyung Han // |d4| | q8 | q9 | q10 | q11 |
1414*5f39d1b3SJooyung Han // |d5| | q8 | q9 | q10 | q11 |
1415*5f39d1b3SJooyung Han // |d5| | q8 | q9 | q10 | q11 |
1416*5f39d1b3SJooyung Han // +--+ - - - - - - +-----+-----+-----+-----+
1417*5f39d1b3SJooyung Han // |d6| | q12 | q13 | q14 | q15 |
1418*5f39d1b3SJooyung Han // |d6| | q12 | q13 | q14 | q15 |
1419*5f39d1b3SJooyung Han // |d7| | q12 | q13 | q14 | q15 |
1420*5f39d1b3SJooyung Han // |d7| | q12 | q13 | q14 | q15 |
1421*5f39d1b3SJooyung Han // +--+- - - - - - +-----+-----+-----+-----+
1422*5f39d1b3SJooyung Han //
1423*5f39d1b3SJooyung Han // Accumulator
1424*5f39d1b3SJooyung Han
1425*5f39d1b3SJooyung Han // Load Rhs cell
1426*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr]]\n"
1427*5f39d1b3SJooyung Han "ldr r2, [%[rhs_ptr], #8]\n"
1428*5f39d1b3SJooyung Han "ldr r3, [%[rhs_ptr], #12]\n"
1429*5f39d1b3SJooyung Han
1430*5f39d1b3SJooyung Han // Load 1st Lhs Cell
1431*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]\n"
1432*5f39d1b3SJooyung Han
1433*5f39d1b3SJooyung Han // Loop head - handling 2 levels of depth at once
1434*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1435*5f39d1b3SJooyung Han ":\n"
1436*5f39d1b3SJooyung Han
1437*5f39d1b3SJooyung Han // Level of depth 1
1438*5f39d1b3SJooyung Han
1439*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #32]\n" // Load 1st half of 2nd Lhs cell
1440*5f39d1b3SJooyung Han "vmov d1, r2, r3\n" // Prepare 2nd half of Rhs cell
1441*5f39d1b3SJooyung Han "vmla.f32 q4, q1, d0[0]\n" // Multiply 1st Lhs cell with column 0
1442*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #40]\n" // Load 2nd half of 2nd Lhs cell, part 1
1443*5f39d1b3SJooyung Han "vmla.f32 q5, q1, d0[1]\n" // Multiply 1st Lhs cell with column 1
1444*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #44]\n" // Load 2nd half of 2nd Lhs cell, part 2
1445*5f39d1b3SJooyung Han "vmla.f32 q6, q1, d1[0]\n" // Multiply 1st Lhs cell with column 2
1446*5f39d1b3SJooyung Han
1447*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #64]\n" // Load 1st half of 3rd Lhs cell
1448*5f39d1b3SJooyung Han "vmov d5, r2, r3\n" // Prepare 2nd half of 2nd Lhs cell
1449*5f39d1b3SJooyung Han "vmla.f32 q7, q1, d1[1]\n" // Multiply 1st Lhs cell with column 3
1450*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #72]\n" // Load 2nd half of 3rd Lhs cell, part 1
1451*5f39d1b3SJooyung Han "vmla.f32 q8, q2, d0[0]\n" // Multiply 2nd Lhs cell with column 0
1452*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #76]\n" // Load 2nd half of 3rd Lhs cell, part 2
1453*5f39d1b3SJooyung Han "vmla.f32 q9, q2, d0[1]\n" // Multiply 2nd Lhs cell with column 1
1454*5f39d1b3SJooyung Han
1455*5f39d1b3SJooyung Han "vldr d2, [%[lhs_ptr], #16]\n" // Load 1st half of 1st Lhs cell of next
1456*5f39d1b3SJooyung Han // iteration
1457*5f39d1b3SJooyung Han "vmov d7, r2, r3\n" // Prepare 2nd half of 3rd Lhs cell
1458*5f39d1b3SJooyung Han "vmla.f32 q10, q2, d1[0]\n" // Multiply 2nd Lhs cell with column 2
1459*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #24]\n" // Load 2nd half of 1st Lhs cell of next
1460*5f39d1b3SJooyung Han // iter, part 1
1461*5f39d1b3SJooyung Han "vmla.f32 q12, q3, d0[0]\n" // Multiply 3rd Lhs cell with column 0
1462*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #28]\n" // Load 2nd half of 1st Lhs cell of next
1463*5f39d1b3SJooyung Han // iter, part 2
1464*5f39d1b3SJooyung Han "vmla.f32 q13, q3, d0[1]\n" // Multiply 3rd Lhs cell with column 1
1465*5f39d1b3SJooyung Han
1466*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr], #16]\n" // Load 1st half of Rhs cell of next
1467*5f39d1b3SJooyung Han // iteration
1468*5f39d1b3SJooyung Han "vmov d3, r2, r3\n" // Prepare 2nd half of 1st Lhs cell of next
1469*5f39d1b3SJooyung Han // iteration
1470*5f39d1b3SJooyung Han "vmla.f32 q11, q2, d1[1]\n" // Multiply 2nd Lhs cell with column 3
1471*5f39d1b3SJooyung Han "ldr r2, [%[rhs_ptr], #24]\n" // Load 2nd half of Rhs cell of next
1472*5f39d1b3SJooyung Han // iteration, part 1
1473*5f39d1b3SJooyung Han "vmla.f32 q14, q3, d1[0]\n" // Multiply 3rd Lhs cell with column 2
1474*5f39d1b3SJooyung Han "ldr r3, [%[rhs_ptr], #28]\n" // Load 2nd half of Rhs cell of next
1475*5f39d1b3SJooyung Han // iteration, part 2
1476*5f39d1b3SJooyung Han "vmla.f32 q15, q3, d1[1]\n" // Multiply 3rd Lhs cell with column 3
1477*5f39d1b3SJooyung Han
1478*5f39d1b3SJooyung Han // Level of depth 2
1479*5f39d1b3SJooyung Han "vldr d4, [%[lhs_ptr], #48]\n" // Load 1st half of 2nd Lhs cell
1480*5f39d1b3SJooyung Han "vmov d1, r2, r3\n" // Prepare 2nd half of Rhs cell
1481*5f39d1b3SJooyung Han "vmla.f32 q4, q1, d0[0]\n" // Multiply 1st Lhs cell with column 0
1482*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #56]\n" // Load 2nd half of 2nd Lhs cell, part 1
1483*5f39d1b3SJooyung Han "vmla.f32 q5, q1, d0[1]\n" // Multiply 1st Lhs cell with column 1
1484*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #60]\n" // Load 2nd half of 2nd Lhs cell, part 2
1485*5f39d1b3SJooyung Han "vmla.f32 q6, q1, d1[0]\n" // Multiply 1st Lhs cell with column 2
1486*5f39d1b3SJooyung Han "subs %[depth], #2\n" // Decrement depth counter
1487*5f39d1b3SJooyung Han
1488*5f39d1b3SJooyung Han "vldr d6, [%[lhs_ptr], #80]\n" // Load 1st half of 3rd Lhs cell
1489*5f39d1b3SJooyung Han "vmov d5, r2, r3\n" // Prepare 2nd half of 2nd Lhs cell
1490*5f39d1b3SJooyung Han "vmla.f32 q7, q1, d1[1]\n" // Multiply 1st Lhs cell with column 3
1491*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #88]\n" // Load 2nd half of 3rd Lhs cell, part 1
1492*5f39d1b3SJooyung Han "vmla.f32 q8, q2, d0[0]\n" // Multiply 2nd Lhs cell with column 0
1493*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #92]\n" // Load 2nd half of 3rd Lhs cell, part 2
1494*5f39d1b3SJooyung Han "vmla.f32 q9, q2, d0[1]\n" // Multiply 2nd Lhs cell with column 1
1495*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #32\n" // Move forward by 1 Rhs cell
1496*5f39d1b3SJooyung Han
1497*5f39d1b3SJooyung Han "vldr d2, [%[lhs_ptr], #96]\n" // Load 1st half of 1st Lhs cell of next
1498*5f39d1b3SJooyung Han // iteration
1499*5f39d1b3SJooyung Han "vmov d7, r2, r3\n" // Prepare 2nd half of 3rd Lhs cell
1500*5f39d1b3SJooyung Han "vmla.f32 q10, q2, d1[0]\n" // Multiply 2nd Lhs cell with column 2
1501*5f39d1b3SJooyung Han "ldr r2, [%[lhs_ptr], #104]\n" // Load 2nd half of 1st Lhs cell of next
1502*5f39d1b3SJooyung Han // iter, part 1
1503*5f39d1b3SJooyung Han "vmla.f32 q12, q3, d0[0]\n" // Multiply 3rd Lhs cell with column 0
1504*5f39d1b3SJooyung Han "ldr r3, [%[lhs_ptr], #108]\n" // Load 2nd half of 1st Lhs cell of next
1505*5f39d1b3SJooyung Han // iter, part 2
1506*5f39d1b3SJooyung Han "vmla.f32 q13, q3, d0[1]\n" // Multiply 3rd Lhs cell with column 1
1507*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #96\n" // Move forward by 3 Lhs cells
1508*5f39d1b3SJooyung Han
1509*5f39d1b3SJooyung Han "vldr d0, [%[rhs_ptr]]\n" // Load 1st half of Rhs cell of next
1510*5f39d1b3SJooyung Han // iteration
1511*5f39d1b3SJooyung Han "vmov d3, r2, r3\n" // Prepare 2nd half of 1st Lhs cell of next
1512*5f39d1b3SJooyung Han // iteration
1513*5f39d1b3SJooyung Han "vmla.f32 q11, q2, d1[1]\n" // Multiply 2nd Lhs cell with column 3
1514*5f39d1b3SJooyung Han "ldr r2, [%[rhs_ptr], #8]\n" // Load 2nd half of Rhs cell of next
1515*5f39d1b3SJooyung Han // iteration, part 1
1516*5f39d1b3SJooyung Han "vmla.f32 q14, q3, d1[0]\n" // Multiply 3rd Lhs cell with column 2
1517*5f39d1b3SJooyung Han "ldr r3, [%[rhs_ptr], #12]\n" // Load 2nd half of Rhs cell of next
1518*5f39d1b3SJooyung Han // iteration, part 2
1519*5f39d1b3SJooyung Han "vmla.f32 q15, q3, d1[1]\n" // Multiply 3rd Lhs cell with column 3
1520*5f39d1b3SJooyung Han
1521*5f39d1b3SJooyung Han // Loop branch. This will dual issue in fmla cycle 3 of the 4th block.
1522*5f39d1b3SJooyung Han //"bne loop_%=\n"
1523*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1524*5f39d1b3SJooyung Han "b\n"
1525*5f39d1b3SJooyung Han
1526*5f39d1b3SJooyung Han // Store accumulators
1527*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1528*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1529*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1530*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1531*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1532*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1533*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1534*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1535*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1536*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1537*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1538*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1539*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1540*5f39d1b3SJooyung Han : // outputs
1541*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1542*5f39d1b3SJooyung Han [depth] "+r"(depth)
1543*5f39d1b3SJooyung Han : // inputs
1544*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1545*5f39d1b3SJooyung Han : // clobbers
1546*5f39d1b3SJooyung Han "cc", "memory", "r0", "r2", "r3", "d0", "d1", "d2", "d3", "d4", "d5",
1547*5f39d1b3SJooyung Han "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16",
1548*5f39d1b3SJooyung Han "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26",
1549*5f39d1b3SJooyung Han "d27", "d28", "d29", "d30", "d31");
1550*5f39d1b3SJooyung Han }
1551*5f39d1b3SJooyung Han };
1552*5f39d1b3SJooyung Han
1553*5f39d1b3SJooyung Han // This rotating variant performs well when permutations (vext) can be
1554*5f39d1b3SJooyung Han // dual-issued with arithmetic instructions.
1555*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_MLA_Rotating {
1556*5f39d1b3SJooyung Han typedef float OperandType;
1557*5f39d1b3SJooyung Han typedef float AccumulatorType;
1558*5f39d1b3SJooyung Han typedef KernelFormat<
1559*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
1560*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
1561*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_MLA_Rotating1562*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1563*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1564*5f39d1b3SJooyung Han asm volatile(
1565*5f39d1b3SJooyung Han // Load accumulators
1566*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1567*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1568*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1569*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1570*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1571*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1572*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1573*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1574*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1575*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1576*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1577*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1578*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1579*5f39d1b3SJooyung Han
1580*5f39d1b3SJooyung Han #define NEON_32BIT_ROTATING_FLOAT_KERNEL_TRANSPOSE_ACCUMULATOR_CELLS \
1581*5f39d1b3SJooyung Han "vtrn.32 q4, q5\n" \
1582*5f39d1b3SJooyung Han "vtrn.32 q6, q7\n" \
1583*5f39d1b3SJooyung Han "vswp d9, d12\n" \
1584*5f39d1b3SJooyung Han "vswp d11, d14\n" \
1585*5f39d1b3SJooyung Han "vtrn.32 q8, q9\n" \
1586*5f39d1b3SJooyung Han "vtrn.32 q10, q11\n" \
1587*5f39d1b3SJooyung Han "vswp d17, d20\n" \
1588*5f39d1b3SJooyung Han "vswp d19, d22\n" \
1589*5f39d1b3SJooyung Han "vtrn.32 q12, q13\n" \
1590*5f39d1b3SJooyung Han "vtrn.32 q14, q15\n" \
1591*5f39d1b3SJooyung Han "vswp d25, d28\n" \
1592*5f39d1b3SJooyung Han "vswp d27, d30\n"
1593*5f39d1b3SJooyung Han
1594*5f39d1b3SJooyung Han #define NEON_32BIT_ROTATING_FLOAT_KERNEL_ROTATE_ACCUMULATOR_CELLS(a, b, c) \
1595*5f39d1b3SJooyung Han NEON_32BIT_ROTATING_FLOAT_KERNEL_TRANSPOSE_ACCUMULATOR_CELLS \
1596*5f39d1b3SJooyung Han "vext.32 q5, q5, q5, #" #a \
1597*5f39d1b3SJooyung Han "\n" \
1598*5f39d1b3SJooyung Han "vext.32 q6, q6, q6, #" #b \
1599*5f39d1b3SJooyung Han "\n" \
1600*5f39d1b3SJooyung Han "vext.32 q7, q7, q7, #" #c \
1601*5f39d1b3SJooyung Han "\n" \
1602*5f39d1b3SJooyung Han "vext.32 q9, q9, q9, #" #a \
1603*5f39d1b3SJooyung Han "\n" \
1604*5f39d1b3SJooyung Han "vext.32 q10, q10, q10, #" #b \
1605*5f39d1b3SJooyung Han "\n" \
1606*5f39d1b3SJooyung Han "vext.32 q11, q11, q11, #" #c \
1607*5f39d1b3SJooyung Han "\n" \
1608*5f39d1b3SJooyung Han "vext.32 q13, q13, q13, #" #a \
1609*5f39d1b3SJooyung Han "\n" \
1610*5f39d1b3SJooyung Han "vext.32 q14, q14, q14, #" #b \
1611*5f39d1b3SJooyung Han "\n" \
1612*5f39d1b3SJooyung Han "vext.32 q15, q15, q15, #" #c \
1613*5f39d1b3SJooyung Han "\n" NEON_32BIT_ROTATING_FLOAT_KERNEL_TRANSPOSE_ACCUMULATOR_CELLS
1614*5f39d1b3SJooyung Han
1615*5f39d1b3SJooyung Han NEON_32BIT_ROTATING_FLOAT_KERNEL_ROTATE_ACCUMULATOR_CELLS(1, 2, 3)
1616*5f39d1b3SJooyung Han
1617*5f39d1b3SJooyung Han //"loop_%=:\n"
1618*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1619*5f39d1b3SJooyung Han ":\n"
1620*5f39d1b3SJooyung Han
1621*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 1x4
1622*5f39d1b3SJooyung Han "vld1.32 {d0, d1}, [%[rhs_ptr]]!\n"
1623*5f39d1b3SJooyung Han
1624*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
1625*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
1626*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
1627*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
1628*5f39d1b3SJooyung Han
1629*5f39d1b3SJooyung Han // Multiply-accumulate
1630*5f39d1b3SJooyung Han "vmla.f32 q4, q1, q0\n"
1631*5f39d1b3SJooyung Han "vmla.f32 q8, q2, q0\n"
1632*5f39d1b3SJooyung Han "vmla.f32 q12, q3, q0\n"
1633*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1634*5f39d1b3SJooyung Han "vmla.f32 q5, q1, q0\n"
1635*5f39d1b3SJooyung Han "vmla.f32 q9, q2, q0\n"
1636*5f39d1b3SJooyung Han "vmla.f32 q13, q3, q0\n"
1637*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1638*5f39d1b3SJooyung Han "vmla.f32 q6, q1, q0\n"
1639*5f39d1b3SJooyung Han "vmla.f32 q10, q2, q0\n"
1640*5f39d1b3SJooyung Han "vmla.f32 q14, q3, q0\n"
1641*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1642*5f39d1b3SJooyung Han "vmla.f32 q7, q1, q0\n"
1643*5f39d1b3SJooyung Han "vmla.f32 q11, q2, q0\n"
1644*5f39d1b3SJooyung Han "vmla.f32 q15, q3, q0\n"
1645*5f39d1b3SJooyung Han
1646*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
1647*5f39d1b3SJooyung Han // level of depth.
1648*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1649*5f39d1b3SJooyung Han //"bne loop_%=\n"
1650*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
1651*5f39d1b3SJooyung Han "b\n"
1652*5f39d1b3SJooyung Han
1653*5f39d1b3SJooyung Han // Store accumulators
1654*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1655*5f39d1b3SJooyung Han
1656*5f39d1b3SJooyung Han NEON_32BIT_ROTATING_FLOAT_KERNEL_ROTATE_ACCUMULATOR_CELLS(3, 2, 1)
1657*5f39d1b3SJooyung Han
1658*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1659*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1660*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1661*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1662*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1663*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1664*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1665*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1666*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1667*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1668*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1669*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1670*5f39d1b3SJooyung Han : // outputs
1671*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1672*5f39d1b3SJooyung Han [depth] "+r"(depth)
1673*5f39d1b3SJooyung Han : // inputs
1674*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1675*5f39d1b3SJooyung Han : // clobbers
1676*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1677*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
1678*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
1679*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
1680*5f39d1b3SJooyung Han }
1681*5f39d1b3SJooyung Han };
1682*5f39d1b3SJooyung Han
1683*5f39d1b3SJooyung Han // This rotating variant performs well when permutations (vext) can be
1684*5f39d1b3SJooyung Han // dual-issued with arithmetic instructions. It is relevant as the rotating
1685*5f39d1b3SJooyung Han // approach removes the need for multiply-with-scalar instructions, and ARMv7
1686*5f39d1b3SJooyung Han // FMA does not have a with-scalar variant.
1687*5f39d1b3SJooyung Han struct NEON_32bit_GEMM_Float32_FMA_Rotating {
1688*5f39d1b3SJooyung Han typedef float OperandType;
1689*5f39d1b3SJooyung Han typedef float AccumulatorType;
1690*5f39d1b3SJooyung Han typedef KernelFormat<
1691*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
1692*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 1> >
1693*5f39d1b3SJooyung Han Format;
RunNEON_32bit_GEMM_Float32_FMA_Rotating1694*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1695*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1696*5f39d1b3SJooyung Han asm volatile(
1697*5f39d1b3SJooyung Han // Load accumulators
1698*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1699*5f39d1b3SJooyung Han "vld1.32 {d8, d9}, [r0]!\n"
1700*5f39d1b3SJooyung Han "vld1.32 {d16, d17}, [r0]!\n"
1701*5f39d1b3SJooyung Han "vld1.32 {d24, d25}, [r0]!\n"
1702*5f39d1b3SJooyung Han "vld1.32 {d10, d11}, [r0]!\n"
1703*5f39d1b3SJooyung Han "vld1.32 {d18, d19}, [r0]!\n"
1704*5f39d1b3SJooyung Han "vld1.32 {d26, d27}, [r0]!\n"
1705*5f39d1b3SJooyung Han "vld1.32 {d12, d13}, [r0]!\n"
1706*5f39d1b3SJooyung Han "vld1.32 {d20, d21}, [r0]!\n"
1707*5f39d1b3SJooyung Han "vld1.32 {d28, d29}, [r0]!\n"
1708*5f39d1b3SJooyung Han "vld1.32 {d14, d15}, [r0]!\n"
1709*5f39d1b3SJooyung Han "vld1.32 {d22, d23}, [r0]!\n"
1710*5f39d1b3SJooyung Han "vld1.32 {d30, d31}, [r0]!\n"
1711*5f39d1b3SJooyung Han
1712*5f39d1b3SJooyung Han NEON_32BIT_ROTATING_FLOAT_KERNEL_ROTATE_ACCUMULATOR_CELLS(1, 2, 3)
1713*5f39d1b3SJooyung Han
1714*5f39d1b3SJooyung Han //"loop_%=:\n"
1715*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1716*5f39d1b3SJooyung Han ":\n"
1717*5f39d1b3SJooyung Han
1718*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 1x4
1719*5f39d1b3SJooyung Han "vld1.32 {d0, d1}, [%[rhs_ptr]]!\n"
1720*5f39d1b3SJooyung Han
1721*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
1722*5f39d1b3SJooyung Han "vld1.32 {d2, d3}, [%[lhs_ptr]]!\n"
1723*5f39d1b3SJooyung Han "vld1.32 {d4, d5}, [%[lhs_ptr]]!\n"
1724*5f39d1b3SJooyung Han "vld1.32 {d6, d7}, [%[lhs_ptr]]!\n"
1725*5f39d1b3SJooyung Han
1726*5f39d1b3SJooyung Han // Multiply-accumulate
1727*5f39d1b3SJooyung Han "vfma.f32 q4, q1, q0\n"
1728*5f39d1b3SJooyung Han "vfma.f32 q8, q2, q0\n"
1729*5f39d1b3SJooyung Han "vfma.f32 q12, q3, q0\n"
1730*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1731*5f39d1b3SJooyung Han "vfma.f32 q5, q1, q0\n"
1732*5f39d1b3SJooyung Han "vfma.f32 q9, q2, q0\n"
1733*5f39d1b3SJooyung Han "vfma.f32 q13, q3, q0\n"
1734*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1735*5f39d1b3SJooyung Han "vfma.f32 q6, q1, q0\n"
1736*5f39d1b3SJooyung Han "vfma.f32 q10, q2, q0\n"
1737*5f39d1b3SJooyung Han "vfma.f32 q14, q3, q0\n"
1738*5f39d1b3SJooyung Han "vext.f32 q0, q0, q0, #1\n"
1739*5f39d1b3SJooyung Han "vfma.f32 q7, q1, q0\n"
1740*5f39d1b3SJooyung Han "vfma.f32 q11, q2, q0\n"
1741*5f39d1b3SJooyung Han "vfma.f32 q15, q3, q0\n"
1742*5f39d1b3SJooyung Han
1743*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
1744*5f39d1b3SJooyung Han // level of depth.
1745*5f39d1b3SJooyung Han "subs %[depth], #1\n"
1746*5f39d1b3SJooyung Han //"bne loop_%=\n"
1747*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP "b\n"
1748*5f39d1b3SJooyung Han
1749*5f39d1b3SJooyung Han NEON_32BIT_ROTATING_FLOAT_KERNEL_ROTATE_ACCUMULATOR_CELLS(3, 2, 1)
1750*5f39d1b3SJooyung Han
1751*5f39d1b3SJooyung Han // Store accumulators
1752*5f39d1b3SJooyung Han "mov r0, %[accum_ptr]\n"
1753*5f39d1b3SJooyung Han "vst1.32 {d8, d9}, [r0]!\n"
1754*5f39d1b3SJooyung Han "vst1.32 {d16, d17}, [r0]!\n"
1755*5f39d1b3SJooyung Han "vst1.32 {d24, d25}, [r0]!\n"
1756*5f39d1b3SJooyung Han "vst1.32 {d10, d11}, [r0]!\n"
1757*5f39d1b3SJooyung Han "vst1.32 {d18, d19}, [r0]!\n"
1758*5f39d1b3SJooyung Han "vst1.32 {d26, d27}, [r0]!\n"
1759*5f39d1b3SJooyung Han "vst1.32 {d12, d13}, [r0]!\n"
1760*5f39d1b3SJooyung Han "vst1.32 {d20, d21}, [r0]!\n"
1761*5f39d1b3SJooyung Han "vst1.32 {d28, d29}, [r0]!\n"
1762*5f39d1b3SJooyung Han "vst1.32 {d14, d15}, [r0]!\n"
1763*5f39d1b3SJooyung Han "vst1.32 {d22, d23}, [r0]!\n"
1764*5f39d1b3SJooyung Han "vst1.32 {d30, d31}, [r0]!\n"
1765*5f39d1b3SJooyung Han : // outputs
1766*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1767*5f39d1b3SJooyung Han [depth] "+r"(depth)
1768*5f39d1b3SJooyung Han : // inputs
1769*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
1770*5f39d1b3SJooyung Han : // clobbers
1771*5f39d1b3SJooyung Han "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1772*5f39d1b3SJooyung Han "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
1773*5f39d1b3SJooyung Han "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
1774*5f39d1b3SJooyung Han "d28", "d29", "d30", "d31");
1775*5f39d1b3SJooyung Han }
1776*5f39d1b3SJooyung Han };
1777*5f39d1b3SJooyung Han
1778*5f39d1b3SJooyung Han #endif // __arm__
1779*5f39d1b3SJooyung Han
1780*5f39d1b3SJooyung Han #ifdef __aarch64__
1781*5f39d1b3SJooyung Han
1782*5f39d1b3SJooyung Han // This is the current standard kernel in gemmlowp, see:
1783*5f39d1b3SJooyung Han // https://github.com/google/gemmlowp/blob/b1e2a29ff866680028f3080efc244e10e8dd7f46/internal/kernel_neon.h#L646
1784*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators {
1785*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
1786*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
1787*5f39d1b3SJooyung Han typedef KernelFormat<
1788*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 3>,
1789*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 2> >
1790*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Uint8Operands_Uint32Accumulators1791*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
1792*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
1793*5f39d1b3SJooyung Han asm volatile(
1794*5f39d1b3SJooyung Han // Load 1 Rhs cell of size 2x8
1795*5f39d1b3SJooyung Han "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1796*5f39d1b3SJooyung Han "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1797*5f39d1b3SJooyung Han
1798*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x2 each
1799*5f39d1b3SJooyung Han "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1800*5f39d1b3SJooyung Han "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1801*5f39d1b3SJooyung Han "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1802*5f39d1b3SJooyung Han
1803*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #2\n"
1804*5f39d1b3SJooyung Han
1805*5f39d1b3SJooyung Han // Load accumulators
1806*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
1807*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
1808*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
1809*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
1810*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
1811*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
1812*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
1813*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
1814*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
1815*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
1816*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
1817*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
1818*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
1819*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
1820*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
1821*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
1822*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
1823*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
1824*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
1825*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
1826*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
1827*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
1828*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
1829*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
1830*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
1831*5f39d1b3SJooyung Han
1832*5f39d1b3SJooyung Han "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
1833*5f39d1b3SJooyung Han
1834*5f39d1b3SJooyung Han //"loop_%=:\n"
1835*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
1836*5f39d1b3SJooyung Han ":\n"
1837*5f39d1b3SJooyung Han
1838*5f39d1b3SJooyung Han // Overview of register layout:
1839*5f39d1b3SJooyung Han //
1840*5f39d1b3SJooyung Han // A 2x8 block of 2 2x4 cells of Rhs is stored in 16bit in v0--v1.
1841*5f39d1b3SJooyung Han // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in v2--v4.
1842*5f39d1b3SJooyung Han // A 12x8 block of accumulators is stored in 32bit in v8--v31.
1843*5f39d1b3SJooyung Han //
1844*5f39d1b3SJooyung Han // +--------+--------+-----+--------+--------+
1845*5f39d1b3SJooyung Han // |v0.h[0] |v0.h[1] | ... |v1.h[2] |v1.h[3] |
1846*5f39d1b3SJooyung Han // Rhs +--------+--------+-----+--------+--------+
1847*5f39d1b3SJooyung Han // |v0.h[4] |v0.h[5] | ... |v1.h[6] |v1.h[7] |
1848*5f39d1b3SJooyung Han // +--------+--------+-----+--------+--------+
1849*5f39d1b3SJooyung Han //
1850*5f39d1b3SJooyung Han // | | | | | |
1851*5f39d1b3SJooyung Han //
1852*5f39d1b3SJooyung Han // Lhs | | | | | |
1853*5f39d1b3SJooyung Han //
1854*5f39d1b3SJooyung Han // +-------+-------+ - - +--------+--------+-----+--------+--------+
1855*5f39d1b3SJooyung Han // |v2.h[0]|v2.h[4]| |v8.s[0] |v9.s[0] | ... |v14.s[0]|v15.s[0]|
1856*5f39d1b3SJooyung Han // |v2.h[1]|v2.h[5]| |v8.s[1] |v9.s[1] | ... |v14.s[1]|v15.s[1]|
1857*5f39d1b3SJooyung Han // |v2.h[2]|v2.h[6]| |v8.s[2] |v9.s[2] | ... |v14.s[2]|v15.s[2]|
1858*5f39d1b3SJooyung Han // |v2.h[3]|v2.h[7]| |v8.s[3] |v9.s[3] | ... |v14.s[3]|v15.s[3]|
1859*5f39d1b3SJooyung Han // +-------+-------+ - - +--------+--------+-----+--------+--------+
1860*5f39d1b3SJooyung Han // |v3.h[0]|v3.h[4]| |v16.s[0]|v17.s[0]| ... |v22.s[0]|v23.s[0]|
1861*5f39d1b3SJooyung Han // |v3.h[1]|v3.h[5]| |v16.s[1]|v17.s[1]| ... |v22.s[1]|v23.s[1]|
1862*5f39d1b3SJooyung Han // |v3.h[2]|v3.h[6]| |v16.s[2]|v17.s[2]| ... |v22.s[2]|v23.s[2]|
1863*5f39d1b3SJooyung Han // |v3.h[3]|v3.h[7]| |v16.s[3]|v17.s[3]| ... |v22.s[3]|v23.s[3]|
1864*5f39d1b3SJooyung Han // +-------+-------+ - - +--------+--------+-----+--------+--------+
1865*5f39d1b3SJooyung Han // |v4.h[0]|v4.h[4]| |v24.s[0]|v25.s[0]| ... |v30.s[0]|v31.s[0]|
1866*5f39d1b3SJooyung Han // |v4.h[1]|v4.h[5]| |v24.s[1]|v25.s[1]| ... |v30.s[1]|v31.s[1]|
1867*5f39d1b3SJooyung Han // |v4.h[2]|v4.h[6]| |v24.s[2]|v25.s[2]| ... |v30.s[2]|v31.s[2]|
1868*5f39d1b3SJooyung Han // |v4.h[3]|v4.h[7]| |v24.s[3]|v25.s[3]| ... |v30.s[3]|v31.s[3]|
1869*5f39d1b3SJooyung Han // +-------+-------+ - - +--------+--------+-----+--------+--------+
1870*5f39d1b3SJooyung Han //
1871*5f39d1b3SJooyung Han // Accumulator
1872*5f39d1b3SJooyung Han
1873*5f39d1b3SJooyung Han // Expand Lhs/Rhs cells to 16 bit.
1874*5f39d1b3SJooyung Han "uxtl v0.8h, v5.8b\n"
1875*5f39d1b3SJooyung Han "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1876*5f39d1b3SJooyung Han "uxtl v1.8h, v6.8b\n"
1877*5f39d1b3SJooyung Han "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1878*5f39d1b3SJooyung Han "uxtl v2.8h, v2.8b\n"
1879*5f39d1b3SJooyung Han "uxtl v3.8h, v3.8b\n"
1880*5f39d1b3SJooyung Han "uxtl v4.8h, v4.8b\n"
1881*5f39d1b3SJooyung Han
1882*5f39d1b3SJooyung Han // Multiply-accumulate, top third
1883*5f39d1b3SJooyung Han "umlal v8.4s, v2.4h, v0.h[0]\n"
1884*5f39d1b3SJooyung Han "umlal v9.4s, v2.4h, v0.h[1]\n"
1885*5f39d1b3SJooyung Han "umlal v10.4s, v2.4h, v0.h[2]\n"
1886*5f39d1b3SJooyung Han "umlal v11.4s, v2.4h, v0.h[3]\n"
1887*5f39d1b3SJooyung Han "umlal v12.4s, v2.4h, v1.h[0]\n"
1888*5f39d1b3SJooyung Han "umlal v13.4s, v2.4h, v1.h[1]\n"
1889*5f39d1b3SJooyung Han "umlal v14.4s, v2.4h, v1.h[2]\n"
1890*5f39d1b3SJooyung Han "umlal v15.4s, v2.4h, v1.h[3]\n"
1891*5f39d1b3SJooyung Han "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1892*5f39d1b3SJooyung Han "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1893*5f39d1b3SJooyung Han "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1894*5f39d1b3SJooyung Han "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1895*5f39d1b3SJooyung Han "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1896*5f39d1b3SJooyung Han "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1897*5f39d1b3SJooyung Han "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1898*5f39d1b3SJooyung Han "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1899*5f39d1b3SJooyung Han "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1900*5f39d1b3SJooyung Han
1901*5f39d1b3SJooyung Han // Multiply-accumulate, middle third
1902*5f39d1b3SJooyung Han "umlal v16.4s, v3.4h, v0.h[0]\n"
1903*5f39d1b3SJooyung Han "umlal v17.4s, v3.4h, v0.h[1]\n"
1904*5f39d1b3SJooyung Han "umlal v18.4s, v3.4h, v0.h[2]\n"
1905*5f39d1b3SJooyung Han "umlal v19.4s, v3.4h, v0.h[3]\n"
1906*5f39d1b3SJooyung Han "umlal v20.4s, v3.4h, v1.h[0]\n"
1907*5f39d1b3SJooyung Han "umlal v21.4s, v3.4h, v1.h[1]\n"
1908*5f39d1b3SJooyung Han "umlal v22.4s, v3.4h, v1.h[2]\n"
1909*5f39d1b3SJooyung Han "umlal v23.4s, v3.4h, v1.h[3]\n"
1910*5f39d1b3SJooyung Han "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1911*5f39d1b3SJooyung Han "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1912*5f39d1b3SJooyung Han "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1913*5f39d1b3SJooyung Han "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1914*5f39d1b3SJooyung Han "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1915*5f39d1b3SJooyung Han "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1916*5f39d1b3SJooyung Han "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1917*5f39d1b3SJooyung Han "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1918*5f39d1b3SJooyung Han "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1919*5f39d1b3SJooyung Han
1920*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #2\n"
1921*5f39d1b3SJooyung Han
1922*5f39d1b3SJooyung Han // Multiply-accumulate, bottom third
1923*5f39d1b3SJooyung Han "umlal v24.4s, v4.4h, v0.h[0]\n"
1924*5f39d1b3SJooyung Han "umlal v25.4s, v4.4h, v0.h[1]\n"
1925*5f39d1b3SJooyung Han "umlal v26.4s, v4.4h, v0.h[2]\n"
1926*5f39d1b3SJooyung Han "umlal v27.4s, v4.4h, v0.h[3]\n"
1927*5f39d1b3SJooyung Han "umlal v28.4s, v4.4h, v1.h[0]\n"
1928*5f39d1b3SJooyung Han "umlal v29.4s, v4.4h, v1.h[1]\n"
1929*5f39d1b3SJooyung Han "umlal v30.4s, v4.4h, v1.h[2]\n"
1930*5f39d1b3SJooyung Han "umlal v31.4s, v4.4h, v1.h[3]\n"
1931*5f39d1b3SJooyung Han "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1932*5f39d1b3SJooyung Han "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1933*5f39d1b3SJooyung Han "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1934*5f39d1b3SJooyung Han "umlal2 v27.4s, v4.8h, v0.h[7]\n"
1935*5f39d1b3SJooyung Han "umlal2 v28.4s, v4.8h, v1.h[4]\n"
1936*5f39d1b3SJooyung Han "umlal2 v29.4s, v4.8h, v1.h[5]\n"
1937*5f39d1b3SJooyung Han "umlal2 v30.4s, v4.8h, v1.h[6]\n"
1938*5f39d1b3SJooyung Han "umlal2 v31.4s, v4.8h, v1.h[7]\n"
1939*5f39d1b3SJooyung Han "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1940*5f39d1b3SJooyung Han
1941*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP "b\n"
1942*5f39d1b3SJooyung Han
1943*5f39d1b3SJooyung Han GEMMLOWP_LABEL_AFTER_LOOP
1944*5f39d1b3SJooyung Han ":\n"
1945*5f39d1b3SJooyung Han
1946*5f39d1b3SJooyung Han // Expand Lhs/Rhs cells to 16 bit.
1947*5f39d1b3SJooyung Han "uxtl v0.8h, v5.8b\n"
1948*5f39d1b3SJooyung Han "uxtl v1.8h, v6.8b\n"
1949*5f39d1b3SJooyung Han "uxtl v2.8h, v2.8b\n"
1950*5f39d1b3SJooyung Han "uxtl v3.8h, v3.8b\n"
1951*5f39d1b3SJooyung Han "uxtl v4.8h, v4.8b\n"
1952*5f39d1b3SJooyung Han
1953*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 0
1954*5f39d1b3SJooyung Han "umlal v8.4s, v2.4h, v0.h[0]\n"
1955*5f39d1b3SJooyung Han "umlal v9.4s, v2.4h, v0.h[1]\n"
1956*5f39d1b3SJooyung Han "umlal v10.4s, v2.4h, v0.h[2]\n"
1957*5f39d1b3SJooyung Han "umlal v11.4s, v2.4h, v0.h[3]\n"
1958*5f39d1b3SJooyung Han "umlal v12.4s, v2.4h, v1.h[0]\n"
1959*5f39d1b3SJooyung Han "umlal v13.4s, v2.4h, v1.h[1]\n"
1960*5f39d1b3SJooyung Han "umlal v14.4s, v2.4h, v1.h[2]\n"
1961*5f39d1b3SJooyung Han "umlal v15.4s, v2.4h, v1.h[3]\n"
1962*5f39d1b3SJooyung Han "umlal v16.4s, v3.4h, v0.h[0]\n"
1963*5f39d1b3SJooyung Han "umlal v17.4s, v3.4h, v0.h[1]\n"
1964*5f39d1b3SJooyung Han "umlal v18.4s, v3.4h, v0.h[2]\n"
1965*5f39d1b3SJooyung Han "umlal v19.4s, v3.4h, v0.h[3]\n"
1966*5f39d1b3SJooyung Han "umlal v20.4s, v3.4h, v1.h[0]\n"
1967*5f39d1b3SJooyung Han "umlal v21.4s, v3.4h, v1.h[1]\n"
1968*5f39d1b3SJooyung Han "umlal v22.4s, v3.4h, v1.h[2]\n"
1969*5f39d1b3SJooyung Han "umlal v23.4s, v3.4h, v1.h[3]\n"
1970*5f39d1b3SJooyung Han "umlal v24.4s, v4.4h, v0.h[0]\n"
1971*5f39d1b3SJooyung Han "umlal v25.4s, v4.4h, v0.h[1]\n"
1972*5f39d1b3SJooyung Han "umlal v26.4s, v4.4h, v0.h[2]\n"
1973*5f39d1b3SJooyung Han "umlal v27.4s, v4.4h, v0.h[3]\n"
1974*5f39d1b3SJooyung Han "umlal v28.4s, v4.4h, v1.h[0]\n"
1975*5f39d1b3SJooyung Han "umlal v29.4s, v4.4h, v1.h[1]\n"
1976*5f39d1b3SJooyung Han "umlal v30.4s, v4.4h, v1.h[2]\n"
1977*5f39d1b3SJooyung Han "umlal v31.4s, v4.4h, v1.h[3]\n"
1978*5f39d1b3SJooyung Han
1979*5f39d1b3SJooyung Han // Multiply-accumulate, level of depth 1
1980*5f39d1b3SJooyung Han "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1981*5f39d1b3SJooyung Han "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1982*5f39d1b3SJooyung Han "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1983*5f39d1b3SJooyung Han "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1984*5f39d1b3SJooyung Han "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1985*5f39d1b3SJooyung Han "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1986*5f39d1b3SJooyung Han "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1987*5f39d1b3SJooyung Han "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1988*5f39d1b3SJooyung Han "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1989*5f39d1b3SJooyung Han "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1990*5f39d1b3SJooyung Han "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1991*5f39d1b3SJooyung Han "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1992*5f39d1b3SJooyung Han "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1993*5f39d1b3SJooyung Han "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1994*5f39d1b3SJooyung Han "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1995*5f39d1b3SJooyung Han "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1996*5f39d1b3SJooyung Han "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1997*5f39d1b3SJooyung Han "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1998*5f39d1b3SJooyung Han "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1999*5f39d1b3SJooyung Han "umlal2 v27.4s, v4.8h, v0.h[7]\n"
2000*5f39d1b3SJooyung Han "umlal2 v28.4s, v4.8h, v1.h[4]\n"
2001*5f39d1b3SJooyung Han "umlal2 v29.4s, v4.8h, v1.h[5]\n"
2002*5f39d1b3SJooyung Han "umlal2 v30.4s, v4.8h, v1.h[6]\n"
2003*5f39d1b3SJooyung Han "umlal2 v31.4s, v4.8h, v1.h[7]\n"
2004*5f39d1b3SJooyung Han
2005*5f39d1b3SJooyung Han // Store accumulators
2006*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
2007*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
2008*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
2009*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
2010*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
2011*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
2012*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
2013*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
2014*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
2015*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
2016*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
2017*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
2018*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
2019*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
2020*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
2021*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
2022*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
2023*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
2024*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
2025*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
2026*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
2027*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
2028*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
2029*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
2030*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
2031*5f39d1b3SJooyung Han : // outputs
2032*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
2033*5f39d1b3SJooyung Han [depth] "+r"(depth)
2034*5f39d1b3SJooyung Han : // inputs
2035*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
2036*5f39d1b3SJooyung Han : // clobbers
2037*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
2038*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
2039*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
2040*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
2041*5f39d1b3SJooyung Han }
2042*5f39d1b3SJooyung Han };
2043*5f39d1b3SJooyung Han
2044*5f39d1b3SJooyung Han // Faster kernel by ARM. Not expanding operands before multiplication.
2045*5f39d1b3SJooyung Han // Tuned for A57. Compare to
2046*5f39d1b3SJooyung Han // NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand
2047*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand_A57 {
2048*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
2049*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
2050*5f39d1b3SJooyung Han typedef KernelFormat<
2051*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<5, 16, CellOrder::WidthMajor>, 1>,
2052*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1> >
2053*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand_A572054*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
2055*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
2056*5f39d1b3SJooyung Han static const int kLhsWidth = Format::Lhs::kWidth;
2057*5f39d1b3SJooyung Han static const int kRhsWidth = Format::Rhs::kWidth;
2058*5f39d1b3SJooyung Han AccumulatorType rowmajor_accumulator_buffer[kLhsWidth * kRhsWidth];
2059*5f39d1b3SJooyung Han asm volatile(
2060*5f39d1b3SJooyung Han // Clear aggregators
2061*5f39d1b3SJooyung Han "dup v12.4s, wzr\n"
2062*5f39d1b3SJooyung Han "dup v13.4s, wzr\n"
2063*5f39d1b3SJooyung Han "dup v14.4s, wzr\n"
2064*5f39d1b3SJooyung Han "dup v15.4s, wzr\n"
2065*5f39d1b3SJooyung Han "dup v16.4s, wzr\n"
2066*5f39d1b3SJooyung Han "dup v17.4s, wzr\n"
2067*5f39d1b3SJooyung Han "dup v18.4s, wzr\n"
2068*5f39d1b3SJooyung Han "dup v19.4s, wzr\n"
2069*5f39d1b3SJooyung Han "dup v20.4s, wzr\n"
2070*5f39d1b3SJooyung Han "dup v21.4s, wzr\n"
2071*5f39d1b3SJooyung Han "dup v22.4s, wzr\n"
2072*5f39d1b3SJooyung Han "dup v23.4s, wzr\n"
2073*5f39d1b3SJooyung Han "dup v24.4s, wzr\n"
2074*5f39d1b3SJooyung Han "dup v25.4s, wzr\n"
2075*5f39d1b3SJooyung Han "dup v26.4s, wzr\n"
2076*5f39d1b3SJooyung Han "dup v27.4s, wzr\n"
2077*5f39d1b3SJooyung Han "dup v28.4s, wzr\n"
2078*5f39d1b3SJooyung Han "dup v29.4s, wzr\n"
2079*5f39d1b3SJooyung Han "dup v30.4s, wzr\n"
2080*5f39d1b3SJooyung Han "dup v31.4s, wzr\n"
2081*5f39d1b3SJooyung Han
2082*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
2083*5f39d1b3SJooyung Han ":\n"
2084*5f39d1b3SJooyung Han
2085*5f39d1b3SJooyung Han // Overview of register layout:
2086*5f39d1b3SJooyung Han //
2087*5f39d1b3SJooyung Han // A 4x16 block of Rhs is stored in 8 bit in v0--v3.
2088*5f39d1b3SJooyung Han // A 5x16 block of Lhs is cycled through v4 and v5 in 8 bit.
2089*5f39d1b3SJooyung Han //
2090*5f39d1b3SJooyung Han // A 4x5 block of aggregators is stored in v12-v31 (as 4x32 bit
2091*5f39d1b3SJooyung Han // components which would need to be added at the end)
2092*5f39d1b3SJooyung Han //
2093*5f39d1b3SJooyung Han // The Lhs vectors are multiplied by the Rhs vectors with a widening
2094*5f39d1b3SJooyung Han // multiply to produce an intermediate result which is stored in
2095*5f39d1b3SJooyung Han // v6-v11. Each intermediate result is 8x16 bits so this happens
2096*5f39d1b3SJooyung Han // twice for each Lhs/Rhs combination (once with UMULL for elements
2097*5f39d1b3SJooyung Han // 0-7 and once with UMULL2 for elements 8-15).
2098*5f39d1b3SJooyung Han //
2099*5f39d1b3SJooyung Han // UADALP is used to accumulate these intermediate results into the
2100*5f39d1b3SJooyung Han // result aggregators.
2101*5f39d1b3SJooyung Han //
2102*5f39d1b3SJooyung Han //
2103*5f39d1b3SJooyung Han //
2104*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2105*5f39d1b3SJooyung Han // |v0.b[0] |v1.b[0] |v2.b[0] |v3.b[0] |
2106*5f39d1b3SJooyung Han // Rhs +--------+--------+--------+--------+
2107*5f39d1b3SJooyung Han // | ... | ... | ... | ... |
2108*5f39d1b3SJooyung Han // +--------+--------+--------+--------|
2109*5f39d1b3SJooyung Han // |v0.b[15]|v1.b[15]|v2.b[15]|v3.b[15]|
2110*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2111*5f39d1b3SJooyung Han //
2112*5f39d1b3SJooyung Han // | | | | |
2113*5f39d1b3SJooyung Han //
2114*5f39d1b3SJooyung Han // Lhs | | | | |
2115*5f39d1b3SJooyung Han //
2116*5f39d1b3SJooyung Han // +-------+-----+--------+ - - +--------+--------+--------+--------+
2117*5f39d1b3SJooyung Han // |v4.b[0]| ... |v4.b[15]| | v12.4s | v13.4s | v14.4s | v15.4s |
2118*5f39d1b3SJooyung Han // |v5.b[0]| ... |v5.b[15]| | v16.4s | v17.4s | v18.4s | v19.4s |
2119*5f39d1b3SJooyung Han // |v4.b[0]| ... |v4.b[15]| | v20.4s | v21.4s | v22.4s | v23.4s |
2120*5f39d1b3SJooyung Han // |v5.b[0]| ... |v5.b[15]| | v24.4s | v25.4s | v26.4s | v27.4s |
2121*5f39d1b3SJooyung Han // |v4.b[0]| ... |v4.b[15]| | v28.4s | v29.4s | v30.4s | v31.4s |
2122*5f39d1b3SJooyung Han // +-------+--------------+ - - +--------+--------+--------+--------+
2123*5f39d1b3SJooyung Han //
2124*5f39d1b3SJooyung Han // Accumulator
2125*5f39d1b3SJooyung Han //
2126*5f39d1b3SJooyung Han //
2127*5f39d1b3SJooyung Han // Further possible optimisations (not tried):
2128*5f39d1b3SJooyung Han // - Move early loads into previous iteration (see Float32_WithScalar
2129*5f39d1b3SJooyung Han // for example). - Unroll loop 2x to alternate more smoothly between
2130*5f39d1b3SJooyung Han // v4 and v5. - A different number of temporary registers might work
2131*5f39d1b3SJooyung Han // better. - Pairing umull with corresponding umull2 might allow
2132*5f39d1b3SJooyung Han // better
2133*5f39d1b3SJooyung Han // register loading (e.g. at the start of the loop)
2134*5f39d1b3SJooyung Han // - Interleaving umull{2} and uadalp even more aggressively might
2135*5f39d1b3SJooyung Han // help, (not sure about latency vs. dispatch rate).
2136*5f39d1b3SJooyung Han //
2137*5f39d1b3SJooyung Han //
2138*5f39d1b3SJooyung Han // Start loading Rhs - further loads are interleaved amongst the
2139*5f39d1b3SJooyung Han // multiplies for better dispatch on A57.
2140*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2141*5f39d1b3SJooyung Han
2142*5f39d1b3SJooyung Han // Load first Lhs vector - further loads are interleaved amongst the
2143*5f39d1b3SJooyung Han // multiplies
2144*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2145*5f39d1b3SJooyung Han
2146*5f39d1b3SJooyung Han "umull v6.8h, v0.8b, v4.8b\n"
2147*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n" // 2nd RHS element
2148*5f39d1b3SJooyung Han "umull v7.8h, v1.8b, v4.8b\n"
2149*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n" // 3rd RHS element
2150*5f39d1b3SJooyung Han "umull v8.8h, v2.8b, v4.8b\n"
2151*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n" // 4th RHS element
2152*5f39d1b3SJooyung Han "umull v9.8h, v3.8b, v4.8b\n"
2153*5f39d1b3SJooyung Han "umull2 v10.8h, v0.16b, v4.16b\n"
2154*5f39d1b3SJooyung Han "umull2 v11.8h, v1.16b, v4.16b\n"
2155*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n" // 2nd LHS element
2156*5f39d1b3SJooyung Han
2157*5f39d1b3SJooyung Han "uadalp v12.4s, v6.8h\n"
2158*5f39d1b3SJooyung Han "umull2 v6.8h, v2.16b, v4.16b\n"
2159*5f39d1b3SJooyung Han "uadalp v13.4s, v7.8h\n"
2160*5f39d1b3SJooyung Han "umull2 v7.8h, v3.16b, v4.16b\n"
2161*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n" // 1st LHS element done - Reuse v4
2162*5f39d1b3SJooyung Han // for 3rd LHS element
2163*5f39d1b3SJooyung Han "uadalp v14.4s, v8.8h\n"
2164*5f39d1b3SJooyung Han "umull v8.8h, v0.8b, v5.8b\n"
2165*5f39d1b3SJooyung Han "uadalp v15.4s, v9.8h\n"
2166*5f39d1b3SJooyung Han "umull v9.8h, v1.8b, v5.8b\n"
2167*5f39d1b3SJooyung Han "uadalp v12.4s, v10.8h\n"
2168*5f39d1b3SJooyung Han "umull v10.8h, v2.8b, v5.8b\n"
2169*5f39d1b3SJooyung Han "uadalp v13.4s, v11.8h\n"
2170*5f39d1b3SJooyung Han "umull v11.8h, v3.8b, v5.8b\n"
2171*5f39d1b3SJooyung Han
2172*5f39d1b3SJooyung Han "uadalp v14.4s, v6.8h\n"
2173*5f39d1b3SJooyung Han "umull2 v6.8h, v0.16b, v5.16b\n"
2174*5f39d1b3SJooyung Han "uadalp v15.4s, v7.8h\n"
2175*5f39d1b3SJooyung Han "umull2 v7.8h, v1.16b, v5.16b\n"
2176*5f39d1b3SJooyung Han "uadalp v16.4s, v8.8h\n"
2177*5f39d1b3SJooyung Han "umull2 v8.8h, v2.16b, v5.16b\n"
2178*5f39d1b3SJooyung Han "uadalp v17.4s, v9.8h\n"
2179*5f39d1b3SJooyung Han "umull2 v9.8h, v3.16b, v5.16b\n"
2180*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n" // 2nd LHS element done - Reuse v5
2181*5f39d1b3SJooyung Han // for 4th LHS element
2182*5f39d1b3SJooyung Han "uadalp v18.4s, v10.8h\n"
2183*5f39d1b3SJooyung Han "umull v10.8h, v0.8b, v4.8b\n"
2184*5f39d1b3SJooyung Han "uadalp v19.4s, v11.8h\n"
2185*5f39d1b3SJooyung Han "umull v11.8h, v1.8b, v4.8b\n"
2186*5f39d1b3SJooyung Han
2187*5f39d1b3SJooyung Han "uadalp v16.4s, v6.8h\n"
2188*5f39d1b3SJooyung Han "umull v6.8h, v2.8b, v4.8b\n"
2189*5f39d1b3SJooyung Han "uadalp v17.4s, v7.8h\n"
2190*5f39d1b3SJooyung Han "umull v7.8h, v3.8b, v4.8b\n"
2191*5f39d1b3SJooyung Han "uadalp v18.4s, v8.8h\n"
2192*5f39d1b3SJooyung Han "umull2 v8.8h, v0.16b, v4.16b\n"
2193*5f39d1b3SJooyung Han "uadalp v19.4s, v9.8h\n"
2194*5f39d1b3SJooyung Han "umull2 v9.8h, v1.16b, v4.16b\n"
2195*5f39d1b3SJooyung Han "uadalp v20.4s, v10.8h\n"
2196*5f39d1b3SJooyung Han "umull2 v10.8h, v2.16b, v4.16b\n"
2197*5f39d1b3SJooyung Han "uadalp v21.4s, v11.8h\n"
2198*5f39d1b3SJooyung Han "umull2 v11.8h, v3.16b, v4.16b\n"
2199*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n" // 3rd LHS element done - Reuse v4
2200*5f39d1b3SJooyung Han // for 5th LHS element
2201*5f39d1b3SJooyung Han
2202*5f39d1b3SJooyung Han "uadalp v22.4s, v6.8h\n"
2203*5f39d1b3SJooyung Han "umull v6.8h, v0.8b, v5.8b\n"
2204*5f39d1b3SJooyung Han "uadalp v23.4s, v7.8h\n"
2205*5f39d1b3SJooyung Han "umull v7.8h, v1.8b, v5.8b\n"
2206*5f39d1b3SJooyung Han "uadalp v20.4s, v8.8h\n"
2207*5f39d1b3SJooyung Han "umull v8.8h, v2.8b, v5.8b\n"
2208*5f39d1b3SJooyung Han "uadalp v21.4s, v9.8h\n"
2209*5f39d1b3SJooyung Han "umull v9.8h, v3.8b, v5.8b\n"
2210*5f39d1b3SJooyung Han "uadalp v22.4s, v10.8h\n"
2211*5f39d1b3SJooyung Han "umull2 v10.8h, v0.16b, v5.16b\n"
2212*5f39d1b3SJooyung Han "uadalp v23.4s, v11.8h\n"
2213*5f39d1b3SJooyung Han "umull2 v11.8h, v1.16b, v5.16b\n"
2214*5f39d1b3SJooyung Han
2215*5f39d1b3SJooyung Han "uadalp v24.4s, v6.8h\n"
2216*5f39d1b3SJooyung Han "umull2 v6.8h, v2.16b, v5.16b\n"
2217*5f39d1b3SJooyung Han "uadalp v25.4s, v7.8h\n"
2218*5f39d1b3SJooyung Han "umull2 v7.8h, v3.16b, v5.16b\n"
2219*5f39d1b3SJooyung Han "uadalp v26.4s, v8.8h\n"
2220*5f39d1b3SJooyung Han "umull v8.8h, v0.8b, v4.8b\n"
2221*5f39d1b3SJooyung Han "uadalp v27.4s, v9.8h\n"
2222*5f39d1b3SJooyung Han "umull v9.8h, v1.8b, v4.8b\n"
2223*5f39d1b3SJooyung Han "uadalp v24.4s, v10.8h\n"
2224*5f39d1b3SJooyung Han "umull v10.8h, v2.8b, v4.8b\n"
2225*5f39d1b3SJooyung Han "uadalp v25.4s, v11.8h\n"
2226*5f39d1b3SJooyung Han "umull v11.8h, v3.8b, v4.8b\n"
2227*5f39d1b3SJooyung Han
2228*5f39d1b3SJooyung Han "uadalp v26.4s, v6.8h\n"
2229*5f39d1b3SJooyung Han "umull2 v6.8h, v0.16b, v4.16b\n"
2230*5f39d1b3SJooyung Han "uadalp v27.4s, v7.8h\n"
2231*5f39d1b3SJooyung Han "umull2 v7.8h, v1.16b, v4.16b\n"
2232*5f39d1b3SJooyung Han "uadalp v28.4s, v8.8h\n"
2233*5f39d1b3SJooyung Han "umull2 v8.8h, v2.16b, v4.16b\n"
2234*5f39d1b3SJooyung Han "uadalp v29.4s, v9.8h\n"
2235*5f39d1b3SJooyung Han "umull2 v9.8h, v3.16b, v4.16b\n"
2236*5f39d1b3SJooyung Han "uadalp v30.4s, v10.8h\n"
2237*5f39d1b3SJooyung Han "uadalp v31.4s, v11.8h\n"
2238*5f39d1b3SJooyung Han
2239*5f39d1b3SJooyung Han "uadalp v28.4s, v6.8h\n"
2240*5f39d1b3SJooyung Han "uadalp v29.4s, v7.8h\n"
2241*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 16, since we just handled
2242*5f39d1b3SJooyung Han // 16 levels of depth. Do this subs a bit before the end of the loop
2243*5f39d1b3SJooyung Han // for better dispatch on A57.
2244*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #16\n"
2245*5f39d1b3SJooyung Han "uadalp v30.4s, v8.8h\n"
2246*5f39d1b3SJooyung Han "uadalp v31.4s, v9.8h\n"
2247*5f39d1b3SJooyung Han
2248*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
2249*5f39d1b3SJooyung Han "b\n"
2250*5f39d1b3SJooyung Han
2251*5f39d1b3SJooyung Han // Reduce aggregators horizontally
2252*5f39d1b3SJooyung Han "addp v0.4s, v12.4s, v13.4s\n"
2253*5f39d1b3SJooyung Han "addp v1.4s, v14.4s, v15.4s\n"
2254*5f39d1b3SJooyung Han "addp v2.4s, v16.4s, v17.4s\n"
2255*5f39d1b3SJooyung Han "addp v3.4s, v18.4s, v19.4s\n"
2256*5f39d1b3SJooyung Han "addp v4.4s, v20.4s, v21.4s\n"
2257*5f39d1b3SJooyung Han "addp v5.4s, v22.4s, v23.4s\n"
2258*5f39d1b3SJooyung Han "addp v6.4s, v24.4s, v25.4s\n"
2259*5f39d1b3SJooyung Han "addp v7.4s, v26.4s, v27.4s\n"
2260*5f39d1b3SJooyung Han "addp v8.4s, v28.4s, v29.4s\n"
2261*5f39d1b3SJooyung Han "addp v9.4s, v30.4s, v31.4s\n"
2262*5f39d1b3SJooyung Han
2263*5f39d1b3SJooyung Han "addp v10.4s, v0.4s, v1.4s\n"
2264*5f39d1b3SJooyung Han "addp v11.4s, v2.4s, v3.4s\n"
2265*5f39d1b3SJooyung Han "addp v12.4s, v4.4s, v5.4s\n"
2266*5f39d1b3SJooyung Han "addp v13.4s, v6.4s, v7.4s\n"
2267*5f39d1b3SJooyung Han "addp v14.4s, v8.4s, v9.4s\n"
2268*5f39d1b3SJooyung Han
2269*5f39d1b3SJooyung Han "mov x0, %[rowmajor_accumulator_buffer]\n"
2270*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
2271*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
2272*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
2273*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
2274*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
2275*5f39d1b3SJooyung Han : // outputs
2276*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
2277*5f39d1b3SJooyung Han [depth] "+r"(depth)
2278*5f39d1b3SJooyung Han : // inputs
2279*5f39d1b3SJooyung Han [rowmajor_accumulator_buffer] "r"(rowmajor_accumulator_buffer)
2280*5f39d1b3SJooyung Han : // clobbers
2281*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
2282*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
2283*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
2284*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
2285*5f39d1b3SJooyung Han
2286*5f39d1b3SJooyung Han // accumulate row-major accumulators into global (column-major) accumulators
2287*5f39d1b3SJooyung Han for (int l = 0; l < kLhsWidth; l++) {
2288*5f39d1b3SJooyung Han for (int r = 0; r < kRhsWidth; r++) {
2289*5f39d1b3SJooyung Han accum_ptr[l + kLhsWidth * r] +=
2290*5f39d1b3SJooyung Han rowmajor_accumulator_buffer[r + l * kRhsWidth];
2291*5f39d1b3SJooyung Han }
2292*5f39d1b3SJooyung Han }
2293*5f39d1b3SJooyung Han }
2294*5f39d1b3SJooyung Han };
2295*5f39d1b3SJooyung Han
2296*5f39d1b3SJooyung Han // Fast kernel operating on int8 operands.
2297*5f39d1b3SJooyung Han // It is assumed that one of the two int8 operands only takes values
2298*5f39d1b3SJooyung Han // in [-127, 127], while the other may freely range in [-128, 127].
2299*5f39d1b3SJooyung Han // The issue with both operands taking the value -128 is that:
2300*5f39d1b3SJooyung Han // -128*-128 + -128*-128 == -32768 overflows int16.
2301*5f39d1b3SJooyung Han // Every other expression a*b + c*d, for any int8 a,b,c,d, fits in int16
2302*5f39d1b3SJooyung Han // range. That is the basic idea of this kernel.
2303*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int8Operands_AccumTwoWithin16Bits {
2304*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
2305*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
2306*5f39d1b3SJooyung Han typedef KernelFormat<
2307*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
2308*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1> >
2309*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int8Operands_AccumTwoWithin16Bits2310*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
2311*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
2312*5f39d1b3SJooyung Han std::size_t start_depth = 123;
2313*5f39d1b3SJooyung Han std::size_t run_depth = depth;
2314*5f39d1b3SJooyung Han std::size_t dst_col_stride = 4;
2315*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
2316*5f39d1b3SJooyung Han asm volatile(
2317*5f39d1b3SJooyung Han // Overview of register layout:
2318*5f39d1b3SJooyung Han //
2319*5f39d1b3SJooyung Han // A 4x16 block of Rhs is stored in 8 bit in v0--v3.
2320*5f39d1b3SJooyung Han // A 4x16 block of Lhs is stored in 8 bit in v4--v7.
2321*5f39d1b3SJooyung Han //
2322*5f39d1b3SJooyung Han // A 4x4 block of accumulators is stored in v16-v31 (as 4x32 bit
2323*5f39d1b3SJooyung Han // components which need to be horizontally-added at the end)
2324*5f39d1b3SJooyung Han //
2325*5f39d1b3SJooyung Han // The Lhs vectors are multiplied by the Rhs vectors with a widening
2326*5f39d1b3SJooyung Han // multiply over the 8 first levels of depth, producing int16x8
2327*5f39d1b3SJooyung Han // vectors of products for each position in the accumulator matrix.
2328*5f39d1b3SJooyung Han // Here comes the special trick: since the operands are signed int8,
2329*5f39d1b3SJooyung Han // their range being [ -2^7 , 2^7 ), their products are in range
2330*5f39d1b3SJooyung Han // [ -2^14 , 2^14 - 1 ), meaning that we can add two such values
2331*5f39d1b3SJooyung Han // without any risk of overflowing int16.
2332*5f39d1b3SJooyung Han // We thus proceed with the 8 next levels of depth, multiplying
2333*5f39d1b3SJooyung Han // again Lhs by Rhs, accumulating into this existing int16x8 vector.
2334*5f39d1b3SJooyung Han //
2335*5f39d1b3SJooyung Han // Only then, having processed 16 levels of depth, do we need to
2336*5f39d1b3SJooyung Han // horizontally add these int16x8 accumulators into the final
2337*5f39d1b3SJooyung Han // int32x4 accumulators.
2338*5f39d1b3SJooyung Han //
2339*5f39d1b3SJooyung Han // As we do not have enough registers to store all 16 int16x8
2340*5f39d1b3SJooyung Han // temporary-16bit-accumulators, we have them cycle through v8--v15.
2341*5f39d1b3SJooyung Han //
2342*5f39d1b3SJooyung Han //
2343*5f39d1b3SJooyung Han // Register layout (ignoring the v8--v15 temporary 16bit accumulators):
2344*5f39d1b3SJooyung Han //
2345*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2346*5f39d1b3SJooyung Han // |v0.b[0] |v1.b[0] |v2.b[0] |v3.b[0] |
2347*5f39d1b3SJooyung Han // Rhs +--------+--------+--------+--------+
2348*5f39d1b3SJooyung Han // | ... | ... | ... | ... |
2349*5f39d1b3SJooyung Han // +--------+--------+--------+--------|
2350*5f39d1b3SJooyung Han // |v0.b[15]|v1.b[15]|v2.b[15]|v3.b[15]|
2351*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2352*5f39d1b3SJooyung Han //
2353*5f39d1b3SJooyung Han // | | | | |
2354*5f39d1b3SJooyung Han //
2355*5f39d1b3SJooyung Han // Lhs | | | | |
2356*5f39d1b3SJooyung Han //
2357*5f39d1b3SJooyung Han // +-------+-----+--------+ - - +--------+--------+--------+--------+
2358*5f39d1b3SJooyung Han // |v4.b[0]| ... |v4.b[15]| | v16.4s | v17.4s | v18.4s | v19.4s |
2359*5f39d1b3SJooyung Han // |v5.b[0]| ... |v5.b[15]| | v20.4s | v21.4s | v22.4s | v23.4s |
2360*5f39d1b3SJooyung Han // |v6.b[0]| ... |v6.b[15]| | v24.4s | v25.4s | v26.4s | v27.4s |
2361*5f39d1b3SJooyung Han // |v7.b[0]| ... |v7.b[15]| | v28.4s | v29.4s | v30.4s | v31.4s |
2362*5f39d1b3SJooyung Han // +-------+--------------+ - - +--------+--------+--------+--------+
2363*5f39d1b3SJooyung Han //
2364*5f39d1b3SJooyung Han // Accumulator
2365*5f39d1b3SJooyung Han //
2366*5f39d1b3SJooyung Han
2367*5f39d1b3SJooyung Han // Clear accumulators
2368*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2369*5f39d1b3SJooyung Han "dup v16.4s, wzr\n"
2370*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
2371*5f39d1b3SJooyung Han "dup v17.4s, wzr\n"
2372*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2373*5f39d1b3SJooyung Han "dup v18.4s, wzr\n"
2374*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2375*5f39d1b3SJooyung Han "dup v19.4s, wzr\n"
2376*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2377*5f39d1b3SJooyung Han "dup v20.4s, wzr\n"
2378*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2379*5f39d1b3SJooyung Han "dup v21.4s, wzr\n"
2380*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
2381*5f39d1b3SJooyung Han "dup v22.4s, wzr\n"
2382*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
2383*5f39d1b3SJooyung Han "dup v23.4s, wzr\n"
2384*5f39d1b3SJooyung Han "subs %[run_depth], %[run_depth], #16\n"
2385*5f39d1b3SJooyung Han "dup v24.4s, wzr\n"
2386*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
2387*5f39d1b3SJooyung Han "dup v25.4s, wzr\n"
2388*5f39d1b3SJooyung Han "dup v26.4s, wzr\n"
2389*5f39d1b3SJooyung Han "dup v27.4s, wzr\n"
2390*5f39d1b3SJooyung Han "dup v28.4s, wzr\n"
2391*5f39d1b3SJooyung Han "dup v29.4s, wzr\n"
2392*5f39d1b3SJooyung Han "dup v30.4s, wzr\n"
2393*5f39d1b3SJooyung Han "dup v31.4s, wzr\n"
2394*5f39d1b3SJooyung Han
2395*5f39d1b3SJooyung Han "smull v12.8h, v0.8b, v4.8b\n"
2396*5f39d1b3SJooyung Han "smull v13.8h, v1.8b, v4.8b\n"
2397*5f39d1b3SJooyung Han "smull v14.8h, v0.8b, v5.8b\n"
2398*5f39d1b3SJooyung Han "smull v15.8h, v1.8b, v5.8b\n"
2399*5f39d1b3SJooyung Han "smlal2 v12.8h, v0.16b, v4.16b\n"
2400*5f39d1b3SJooyung Han "smlal2 v13.8h, v1.16b, v4.16b\n"
2401*5f39d1b3SJooyung Han "smlal2 v14.8h, v0.16b, v5.16b\n"
2402*5f39d1b3SJooyung Han "smlal2 v15.8h, v1.16b, v5.16b\n"
2403*5f39d1b3SJooyung Han
2404*5f39d1b3SJooyung Han "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
2405*5f39d1b3SJooyung Han
2406*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
2407*5f39d1b3SJooyung Han ":\n"
2408*5f39d1b3SJooyung Han
2409*5f39d1b3SJooyung Han "subs %[run_depth], %[run_depth], #16\n"
2410*5f39d1b3SJooyung Han
2411*5f39d1b3SJooyung Han "sadalp v16.4s, v12.8h\n"
2412*5f39d1b3SJooyung Han "smull v12.8h, v0.8b, v6.8b\n"
2413*5f39d1b3SJooyung Han "sadalp v17.4s, v13.8h\n"
2414*5f39d1b3SJooyung Han "smull v13.8h, v0.8b, v7.8b\n"
2415*5f39d1b3SJooyung Han "sadalp v20.4s, v14.8h\n"
2416*5f39d1b3SJooyung Han "smull v14.8h, v1.8b, v6.8b\n"
2417*5f39d1b3SJooyung Han "sadalp v21.4s, v15.8h\n"
2418*5f39d1b3SJooyung Han "smull v15.8h, v1.8b, v7.8b\n"
2419*5f39d1b3SJooyung Han "smlal2 v12.8h, v0.16b, v6.16b\n"
2420*5f39d1b3SJooyung Han "smlal2 v13.8h, v0.16b, v7.16b\n"
2421*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2422*5f39d1b3SJooyung Han "smlal2 v14.8h, v1.16b, v6.16b\n"
2423*5f39d1b3SJooyung Han "smlal2 v15.8h, v1.16b, v7.16b\n"
2424*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
2425*5f39d1b3SJooyung Han "sadalp v24.4s, v12.8h\n"
2426*5f39d1b3SJooyung Han "smull v12.8h, v2.8b, v4.8b\n"
2427*5f39d1b3SJooyung Han "sadalp v28.4s, v13.8h\n"
2428*5f39d1b3SJooyung Han "smull v13.8h, v3.8b, v4.8b\n"
2429*5f39d1b3SJooyung Han "sadalp v25.4s, v14.8h\n"
2430*5f39d1b3SJooyung Han "smull v14.8h, v2.8b, v5.8b\n"
2431*5f39d1b3SJooyung Han "sadalp v29.4s, v15.8h\n"
2432*5f39d1b3SJooyung Han "smull v15.8h, v3.8b, v5.8b\n"
2433*5f39d1b3SJooyung Han "smlal2 v12.8h, v2.16b, v4.16b\n"
2434*5f39d1b3SJooyung Han "smlal2 v13.8h, v3.16b, v4.16b\n"
2435*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2436*5f39d1b3SJooyung Han "smlal2 v14.8h, v2.16b, v5.16b\n"
2437*5f39d1b3SJooyung Han "smlal2 v15.8h, v3.16b, v5.16b\n"
2438*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2439*5f39d1b3SJooyung Han "sadalp v18.4s, v12.8h\n"
2440*5f39d1b3SJooyung Han "smull v12.8h, v2.8b, v6.8b\n"
2441*5f39d1b3SJooyung Han "sadalp v19.4s, v13.8h\n"
2442*5f39d1b3SJooyung Han "smull v13.8h, v2.8b, v7.8b\n"
2443*5f39d1b3SJooyung Han "sadalp v22.4s, v14.8h\n"
2444*5f39d1b3SJooyung Han "smull v14.8h, v3.8b, v6.8b\n"
2445*5f39d1b3SJooyung Han "sadalp v23.4s, v15.8h\n"
2446*5f39d1b3SJooyung Han "smull v15.8h, v3.8b, v7.8b\n"
2447*5f39d1b3SJooyung Han "smlal2 v12.8h, v2.16b, v6.16b\n"
2448*5f39d1b3SJooyung Han "smlal2 v13.8h, v2.16b, v7.16b\n"
2449*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
2450*5f39d1b3SJooyung Han "smlal2 v14.8h, v3.16b, v6.16b\n"
2451*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2452*5f39d1b3SJooyung Han "smlal2 v15.8h, v3.16b, v7.16b\n"
2453*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2454*5f39d1b3SJooyung Han "sadalp v26.4s, v12.8h\n"
2455*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
2456*5f39d1b3SJooyung Han "smull v12.8h, v0.8b, v4.8b\n"
2457*5f39d1b3SJooyung Han "sadalp v30.4s, v13.8h\n"
2458*5f39d1b3SJooyung Han "smull v13.8h, v1.8b, v4.8b\n"
2459*5f39d1b3SJooyung Han "sadalp v27.4s, v14.8h\n"
2460*5f39d1b3SJooyung Han "smull v14.8h, v0.8b, v5.8b\n"
2461*5f39d1b3SJooyung Han "sadalp v31.4s, v15.8h\n"
2462*5f39d1b3SJooyung Han "smull v15.8h, v1.8b, v5.8b\n"
2463*5f39d1b3SJooyung Han "smlal2 v12.8h, v0.16b, v4.16b\n"
2464*5f39d1b3SJooyung Han "smlal2 v13.8h, v1.16b, v4.16b\n"
2465*5f39d1b3SJooyung Han "smlal2 v14.8h, v0.16b, v5.16b\n"
2466*5f39d1b3SJooyung Han "smlal2 v15.8h, v1.16b, v5.16b\n"
2467*5f39d1b3SJooyung Han
2468*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP "b\n"
2469*5f39d1b3SJooyung Han
2470*5f39d1b3SJooyung Han GEMMLOWP_LABEL_AFTER_LOOP
2471*5f39d1b3SJooyung Han ":\n"
2472*5f39d1b3SJooyung Han
2473*5f39d1b3SJooyung Han // Load accumulators from memory
2474*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
2475*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
2476*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
2477*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
2478*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
2479*5f39d1b3SJooyung Han
2480*5f39d1b3SJooyung Han // Do the remaining arithmetic for the 16 last levels of depths.
2481*5f39d1b3SJooyung Han // All the operands are already loaded.
2482*5f39d1b3SJooyung Han "sadalp v16.4s, v12.8h\n"
2483*5f39d1b3SJooyung Han "smull v12.8h, v0.8b, v6.8b\n"
2484*5f39d1b3SJooyung Han "sadalp v17.4s, v13.8h\n"
2485*5f39d1b3SJooyung Han "smull v13.8h, v0.8b, v7.8b\n"
2486*5f39d1b3SJooyung Han "sadalp v20.4s, v14.8h\n"
2487*5f39d1b3SJooyung Han "smull v14.8h, v1.8b, v6.8b\n"
2488*5f39d1b3SJooyung Han "sadalp v21.4s, v15.8h\n"
2489*5f39d1b3SJooyung Han "smull v15.8h, v1.8b, v7.8b\n"
2490*5f39d1b3SJooyung Han "smlal2 v12.8h, v0.16b, v6.16b\n"
2491*5f39d1b3SJooyung Han "smlal2 v13.8h, v0.16b, v7.16b\n"
2492*5f39d1b3SJooyung Han "smlal2 v14.8h, v1.16b, v6.16b\n"
2493*5f39d1b3SJooyung Han "smlal2 v15.8h, v1.16b, v7.16b\n"
2494*5f39d1b3SJooyung Han "sadalp v24.4s, v12.8h\n"
2495*5f39d1b3SJooyung Han "smull v12.8h, v2.8b, v4.8b\n"
2496*5f39d1b3SJooyung Han "sadalp v28.4s, v13.8h\n"
2497*5f39d1b3SJooyung Han "smull v13.8h, v3.8b, v4.8b\n"
2498*5f39d1b3SJooyung Han "sadalp v25.4s, v14.8h\n"
2499*5f39d1b3SJooyung Han "smull v14.8h, v2.8b, v5.8b\n"
2500*5f39d1b3SJooyung Han "sadalp v29.4s, v15.8h\n"
2501*5f39d1b3SJooyung Han "smull v15.8h, v3.8b, v5.8b\n"
2502*5f39d1b3SJooyung Han "smlal2 v12.8h, v2.16b, v4.16b\n"
2503*5f39d1b3SJooyung Han "smlal2 v13.8h, v3.16b, v4.16b\n"
2504*5f39d1b3SJooyung Han "smlal2 v14.8h, v2.16b, v5.16b\n"
2505*5f39d1b3SJooyung Han "smlal2 v15.8h, v3.16b, v5.16b\n"
2506*5f39d1b3SJooyung Han "sadalp v18.4s, v12.8h\n"
2507*5f39d1b3SJooyung Han "smull v12.8h, v2.8b, v6.8b\n"
2508*5f39d1b3SJooyung Han "sadalp v19.4s, v13.8h\n"
2509*5f39d1b3SJooyung Han "smull v13.8h, v2.8b, v7.8b\n"
2510*5f39d1b3SJooyung Han "sadalp v22.4s, v14.8h\n"
2511*5f39d1b3SJooyung Han "smull v14.8h, v3.8b, v6.8b\n"
2512*5f39d1b3SJooyung Han "sadalp v23.4s, v15.8h\n"
2513*5f39d1b3SJooyung Han "smull v15.8h, v3.8b, v7.8b\n"
2514*5f39d1b3SJooyung Han "smlal2 v12.8h, v2.16b, v6.16b\n"
2515*5f39d1b3SJooyung Han "smlal2 v13.8h, v2.16b, v7.16b\n"
2516*5f39d1b3SJooyung Han "smlal2 v14.8h, v3.16b, v6.16b\n"
2517*5f39d1b3SJooyung Han "smlal2 v15.8h, v3.16b, v7.16b\n"
2518*5f39d1b3SJooyung Han "sadalp v26.4s, v12.8h\n"
2519*5f39d1b3SJooyung Han "sadalp v30.4s, v13.8h\n"
2520*5f39d1b3SJooyung Han "sadalp v27.4s, v14.8h\n"
2521*5f39d1b3SJooyung Han "sadalp v31.4s, v15.8h\n"
2522*5f39d1b3SJooyung Han
2523*5f39d1b3SJooyung Han // Reduce aggregators horizontally
2524*5f39d1b3SJooyung Han "addp v0.4s, v16.4s, v20.4s\n"
2525*5f39d1b3SJooyung Han "addp v1.4s, v17.4s, v21.4s\n"
2526*5f39d1b3SJooyung Han "addp v2.4s, v18.4s, v22.4s\n"
2527*5f39d1b3SJooyung Han "addp v3.4s, v19.4s, v23.4s\n"
2528*5f39d1b3SJooyung Han "addp v4.4s, v24.4s, v28.4s\n"
2529*5f39d1b3SJooyung Han "addp v5.4s, v25.4s, v29.4s\n"
2530*5f39d1b3SJooyung Han "addp v6.4s, v26.4s, v30.4s\n"
2531*5f39d1b3SJooyung Han "addp v7.4s, v27.4s, v31.4s\n"
2532*5f39d1b3SJooyung Han
2533*5f39d1b3SJooyung Han "addp v12.4s, v0.4s, v4.4s\n"
2534*5f39d1b3SJooyung Han "addp v13.4s, v1.4s, v5.4s\n"
2535*5f39d1b3SJooyung Han "addp v14.4s, v2.4s, v6.4s\n"
2536*5f39d1b3SJooyung Han "addp v15.4s, v3.4s, v7.4s\n"
2537*5f39d1b3SJooyung Han
2538*5f39d1b3SJooyung Han // Add to the accumulators loaded from memory
2539*5f39d1b3SJooyung Han "add v8.4s, v8.4s, v12.4s\n"
2540*5f39d1b3SJooyung Han "add v9.4s, v9.4s, v13.4s\n"
2541*5f39d1b3SJooyung Han "add v10.4s, v10.4s, v14.4s\n"
2542*5f39d1b3SJooyung Han "add v11.4s, v11.4s, v15.4s\n"
2543*5f39d1b3SJooyung Han
2544*5f39d1b3SJooyung Han // Store accumulators back to memory
2545*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
2546*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
2547*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
2548*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
2549*5f39d1b3SJooyung Han : // outputs
2550*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
2551*5f39d1b3SJooyung Han [dst_ptr] "+r"(dst_ptr), [run_depth] "+r"(run_depth),
2552*5f39d1b3SJooyung Han [dst_col_stride] "+r"(dst_col_stride)
2553*5f39d1b3SJooyung Han : // inputs
2554*5f39d1b3SJooyung Han [start_depth] "r"(start_depth)
2555*5f39d1b3SJooyung Han : // clobbers
2556*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
2557*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
2558*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
2559*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
2560*5f39d1b3SJooyung Han }
2561*5f39d1b3SJooyung Han };
2562*5f39d1b3SJooyung Han
2563*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_narrow {
2564*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
2565*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
2566*5f39d1b3SJooyung Han typedef KernelFormat<
2567*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
2568*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1> >
2569*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_narrow2570*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
2571*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
2572*5f39d1b3SJooyung Han std::size_t start_depth = 123;
2573*5f39d1b3SJooyung Han std::size_t run_depth = depth;
2574*5f39d1b3SJooyung Han std::size_t dst_col_stride = 4;
2575*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
2576*5f39d1b3SJooyung Han asm volatile(
2577*5f39d1b3SJooyung Han // Overview of register layout:
2578*5f39d1b3SJooyung Han //
2579*5f39d1b3SJooyung Han // A 4x16 block of Rhs is stored in 8 bit in v0--v3.
2580*5f39d1b3SJooyung Han // A 4x16 block of Lhs is stored in 8 bit in v4--v7.
2581*5f39d1b3SJooyung Han //
2582*5f39d1b3SJooyung Han // A 4x4 block of accumulators is stored in v16-v31 (as 4x32 bit
2583*5f39d1b3SJooyung Han // components which need to be horizontally-added at the end)
2584*5f39d1b3SJooyung Han //
2585*5f39d1b3SJooyung Han // Register layout:
2586*5f39d1b3SJooyung Han //
2587*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2588*5f39d1b3SJooyung Han // |v0.b[0] |v1.b[0] |v2.b[0] |v3.b[0] |
2589*5f39d1b3SJooyung Han // Rhs +--------+--------+--------+--------+
2590*5f39d1b3SJooyung Han // | ... | ... | ... | ... |
2591*5f39d1b3SJooyung Han // +--------+--------+--------+--------|
2592*5f39d1b3SJooyung Han // |v0.b[15]|v1.b[15]|v2.b[15]|v3.b[15]|
2593*5f39d1b3SJooyung Han // +--------+--------+--------+--------+
2594*5f39d1b3SJooyung Han //
2595*5f39d1b3SJooyung Han // | | | | |
2596*5f39d1b3SJooyung Han //
2597*5f39d1b3SJooyung Han // Lhs | | | | |
2598*5f39d1b3SJooyung Han //
2599*5f39d1b3SJooyung Han // +-------+-----+--------+ - - +--------+--------+--------+--------+
2600*5f39d1b3SJooyung Han // |v4.b[0]| ... |v4.b[15]| | v16.4s | v17.4s | v18.4s | v19.4s |
2601*5f39d1b3SJooyung Han // |v5.b[0]| ... |v5.b[15]| | v20.4s | v21.4s | v22.4s | v23.4s |
2602*5f39d1b3SJooyung Han // |v6.b[0]| ... |v6.b[15]| | v24.4s | v25.4s | v26.4s | v27.4s |
2603*5f39d1b3SJooyung Han // |v7.b[0]| ... |v7.b[15]| | v28.4s | v29.4s | v30.4s | v31.4s |
2604*5f39d1b3SJooyung Han // +-------+--------------+ - - +--------+--------+--------+--------+
2605*5f39d1b3SJooyung Han //
2606*5f39d1b3SJooyung Han // Accumulator
2607*5f39d1b3SJooyung Han //
2608*5f39d1b3SJooyung Han
2609*5f39d1b3SJooyung Han // Clear accumulators
2610*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2611*5f39d1b3SJooyung Han "dup v16.4s, wzr\n"
2612*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
2613*5f39d1b3SJooyung Han "dup v17.4s, wzr\n"
2614*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2615*5f39d1b3SJooyung Han "dup v18.4s, wzr\n"
2616*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2617*5f39d1b3SJooyung Han "dup v19.4s, wzr\n"
2618*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2619*5f39d1b3SJooyung Han "dup v20.4s, wzr\n"
2620*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2621*5f39d1b3SJooyung Han "dup v21.4s, wzr\n"
2622*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
2623*5f39d1b3SJooyung Han "dup v22.4s, wzr\n"
2624*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
2625*5f39d1b3SJooyung Han "dup v23.4s, wzr\n"
2626*5f39d1b3SJooyung Han "subs %w[run_depth], %w[run_depth], #16\n"
2627*5f39d1b3SJooyung Han "dup v24.4s, wzr\n"
2628*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
2629*5f39d1b3SJooyung Han "dup v25.4s, wzr\n"
2630*5f39d1b3SJooyung Han "dup v26.4s, wzr\n"
2631*5f39d1b3SJooyung Han "dup v27.4s, wzr\n"
2632*5f39d1b3SJooyung Han "dup v28.4s, wzr\n"
2633*5f39d1b3SJooyung Han "dup v29.4s, wzr\n"
2634*5f39d1b3SJooyung Han "dup v30.4s, wzr\n"
2635*5f39d1b3SJooyung Han "dup v31.4s, wzr\n"
2636*5f39d1b3SJooyung Han
2637*5f39d1b3SJooyung Han "beq 1f\n"
2638*5f39d1b3SJooyung Han
2639*5f39d1b3SJooyung Han "cmp %w[run_depth], #32\n"
2640*5f39d1b3SJooyung Han "blt 2f\n"
2641*5f39d1b3SJooyung Han
2642*5f39d1b3SJooyung Han "3:\n"
2643*5f39d1b3SJooyung Han "ld1 {v12.16b}, [%[lhs_ptr]], #16\n"
2644*5f39d1b3SJooyung Han ".word 0x6e809490 // udot v16.4s, v4.16b, v0.16b\n"
2645*5f39d1b3SJooyung Han ".word 0x6e819491 // udot v17.4s, v4.16b, v1.16b\n"
2646*5f39d1b3SJooyung Han "ld1 {v13.16b}, [%[lhs_ptr]], #16\n"
2647*5f39d1b3SJooyung Han ".word 0x6e829492 // udot v18.4s, v4.16b, v2.16b\n"
2648*5f39d1b3SJooyung Han ".word 0x6e839493 // udot v19.4s, v4.16b, v3.16b\n"
2649*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
2650*5f39d1b3SJooyung Han ".word 0x6e8094b4 // udot v20.4s, v5.16b, v0.16b\n"
2651*5f39d1b3SJooyung Han ".word 0x6e8194b5 // udot v21.4s, v5.16b, v1.16b\n"
2652*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
2653*5f39d1b3SJooyung Han ".word 0x6e8294b6 // udot v22.4s, v5.16b, v2.16b\n"
2654*5f39d1b3SJooyung Han ".word 0x6e8394b7 // udot v23.4s, v5.16b, v3.16b\n"
2655*5f39d1b3SJooyung Han "ld1 {v10.16b}, [%[rhs_ptr]], #16\n"
2656*5f39d1b3SJooyung Han ".word 0x6e8094d8 // udot v24.4s, v6.16b, v0.16b\n"
2657*5f39d1b3SJooyung Han ".word 0x6e8194d9 // udot v25.4s, v6.16b, v1.16b\n"
2658*5f39d1b3SJooyung Han "ld1 {v11.16b}, [%[rhs_ptr]], #16\n"
2659*5f39d1b3SJooyung Han ".word 0x6e8294da // udot v26.4s, v6.16b, v2.16b\n"
2660*5f39d1b3SJooyung Han "prfm pldl1keep, [%[rhs_ptr], #128]\n"
2661*5f39d1b3SJooyung Han ".word 0x6e8394db // udot v27.4s, v6.16b, v3.16b\n"
2662*5f39d1b3SJooyung Han "ld1 {v14.16b}, [%[lhs_ptr]], #16\n"
2663*5f39d1b3SJooyung Han ".word 0x6e8094fc // udot v28.4s, v7.16b, v0.16b\n"
2664*5f39d1b3SJooyung Han ".word 0x6e8194fd // udot v29.4s, v7.16b, v1.16b\n"
2665*5f39d1b3SJooyung Han "ld1 {v15.16b}, [%[lhs_ptr]], #16\n"
2666*5f39d1b3SJooyung Han ".word 0x6e8294fe // udot v30.4s, v7.16b, v2.16b\n"
2667*5f39d1b3SJooyung Han "prfm pldl1keep, [%[lhs_ptr], #128]\n"
2668*5f39d1b3SJooyung Han ".word 0x6e8394ff // udot v31.4s, v7.16b, v3.16b\n"
2669*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2670*5f39d1b3SJooyung Han ".word 0x6e889590 // udot v16.4s, v12.16b, v8.16b\n"
2671*5f39d1b3SJooyung Han ".word 0x6e899591 // udot v17.4s, v12.16b, v9.16b\n"
2672*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2673*5f39d1b3SJooyung Han ".word 0x6e8a9592 // udot v18.4s, v12.16b, v10.16b\n"
2674*5f39d1b3SJooyung Han ".word 0x6e8b9593 // udot v19.4s, v12.16b, v11.16b\n"
2675*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2676*5f39d1b3SJooyung Han ".word 0x6e8895b4 // udot v20.4s, v13.16b, v8.16b\n"
2677*5f39d1b3SJooyung Han ".word 0x6e8995b5 // udot v21.4s, v13.16b, v9.16b\n"
2678*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2679*5f39d1b3SJooyung Han "sub %[run_depth], %[run_depth], #32\n"
2680*5f39d1b3SJooyung Han ".word 0x6e8a95b6 // udot v22.4s, v13.16b, v10.16b\n"
2681*5f39d1b3SJooyung Han ".word 0x6e8b95b7 // udot v23.4s, v13.16b, v11.16b\n"
2682*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
2683*5f39d1b3SJooyung Han ".word 0x6e8895d8 // udot v24.4s, v14.16b, v8.16b\n"
2684*5f39d1b3SJooyung Han ".word 0x6e8995d9 // udot v25.4s, v14.16b, v9.16b\n"
2685*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
2686*5f39d1b3SJooyung Han ".word 0x6e8a95da // udot v26.4s, v14.16b, v10.16b\n"
2687*5f39d1b3SJooyung Han ".word 0x6e8b95db // udot v27.4s, v14.16b, v11.16b\n"
2688*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
2689*5f39d1b3SJooyung Han ".word 0x6e8895fc // udot v28.4s, v15.16b, v8.16b\n"
2690*5f39d1b3SJooyung Han "prfm pldl1keep, [%[rhs_ptr], #128]\n"
2691*5f39d1b3SJooyung Han ".word 0x6e8995fd // udot v29.4s, v15.16b, v9.16b\n"
2692*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2693*5f39d1b3SJooyung Han "cmp %w[run_depth], #32\n"
2694*5f39d1b3SJooyung Han ".word 0x6e8a95fe // udot v30.4s, v15.16b, v10.16b\n"
2695*5f39d1b3SJooyung Han "prfm pldl1keep, [%[lhs_ptr], #128]\n"
2696*5f39d1b3SJooyung Han ".word 0x6e8b95ff // udot v31.4s, v15.16b, v11.16b\n"
2697*5f39d1b3SJooyung Han
2698*5f39d1b3SJooyung Han "bge 3b\n"
2699*5f39d1b3SJooyung Han
2700*5f39d1b3SJooyung Han "cmp %w[run_depth], #0\n"
2701*5f39d1b3SJooyung Han "beq 1f\n"
2702*5f39d1b3SJooyung Han
2703*5f39d1b3SJooyung Han "2:\n"
2704*5f39d1b3SJooyung Han
2705*5f39d1b3SJooyung Han "subs %w[run_depth], %w[run_depth], #16\n"
2706*5f39d1b3SJooyung Han
2707*5f39d1b3SJooyung Han ".word 0x6e809490 // udot v16.4s, v4.16b, v0.16b\n"
2708*5f39d1b3SJooyung Han ".word 0x6e819491 // udot v17.4s, v4.16b, v1.16b\n"
2709*5f39d1b3SJooyung Han ".word 0x6e829492 // udot v18.4s, v4.16b, v2.16b\n"
2710*5f39d1b3SJooyung Han ".word 0x6e839493 // udot v19.4s, v4.16b, v3.16b\n"
2711*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2712*5f39d1b3SJooyung Han ".word 0x6e8094b4 // udot v20.4s, v5.16b, v0.16b\n"
2713*5f39d1b3SJooyung Han ".word 0x6e8194b5 // udot v21.4s, v5.16b, v1.16b\n"
2714*5f39d1b3SJooyung Han ".word 0x6e8294b6 // udot v22.4s, v5.16b, v2.16b\n"
2715*5f39d1b3SJooyung Han ".word 0x6e8394b7 // udot v23.4s, v5.16b, v3.16b\n"
2716*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2717*5f39d1b3SJooyung Han ".word 0x6e8094d8 // udot v24.4s, v6.16b, v0.16b\n"
2718*5f39d1b3SJooyung Han ".word 0x6e8194d9 // udot v25.4s, v6.16b, v1.16b\n"
2719*5f39d1b3SJooyung Han ".word 0x6e8294da // udot v26.4s, v6.16b, v2.16b\n"
2720*5f39d1b3SJooyung Han ".word 0x6e8394db // udot v27.4s, v6.16b, v3.16b\n"
2721*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2722*5f39d1b3SJooyung Han ".word 0x6e8094fc // udot v28.4s, v7.16b, v0.16b\n"
2723*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
2724*5f39d1b3SJooyung Han ".word 0x6e8194fd // udot v29.4s, v7.16b, v1.16b\n"
2725*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
2726*5f39d1b3SJooyung Han ".word 0x6e8294fe // udot v30.4s, v7.16b, v2.16b\n"
2727*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
2728*5f39d1b3SJooyung Han ".word 0x6e8394ff // udot v31.4s, v7.16b, v3.16b\n"
2729*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
2730*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2731*5f39d1b3SJooyung Han
2732*5f39d1b3SJooyung Han "bne 2b\n"
2733*5f39d1b3SJooyung Han
2734*5f39d1b3SJooyung Han "1:\n"
2735*5f39d1b3SJooyung Han
2736*5f39d1b3SJooyung Han ".word 0x6e809490 // udot v16.4s, v4.16b, v0.16b\n"
2737*5f39d1b3SJooyung Han ".word 0x6e819491 // udot v17.4s, v4.16b, v1.16b\n"
2738*5f39d1b3SJooyung Han ".word 0x6e829492 // udot v18.4s, v4.16b, v2.16b\n"
2739*5f39d1b3SJooyung Han ".word 0x6e839493 // udot v19.4s, v4.16b, v3.16b\n"
2740*5f39d1b3SJooyung Han ".word 0x6e8094b4 // udot v20.4s, v5.16b, v0.16b\n"
2741*5f39d1b3SJooyung Han ".word 0x6e8194b5 // udot v21.4s, v5.16b, v1.16b\n"
2742*5f39d1b3SJooyung Han ".word 0x6e8294b6 // udot v22.4s, v5.16b, v2.16b\n"
2743*5f39d1b3SJooyung Han ".word 0x6e8394b7 // udot v23.4s, v5.16b, v3.16b\n"
2744*5f39d1b3SJooyung Han ".word 0x6e8094d8 // udot v24.4s, v6.16b, v0.16b\n"
2745*5f39d1b3SJooyung Han ".word 0x6e8194d9 // udot v25.4s, v6.16b, v1.16b\n"
2746*5f39d1b3SJooyung Han ".word 0x6e8294da // udot v26.4s, v6.16b, v2.16b\n"
2747*5f39d1b3SJooyung Han ".word 0x6e8394db // udot v27.4s, v6.16b, v3.16b\n"
2748*5f39d1b3SJooyung Han ".word 0x6e8094fc // udot v28.4s, v7.16b, v0.16b\n"
2749*5f39d1b3SJooyung Han ".word 0x6e8194fd // udot v29.4s, v7.16b, v1.16b\n"
2750*5f39d1b3SJooyung Han ".word 0x6e8294fe // udot v30.4s, v7.16b, v2.16b\n"
2751*5f39d1b3SJooyung Han ".word 0x6e8394ff // udot v31.4s, v7.16b, v3.16b\n"
2752*5f39d1b3SJooyung Han
2753*5f39d1b3SJooyung Han // Load accumulators from memory
2754*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
2755*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
2756*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
2757*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
2758*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
2759*5f39d1b3SJooyung Han
2760*5f39d1b3SJooyung Han // Reduce aggregators horizontally
2761*5f39d1b3SJooyung Han "addp v0.4s, v16.4s, v20.4s\n"
2762*5f39d1b3SJooyung Han "addp v1.4s, v17.4s, v21.4s\n"
2763*5f39d1b3SJooyung Han "addp v2.4s, v18.4s, v22.4s\n"
2764*5f39d1b3SJooyung Han "addp v3.4s, v19.4s, v23.4s\n"
2765*5f39d1b3SJooyung Han "addp v4.4s, v24.4s, v28.4s\n"
2766*5f39d1b3SJooyung Han "addp v5.4s, v25.4s, v29.4s\n"
2767*5f39d1b3SJooyung Han "addp v6.4s, v26.4s, v30.4s\n"
2768*5f39d1b3SJooyung Han "addp v7.4s, v27.4s, v31.4s\n"
2769*5f39d1b3SJooyung Han
2770*5f39d1b3SJooyung Han "addp v12.4s, v0.4s, v4.4s\n"
2771*5f39d1b3SJooyung Han "addp v13.4s, v1.4s, v5.4s\n"
2772*5f39d1b3SJooyung Han "addp v14.4s, v2.4s, v6.4s\n"
2773*5f39d1b3SJooyung Han "addp v15.4s, v3.4s, v7.4s\n"
2774*5f39d1b3SJooyung Han
2775*5f39d1b3SJooyung Han // Add to the accumulators loaded from memory
2776*5f39d1b3SJooyung Han "add v8.4s, v8.4s, v12.4s\n"
2777*5f39d1b3SJooyung Han "add v9.4s, v9.4s, v13.4s\n"
2778*5f39d1b3SJooyung Han "add v10.4s, v10.4s, v14.4s\n"
2779*5f39d1b3SJooyung Han "add v11.4s, v11.4s, v15.4s\n"
2780*5f39d1b3SJooyung Han
2781*5f39d1b3SJooyung Han // Store accumulators back to memory
2782*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
2783*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
2784*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
2785*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
2786*5f39d1b3SJooyung Han : // outputs
2787*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
2788*5f39d1b3SJooyung Han [dst_ptr] "+r"(dst_ptr), [run_depth] "+r"(run_depth),
2789*5f39d1b3SJooyung Han [dst_col_stride] "+r"(dst_col_stride)
2790*5f39d1b3SJooyung Han : // inputs
2791*5f39d1b3SJooyung Han [start_depth] "r"(start_depth)
2792*5f39d1b3SJooyung Han : // clobbers
2793*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
2794*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
2795*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
2796*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
2797*5f39d1b3SJooyung Han }
2798*5f39d1b3SJooyung Han };
2799*5f39d1b3SJooyung Han
2800*5f39d1b3SJooyung Han // Fast kernel operating on int8 operands with 7-bit range.
2801*5f39d1b3SJooyung Han // It is assumed that one of the two operands only takes values in [-63, 63],
2802*5f39d1b3SJooyung Han // while the other take values in [-64, 63].
2803*5f39d1b3SJooyung Han // With this restriction, it is possible to multiply-accumulate operands into
2804*5f39d1b3SJooyung Han // a 16-bit integer eight times without overflow.
2805*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits {
2806*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
2807*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
2808*5f39d1b3SJooyung Han typedef KernelFormat<
2809*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
2810*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<2, 16, CellOrder::WidthMajor>, 1> >
2811*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits2812*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
2813*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
2814*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_64_DEPTH_LOOP "1"
2815*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_64_DEPTH_AFTER_LOOP "2"
2816*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_16_DEPTH_LOOP "3"
2817*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_16_DEPTH_AFTER_LOOP "4"
2818*5f39d1b3SJooyung Han
2819*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
2820*5f39d1b3SJooyung Han asm volatile(
2821*5f39d1b3SJooyung Han // Overview of register layout:
2822*5f39d1b3SJooyung Han //
2823*5f39d1b3SJooyung Han // A 4x16 block of Lhs is stored in 8 bit in v0--v7.
2824*5f39d1b3SJooyung Han // A 2x16 block of Rhs is stored in 8 bit in v8--v15.
2825*5f39d1b3SJooyung Han //
2826*5f39d1b3SJooyung Han // A 4x2 block of global accumulators is stored in v24-v31 (as 4x32 bit
2827*5f39d1b3SJooyung Han // components which need to be horizontally-added at the end).
2828*5f39d1b3SJooyung Han //
2829*5f39d1b3SJooyung Han // A 4x2 block of local accumulators is stored in v16-v23 (as 8x16 bit
2830*5f39d1b3SJooyung Han // components which are added to global accumulators every 64 depth
2831*5f39d1b3SJooyung Han // iteration.
2832*5f39d1b3SJooyung Han //
2833*5f39d1b3SJooyung Han // The Lhs vectors are multiplied by the Rhs vectors with a widening
2834*5f39d1b3SJooyung Han // multiply over the 8 first levels of depth, producing int16x8
2835*5f39d1b3SJooyung Han // vectors of products for each position in the accumulator matrix.
2836*5f39d1b3SJooyung Han //
2837*5f39d1b3SJooyung Han // Like the trick used in the fast 8-bit kernel, the operands are
2838*5f39d1b3SJooyung Han // restricted to 7-bit range [-2^6, 2^6) so their products are in range
2839*5f39d1b3SJooyung Han // [-2^12, 2^12 -1). This enables adding eight such products without any
2840*5f39d1b3SJooyung Han // risk of overflowing int16, equating to 64 levels of depth before
2841*5f39d1b3SJooyung Han // horizontally adding these int16x8 accumulators into the final int32x4
2842*5f39d1b3SJooyung Han // accumulators.
2843*5f39d1b3SJooyung Han //
2844*5f39d1b3SJooyung Han // Register layout including both local and global accumulators.
2845*5f39d1b3SJooyung Han // Since we do not have enough registers to store all Lhs values, we
2846*5f39d1b3SJooyung Han // reuse the same registers v0--v7 to load the rest of the Lhs values.
2847*5f39d1b3SJooyung Han //
2848*5f39d1b3SJooyung Han // +-----+-----+
2849*5f39d1b3SJooyung Han // | v8 | v9 |
2850*5f39d1b3SJooyung Han // Rhs +-----+-----+
2851*5f39d1b3SJooyung Han // | v10 | v11 |
2852*5f39d1b3SJooyung Han // +-----+-----+
2853*5f39d1b3SJooyung Han // | v12 | v13 |
2854*5f39d1b3SJooyung Han // +-----+-----+
2855*5f39d1b3SJooyung Han // | v14 | v15 |
2856*5f39d1b3SJooyung Han // Lhs +-----+-----+
2857*5f39d1b3SJooyung Han // +----+----+----+----+ - - +-----+-----+ +--------+--------+
2858*5f39d1b3SJooyung Han // | v0 | v4 | v0 | v4 | | v16 | v20 | | v24.4s | v28.4s |
2859*5f39d1b3SJooyung Han // | v1 | v5 | v1 | v5 | | v17 | v21 | -> | v25.4s | v29.4s |
2860*5f39d1b3SJooyung Han // | v2 | v6 | v2 | v6 | | v18 | v22 | | v26.4s | v30.4s |
2861*5f39d1b3SJooyung Han // | v3 | v7 | v3 | v7 | | v19 | v23 | | v27.4s | v31.4s |
2862*5f39d1b3SJooyung Han // +----+----+----+----+ - - +-----+-----+ +--------+--------+
2863*5f39d1b3SJooyung Han //
2864*5f39d1b3SJooyung Han // Local Accumulator Global Accumulator
2865*5f39d1b3SJooyung Han //
2866*5f39d1b3SJooyung Han
2867*5f39d1b3SJooyung Han // Clear accumulators.
2868*5f39d1b3SJooyung Han "dup v16.4s, wzr\n"
2869*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
2870*5f39d1b3SJooyung Han "dup v24.4s, wzr\n"
2871*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
2872*5f39d1b3SJooyung Han "dup v17.4s, wzr\n"
2873*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
2874*5f39d1b3SJooyung Han "dup v25.4s, wzr\n"
2875*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
2876*5f39d1b3SJooyung Han "dup v18.4s, wzr\n"
2877*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
2878*5f39d1b3SJooyung Han "dup v26.4s, wzr\n"
2879*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
2880*5f39d1b3SJooyung Han "dup v19.4s, wzr\n"
2881*5f39d1b3SJooyung Han "dup v27.4s, wzr\n"
2882*5f39d1b3SJooyung Han "dup v20.4s, wzr\n"
2883*5f39d1b3SJooyung Han "dup v28.4s, wzr\n"
2884*5f39d1b3SJooyung Han "dup v21.4s, wzr\n"
2885*5f39d1b3SJooyung Han "dup v29.4s, wzr\n"
2886*5f39d1b3SJooyung Han "dup v22.4s, wzr\n"
2887*5f39d1b3SJooyung Han "dup v30.4s, wzr\n"
2888*5f39d1b3SJooyung Han "dup v23.4s, wzr\n"
2889*5f39d1b3SJooyung Han "dup v31.4s, wzr\n"
2890*5f39d1b3SJooyung Han
2891*5f39d1b3SJooyung Han "cmp %w[depth], #64\n"
2892*5f39d1b3SJooyung Han "blt " GEMMLOWP_LABEL_64_DEPTH_AFTER_LOOP "f\n"
2893*5f39d1b3SJooyung Han
2894*5f39d1b3SJooyung Han //"loop_%=:\n"
2895*5f39d1b3SJooyung Han GEMMLOWP_LABEL_64_DEPTH_LOOP
2896*5f39d1b3SJooyung Han ":\n"
2897*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #64\n"
2898*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2899*5f39d1b3SJooyung Han "sadalp v24.4s, v16.8h\n"
2900*5f39d1b3SJooyung Han "smull v16.8h, v0.8b, v8.8b\n"
2901*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2902*5f39d1b3SJooyung Han "sadalp v25.4s, v17.8h\n"
2903*5f39d1b3SJooyung Han "smull v17.8h, v1.8b, v8.8b\n"
2904*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2905*5f39d1b3SJooyung Han "sadalp v26.4s, v18.8h\n"
2906*5f39d1b3SJooyung Han "smull v18.8h, v2.8b, v8.8b\n"
2907*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2908*5f39d1b3SJooyung Han "sadalp v27.4s, v19.8h\n"
2909*5f39d1b3SJooyung Han "smull v19.8h, v3.8b, v8.8b\n"
2910*5f39d1b3SJooyung Han "ld1 {v10.16b}, [%[rhs_ptr]], #16\n"
2911*5f39d1b3SJooyung Han "sadalp v28.4s, v20.8h\n"
2912*5f39d1b3SJooyung Han "smull v20.8h, v0.8b, v9.8b\n"
2913*5f39d1b3SJooyung Han "ld1 {v11.16b}, [%[rhs_ptr]], #16\n"
2914*5f39d1b3SJooyung Han "sadalp v29.4s, v21.8h\n"
2915*5f39d1b3SJooyung Han "smull v21.8h, v1.8b, v9.8b\n"
2916*5f39d1b3SJooyung Han "ld1 {v12.16b}, [%[rhs_ptr]], #16\n"
2917*5f39d1b3SJooyung Han "sadalp v30.4s, v22.8h\n"
2918*5f39d1b3SJooyung Han "smull v22.8h, v2.8b, v9.8b\n"
2919*5f39d1b3SJooyung Han "ld1 {v13.16b}, [%[rhs_ptr]], #16\n"
2920*5f39d1b3SJooyung Han "sadalp v31.4s, v23.8h\n"
2921*5f39d1b3SJooyung Han "smull v23.8h, v3.8b, v9.8b\n"
2922*5f39d1b3SJooyung Han
2923*5f39d1b3SJooyung Han "cmp %w[depth], #64\n"
2924*5f39d1b3SJooyung Han "smlal2 v16.8h, v0.16b, v8.16b\n"
2925*5f39d1b3SJooyung Han "ld1 {v14.16b}, [%[rhs_ptr]], #16\n"
2926*5f39d1b3SJooyung Han "smlal2 v17.8h, v1.16b, v8.16b\n"
2927*5f39d1b3SJooyung Han "ld1 {v15.16b}, [%[rhs_ptr]], #16\n"
2928*5f39d1b3SJooyung Han "smlal2 v18.8h, v2.16b, v8.16b\n"
2929*5f39d1b3SJooyung Han "smlal2 v19.8h, v3.16b, v8.16b\n"
2930*5f39d1b3SJooyung Han
2931*5f39d1b3SJooyung Han "smlal2 v20.8h, v0.16b, v9.16b\n"
2932*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
2933*5f39d1b3SJooyung Han "smlal2 v21.8h, v1.16b, v9.16b\n"
2934*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
2935*5f39d1b3SJooyung Han "smlal2 v22.8h, v2.16b, v9.16b\n"
2936*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
2937*5f39d1b3SJooyung Han "smlal2 v23.8h, v3.16b, v9.16b\n"
2938*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
2939*5f39d1b3SJooyung Han
2940*5f39d1b3SJooyung Han "smlal v16.8h, v4.8b, v10.8b\n"
2941*5f39d1b3SJooyung Han "smlal v17.8h, v5.8b, v10.8b\n"
2942*5f39d1b3SJooyung Han "smlal v18.8h, v6.8b, v10.8b\n"
2943*5f39d1b3SJooyung Han "smlal v19.8h, v7.8b, v10.8b\n"
2944*5f39d1b3SJooyung Han "smlal v20.8h, v4.8b, v11.8b\n"
2945*5f39d1b3SJooyung Han
2946*5f39d1b3SJooyung Han "smlal v21.8h, v5.8b, v11.8b\n"
2947*5f39d1b3SJooyung Han "smlal v22.8h, v6.8b, v11.8b\n"
2948*5f39d1b3SJooyung Han "smlal v23.8h, v7.8b, v11.8b\n"
2949*5f39d1b3SJooyung Han
2950*5f39d1b3SJooyung Han "smlal2 v16.8h, v4.16b, v10.16b\n"
2951*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
2952*5f39d1b3SJooyung Han "smlal2 v17.8h, v5.16b, v10.16b\n"
2953*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
2954*5f39d1b3SJooyung Han "smlal2 v18.8h, v6.16b, v10.16b\n"
2955*5f39d1b3SJooyung Han "smlal2 v19.8h, v7.16b, v10.16b\n"
2956*5f39d1b3SJooyung Han
2957*5f39d1b3SJooyung Han "smlal2 v20.8h, v4.16b, v11.16b\n"
2958*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
2959*5f39d1b3SJooyung Han "smlal2 v21.8h, v5.16b, v11.16b\n"
2960*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
2961*5f39d1b3SJooyung Han "smlal2 v22.8h, v6.16b, v11.16b\n"
2962*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
2963*5f39d1b3SJooyung Han "smlal2 v23.8h, v7.16b, v11.16b\n"
2964*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
2965*5f39d1b3SJooyung Han
2966*5f39d1b3SJooyung Han "smlal v16.8h, v0.8b, v12.8b\n"
2967*5f39d1b3SJooyung Han "smlal v17.8h, v1.8b, v12.8b\n"
2968*5f39d1b3SJooyung Han "smlal v18.8h, v2.8b, v12.8b\n"
2969*5f39d1b3SJooyung Han "smlal v19.8h, v3.8b, v12.8b\n"
2970*5f39d1b3SJooyung Han "smlal v20.8h, v0.8b, v13.8b\n"
2971*5f39d1b3SJooyung Han "smlal v21.8h, v1.8b, v13.8b\n"
2972*5f39d1b3SJooyung Han "smlal v22.8h, v2.8b, v13.8b\n"
2973*5f39d1b3SJooyung Han "smlal v23.8h, v3.8b, v13.8b\n"
2974*5f39d1b3SJooyung Han
2975*5f39d1b3SJooyung Han "smlal2 v16.8h, v0.16b, v12.16b\n"
2976*5f39d1b3SJooyung Han "smlal2 v17.8h, v1.16b, v12.16b\n"
2977*5f39d1b3SJooyung Han "smlal2 v18.8h, v2.16b, v12.16b\n"
2978*5f39d1b3SJooyung Han "smlal2 v19.8h, v3.16b, v12.16b\n"
2979*5f39d1b3SJooyung Han
2980*5f39d1b3SJooyung Han "smlal2 v20.8h, v0.16b, v13.16b\n"
2981*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
2982*5f39d1b3SJooyung Han "smlal2 v21.8h, v1.16b, v13.16b\n"
2983*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
2984*5f39d1b3SJooyung Han "smlal2 v22.8h, v2.16b, v13.16b\n"
2985*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
2986*5f39d1b3SJooyung Han "smlal2 v23.8h, v3.16b, v13.16b\n"
2987*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
2988*5f39d1b3SJooyung Han
2989*5f39d1b3SJooyung Han "smlal v16.8h, v4.8b, v14.8b\n"
2990*5f39d1b3SJooyung Han "smlal v17.8h, v5.8b, v14.8b\n"
2991*5f39d1b3SJooyung Han "smlal v18.8h, v6.8b, v14.8b\n"
2992*5f39d1b3SJooyung Han "smlal v19.8h, v7.8b, v14.8b\n"
2993*5f39d1b3SJooyung Han
2994*5f39d1b3SJooyung Han "smlal v20.8h, v4.8b, v15.8b\n"
2995*5f39d1b3SJooyung Han "smlal v21.8h, v5.8b, v15.8b\n"
2996*5f39d1b3SJooyung Han "smlal v22.8h, v6.8b, v15.8b\n"
2997*5f39d1b3SJooyung Han "smlal v23.8h, v7.8b, v15.8b\n"
2998*5f39d1b3SJooyung Han
2999*5f39d1b3SJooyung Han "smlal2 v16.8h, v4.16b, v14.16b\n"
3000*5f39d1b3SJooyung Han "smlal2 v17.8h, v5.16b, v14.16b\n"
3001*5f39d1b3SJooyung Han "smlal2 v18.8h, v6.16b, v14.16b\n"
3002*5f39d1b3SJooyung Han "smlal2 v19.8h, v7.16b, v14.16b\n"
3003*5f39d1b3SJooyung Han
3004*5f39d1b3SJooyung Han "smlal2 v20.8h, v4.16b, v15.16b\n"
3005*5f39d1b3SJooyung Han "smlal2 v21.8h, v5.16b, v15.16b\n"
3006*5f39d1b3SJooyung Han "smlal2 v22.8h, v6.16b, v15.16b\n"
3007*5f39d1b3SJooyung Han "smlal2 v23.8h, v7.16b, v15.16b\n"
3008*5f39d1b3SJooyung Han
3009*5f39d1b3SJooyung Han "bge " GEMMLOWP_LABEL_64_DEPTH_LOOP "b\n"
3010*5f39d1b3SJooyung Han
3011*5f39d1b3SJooyung Han GEMMLOWP_LABEL_64_DEPTH_AFTER_LOOP
3012*5f39d1b3SJooyung Han ":\n"
3013*5f39d1b3SJooyung Han
3014*5f39d1b3SJooyung Han "cmp %w[depth], #16\n"
3015*5f39d1b3SJooyung Han "blt " GEMMLOWP_LABEL_16_DEPTH_AFTER_LOOP "f\n"
3016*5f39d1b3SJooyung Han
3017*5f39d1b3SJooyung Han //"loop_%=:\n"
3018*5f39d1b3SJooyung Han GEMMLOWP_LABEL_16_DEPTH_LOOP
3019*5f39d1b3SJooyung Han ":\n"
3020*5f39d1b3SJooyung Han "sadalp v24.4s, v16.8h\n"
3021*5f39d1b3SJooyung Han "smull v16.8h, v0.8b, v8.8b\n"
3022*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #16\n"
3023*5f39d1b3SJooyung Han "sadalp v25.4s, v17.8h\n"
3024*5f39d1b3SJooyung Han "smull v17.8h, v1.8b, v8.8b\n"
3025*5f39d1b3SJooyung Han "sadalp v26.4s, v18.8h\n"
3026*5f39d1b3SJooyung Han "smull v18.8h, v2.8b, v8.8b\n"
3027*5f39d1b3SJooyung Han "sadalp v27.4s, v19.8h\n"
3028*5f39d1b3SJooyung Han "smull v19.8h, v3.8b, v8.8b\n"
3029*5f39d1b3SJooyung Han "sadalp v28.4s, v20.8h\n"
3030*5f39d1b3SJooyung Han "smull v20.8h, v0.8b, v9.8b\n"
3031*5f39d1b3SJooyung Han "sadalp v29.4s, v21.8h\n"
3032*5f39d1b3SJooyung Han "smull v21.8h, v1.8b, v9.8b\n"
3033*5f39d1b3SJooyung Han "sadalp v30.4s, v22.8h\n"
3034*5f39d1b3SJooyung Han "smull v22.8h, v2.8b, v9.8b\n"
3035*5f39d1b3SJooyung Han "sadalp v31.4s, v23.8h\n"
3036*5f39d1b3SJooyung Han "smull v23.8h, v3.8b, v9.8b\n"
3037*5f39d1b3SJooyung Han
3038*5f39d1b3SJooyung Han "cmp %w[depth], #16\n"
3039*5f39d1b3SJooyung Han "smlal2 v16.8h, v0.16b, v8.16b\n"
3040*5f39d1b3SJooyung Han "smlal2 v17.8h, v1.16b, v8.16b\n"
3041*5f39d1b3SJooyung Han "smlal2 v18.8h, v2.16b, v8.16b\n"
3042*5f39d1b3SJooyung Han "smlal2 v19.8h, v3.16b, v8.16b\n"
3043*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
3044*5f39d1b3SJooyung Han
3045*5f39d1b3SJooyung Han "smlal2 v20.8h, v0.16b, v9.16b\n"
3046*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
3047*5f39d1b3SJooyung Han "smlal2 v21.8h, v1.16b, v9.16b\n"
3048*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
3049*5f39d1b3SJooyung Han "smlal2 v22.8h, v2.16b, v9.16b\n"
3050*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
3051*5f39d1b3SJooyung Han "smlal2 v23.8h, v3.16b, v9.16b\n"
3052*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
3053*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
3054*5f39d1b3SJooyung Han
3055*5f39d1b3SJooyung Han "bge " GEMMLOWP_LABEL_16_DEPTH_LOOP "b\n"
3056*5f39d1b3SJooyung Han
3057*5f39d1b3SJooyung Han GEMMLOWP_LABEL_16_DEPTH_AFTER_LOOP
3058*5f39d1b3SJooyung Han ":\n"
3059*5f39d1b3SJooyung Han
3060*5f39d1b3SJooyung Han "sadalp v24.4s, v16.8h\n"
3061*5f39d1b3SJooyung Han "sadalp v25.4s, v17.8h\n"
3062*5f39d1b3SJooyung Han "sadalp v26.4s, v18.8h\n"
3063*5f39d1b3SJooyung Han "sadalp v27.4s, v19.8h\n"
3064*5f39d1b3SJooyung Han "sadalp v28.4s, v20.8h\n"
3065*5f39d1b3SJooyung Han "sadalp v29.4s, v21.8h\n"
3066*5f39d1b3SJooyung Han "sadalp v30.4s, v22.8h\n"
3067*5f39d1b3SJooyung Han "sadalp v31.4s, v23.8h\n"
3068*5f39d1b3SJooyung Han
3069*5f39d1b3SJooyung Han // Reduce aggregators horizontally.
3070*5f39d1b3SJooyung Han "addp v0.4s, v24.4s, v25.4s\n"
3071*5f39d1b3SJooyung Han "addp v1.4s, v26.4s, v27.4s\n"
3072*5f39d1b3SJooyung Han "addp v2.4s, v28.4s, v29.4s\n"
3073*5f39d1b3SJooyung Han "addp v3.4s, v30.4s, v31.4s\n"
3074*5f39d1b3SJooyung Han
3075*5f39d1b3SJooyung Han "addp v4.4s, v0.4s, v1.4s\n"
3076*5f39d1b3SJooyung Han "addp v5.4s, v2.4s, v3.4s\n"
3077*5f39d1b3SJooyung Han
3078*5f39d1b3SJooyung Han // Load accumulators from memory.
3079*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
3080*5f39d1b3SJooyung Han "ld1 {v6.16b}, [x0], #16\n"
3081*5f39d1b3SJooyung Han "ld1 {v7.16b}, [x0], #16\n"
3082*5f39d1b3SJooyung Han
3083*5f39d1b3SJooyung Han // Add to the accumulators loaded from memory.
3084*5f39d1b3SJooyung Han "add v6.4s, v6.4s, v4.4s\n"
3085*5f39d1b3SJooyung Han "add v7.4s, v7.4s, v5.4s\n"
3086*5f39d1b3SJooyung Han
3087*5f39d1b3SJooyung Han // Store accumulators back to memory.
3088*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
3089*5f39d1b3SJooyung Han "st1 {v6.16b}, [x0], #16\n"
3090*5f39d1b3SJooyung Han "st1 {v7.16b}, [x0], #16\n"
3091*5f39d1b3SJooyung Han
3092*5f39d1b3SJooyung Han :
3093*5f39d1b3SJooyung Han // Outputs.
3094*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3095*5f39d1b3SJooyung Han [dst_ptr] "+r"(dst_ptr), [depth] "+r"(depth)
3096*5f39d1b3SJooyung Han :
3097*5f39d1b3SJooyung Han // Inputs.
3098*5f39d1b3SJooyung Han
3099*5f39d1b3SJooyung Han :
3100*5f39d1b3SJooyung Han // Clobbers.
3101*5f39d1b3SJooyung Han "cc", "memory",
3102*5f39d1b3SJooyung Han // We use these NEON registers
3103*5f39d1b3SJooyung Han "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10",
3104*5f39d1b3SJooyung Han "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20",
3105*5f39d1b3SJooyung Han "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30",
3106*5f39d1b3SJooyung Han "v31", "x0");
3107*5f39d1b3SJooyung Han }
3108*5f39d1b3SJooyung Han };
3109*5f39d1b3SJooyung Han
3110*5f39d1b3SJooyung Han SET_7BIT_RANGES(NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits);
3111*5f39d1b3SJooyung Han
3112*5f39d1b3SJooyung Han // Kernel operating on int8 operands with 4.25-bit range.
3113*5f39d1b3SJooyung Han // It is assumed that one of the two operands only takes values in [-7, 7],
3114*5f39d1b3SJooyung Han // while the other take values in [-9, 9].
3115*5f39d1b3SJooyung Han // With this restriction, it is possible to multiply-accumulate operands into
3116*5f39d1b3SJooyung Han // a 16-bit integer thirty-two times without overflow.
3117*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int425Operands {
3118*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
3119*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
3120*5f39d1b3SJooyung Han typedef KernelFormat<
3121*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 32, CellOrder::WidthMajor>, 1>,
3122*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<2, 32, CellOrder::WidthMajor>, 1> >
3123*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int425Operands3124*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3125*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3126*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_512_DEPTH_LOOP "1"
3127*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_32_DEPTH_LOOP "2"
3128*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_32_DEPTH_AFTER_LOOP "3"
3129*5f39d1b3SJooyung Han
3130*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
3131*5f39d1b3SJooyung Han int outer_depth = depth / 512 + 1;
3132*5f39d1b3SJooyung Han
3133*5f39d1b3SJooyung Han asm volatile(
3134*5f39d1b3SJooyung Han // Overview of register layout:
3135*5f39d1b3SJooyung Han //
3136*5f39d1b3SJooyung Han // A 4x32 block of Lhs is stored in 8 bit in v0--v7.
3137*5f39d1b3SJooyung Han // A 2x32 block of Rhs is stored in 8 bit in v8--v11.
3138*5f39d1b3SJooyung Han //
3139*5f39d1b3SJooyung Han // A 4x2 block of global accumulators is stored in v24-v31 (as 4x32 bit
3140*5f39d1b3SJooyung Han // components which need to be horizontally-added at the end).
3141*5f39d1b3SJooyung Han //
3142*5f39d1b3SJooyung Han // A 4x2 block of local accumulators is stored in v16-v23 (as 8x16 bit
3143*5f39d1b3SJooyung Han // components which are horizontally-added to global accumulators every
3144*5f39d1b3SJooyung Han // 512 depth iteration.
3145*5f39d1b3SJooyung Han //
3146*5f39d1b3SJooyung Han // The Lhs vectors are multiplied by the Rhs vectors with a multiply
3147*5f39d1b3SJooyung Han // over the 16 first levels of depth, producing int8x16 vectors of
3148*5f39d1b3SJooyung Han // products for each position in the accumulator matrix.
3149*5f39d1b3SJooyung Han //
3150*5f39d1b3SJooyung Han // Like the trick used in the fast 8-bit and 7-bit kernels, the operands
3151*5f39d1b3SJooyung Han // are restricted to 4.25-bit range, [-7, 7] for one operand and [-9, 9]
3152*5f39d1b3SJooyung Han // for the other operand. This enables adding two such products without
3153*5f39d1b3SJooyung Han // any risk of overflowing int8, and thiry-two such products without
3154*5f39d1b3SJooyung Han // overflowing int16. This equates to 512 levels of depth before
3155*5f39d1b3SJooyung Han // horizontally adding these int16x8 accumulators into the final int32x4
3156*5f39d1b3SJooyung Han // accumulators.
3157*5f39d1b3SJooyung Han //
3158*5f39d1b3SJooyung Han // Register layout (ignoring the v12--v15 temporary 8-bit accumulators).
3159*5f39d1b3SJooyung Han // Since we do not have enough registers to store all Lhs values and Rhs
3160*5f39d1b3SJooyung Han // values, we reuse the same registers v0--v7 to load subsequent Lhs
3161*5f39d1b3SJooyung Han // values and v8-v11 to subsequent Rhs values.
3162*5f39d1b3SJooyung Han //
3163*5f39d1b3SJooyung Han // +-----+-----+
3164*5f39d1b3SJooyung Han // | v8 | v9 |
3165*5f39d1b3SJooyung Han // Rhs +-----+-----+
3166*5f39d1b3SJooyung Han // | v10 | v11 |
3167*5f39d1b3SJooyung Han // +-----+-----+
3168*5f39d1b3SJooyung Han // | v8 | v9 |
3169*5f39d1b3SJooyung Han // +-----+-----+
3170*5f39d1b3SJooyung Han // | v10 | v11 |
3171*5f39d1b3SJooyung Han // Lhs +-----+-----+
3172*5f39d1b3SJooyung Han // +----+----+----+----+ - - +-----+-----+ +--------+--------+
3173*5f39d1b3SJooyung Han // | v0 | v4 | v0 | v4 | | v16 | v17 | | v24.4s | v25.4s |
3174*5f39d1b3SJooyung Han // | v1 | v5 | v1 | v5 | | v18 | v19 | -> | v26.4s | v27.4s |
3175*5f39d1b3SJooyung Han // | v2 | v6 | v2 | v6 | | v20 | v21 | | v28.4s | v29.4s |
3176*5f39d1b3SJooyung Han // | v3 | v7 | v3 | v7 | | v22 | v23 | | v30.4s | v31.4s |
3177*5f39d1b3SJooyung Han // +----+----+----+----+ - - +-----+-----+ +--------+--------+
3178*5f39d1b3SJooyung Han //
3179*5f39d1b3SJooyung Han // Local Accumulator Global Accumulator
3180*5f39d1b3SJooyung Han //
3181*5f39d1b3SJooyung Han
3182*5f39d1b3SJooyung Han // Clear global accumulators.
3183*5f39d1b3SJooyung Han "dup v24.4s, wzr\n"
3184*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
3185*5f39d1b3SJooyung Han "dup v25.4s, wzr\n"
3186*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
3187*5f39d1b3SJooyung Han "dup v26.4s, wzr\n"
3188*5f39d1b3SJooyung Han "ld1 {v10.16b}, [%[rhs_ptr]], #16\n"
3189*5f39d1b3SJooyung Han "dup v27.4s, wzr\n"
3190*5f39d1b3SJooyung Han "ld1 {v11.16b}, [%[rhs_ptr]], #16\n"
3191*5f39d1b3SJooyung Han "dup v28.4s, wzr\n"
3192*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
3193*5f39d1b3SJooyung Han "dup v29.4s, wzr\n"
3194*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
3195*5f39d1b3SJooyung Han "dup v30.4s, wzr\n"
3196*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
3197*5f39d1b3SJooyung Han "dup v31.4s, wzr\n"
3198*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
3199*5f39d1b3SJooyung Han
3200*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
3201*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
3202*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
3203*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
3204*5f39d1b3SJooyung Han
3205*5f39d1b3SJooyung Han //"loop_%=:\n"
3206*5f39d1b3SJooyung Han GEMMLOWP_LABEL_512_DEPTH_LOOP
3207*5f39d1b3SJooyung Han ":\n"
3208*5f39d1b3SJooyung Han // Clear local accumulators.
3209*5f39d1b3SJooyung Han "dup v16.8h, wzr\n"
3210*5f39d1b3SJooyung Han "dup v17.8h, wzr\n"
3211*5f39d1b3SJooyung Han "dup v18.8h, wzr\n"
3212*5f39d1b3SJooyung Han "mov x1, #512\n"
3213*5f39d1b3SJooyung Han "dup v19.8h, wzr\n"
3214*5f39d1b3SJooyung Han "dup v20.8h, wzr\n"
3215*5f39d1b3SJooyung Han "dup v21.8h, wzr\n"
3216*5f39d1b3SJooyung Han "dup v22.8h, wzr\n"
3217*5f39d1b3SJooyung Han "dup v23.8h, wzr\n"
3218*5f39d1b3SJooyung Han
3219*5f39d1b3SJooyung Han //"loop_%=:\n"
3220*5f39d1b3SJooyung Han GEMMLOWP_LABEL_32_DEPTH_LOOP
3221*5f39d1b3SJooyung Han ":\n"
3222*5f39d1b3SJooyung Han "mul v12.16b, v0.16b, v8.16b\n"
3223*5f39d1b3SJooyung Han "mul v13.16b, v0.16b, v10.16b\n"
3224*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
3225*5f39d1b3SJooyung Han "mul v14.16b, v2.16b, v8.16b\n"
3226*5f39d1b3SJooyung Han "mul v15.16b, v2.16b, v10.16b\n"
3227*5f39d1b3SJooyung Han
3228*5f39d1b3SJooyung Han "mla v12.16b, v1.16b, v9.16b\n"
3229*5f39d1b3SJooyung Han "mla v13.16b, v1.16b, v11.16b\n"
3230*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
3231*5f39d1b3SJooyung Han "mla v14.16b, v3.16b, v9.16b\n"
3232*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
3233*5f39d1b3SJooyung Han "mla v15.16b, v3.16b, v11.16b\n"
3234*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
3235*5f39d1b3SJooyung Han
3236*5f39d1b3SJooyung Han "sadalp v16.8h, v12.16b\n"
3237*5f39d1b3SJooyung Han "sadalp v17.8h, v13.16b\n"
3238*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #32\n"
3239*5f39d1b3SJooyung Han "sadalp v18.8h, v14.16b\n"
3240*5f39d1b3SJooyung Han "sadalp v19.8h, v15.16b\n"
3241*5f39d1b3SJooyung Han "subs x1, x1, #32\n"
3242*5f39d1b3SJooyung Han
3243*5f39d1b3SJooyung Han "mul v12.16b, v4.16b, v8.16b\n"
3244*5f39d1b3SJooyung Han "mul v13.16b, v4.16b, v10.16b\n"
3245*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
3246*5f39d1b3SJooyung Han "mul v14.16b, v6.16b, v8.16b\n"
3247*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
3248*5f39d1b3SJooyung Han "mul v15.16b, v6.16b, v10.16b\n"
3249*5f39d1b3SJooyung Han
3250*5f39d1b3SJooyung Han "mla v12.16b, v5.16b, v9.16b\n"
3251*5f39d1b3SJooyung Han "mla v13.16b, v5.16b, v11.16b\n"
3252*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
3253*5f39d1b3SJooyung Han "mla v14.16b, v7.16b, v9.16b\n"
3254*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
3255*5f39d1b3SJooyung Han "mla v15.16b, v7.16b, v11.16b\n"
3256*5f39d1b3SJooyung Han "ld1 {v10.16b}, [%[rhs_ptr]], #16\n"
3257*5f39d1b3SJooyung Han
3258*5f39d1b3SJooyung Han "sadalp v20.8h, v12.16b\n"
3259*5f39d1b3SJooyung Han "ld1 {v11.16b}, [%[rhs_ptr]], #16\n"
3260*5f39d1b3SJooyung Han "sadalp v21.8h, v13.16b\n"
3261*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
3262*5f39d1b3SJooyung Han "sadalp v22.8h, v14.16b\n"
3263*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
3264*5f39d1b3SJooyung Han "sadalp v23.8h, v15.16b\n"
3265*5f39d1b3SJooyung Han
3266*5f39d1b3SJooyung Han "mul v12.16b, v0.16b, v8.16b\n"
3267*5f39d1b3SJooyung Han "mul v13.16b, v0.16b, v10.16b\n"
3268*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[lhs_ptr]], #16\n"
3269*5f39d1b3SJooyung Han "mul v14.16b, v2.16b, v8.16b\n"
3270*5f39d1b3SJooyung Han "mul v15.16b, v2.16b, v10.16b\n"
3271*5f39d1b3SJooyung Han
3272*5f39d1b3SJooyung Han "mla v12.16b, v1.16b, v9.16b\n"
3273*5f39d1b3SJooyung Han "mla v13.16b, v1.16b, v11.16b\n"
3274*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[lhs_ptr]], #16\n"
3275*5f39d1b3SJooyung Han "mla v14.16b, v3.16b, v9.16b\n"
3276*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
3277*5f39d1b3SJooyung Han "mla v15.16b, v3.16b, v11.16b\n"
3278*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n"
3279*5f39d1b3SJooyung Han
3280*5f39d1b3SJooyung Han "sadalp v16.8h, v12.16b\n"
3281*5f39d1b3SJooyung Han "sadalp v17.8h, v13.16b\n"
3282*5f39d1b3SJooyung Han "sadalp v18.8h, v14.16b\n"
3283*5f39d1b3SJooyung Han "sadalp v19.8h, v15.16b\n"
3284*5f39d1b3SJooyung Han
3285*5f39d1b3SJooyung Han "mul v12.16b, v4.16b, v8.16b\n"
3286*5f39d1b3SJooyung Han "mul v13.16b, v4.16b, v10.16b\n"
3287*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
3288*5f39d1b3SJooyung Han "mul v14.16b, v6.16b, v8.16b\n"
3289*5f39d1b3SJooyung Han "ld1 {v8.16b}, [%[rhs_ptr]], #16\n"
3290*5f39d1b3SJooyung Han "mul v15.16b, v6.16b, v10.16b\n"
3291*5f39d1b3SJooyung Han
3292*5f39d1b3SJooyung Han "mla v12.16b, v5.16b, v9.16b\n"
3293*5f39d1b3SJooyung Han "mla v13.16b, v5.16b, v11.16b\n"
3294*5f39d1b3SJooyung Han "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
3295*5f39d1b3SJooyung Han "mla v14.16b, v7.16b, v9.16b\n"
3296*5f39d1b3SJooyung Han "ld1 {v9.16b}, [%[rhs_ptr]], #16\n"
3297*5f39d1b3SJooyung Han "mla v15.16b, v7.16b, v11.16b\n"
3298*5f39d1b3SJooyung Han "ld1 {v10.16b}, [%[rhs_ptr]], #16\n"
3299*5f39d1b3SJooyung Han
3300*5f39d1b3SJooyung Han "sadalp v20.8h, v12.16b\n"
3301*5f39d1b3SJooyung Han "ld1 {v11.16b}, [%[rhs_ptr]], #16\n"
3302*5f39d1b3SJooyung Han "sadalp v21.8h, v13.16b\n"
3303*5f39d1b3SJooyung Han "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
3304*5f39d1b3SJooyung Han "sadalp v22.8h, v14.16b\n"
3305*5f39d1b3SJooyung Han "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
3306*5f39d1b3SJooyung Han "sadalp v23.8h, v15.16b\n"
3307*5f39d1b3SJooyung Han
3308*5f39d1b3SJooyung Han "beq " GEMMLOWP_LABEL_32_DEPTH_AFTER_LOOP
3309*5f39d1b3SJooyung Han "f\n"
3310*5f39d1b3SJooyung Han
3311*5f39d1b3SJooyung Han "cmp %w[depth], #0\n"
3312*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_32_DEPTH_LOOP "b\n"
3313*5f39d1b3SJooyung Han
3314*5f39d1b3SJooyung Han GEMMLOWP_LABEL_32_DEPTH_AFTER_LOOP
3315*5f39d1b3SJooyung Han ":\n"
3316*5f39d1b3SJooyung Han
3317*5f39d1b3SJooyung Han // Pairwise add 16-bit local accums to 32-bit global accums.
3318*5f39d1b3SJooyung Han "sadalp v24.4s, v16.8h\n"
3319*5f39d1b3SJooyung Han "sadalp v25.4s, v17.8h\n"
3320*5f39d1b3SJooyung Han "sadalp v26.4s, v18.8h\n"
3321*5f39d1b3SJooyung Han "sadalp v27.4s, v19.8h\n"
3322*5f39d1b3SJooyung Han "sadalp v28.4s, v20.8h\n"
3323*5f39d1b3SJooyung Han "sadalp v29.4s, v21.8h\n"
3324*5f39d1b3SJooyung Han "sadalp v30.4s, v22.8h\n"
3325*5f39d1b3SJooyung Han "sadalp v31.4s, v23.8h\n"
3326*5f39d1b3SJooyung Han
3327*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_512_DEPTH_LOOP
3328*5f39d1b3SJooyung Han "b\n"
3329*5f39d1b3SJooyung Han
3330*5f39d1b3SJooyung Han // Reduce aggregators horizontally.
3331*5f39d1b3SJooyung Han "addp v0.4s, v24.4s, v26.4s\n"
3332*5f39d1b3SJooyung Han "addp v1.4s, v28.4s, v30.4s\n"
3333*5f39d1b3SJooyung Han "addp v2.4s, v25.4s, v27.4s\n"
3334*5f39d1b3SJooyung Han "addp v3.4s, v29.4s, v31.4s\n"
3335*5f39d1b3SJooyung Han
3336*5f39d1b3SJooyung Han "addp v4.4s, v0.4s, v1.4s\n"
3337*5f39d1b3SJooyung Han "addp v5.4s, v2.4s, v3.4s\n"
3338*5f39d1b3SJooyung Han
3339*5f39d1b3SJooyung Han // Load accumulators from memory.
3340*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
3341*5f39d1b3SJooyung Han "ld1 {v6.16b}, [x0], #16\n"
3342*5f39d1b3SJooyung Han "ld1 {v7.16b}, [x0], #16\n"
3343*5f39d1b3SJooyung Han
3344*5f39d1b3SJooyung Han // Add to the accumulators loaded from memory.
3345*5f39d1b3SJooyung Han "add v6.4s, v6.4s, v4.4s\n"
3346*5f39d1b3SJooyung Han "add v7.4s, v7.4s, v5.4s\n"
3347*5f39d1b3SJooyung Han
3348*5f39d1b3SJooyung Han // Store accumulators back to memory.
3349*5f39d1b3SJooyung Han "mov x0, %[dst_ptr]\n"
3350*5f39d1b3SJooyung Han "st1 {v6.16b}, [x0], #16\n"
3351*5f39d1b3SJooyung Han "st1 {v7.16b}, [x0], #16\n"
3352*5f39d1b3SJooyung Han
3353*5f39d1b3SJooyung Han :
3354*5f39d1b3SJooyung Han // Outputs.
3355*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3356*5f39d1b3SJooyung Han [dst_ptr] "+r"(dst_ptr), [depth] "+r"(depth),
3357*5f39d1b3SJooyung Han [outer_depth] "+r"(outer_depth)
3358*5f39d1b3SJooyung Han :
3359*5f39d1b3SJooyung Han // Inputs.
3360*5f39d1b3SJooyung Han
3361*5f39d1b3SJooyung Han :
3362*5f39d1b3SJooyung Han // Clobbers.
3363*5f39d1b3SJooyung Han "cc", "memory",
3364*5f39d1b3SJooyung Han // We use these NEON registers
3365*5f39d1b3SJooyung Han "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10",
3366*5f39d1b3SJooyung Han "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20",
3367*5f39d1b3SJooyung Han "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30",
3368*5f39d1b3SJooyung Han "v31", "x0", "x1");
3369*5f39d1b3SJooyung Han }
3370*5f39d1b3SJooyung Han };
3371*5f39d1b3SJooyung Han
3372*5f39d1b3SJooyung Han SET_425BIT_RANGES(NEON_64bit_GEMM_Int425Operands);
3373*5f39d1b3SJooyung Han
3374*5f39d1b3SJooyung Han #ifdef __ARM_FEATURE_DOTPROD
3375*5f39d1b3SJooyung Han // Kernels utilizing the Armv8.2 Dot Product extension.
3376*5f39d1b3SJooyung Han //
3377*5f39d1b3SJooyung Han // The dot product instructions work by taking 4 consecutive 8-bit depth
3378*5f39d1b3SJooyung Han // values from each operand, multiplying the 4 pairs together and
3379*5f39d1b3SJooyung Han // accumulating all the results into the corresponding 32-bit accumulator
3380*5f39d1b3SJooyung Han // lane. As such, the operation is identical to a 32-bit instruction (like
3381*5f39d1b3SJooyung Han // FMLA used in SGEMM), except that 4 depth values are processed at a time
3382*5f39d1b3SJooyung Han // instead of 1.
3383*5f39d1b3SJooyung Han
3384*5f39d1b3SJooyung Han // Thus, this first kernel is a carbon copy of
3385*5f39d1b3SJooyung Han // "NEON_64bit_GEMM_Float32_WithScalar_A57" (which should provide good
3386*5f39d1b3SJooyung Han // performance for most processors) below with the opcode (fmla -> udot) and
3387*5f39d1b3SJooyung Han // types (float32 -> uint8/uint32) changed.
3388*5f39d1b3SJooyung Han //
3389*5f39d1b3SJooyung Han // A signed version of this kernel could be produced by replacing "udot"
3390*5f39d1b3SJooyung Han // with "sdot" - performance should be identical to this udot kernel.
3391*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct {
3392*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
3393*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
3394*5f39d1b3SJooyung Han typedef KernelFormat<
3395*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 4, CellOrder::WidthMajor>, 3>,
3396*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 4, CellOrder::WidthMajor>, 2> >
3397*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct3398*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3399*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3400*5f39d1b3SJooyung Han asm volatile(
3401*5f39d1b3SJooyung Han // Load accumulators
3402*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3403*5f39d1b3SJooyung Han "ld1 {v8.4s}, [x0], #16\n"
3404*5f39d1b3SJooyung Han "ld1 {v16.4s}, [x0], #16\n"
3405*5f39d1b3SJooyung Han "ld1 {v24.4s}, [x0], #16\n"
3406*5f39d1b3SJooyung Han "ld1 {v9.4s}, [x0], #16\n"
3407*5f39d1b3SJooyung Han "ld1 {v17.4s}, [x0], #16\n"
3408*5f39d1b3SJooyung Han "ld1 {v25.4s}, [x0], #16\n"
3409*5f39d1b3SJooyung Han "ld1 {v10.4s}, [x0], #16\n"
3410*5f39d1b3SJooyung Han "ld1 {v18.4s}, [x0], #16\n"
3411*5f39d1b3SJooyung Han "ld1 {v26.4s}, [x0], #16\n"
3412*5f39d1b3SJooyung Han "ld1 {v11.4s}, [x0], #16\n"
3413*5f39d1b3SJooyung Han "ld1 {v19.4s}, [x0], #16\n"
3414*5f39d1b3SJooyung Han "ld1 {v27.4s}, [x0], #16\n"
3415*5f39d1b3SJooyung Han "ld1 {v12.4s}, [x0], #16\n"
3416*5f39d1b3SJooyung Han "ld1 {v20.4s}, [x0], #16\n"
3417*5f39d1b3SJooyung Han "ld1 {v28.4s}, [x0], #16\n"
3418*5f39d1b3SJooyung Han "ld1 {v13.4s}, [x0], #16\n"
3419*5f39d1b3SJooyung Han "ld1 {v21.4s}, [x0], #16\n"
3420*5f39d1b3SJooyung Han "ld1 {v29.4s}, [x0], #16\n"
3421*5f39d1b3SJooyung Han "ld1 {v14.4s}, [x0], #16\n"
3422*5f39d1b3SJooyung Han "ld1 {v22.4s}, [x0], #16\n"
3423*5f39d1b3SJooyung Han "ld1 {v30.4s}, [x0], #16\n"
3424*5f39d1b3SJooyung Han "ld1 {v15.4s}, [x0], #16\n"
3425*5f39d1b3SJooyung Han "ld1 {v23.4s}, [x0], #16\n"
3426*5f39d1b3SJooyung Han "ld1 {v31.4s}, [x0], #16\n"
3427*5f39d1b3SJooyung Han
3428*5f39d1b3SJooyung Han // The start of the loop assumes first Rhs cell is already loaded, so
3429*5f39d1b3SJooyung Han // do it here for first iteration.
3430*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
3431*5f39d1b3SJooyung Han
3432*5f39d1b3SJooyung Han // And the same for the first Lhs cell.
3433*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n"
3434*5f39d1b3SJooyung Han
3435*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
3436*5f39d1b3SJooyung Han ":\n"
3437*5f39d1b3SJooyung Han
3438*5f39d1b3SJooyung Han // Start the MACs at the head of the loop - 1st cell from each side
3439*5f39d1b3SJooyung Han // already loaded.
3440*5f39d1b3SJooyung Han ".word 0x6f80e048 // udot v8.4s, v2.16b, v0.4b[0]\n"
3441*5f39d1b3SJooyung Han ".word 0x6fa0e049 // udot v9.4s, v2.16b, v0.4b[1]\n"
3442*5f39d1b3SJooyung Han "ld1 {v1.16b}, [%[rhs_ptr]], #16\n" // Load second Rhs cell.
3443*5f39d1b3SJooyung Han ".word 0x6f80e84a // udot v10.4s, v2.16b, v0.4b[2]\n"
3444*5f39d1b3SJooyung Han ".word 0x6fa0e84b // udot v11.4s, v2.16b, v0.4b[3]\n"
3445*5f39d1b3SJooyung Han "ld1 {v3.16b}, [%[lhs_ptr]], #16\n" // Load second Lhs cell.
3446*5f39d1b3SJooyung Han ".word 0x6f81e04c // udot v12.4s, v2.16b, v1.4b[0]\n"
3447*5f39d1b3SJooyung Han ".word 0x6fa1e04d // udot v13.4s, v2.16b, v1.4b[1]\n"
3448*5f39d1b3SJooyung Han "ld1 {v4.16b}, [%[lhs_ptr]], #16\n" // Load third Lhs cell.
3449*5f39d1b3SJooyung Han ".word 0x6f81e84e // udot v14.4s, v2.16b, v1.4b[2]\n"
3450*5f39d1b3SJooyung Han ".word 0x6fa1e84f // udot v15.4s, v2.16b, v1.4b[3]\n"
3451*5f39d1b3SJooyung Han "ld1 {v2.16b}, [%[lhs_ptr]], #16\n" // Done with first Lhs cell - load
3452*5f39d1b3SJooyung Han // for the next iteration early.
3453*5f39d1b3SJooyung Han ".word 0x6f80e070 // udot v16.4s, v3.16b, v0.4b[0]\n"
3454*5f39d1b3SJooyung Han ".word 0x6fa0e071 // udot v17.4s, v3.16b, v0.4b[1]\n"
3455*5f39d1b3SJooyung Han ".word 0x6f80e872 // udot v18.4s, v3.16b, v0.4b[2]\n"
3456*5f39d1b3SJooyung Han ".word 0x6fa0e873 // udot v19.4s, v3.16b, v0.4b[3]\n"
3457*5f39d1b3SJooyung Han ".word 0x6f81e074 // udot v20.4s, v3.16b, v1.4b[0]\n"
3458*5f39d1b3SJooyung Han ".word 0x6fa1e075 // udot v21.4s, v3.16b, v1.4b[1]\n"
3459*5f39d1b3SJooyung Han ".word 0x6f81e876 // udot v22.4s, v3.16b, v1.4b[2]\n"
3460*5f39d1b3SJooyung Han ".word 0x6fa1e877 // udot v23.4s, v3.16b, v1.4b[3]\n"
3461*5f39d1b3SJooyung Han ".word 0x6f80e098 // udot v24.4s, v4.16b, v0.4b[0]\n"
3462*5f39d1b3SJooyung Han ".word 0x6fa0e099 // udot v25.4s, v4.16b, v0.4b[1]\n"
3463*5f39d1b3SJooyung Han ".word 0x6f80e89a // udot v26.4s, v4.16b, v0.4b[2]\n"
3464*5f39d1b3SJooyung Han ".word 0x6fa0e89b // udot v27.4s, v4.16b, v0.4b[3]\n"
3465*5f39d1b3SJooyung Han "ld1 {v0.16b}, [%[rhs_ptr]], #16\n" // Done with the first Rhs cell -
3466*5f39d1b3SJooyung Han // load for the next iteration early.
3467*5f39d1b3SJooyung Han ".word 0x6f81e09c // udot v28.4s, v4.16b, v1.4b[0]\n"
3468*5f39d1b3SJooyung Han ".word 0x6fa1e09d // udot v29.4s, v4.16b, v1.4b[1]\n"
3469*5f39d1b3SJooyung Han
3470*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 4 as udot processes 4
3471*5f39d1b3SJooyung Han // depth values.
3472*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #4\n"
3473*5f39d1b3SJooyung Han ".word 0x6f81e89e // udot v30.4s, v4.16b, v1.4b[2]\n"
3474*5f39d1b3SJooyung Han ".word 0x6fa1e89f // udot v31.4s, v4.16b, v1.4b[3]\n"
3475*5f39d1b3SJooyung Han
3476*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
3477*5f39d1b3SJooyung Han "b\n"
3478*5f39d1b3SJooyung Han
3479*5f39d1b3SJooyung Han // Store accumulators
3480*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3481*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
3482*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
3483*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
3484*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
3485*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
3486*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
3487*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
3488*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
3489*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
3490*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
3491*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
3492*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
3493*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
3494*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
3495*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
3496*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
3497*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
3498*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
3499*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
3500*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
3501*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
3502*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
3503*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
3504*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
3505*5f39d1b3SJooyung Han : // outputs
3506*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3507*5f39d1b3SJooyung Han [depth] "+r"(depth)
3508*5f39d1b3SJooyung Han : // inputs
3509*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
3510*5f39d1b3SJooyung Han : // clobbers
3511*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
3512*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
3513*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
3514*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
3515*5f39d1b3SJooyung Han }
3516*5f39d1b3SJooyung Han };
3517*5f39d1b3SJooyung Han
3518*5f39d1b3SJooyung Han // As above, except tuned for Cortex-A55r1.
3519*5f39d1b3SJooyung Han //
3520*5f39d1b3SJooyung Han // Similarly, this is a clone of NEON_64bit_GEMM_Float32_WithScalar_A55r1
3521*5f39d1b3SJooyung Han // with the names changed.
3522*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_A55r1 {
3523*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
3524*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
3525*5f39d1b3SJooyung Han typedef KernelFormat<
3526*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 4, CellOrder::WidthMajor>, 3>,
3527*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 4, CellOrder::WidthMajor>, 2> >
3528*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_A55r13529*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3530*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3531*5f39d1b3SJooyung Han asm volatile(
3532*5f39d1b3SJooyung Han // Load accumulators
3533*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3534*5f39d1b3SJooyung Han "ld1 {v8.4s}, [x0], #16\n"
3535*5f39d1b3SJooyung Han "ld1 {v16.4s}, [x0], #16\n"
3536*5f39d1b3SJooyung Han "ld1 {v24.4s}, [x0], #16\n"
3537*5f39d1b3SJooyung Han "ld1 {v9.4s}, [x0], #16\n"
3538*5f39d1b3SJooyung Han "ld1 {v17.4s}, [x0], #16\n"
3539*5f39d1b3SJooyung Han "ld1 {v25.4s}, [x0], #16\n"
3540*5f39d1b3SJooyung Han "ld1 {v10.4s}, [x0], #16\n"
3541*5f39d1b3SJooyung Han "ld1 {v18.4s}, [x0], #16\n"
3542*5f39d1b3SJooyung Han "ld1 {v26.4s}, [x0], #16\n"
3543*5f39d1b3SJooyung Han "ld1 {v11.4s}, [x0], #16\n"
3544*5f39d1b3SJooyung Han "ld1 {v19.4s}, [x0], #16\n"
3545*5f39d1b3SJooyung Han "ld1 {v27.4s}, [x0], #16\n"
3546*5f39d1b3SJooyung Han "ld1 {v12.4s}, [x0], #16\n"
3547*5f39d1b3SJooyung Han "ld1 {v20.4s}, [x0], #16\n"
3548*5f39d1b3SJooyung Han "ld1 {v28.4s}, [x0], #16\n"
3549*5f39d1b3SJooyung Han "ld1 {v13.4s}, [x0], #16\n"
3550*5f39d1b3SJooyung Han "ld1 {v21.4s}, [x0], #16\n"
3551*5f39d1b3SJooyung Han "ld1 {v29.4s}, [x0], #16\n"
3552*5f39d1b3SJooyung Han "ld1 {v14.4s}, [x0], #16\n"
3553*5f39d1b3SJooyung Han "ld1 {v22.4s}, [x0], #16\n"
3554*5f39d1b3SJooyung Han "ld1 {v30.4s}, [x0], #16\n"
3555*5f39d1b3SJooyung Han "ld1 {v15.4s}, [x0], #16\n"
3556*5f39d1b3SJooyung Han "ld1 {v23.4s}, [x0], #16\n"
3557*5f39d1b3SJooyung Han "ld1 {v31.4s}, [x0], #16\n"
3558*5f39d1b3SJooyung Han
3559*5f39d1b3SJooyung Han // For details on how this kernel works, see the Float32 kernel below.
3560*5f39d1b3SJooyung Han
3561*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n"
3562*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n"
3563*5f39d1b3SJooyung Han
3564*5f39d1b3SJooyung Han "ldr q2, [%[lhs_ptr]]\n"
3565*5f39d1b3SJooyung Han "ldr q3, [%[lhs_ptr], #16]\n"
3566*5f39d1b3SJooyung Han
3567*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
3568*5f39d1b3SJooyung Han ":\n"
3569*5f39d1b3SJooyung Han
3570*5f39d1b3SJooyung Han ".word 0x6f80e048 // udot v8.4s, v2.16b, v0.4b[0]\n"
3571*5f39d1b3SJooyung Han "ldr d1, [%[rhs_ptr], #16]\n" // Bottom half of v1
3572*5f39d1b3SJooyung Han ".word 0x6fa0e049 // udot v9.4s, v2.16b, v0.4b[1]\n"
3573*5f39d1b3SJooyung Han "ins v0.d[1], x18\n" // Finish loading v0
3574*5f39d1b3SJooyung Han ".word 0x6f80e070 // udot v16.4s, v3.16b, v0.4b[0]\n" // out of
3575*5f39d1b3SJooyung Han // sequence -
3576*5f39d1b3SJooyung Han // used to
3577*5f39d1b3SJooyung Han // reduce
3578*5f39d1b3SJooyung Han // load/use
3579*5f39d1b3SJooyung Han // pressure.
3580*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #24]\n" // Top half of v1 to X register
3581*5f39d1b3SJooyung Han ".word 0x6fa0e071 // udot v17.4s, v3.16b, v0.4b[1]\n" // out of
3582*5f39d1b3SJooyung Han // sequence -
3583*5f39d1b3SJooyung Han // used to
3584*5f39d1b3SJooyung Han // reduce
3585*5f39d1b3SJooyung Han // load/use
3586*5f39d1b3SJooyung Han // pressure.
3587*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #32\n" // RHS loads complete - increment
3588*5f39d1b3SJooyung Han // pointer.
3589*5f39d1b3SJooyung Han ".word 0x6f80e84a // udot v10.4s, v2.16b, v0.4b[2]\n"
3590*5f39d1b3SJooyung Han "ldr d4, [%[lhs_ptr], #32]\n" // Bottom half of v4
3591*5f39d1b3SJooyung Han ".word 0x6fa0e84b // udot v11.4s, v2.16b, v0.4b[3]\n"
3592*5f39d1b3SJooyung Han "ins v1.d[1], x18\n" // Finish loading v1
3593*5f39d1b3SJooyung Han ".word 0x6f81e04c // udot v12.4s, v2.16b, v1.4b[0]\n"
3594*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #40]\n" // Top half of v4 to X register
3595*5f39d1b3SJooyung Han ".word 0x6fa1e04d // udot v13.4s, v2.16b, v1.4b[1]\n"
3596*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #48\n" // LHS loads complete - increment
3597*5f39d1b3SJooyung Han // pointer.
3598*5f39d1b3SJooyung Han ".word 0x6f81e84e // udot v14.4s, v2.16b, v1.4b[2]\n"
3599*5f39d1b3SJooyung Han
3600*5f39d1b3SJooyung Han ".word 0x6fa1e84f // udot v15.4s, v2.16b, v1.4b[3]\n"
3601*5f39d1b3SJooyung Han "ldr d2, [%[lhs_ptr]]\n" // Bottom half of v2 (for next time)
3602*5f39d1b3SJooyung Han ".word 0x6f80e872 // udot v18.4s, v3.16b, v0.4b[2]\n"
3603*5f39d1b3SJooyung Han "ins v4.d[1], x18\n" // Finish loading v4
3604*5f39d1b3SJooyung Han ".word 0x6fa0e873 // udot v19.4s, v3.16b, v0.4b[3]\n"
3605*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #8]\n" // Top half of next v2 to X register
3606*5f39d1b3SJooyung Han ".word 0x6f81e074 // udot v20.4s, v3.16b, v1.4b[0]\n"
3607*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #4\n"
3608*5f39d1b3SJooyung Han ".word 0x6fa1e075 // udot v21.4s, v3.16b, v1.4b[1]\n"
3609*5f39d1b3SJooyung Han
3610*5f39d1b3SJooyung Han ".word 0x6f81e876 // udot v22.4s, v3.16b, v1.4b[2]\n"
3611*5f39d1b3SJooyung Han
3612*5f39d1b3SJooyung Han ".word 0x6fa1e877 // udot v23.4s, v3.16b, v1.4b[3]\n"
3613*5f39d1b3SJooyung Han "ldr d3, [%[lhs_ptr], #16]\n" // Bottom half of v3 (for next time)
3614*5f39d1b3SJooyung Han ".word 0x6f80e098 // udot v24.4s, v4.16b, v0.4b[0]\n"
3615*5f39d1b3SJooyung Han "ins v2.d[1], x18\n" // Finish loading next v2
3616*5f39d1b3SJooyung Han ".word 0x6fa0e099 // udot v25.4s, v4.16b, v0.4b[1]\n"
3617*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #24]\n" // Top half of next v3 to X register
3618*5f39d1b3SJooyung Han ".word 0x6f80e89a // udot v26.4s, v4.16b, v0.4b[2]\n"
3619*5f39d1b3SJooyung Han
3620*5f39d1b3SJooyung Han ".word 0x6fa0e89b // udot v27.4s, v4.16b, v0.4b[3]\n"
3621*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n" // Bottom half of v0 (for next time)
3622*5f39d1b3SJooyung Han ".word 0x6f81e09c // udot v28.4s, v4.16b, v1.4b[0]\n"
3623*5f39d1b3SJooyung Han "ins v3.d[1], x18\n" // Finish loading next v3
3624*5f39d1b3SJooyung Han ".word 0x6fa1e09d // udot v29.4s, v4.16b, v1.4b[1]\n"
3625*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n" // Top half of next v0 to X register
3626*5f39d1b3SJooyung Han ".word 0x6f81e89e // udot v30.4s, v4.16b, v1.4b[2]\n"
3627*5f39d1b3SJooyung Han
3628*5f39d1b3SJooyung Han ".word 0x6fa1e89f // udot v31.4s, v4.16b, v1.4b[3]\n"
3629*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
3630*5f39d1b3SJooyung Han "b\n"
3631*5f39d1b3SJooyung Han
3632*5f39d1b3SJooyung Han // Store accumulators
3633*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3634*5f39d1b3SJooyung Han "st1 {v8.4s}, [x0], #16\n"
3635*5f39d1b3SJooyung Han "st1 {v16.4s}, [x0], #16\n"
3636*5f39d1b3SJooyung Han "st1 {v24.4s}, [x0], #16\n"
3637*5f39d1b3SJooyung Han "st1 {v9.4s}, [x0], #16\n"
3638*5f39d1b3SJooyung Han "st1 {v17.4s}, [x0], #16\n"
3639*5f39d1b3SJooyung Han "st1 {v25.4s}, [x0], #16\n"
3640*5f39d1b3SJooyung Han "st1 {v10.4s}, [x0], #16\n"
3641*5f39d1b3SJooyung Han "st1 {v18.4s}, [x0], #16\n"
3642*5f39d1b3SJooyung Han "st1 {v26.4s}, [x0], #16\n"
3643*5f39d1b3SJooyung Han "st1 {v11.4s}, [x0], #16\n"
3644*5f39d1b3SJooyung Han "st1 {v19.4s}, [x0], #16\n"
3645*5f39d1b3SJooyung Han "st1 {v27.4s}, [x0], #16\n"
3646*5f39d1b3SJooyung Han "st1 {v12.4s}, [x0], #16\n"
3647*5f39d1b3SJooyung Han "st1 {v20.4s}, [x0], #16\n"
3648*5f39d1b3SJooyung Han "st1 {v28.4s}, [x0], #16\n"
3649*5f39d1b3SJooyung Han "st1 {v13.4s}, [x0], #16\n"
3650*5f39d1b3SJooyung Han "st1 {v21.4s}, [x0], #16\n"
3651*5f39d1b3SJooyung Han "st1 {v29.4s}, [x0], #16\n"
3652*5f39d1b3SJooyung Han "st1 {v14.4s}, [x0], #16\n"
3653*5f39d1b3SJooyung Han "st1 {v22.4s}, [x0], #16\n"
3654*5f39d1b3SJooyung Han "st1 {v30.4s}, [x0], #16\n"
3655*5f39d1b3SJooyung Han "st1 {v15.4s}, [x0], #16\n"
3656*5f39d1b3SJooyung Han "st1 {v23.4s}, [x0], #16\n"
3657*5f39d1b3SJooyung Han "st1 {v31.4s}, [x0], #16\n"
3658*5f39d1b3SJooyung Han : // outputs
3659*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3660*5f39d1b3SJooyung Han [depth] "+r"(depth)
3661*5f39d1b3SJooyung Han : // inputs
3662*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
3663*5f39d1b3SJooyung Han : // clobbers
3664*5f39d1b3SJooyung Han "cc", "memory", "x0", "x18", "v0", "v1", "v2", "v3", "v4", "v5", "v6",
3665*5f39d1b3SJooyung Han "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16",
3666*5f39d1b3SJooyung Han "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26",
3667*5f39d1b3SJooyung Han "v27", "v28", "v29", "v30", "v31");
3668*5f39d1b3SJooyung Han }
3669*5f39d1b3SJooyung Han };
3670*5f39d1b3SJooyung Han #endif // __ARM_FEATURE_DOTPROD
3671*5f39d1b3SJooyung Han
3672*5f39d1b3SJooyung Han // We don't actually use int32*int32 in production. This is just an
3673*5f39d1b3SJooyung Han // experiment to help dissociate the effect of integer-vs-float, from the
3674*5f39d1b3SJooyung Han // effect of operands width.
3675*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int32_WithScalar {
3676*5f39d1b3SJooyung Han typedef std::int32_t OperandType;
3677*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
3678*5f39d1b3SJooyung Han typedef KernelFormat<
3679*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
3680*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
3681*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int32_WithScalar3682*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3683*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3684*5f39d1b3SJooyung Han asm volatile(
3685*5f39d1b3SJooyung Han // Load accumulators
3686*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3687*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
3688*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
3689*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
3690*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
3691*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
3692*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
3693*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
3694*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
3695*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
3696*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
3697*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
3698*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
3699*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
3700*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
3701*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
3702*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
3703*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
3704*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
3705*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
3706*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
3707*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
3708*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
3709*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
3710*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
3711*5f39d1b3SJooyung Han
3712*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
3713*5f39d1b3SJooyung Han ":\n"
3714*5f39d1b3SJooyung Han
3715*5f39d1b3SJooyung Han // Load 2 Rhs cell of size 1x4 each
3716*5f39d1b3SJooyung Han "ld1 {v0.4s}, [%[rhs_ptr]], #16\n"
3717*5f39d1b3SJooyung Han "ld1 {v1.4s}, [%[rhs_ptr]], #16\n"
3718*5f39d1b3SJooyung Han
3719*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
3720*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n"
3721*5f39d1b3SJooyung Han "ld1 {v3.4s}, [%[lhs_ptr]], #16\n"
3722*5f39d1b3SJooyung Han "ld1 {v4.4s}, [%[lhs_ptr]], #16\n"
3723*5f39d1b3SJooyung Han
3724*5f39d1b3SJooyung Han // Multiply-accumulate
3725*5f39d1b3SJooyung Han "mla v8.4s, v2.4s, v0.s[0]\n"
3726*5f39d1b3SJooyung Han "mla v9.4s, v2.4s, v0.s[1]\n"
3727*5f39d1b3SJooyung Han "mla v10.4s, v2.4s, v0.s[2]\n"
3728*5f39d1b3SJooyung Han "mla v11.4s, v2.4s, v0.s[3]\n"
3729*5f39d1b3SJooyung Han "mla v12.4s, v2.4s, v1.s[0]\n"
3730*5f39d1b3SJooyung Han "mla v13.4s, v2.4s, v1.s[1]\n"
3731*5f39d1b3SJooyung Han "mla v14.4s, v2.4s, v1.s[2]\n"
3732*5f39d1b3SJooyung Han "mla v15.4s, v2.4s, v1.s[3]\n"
3733*5f39d1b3SJooyung Han "mla v16.4s, v3.4s, v0.s[0]\n"
3734*5f39d1b3SJooyung Han "mla v17.4s, v3.4s, v0.s[1]\n"
3735*5f39d1b3SJooyung Han "mla v18.4s, v3.4s, v0.s[2]\n"
3736*5f39d1b3SJooyung Han "mla v19.4s, v3.4s, v0.s[3]\n"
3737*5f39d1b3SJooyung Han "mla v20.4s, v3.4s, v1.s[0]\n"
3738*5f39d1b3SJooyung Han "mla v21.4s, v3.4s, v1.s[1]\n"
3739*5f39d1b3SJooyung Han "mla v22.4s, v3.4s, v1.s[2]\n"
3740*5f39d1b3SJooyung Han "mla v23.4s, v3.4s, v1.s[3]\n"
3741*5f39d1b3SJooyung Han "mla v24.4s, v4.4s, v0.s[0]\n"
3742*5f39d1b3SJooyung Han "mla v25.4s, v4.4s, v0.s[1]\n"
3743*5f39d1b3SJooyung Han "mla v26.4s, v4.4s, v0.s[2]\n"
3744*5f39d1b3SJooyung Han "mla v27.4s, v4.4s, v0.s[3]\n"
3745*5f39d1b3SJooyung Han "mla v28.4s, v4.4s, v1.s[0]\n"
3746*5f39d1b3SJooyung Han "mla v29.4s, v4.4s, v1.s[1]\n"
3747*5f39d1b3SJooyung Han "mla v30.4s, v4.4s, v1.s[2]\n"
3748*5f39d1b3SJooyung Han "mla v31.4s, v4.4s, v1.s[3]\n"
3749*5f39d1b3SJooyung Han
3750*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
3751*5f39d1b3SJooyung Han // level of depth.
3752*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
3753*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
3754*5f39d1b3SJooyung Han "b\n"
3755*5f39d1b3SJooyung Han
3756*5f39d1b3SJooyung Han // Store accumulators
3757*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3758*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
3759*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
3760*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
3761*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
3762*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
3763*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
3764*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
3765*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
3766*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
3767*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
3768*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
3769*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
3770*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
3771*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
3772*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
3773*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
3774*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
3775*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
3776*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
3777*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
3778*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
3779*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
3780*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
3781*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
3782*5f39d1b3SJooyung Han : // outputs
3783*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3784*5f39d1b3SJooyung Han [depth] "+r"(depth)
3785*5f39d1b3SJooyung Han : // inputs
3786*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
3787*5f39d1b3SJooyung Han : // clobbers
3788*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
3789*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
3790*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
3791*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
3792*5f39d1b3SJooyung Han }
3793*5f39d1b3SJooyung Han };
3794*5f39d1b3SJooyung Han
3795*5f39d1b3SJooyung Han // Not very efficient kernel, just an experiment to see what we can do
3796*5f39d1b3SJooyung Han // without using NEON multiply-with-scalar instructions.
3797*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Float32_WithVectorDuplicatingScalar {
3798*5f39d1b3SJooyung Han typedef float OperandType;
3799*5f39d1b3SJooyung Han typedef float AccumulatorType;
3800*5f39d1b3SJooyung Han typedef KernelFormat<
3801*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
3802*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
3803*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Float32_WithVectorDuplicatingScalar3804*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3805*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3806*5f39d1b3SJooyung Han asm volatile(
3807*5f39d1b3SJooyung Han // Load accumulators
3808*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3809*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
3810*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
3811*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
3812*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
3813*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
3814*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
3815*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
3816*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
3817*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
3818*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
3819*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
3820*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
3821*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
3822*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
3823*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
3824*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
3825*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
3826*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
3827*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
3828*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
3829*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
3830*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
3831*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
3832*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
3833*5f39d1b3SJooyung Han
3834*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
3835*5f39d1b3SJooyung Han ":\n"
3836*5f39d1b3SJooyung Han
3837*5f39d1b3SJooyung Han // Load 2 Rhs cell of size 1x4 each
3838*5f39d1b3SJooyung Han "ld1 {v5.4s}, [%[rhs_ptr]], #16\n"
3839*5f39d1b3SJooyung Han "ld1 {v6.4s}, [%[rhs_ptr]], #16\n"
3840*5f39d1b3SJooyung Han
3841*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
3842*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n"
3843*5f39d1b3SJooyung Han "ld1 {v3.4s}, [%[lhs_ptr]], #16\n"
3844*5f39d1b3SJooyung Han "ld1 {v4.4s}, [%[lhs_ptr]], #16\n"
3845*5f39d1b3SJooyung Han
3846*5f39d1b3SJooyung Han // Multiply-accumulate
3847*5f39d1b3SJooyung Han "dup v0.4s, v5.s[0]\n"
3848*5f39d1b3SJooyung Han "dup v1.4s, v5.s[1]\n"
3849*5f39d1b3SJooyung Han "fmla v8.4s, v2.4s, v0.4s\n"
3850*5f39d1b3SJooyung Han "fmla v16.4s, v3.4s, v0.4s\n"
3851*5f39d1b3SJooyung Han "fmla v24.4s, v4.4s, v0.4s\n"
3852*5f39d1b3SJooyung Han "fmla v9.4s, v2.4s, v1.4s\n"
3853*5f39d1b3SJooyung Han "fmla v17.4s, v3.4s, v1.4s\n"
3854*5f39d1b3SJooyung Han "fmla v25.4s, v4.4s, v1.4s\n"
3855*5f39d1b3SJooyung Han "dup v0.4s, v5.s[2]\n"
3856*5f39d1b3SJooyung Han "dup v1.4s, v5.s[3]\n"
3857*5f39d1b3SJooyung Han "fmla v10.4s, v2.4s, v0.4s\n"
3858*5f39d1b3SJooyung Han "fmla v18.4s, v3.4s, v0.4s\n"
3859*5f39d1b3SJooyung Han "fmla v26.4s, v4.4s, v0.4s\n"
3860*5f39d1b3SJooyung Han "fmla v11.4s, v2.4s, v1.4s\n"
3861*5f39d1b3SJooyung Han "fmla v19.4s, v3.4s, v1.4s\n"
3862*5f39d1b3SJooyung Han "fmla v27.4s, v4.4s, v1.4s\n"
3863*5f39d1b3SJooyung Han "dup v0.4s, v6.s[0]\n"
3864*5f39d1b3SJooyung Han "dup v1.4s, v6.s[1]\n"
3865*5f39d1b3SJooyung Han "fmla v12.4s, v2.4s, v0.4s\n"
3866*5f39d1b3SJooyung Han "fmla v20.4s, v3.4s, v0.4s\n"
3867*5f39d1b3SJooyung Han "fmla v28.4s, v4.4s, v0.4s\n"
3868*5f39d1b3SJooyung Han "fmla v13.4s, v2.4s, v1.4s\n"
3869*5f39d1b3SJooyung Han "fmla v21.4s, v3.4s, v1.4s\n"
3870*5f39d1b3SJooyung Han "fmla v29.4s, v4.4s, v1.4s\n"
3871*5f39d1b3SJooyung Han "dup v0.4s, v6.s[2]\n"
3872*5f39d1b3SJooyung Han "dup v1.4s, v6.s[3]\n"
3873*5f39d1b3SJooyung Han "fmla v14.4s, v2.4s, v0.4s\n"
3874*5f39d1b3SJooyung Han "fmla v22.4s, v3.4s, v0.4s\n"
3875*5f39d1b3SJooyung Han "fmla v30.4s, v4.4s, v0.4s\n"
3876*5f39d1b3SJooyung Han "fmla v15.4s, v2.4s, v1.4s\n"
3877*5f39d1b3SJooyung Han "fmla v23.4s, v3.4s, v1.4s\n"
3878*5f39d1b3SJooyung Han "fmla v31.4s, v4.4s, v1.4s\n"
3879*5f39d1b3SJooyung Han
3880*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
3881*5f39d1b3SJooyung Han // level of depth.
3882*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
3883*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
3884*5f39d1b3SJooyung Han "b\n"
3885*5f39d1b3SJooyung Han
3886*5f39d1b3SJooyung Han // Store accumulators
3887*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3888*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
3889*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
3890*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
3891*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
3892*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
3893*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
3894*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
3895*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
3896*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
3897*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
3898*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
3899*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
3900*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
3901*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
3902*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
3903*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
3904*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
3905*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
3906*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
3907*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
3908*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
3909*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
3910*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
3911*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
3912*5f39d1b3SJooyung Han : // outputs
3913*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
3914*5f39d1b3SJooyung Han [depth] "+r"(depth)
3915*5f39d1b3SJooyung Han : // inputs
3916*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
3917*5f39d1b3SJooyung Han : // clobbers
3918*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
3919*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
3920*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
3921*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
3922*5f39d1b3SJooyung Han }
3923*5f39d1b3SJooyung Han };
3924*5f39d1b3SJooyung Han
3925*5f39d1b3SJooyung Han // This is the "most natural" kernel, using NEON multiply-with-scalar
3926*5f39d1b3SJooyung Han // instructions.
3927*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Float32_WithScalar {
3928*5f39d1b3SJooyung Han typedef float OperandType;
3929*5f39d1b3SJooyung Han typedef float AccumulatorType;
3930*5f39d1b3SJooyung Han typedef KernelFormat<
3931*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
3932*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
3933*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Float32_WithScalar3934*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
3935*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
3936*5f39d1b3SJooyung Han asm volatile(
3937*5f39d1b3SJooyung Han // Load accumulators
3938*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
3939*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
3940*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
3941*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
3942*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
3943*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
3944*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
3945*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
3946*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
3947*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
3948*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
3949*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
3950*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
3951*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
3952*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
3953*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
3954*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
3955*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
3956*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
3957*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
3958*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
3959*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
3960*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
3961*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
3962*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
3963*5f39d1b3SJooyung Han
3964*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
3965*5f39d1b3SJooyung Han ":\n"
3966*5f39d1b3SJooyung Han
3967*5f39d1b3SJooyung Han // Load 2 Rhs cell of size 1x4 each
3968*5f39d1b3SJooyung Han "ld1 {v0.4s}, [%[rhs_ptr]], #16\n"
3969*5f39d1b3SJooyung Han "ld1 {v1.4s}, [%[rhs_ptr]], #16\n"
3970*5f39d1b3SJooyung Han
3971*5f39d1b3SJooyung Han // Load 3 Lhs cells of size 4x1 each
3972*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n"
3973*5f39d1b3SJooyung Han "ld1 {v3.4s}, [%[lhs_ptr]], #16\n"
3974*5f39d1b3SJooyung Han "ld1 {v4.4s}, [%[lhs_ptr]], #16\n"
3975*5f39d1b3SJooyung Han
3976*5f39d1b3SJooyung Han // Multiply-accumulate
3977*5f39d1b3SJooyung Han "fmla v8.4s, v2.4s, v0.s[0]\n"
3978*5f39d1b3SJooyung Han "fmla v9.4s, v2.4s, v0.s[1]\n"
3979*5f39d1b3SJooyung Han "fmla v10.4s, v2.4s, v0.s[2]\n"
3980*5f39d1b3SJooyung Han "fmla v11.4s, v2.4s, v0.s[3]\n"
3981*5f39d1b3SJooyung Han "fmla v12.4s, v2.4s, v1.s[0]\n"
3982*5f39d1b3SJooyung Han "fmla v13.4s, v2.4s, v1.s[1]\n"
3983*5f39d1b3SJooyung Han "fmla v14.4s, v2.4s, v1.s[2]\n"
3984*5f39d1b3SJooyung Han "fmla v15.4s, v2.4s, v1.s[3]\n"
3985*5f39d1b3SJooyung Han "fmla v16.4s, v3.4s, v0.s[0]\n"
3986*5f39d1b3SJooyung Han "fmla v17.4s, v3.4s, v0.s[1]\n"
3987*5f39d1b3SJooyung Han "fmla v18.4s, v3.4s, v0.s[2]\n"
3988*5f39d1b3SJooyung Han "fmla v19.4s, v3.4s, v0.s[3]\n"
3989*5f39d1b3SJooyung Han "fmla v20.4s, v3.4s, v1.s[0]\n"
3990*5f39d1b3SJooyung Han "fmla v21.4s, v3.4s, v1.s[1]\n"
3991*5f39d1b3SJooyung Han "fmla v22.4s, v3.4s, v1.s[2]\n"
3992*5f39d1b3SJooyung Han "fmla v23.4s, v3.4s, v1.s[3]\n"
3993*5f39d1b3SJooyung Han "fmla v24.4s, v4.4s, v0.s[0]\n"
3994*5f39d1b3SJooyung Han "fmla v25.4s, v4.4s, v0.s[1]\n"
3995*5f39d1b3SJooyung Han "fmla v26.4s, v4.4s, v0.s[2]\n"
3996*5f39d1b3SJooyung Han "fmla v27.4s, v4.4s, v0.s[3]\n"
3997*5f39d1b3SJooyung Han "fmla v28.4s, v4.4s, v1.s[0]\n"
3998*5f39d1b3SJooyung Han "fmla v29.4s, v4.4s, v1.s[1]\n"
3999*5f39d1b3SJooyung Han "fmla v30.4s, v4.4s, v1.s[2]\n"
4000*5f39d1b3SJooyung Han "fmla v31.4s, v4.4s, v1.s[3]\n"
4001*5f39d1b3SJooyung Han
4002*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled 1
4003*5f39d1b3SJooyung Han // level of depth.
4004*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
4005*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
4006*5f39d1b3SJooyung Han "b\n"
4007*5f39d1b3SJooyung Han
4008*5f39d1b3SJooyung Han // Store accumulators
4009*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4010*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
4011*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
4012*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
4013*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
4014*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
4015*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
4016*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
4017*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
4018*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
4019*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
4020*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
4021*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
4022*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
4023*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
4024*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
4025*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
4026*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
4027*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
4028*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
4029*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
4030*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
4031*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
4032*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
4033*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
4034*5f39d1b3SJooyung Han : // outputs
4035*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
4036*5f39d1b3SJooyung Han [depth] "+r"(depth)
4037*5f39d1b3SJooyung Han : // inputs
4038*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
4039*5f39d1b3SJooyung Han : // clobbers
4040*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
4041*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
4042*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
4043*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
4044*5f39d1b3SJooyung Han }
4045*5f39d1b3SJooyung Han };
4046*5f39d1b3SJooyung Han
4047*5f39d1b3SJooyung Han // Faster kernel contributed by ARM. Tuned for A57.
4048*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Float32_WithScalar_A57 {
4049*5f39d1b3SJooyung Han typedef float OperandType;
4050*5f39d1b3SJooyung Han typedef float AccumulatorType;
4051*5f39d1b3SJooyung Han typedef KernelFormat<
4052*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
4053*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
4054*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Float32_WithScalar_A574055*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4056*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4057*5f39d1b3SJooyung Han asm volatile(
4058*5f39d1b3SJooyung Han // Load accumulators
4059*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4060*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
4061*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
4062*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
4063*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
4064*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
4065*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
4066*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
4067*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
4068*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
4069*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
4070*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
4071*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
4072*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
4073*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
4074*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
4075*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
4076*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
4077*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
4078*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
4079*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
4080*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
4081*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
4082*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
4083*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
4084*5f39d1b3SJooyung Han
4085*5f39d1b3SJooyung Han // The start of the loop assumes first Rhs cell is already loaded, so
4086*5f39d1b3SJooyung Han // do it here for first iteration.
4087*5f39d1b3SJooyung Han "ld1 {v0.4s}, [%[rhs_ptr]], #16\n"
4088*5f39d1b3SJooyung Han
4089*5f39d1b3SJooyung Han // And the same for the first Lhs cell.
4090*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n"
4091*5f39d1b3SJooyung Han
4092*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
4093*5f39d1b3SJooyung Han ":\n"
4094*5f39d1b3SJooyung Han
4095*5f39d1b3SJooyung Han // Start the MACs at the head of the loop - 1st cell from each side
4096*5f39d1b3SJooyung Han // already loaded.
4097*5f39d1b3SJooyung Han "fmla v8.4s, v2.4s, v0.s[0]\n"
4098*5f39d1b3SJooyung Han "fmla v9.4s, v2.4s, v0.s[1]\n"
4099*5f39d1b3SJooyung Han "ld1 {v1.4s}, [%[rhs_ptr]], #16\n" // Load second Rhs cell.
4100*5f39d1b3SJooyung Han "fmla v10.4s, v2.4s, v0.s[2]\n"
4101*5f39d1b3SJooyung Han "fmla v11.4s, v2.4s, v0.s[3]\n"
4102*5f39d1b3SJooyung Han "ld1 {v3.4s}, [%[lhs_ptr]], #16\n" // Load second Lhs cell.
4103*5f39d1b3SJooyung Han "fmla v12.4s, v2.4s, v1.s[0]\n"
4104*5f39d1b3SJooyung Han "fmla v13.4s, v2.4s, v1.s[1]\n"
4105*5f39d1b3SJooyung Han "ld1 {v4.4s}, [%[lhs_ptr]], #16\n" // Load third Lhs cell.
4106*5f39d1b3SJooyung Han "fmla v14.4s, v2.4s, v1.s[2]\n"
4107*5f39d1b3SJooyung Han "fmla v15.4s, v2.4s, v1.s[3]\n"
4108*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n" // Done with first Lhs cell - load
4109*5f39d1b3SJooyung Han // for the next iteration early.
4110*5f39d1b3SJooyung Han "fmla v16.4s, v3.4s, v0.s[0]\n"
4111*5f39d1b3SJooyung Han "fmla v17.4s, v3.4s, v0.s[1]\n"
4112*5f39d1b3SJooyung Han "fmla v18.4s, v3.4s, v0.s[2]\n"
4113*5f39d1b3SJooyung Han "fmla v19.4s, v3.4s, v0.s[3]\n"
4114*5f39d1b3SJooyung Han "fmla v20.4s, v3.4s, v1.s[0]\n"
4115*5f39d1b3SJooyung Han "fmla v21.4s, v3.4s, v1.s[1]\n"
4116*5f39d1b3SJooyung Han "fmla v22.4s, v3.4s, v1.s[2]\n"
4117*5f39d1b3SJooyung Han "fmla v23.4s, v3.4s, v1.s[3]\n"
4118*5f39d1b3SJooyung Han "fmla v24.4s, v4.4s, v0.s[0]\n"
4119*5f39d1b3SJooyung Han "fmla v25.4s, v4.4s, v0.s[1]\n"
4120*5f39d1b3SJooyung Han "fmla v26.4s, v4.4s, v0.s[2]\n"
4121*5f39d1b3SJooyung Han "fmla v27.4s, v4.4s, v0.s[3]\n"
4122*5f39d1b3SJooyung Han "ld1 {v0.4s}, [%[rhs_ptr]], #16\n" // Done with the first Rhs cell -
4123*5f39d1b3SJooyung Han // load for the next iteration
4124*5f39d1b3SJooyung Han // early.
4125*5f39d1b3SJooyung Han "fmla v28.4s, v4.4s, v1.s[0]\n"
4126*5f39d1b3SJooyung Han "fmla v29.4s, v4.4s, v1.s[1]\n"
4127*5f39d1b3SJooyung Han // Loop. Decrement loop index (depth) by 1, since we just handled
4128*5f39d1b3SJooyung Han // 1 level of depth. Do this a bit before the end of the loop for
4129*5f39d1b3SJooyung Han // better dispatch on A57.
4130*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
4131*5f39d1b3SJooyung Han "fmla v30.4s, v4.4s, v1.s[2]\n"
4132*5f39d1b3SJooyung Han "fmla v31.4s, v4.4s, v1.s[3]\n"
4133*5f39d1b3SJooyung Han
4134*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
4135*5f39d1b3SJooyung Han "b\n"
4136*5f39d1b3SJooyung Han
4137*5f39d1b3SJooyung Han // Store accumulators
4138*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4139*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
4140*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
4141*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
4142*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
4143*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
4144*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
4145*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
4146*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
4147*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
4148*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
4149*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
4150*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
4151*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
4152*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
4153*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
4154*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
4155*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
4156*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
4157*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
4158*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
4159*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
4160*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
4161*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
4162*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
4163*5f39d1b3SJooyung Han : // outputs
4164*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
4165*5f39d1b3SJooyung Han [depth] "+r"(depth)
4166*5f39d1b3SJooyung Han : // inputs
4167*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
4168*5f39d1b3SJooyung Han : // clobbers
4169*5f39d1b3SJooyung Han "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
4170*5f39d1b3SJooyung Han "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
4171*5f39d1b3SJooyung Han "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
4172*5f39d1b3SJooyung Han "v28", "v29", "v30", "v31");
4173*5f39d1b3SJooyung Han }
4174*5f39d1b3SJooyung Han };
4175*5f39d1b3SJooyung Han
4176*5f39d1b3SJooyung Han #ifndef __APPLE__
4177*5f39d1b3SJooyung Han // Faster kernel contributed by ARM. Tuned for A53.
4178*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Float32_WithScalar_A53 {
4179*5f39d1b3SJooyung Han typedef float OperandType;
4180*5f39d1b3SJooyung Han typedef float AccumulatorType;
4181*5f39d1b3SJooyung Han typedef KernelFormat<
4182*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
4183*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
4184*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Float32_WithScalar_A534185*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4186*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4187*5f39d1b3SJooyung Han asm volatile(
4188*5f39d1b3SJooyung Han // Load accumulators
4189*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4190*5f39d1b3SJooyung Han "ld1 {v8.16b}, [x0], #16\n"
4191*5f39d1b3SJooyung Han "ld1 {v16.16b}, [x0], #16\n"
4192*5f39d1b3SJooyung Han "ld1 {v24.16b}, [x0], #16\n"
4193*5f39d1b3SJooyung Han "ld1 {v9.16b}, [x0], #16\n"
4194*5f39d1b3SJooyung Han "ld1 {v17.16b}, [x0], #16\n"
4195*5f39d1b3SJooyung Han "ld1 {v25.16b}, [x0], #16\n"
4196*5f39d1b3SJooyung Han "ld1 {v10.16b}, [x0], #16\n"
4197*5f39d1b3SJooyung Han "ld1 {v18.16b}, [x0], #16\n"
4198*5f39d1b3SJooyung Han "ld1 {v26.16b}, [x0], #16\n"
4199*5f39d1b3SJooyung Han "ld1 {v11.16b}, [x0], #16\n"
4200*5f39d1b3SJooyung Han "ld1 {v19.16b}, [x0], #16\n"
4201*5f39d1b3SJooyung Han "ld1 {v27.16b}, [x0], #16\n"
4202*5f39d1b3SJooyung Han "ld1 {v12.16b}, [x0], #16\n"
4203*5f39d1b3SJooyung Han "ld1 {v20.16b}, [x0], #16\n"
4204*5f39d1b3SJooyung Han "ld1 {v28.16b}, [x0], #16\n"
4205*5f39d1b3SJooyung Han "ld1 {v13.16b}, [x0], #16\n"
4206*5f39d1b3SJooyung Han "ld1 {v21.16b}, [x0], #16\n"
4207*5f39d1b3SJooyung Han "ld1 {v29.16b}, [x0], #16\n"
4208*5f39d1b3SJooyung Han "ld1 {v14.16b}, [x0], #16\n"
4209*5f39d1b3SJooyung Han "ld1 {v22.16b}, [x0], #16\n"
4210*5f39d1b3SJooyung Han "ld1 {v30.16b}, [x0], #16\n"
4211*5f39d1b3SJooyung Han "ld1 {v15.16b}, [x0], #16\n"
4212*5f39d1b3SJooyung Han "ld1 {v23.16b}, [x0], #16\n"
4213*5f39d1b3SJooyung Han "ld1 {v31.16b}, [x0], #16\n"
4214*5f39d1b3SJooyung Han
4215*5f39d1b3SJooyung Han // For A53, a very different-looking loop is needed.
4216*5f39d1b3SJooyung Han //
4217*5f39d1b3SJooyung Han // The main reason for this is that on A53 128-bit loads take two
4218*5f39d1b3SJooyung Han // cycles during which no dual issue can occur. Doing two separate
4219*5f39d1b3SJooyung Han // 64-bit loads avoids this issue - they each take one cycle and are
4220*5f39d1b3SJooyung Han // able to dual issue. Since vector register loads don't dual issue
4221*5f39d1b3SJooyung Han // with FMLA, we load half the register as normal and the other half
4222*5f39d1b3SJooyung Han // into an integer register. This second half can then be moved into
4223*5f39d1b3SJooyung Han // place later with an INS instruction - which will dual issue with a
4224*5f39d1b3SJooyung Han // later FP load.
4225*5f39d1b3SJooyung Han //
4226*5f39d1b3SJooyung Han // For this kernel there are approximately 3 times as many multiplies
4227*5f39d1b3SJooyung Han // as loads, so it makes sense to structure the loop into blocks of 4
4228*5f39d1b3SJooyung Han // cycles, with 1 dedicated "load cycle" and 3 "multiply cycles" per
4229*5f39d1b3SJooyung Han // block. Strictly preserving this structure with NOPs where no load
4230*5f39d1b3SJooyung Han // is needed seems to result in higher performance.
4231*5f39d1b3SJooyung Han //
4232*5f39d1b3SJooyung Han // Choice of x18 to store the upper halves on their way into the
4233*5f39d1b3SJooyung Han // vector registers is arbitrary. Added to the clobber list so that
4234*5f39d1b3SJooyung Han // the compiler will make it available.
4235*5f39d1b3SJooyung Han //
4236*5f39d1b3SJooyung Han //
4237*5f39d1b3SJooyung Han // At the start of the loop, it is assumed that v0 is "half loaded" -
4238*5f39d1b3SJooyung Han // bottom half in place in d0 and the upper half in x18 ready to
4239*5f39d1b3SJooyung Han // insert. So set that up here for the first iteration:
4240*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n" // Bottom half of first Rhs cell
4241*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n" // Upper half
4242*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #16\n" // Separate increment (needed as
4243*5f39d1b3SJooyung Han // there is no operation to load at
4244*5f39d1b3SJooyung Han // reg + 8 but then increment reg
4245*5f39d1b3SJooyung Han // by 16).
4246*5f39d1b3SJooyung Han
4247*5f39d1b3SJooyung Han // v2 should be fully loaded - as it's outside the loop proper it's fine
4248*5f39d1b3SJooyung Han // to use a 128-bit load here.
4249*5f39d1b3SJooyung Han "ld1 {v2.4s}, [%[lhs_ptr]], #16\n" // first Lhs cell
4250*5f39d1b3SJooyung Han
4251*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
4252*5f39d1b3SJooyung Han ":\n"
4253*5f39d1b3SJooyung Han
4254*5f39d1b3SJooyung Han // First block of four cycles. Multplies all require v2 and v0; v2 is
4255*5f39d1b3SJooyung Han // loaded earlier and v0 is half loaded and completed in the load
4256*5f39d1b3SJooyung Han // cycle at the start.
4257*5f39d1b3SJooyung Han "ldr d1, [%[rhs_ptr]]\n" // "load" cycle - loading bottom half of v1
4258*5f39d1b3SJooyung Han // (second Rhs cell).
4259*5f39d1b3SJooyung Han "ins v0.d[1], x18\n" // "load" cycle - moving the upper half of v0 into
4260*5f39d1b3SJooyung Han // place.
4261*5f39d1b3SJooyung Han "fmla v8.4s, v2.4s, v0.s[0]\n" // "fmla" cycle 1 - first multiply.
4262*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n" // "fmla" cycle 1 - load upper half of v1
4263*5f39d1b3SJooyung Han // into x18.
4264*5f39d1b3SJooyung Han "fmla v9.4s, v2.4s, v0.s[1]\n" // "fmla" cycle 2 - second multiply
4265*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #16\n" // "fmla" cycle 2 - increment Rhs
4266*5f39d1b3SJooyung Han // pointer (if needed)
4267*5f39d1b3SJooyung Han "fmla v10.4s, v2.4s, v0.s[2]\n" // "fmla" cycle 3 - third multiply. No
4268*5f39d1b3SJooyung Han // more work to dual issue.
4269*5f39d1b3SJooyung Han
4270*5f39d1b3SJooyung Han // Second block. Start loading v3 (second Lhs cell), finish loading v1.
4271*5f39d1b3SJooyung Han "ldr d3, [%[lhs_ptr]]\n"
4272*5f39d1b3SJooyung Han "ins v1.d[1], x18\n" // v1 ready here.
4273*5f39d1b3SJooyung Han "fmla v11.4s, v2.4s, v0.s[3]\n"
4274*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #8]\n"
4275*5f39d1b3SJooyung Han "fmla v12.4s, v2.4s, v1.s[0]\n" // First use of v1.
4276*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #16\n"
4277*5f39d1b3SJooyung Han "fmla v13.4s, v2.4s, v1.s[1]\n"
4278*5f39d1b3SJooyung Han
4279*5f39d1b3SJooyung Han // Third block. Start loading v4 (third Lhs cell), finish loading v3.
4280*5f39d1b3SJooyung Han "ldr d4, [%[lhs_ptr]]\n"
4281*5f39d1b3SJooyung Han "ins v3.d[1], x18\n" // v3 ready here.
4282*5f39d1b3SJooyung Han "fmla v14.4s, v2.4s, v1.s[2]\n"
4283*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #8]\n"
4284*5f39d1b3SJooyung Han "fmla v15.4s, v2.4s, v1.s[3]\n"
4285*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #16\n"
4286*5f39d1b3SJooyung Han "fmla v16.4s, v3.4s, v0.s[0]\n" // First use of v3.
4287*5f39d1b3SJooyung Han
4288*5f39d1b3SJooyung Han // Fourth block. v2 (first Lhs cell) is now finished with, so start
4289*5f39d1b3SJooyung Han // loading value for next iteration. Finish loading v4.
4290*5f39d1b3SJooyung Han "ldr d2, [%[lhs_ptr]]\n"
4291*5f39d1b3SJooyung Han "ins v4.d[1], x18\n" // v4 ready here.
4292*5f39d1b3SJooyung Han "fmla v17.4s, v3.4s, v0.s[1]\n"
4293*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #8]\n"
4294*5f39d1b3SJooyung Han "fmla v18.4s, v3.4s, v0.s[2]\n"
4295*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #16\n"
4296*5f39d1b3SJooyung Han "fmla v19.4s, v3.4s, v0.s[3]\n"
4297*5f39d1b3SJooyung Han
4298*5f39d1b3SJooyung Han // Fifth block, finish loading v2. No new load to start as the other
4299*5f39d1b3SJooyung Han // registers are all still live.
4300*5f39d1b3SJooyung Han "ins v2.d[1], x18\n"
4301*5f39d1b3SJooyung Han "fmla v20.4s, v3.4s, v1.s[0]\n"
4302*5f39d1b3SJooyung Han "fmla v21.4s, v3.4s, v1.s[1]\n"
4303*5f39d1b3SJooyung Han "fmla v22.4s, v3.4s, v1.s[2]\n"
4304*5f39d1b3SJooyung Han
4305*5f39d1b3SJooyung Han // Sixth block, nothing to load. 2 nops needed as a single nop would
4306*5f39d1b3SJooyung Han // dual issue with the FMLA and break the timing.
4307*5f39d1b3SJooyung Han "nop\n"
4308*5f39d1b3SJooyung Han "nop\n"
4309*5f39d1b3SJooyung Han "fmla v23.4s, v3.4s, v1.s[3]\n"
4310*5f39d1b3SJooyung Han "fmla v24.4s, v4.4s, v0.s[0]\n" // First use of v4.
4311*5f39d1b3SJooyung Han "fmla v25.4s, v4.4s, v0.s[1]\n"
4312*5f39d1b3SJooyung Han
4313*5f39d1b3SJooyung Han // Seventh block, nothing to load. Decrement the loop counter in this
4314*5f39d1b3SJooyung Han // block as the last block is very full.
4315*5f39d1b3SJooyung Han "nop\n"
4316*5f39d1b3SJooyung Han "nop\n"
4317*5f39d1b3SJooyung Han "fmla v26.4s, v4.4s, v0.s[2]\n"
4318*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
4319*5f39d1b3SJooyung Han "fmla v27.4s, v4.4s, v0.s[3]\n"
4320*5f39d1b3SJooyung Han "fmla v28.4s, v4.4s, v1.s[0]\n"
4321*5f39d1b3SJooyung Han
4322*5f39d1b3SJooyung Han // Eighth block - start loading v0 for next iteration.
4323*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n"
4324*5f39d1b3SJooyung Han "fmla v29.4s, v4.4s, v1.s[1]\n"
4325*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n"
4326*5f39d1b3SJooyung Han "fmla v30.4s, v4.4s, v1.s[2]\n"
4327*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #16\n"
4328*5f39d1b3SJooyung Han "fmla v31.4s, v4.4s, v1.s[3]\n"
4329*5f39d1b3SJooyung Han
4330*5f39d1b3SJooyung Han // Loop branch. This will dual issue in fmla cycle 3 of the 8th block.
4331*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP
4332*5f39d1b3SJooyung Han "b\n"
4333*5f39d1b3SJooyung Han
4334*5f39d1b3SJooyung Han // Store accumulators
4335*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4336*5f39d1b3SJooyung Han "st1 {v8.16b}, [x0], #16\n"
4337*5f39d1b3SJooyung Han "st1 {v16.16b}, [x0], #16\n"
4338*5f39d1b3SJooyung Han "st1 {v24.16b}, [x0], #16\n"
4339*5f39d1b3SJooyung Han "st1 {v9.16b}, [x0], #16\n"
4340*5f39d1b3SJooyung Han "st1 {v17.16b}, [x0], #16\n"
4341*5f39d1b3SJooyung Han "st1 {v25.16b}, [x0], #16\n"
4342*5f39d1b3SJooyung Han "st1 {v10.16b}, [x0], #16\n"
4343*5f39d1b3SJooyung Han "st1 {v18.16b}, [x0], #16\n"
4344*5f39d1b3SJooyung Han "st1 {v26.16b}, [x0], #16\n"
4345*5f39d1b3SJooyung Han "st1 {v11.16b}, [x0], #16\n"
4346*5f39d1b3SJooyung Han "st1 {v19.16b}, [x0], #16\n"
4347*5f39d1b3SJooyung Han "st1 {v27.16b}, [x0], #16\n"
4348*5f39d1b3SJooyung Han "st1 {v12.16b}, [x0], #16\n"
4349*5f39d1b3SJooyung Han "st1 {v20.16b}, [x0], #16\n"
4350*5f39d1b3SJooyung Han "st1 {v28.16b}, [x0], #16\n"
4351*5f39d1b3SJooyung Han "st1 {v13.16b}, [x0], #16\n"
4352*5f39d1b3SJooyung Han "st1 {v21.16b}, [x0], #16\n"
4353*5f39d1b3SJooyung Han "st1 {v29.16b}, [x0], #16\n"
4354*5f39d1b3SJooyung Han "st1 {v14.16b}, [x0], #16\n"
4355*5f39d1b3SJooyung Han "st1 {v22.16b}, [x0], #16\n"
4356*5f39d1b3SJooyung Han "st1 {v30.16b}, [x0], #16\n"
4357*5f39d1b3SJooyung Han "st1 {v15.16b}, [x0], #16\n"
4358*5f39d1b3SJooyung Han "st1 {v23.16b}, [x0], #16\n"
4359*5f39d1b3SJooyung Han "st1 {v31.16b}, [x0], #16\n"
4360*5f39d1b3SJooyung Han : // outputs
4361*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
4362*5f39d1b3SJooyung Han [depth] "+r"(depth)
4363*5f39d1b3SJooyung Han : // inputs
4364*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
4365*5f39d1b3SJooyung Han : // clobbers
4366*5f39d1b3SJooyung Han "cc", "memory", "x0", "x18", "v0", "v1", "v2", "v3", "v4", "v5", "v6",
4367*5f39d1b3SJooyung Han "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16",
4368*5f39d1b3SJooyung Han "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26",
4369*5f39d1b3SJooyung Han "v27", "v28", "v29", "v30", "v31");
4370*5f39d1b3SJooyung Han }
4371*5f39d1b3SJooyung Han };
4372*5f39d1b3SJooyung Han #endif
4373*5f39d1b3SJooyung Han
4374*5f39d1b3SJooyung Han // Faster kernel contributed by ARM. Tuned for A55r1.
4375*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Float32_WithScalar_A55r1 {
4376*5f39d1b3SJooyung Han typedef float OperandType;
4377*5f39d1b3SJooyung Han typedef float AccumulatorType;
4378*5f39d1b3SJooyung Han typedef KernelFormat<
4379*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
4380*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 2> >
4381*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Float32_WithScalar_A55r14382*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4383*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4384*5f39d1b3SJooyung Han asm volatile(
4385*5f39d1b3SJooyung Han // Load accumulators
4386*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4387*5f39d1b3SJooyung Han "ld1 {v8.4s}, [x0], #16\n"
4388*5f39d1b3SJooyung Han "ld1 {v16.4s}, [x0], #16\n"
4389*5f39d1b3SJooyung Han "ld1 {v24.4s}, [x0], #16\n"
4390*5f39d1b3SJooyung Han "ld1 {v9.4s}, [x0], #16\n"
4391*5f39d1b3SJooyung Han "ld1 {v17.4s}, [x0], #16\n"
4392*5f39d1b3SJooyung Han "ld1 {v25.4s}, [x0], #16\n"
4393*5f39d1b3SJooyung Han "ld1 {v10.4s}, [x0], #16\n"
4394*5f39d1b3SJooyung Han "ld1 {v18.4s}, [x0], #16\n"
4395*5f39d1b3SJooyung Han "ld1 {v26.4s}, [x0], #16\n"
4396*5f39d1b3SJooyung Han "ld1 {v11.4s}, [x0], #16\n"
4397*5f39d1b3SJooyung Han "ld1 {v19.4s}, [x0], #16\n"
4398*5f39d1b3SJooyung Han "ld1 {v27.4s}, [x0], #16\n"
4399*5f39d1b3SJooyung Han "ld1 {v12.4s}, [x0], #16\n"
4400*5f39d1b3SJooyung Han "ld1 {v20.4s}, [x0], #16\n"
4401*5f39d1b3SJooyung Han "ld1 {v28.4s}, [x0], #16\n"
4402*5f39d1b3SJooyung Han "ld1 {v13.4s}, [x0], #16\n"
4403*5f39d1b3SJooyung Han "ld1 {v21.4s}, [x0], #16\n"
4404*5f39d1b3SJooyung Han "ld1 {v29.4s}, [x0], #16\n"
4405*5f39d1b3SJooyung Han "ld1 {v14.4s}, [x0], #16\n"
4406*5f39d1b3SJooyung Han "ld1 {v22.4s}, [x0], #16\n"
4407*5f39d1b3SJooyung Han "ld1 {v30.4s}, [x0], #16\n"
4408*5f39d1b3SJooyung Han "ld1 {v15.4s}, [x0], #16\n"
4409*5f39d1b3SJooyung Han "ld1 {v23.4s}, [x0], #16\n"
4410*5f39d1b3SJooyung Han "ld1 {v31.4s}, [x0], #16\n"
4411*5f39d1b3SJooyung Han
4412*5f39d1b3SJooyung Han // A55r1 requires a hybrid of the A53 and standard approaches.
4413*5f39d1b3SJooyung Han //
4414*5f39d1b3SJooyung Han // Like A53, this processor prefers 64-bit loads.
4415*5f39d1b3SJooyung Han //
4416*5f39d1b3SJooyung Han // Unlike A53, it is capable of dual-issuing a 64-bit vector load
4417*5f39d1b3SJooyung Han // (or INS) with a FMLA instruction.
4418*5f39d1b3SJooyung Han //
4419*5f39d1b3SJooyung Han // Therefore we aim to issue an FMLA instruction every cycle.
4420*5f39d1b3SJooyung Han // Alongside three FMLAs we can dual issue a (vector) 64-bit load, a
4421*5f39d1b3SJooyung Han // scalar 64-bit load and finally an INS to replicate the effect of
4422*5f39d1b3SJooyung Han // a single 128-bit load.
4423*5f39d1b3SJooyung Han //
4424*5f39d1b3SJooyung Han // The loop contains 24 FMLA instructions, and 5 vector registers
4425*5f39d1b3SJooyung Han // need to be loaded, consuming 15 dual issue slots. This leaves 9
4426*5f39d1b3SJooyung Han // dual issue slots. Four of these are used for loop housekeeping
4427*5f39d1b3SJooyung Han // (2 pointer adds, 1 counter update and 1 branch), leaving 5 left
4428*5f39d1b3SJooyung Han // over (marked by blank lines).
4429*5f39d1b3SJooyung Han //
4430*5f39d1b3SJooyung Han // Choice of x18 to store the upper halves on their way into the
4431*5f39d1b3SJooyung Han // vector registers is arbitrary. Added to the clobber list so that
4432*5f39d1b3SJooyung Han // the compiler will make it available.
4433*5f39d1b3SJooyung Han
4434*5f39d1b3SJooyung Han
4435*5f39d1b3SJooyung Han // At the start of the loop, it is assumed that v0 is "half loaded" -
4436*5f39d1b3SJooyung Han // bottom half in place in d0 and the upper half in x18 ready to
4437*5f39d1b3SJooyung Han // insert. So set that up here for the first iteration:
4438*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n" // Bottom half of first Rhs cell
4439*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n" // Upper half
4440*5f39d1b3SJooyung Han
4441*5f39d1b3SJooyung Han // v2-v3 should be fully loaded - as it's outside the loop proper it's fine
4442*5f39d1b3SJooyung Han // to use a 128-bit load here.
4443*5f39d1b3SJooyung Han "ldr q2, [%[lhs_ptr]]\n" // first Lhs cell
4444*5f39d1b3SJooyung Han "ldr q3, [%[lhs_ptr], #16]\n" // second Lhs cell
4445*5f39d1b3SJooyung Han
4446*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
4447*5f39d1b3SJooyung Han ":\n"
4448*5f39d1b3SJooyung Han
4449*5f39d1b3SJooyung Han "fmla v8.4s, v2.4s, v0.s[0]\n"
4450*5f39d1b3SJooyung Han "ldr d1, [%[rhs_ptr], #16]\n" // Bottom half of v1
4451*5f39d1b3SJooyung Han "fmla v9.4s, v2.4s, v0.s[1]\n"
4452*5f39d1b3SJooyung Han "ins v0.d[1], x18\n" // Finish loading v0
4453*5f39d1b3SJooyung Han "fmla v16.4s, v3.4s, v0.s[0]\n" // out of sequence - used to reduce load/use pressure.
4454*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #24]\n" // Top half of v1 to X register
4455*5f39d1b3SJooyung Han "fmla v17.4s, v3.4s, v0.s[1]\n" // out of sequence - used to reduce load/use pressure.
4456*5f39d1b3SJooyung Han "add %[rhs_ptr], %[rhs_ptr], #32\n" // RHS loads complete - increment pointer.
4457*5f39d1b3SJooyung Han "fmla v10.4s, v2.4s, v0.s[2]\n"
4458*5f39d1b3SJooyung Han "ldr d4, [%[lhs_ptr], #32]\n" // Bottom half of v4
4459*5f39d1b3SJooyung Han "fmla v11.4s, v2.4s, v0.s[3]\n"
4460*5f39d1b3SJooyung Han "ins v1.d[1], x18\n" // Finish loading v1
4461*5f39d1b3SJooyung Han "fmla v12.4s, v2.4s, v1.s[0]\n"
4462*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #40]\n" // Top half of v4 to X register
4463*5f39d1b3SJooyung Han "fmla v13.4s, v2.4s, v1.s[1]\n"
4464*5f39d1b3SJooyung Han "add %[lhs_ptr], %[lhs_ptr], #48\n" // LHS loads complete - increment pointer.
4465*5f39d1b3SJooyung Han "fmla v14.4s, v2.4s, v1.s[2]\n"
4466*5f39d1b3SJooyung Han
4467*5f39d1b3SJooyung Han "fmla v15.4s, v2.4s, v1.s[3]\n"
4468*5f39d1b3SJooyung Han "ldr d2, [%[lhs_ptr]]\n" // Bottom half of v2 (for next time)
4469*5f39d1b3SJooyung Han "fmla v18.4s, v3.4s, v0.s[2]\n"
4470*5f39d1b3SJooyung Han "ins v4.d[1], x18\n" // Finish loading v4
4471*5f39d1b3SJooyung Han "fmla v19.4s, v3.4s, v0.s[3]\n"
4472*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #8]\n" // Top half of next v2 to X register
4473*5f39d1b3SJooyung Han "fmla v20.4s, v3.4s, v1.s[0]\n"
4474*5f39d1b3SJooyung Han "subs %w[depth], %w[depth], #1\n"
4475*5f39d1b3SJooyung Han "fmla v21.4s, v3.4s, v1.s[1]\n"
4476*5f39d1b3SJooyung Han
4477*5f39d1b3SJooyung Han "fmla v22.4s, v3.4s, v1.s[2]\n"
4478*5f39d1b3SJooyung Han
4479*5f39d1b3SJooyung Han "fmla v23.4s, v3.4s, v1.s[3]\n"
4480*5f39d1b3SJooyung Han "ldr d3, [%[lhs_ptr], #16]\n" // Bottom half of v3 (for next time)
4481*5f39d1b3SJooyung Han "fmla v24.4s, v4.4s, v0.s[0]\n"
4482*5f39d1b3SJooyung Han "ins v2.d[1], x18\n" // Finish loading next v2
4483*5f39d1b3SJooyung Han "fmla v25.4s, v4.4s, v0.s[1]\n"
4484*5f39d1b3SJooyung Han "ldr x18, [%[lhs_ptr], #24]\n" // Top half of next v3 to X register
4485*5f39d1b3SJooyung Han "fmla v26.4s, v4.4s, v0.s[2]\n"
4486*5f39d1b3SJooyung Han
4487*5f39d1b3SJooyung Han "fmla v27.4s, v4.4s, v0.s[3]\n"
4488*5f39d1b3SJooyung Han "ldr d0, [%[rhs_ptr]]\n" // Bottom half of v0 (for next time)
4489*5f39d1b3SJooyung Han "fmla v28.4s, v4.4s, v1.s[0]\n"
4490*5f39d1b3SJooyung Han "ins v3.d[1], x18\n" // Finish loading next v3
4491*5f39d1b3SJooyung Han "fmla v29.4s, v4.4s, v1.s[1]\n"
4492*5f39d1b3SJooyung Han "ldr x18, [%[rhs_ptr], #8]\n" // Top half of next v0 to X register
4493*5f39d1b3SJooyung Han "fmla v30.4s, v4.4s, v1.s[2]\n"
4494*5f39d1b3SJooyung Han
4495*5f39d1b3SJooyung Han "fmla v31.4s, v4.4s, v1.s[3]\n"
4496*5f39d1b3SJooyung Han "bne " GEMMLOWP_LABEL_LOOP "b\n"
4497*5f39d1b3SJooyung Han
4498*5f39d1b3SJooyung Han // Store accumulators
4499*5f39d1b3SJooyung Han "mov x0, %[accum_ptr]\n"
4500*5f39d1b3SJooyung Han "st1 {v8.4s}, [x0], #16\n"
4501*5f39d1b3SJooyung Han "st1 {v16.4s}, [x0], #16\n"
4502*5f39d1b3SJooyung Han "st1 {v24.4s}, [x0], #16\n"
4503*5f39d1b3SJooyung Han "st1 {v9.4s}, [x0], #16\n"
4504*5f39d1b3SJooyung Han "st1 {v17.4s}, [x0], #16\n"
4505*5f39d1b3SJooyung Han "st1 {v25.4s}, [x0], #16\n"
4506*5f39d1b3SJooyung Han "st1 {v10.4s}, [x0], #16\n"
4507*5f39d1b3SJooyung Han "st1 {v18.4s}, [x0], #16\n"
4508*5f39d1b3SJooyung Han "st1 {v26.4s}, [x0], #16\n"
4509*5f39d1b3SJooyung Han "st1 {v11.4s}, [x0], #16\n"
4510*5f39d1b3SJooyung Han "st1 {v19.4s}, [x0], #16\n"
4511*5f39d1b3SJooyung Han "st1 {v27.4s}, [x0], #16\n"
4512*5f39d1b3SJooyung Han "st1 {v12.4s}, [x0], #16\n"
4513*5f39d1b3SJooyung Han "st1 {v20.4s}, [x0], #16\n"
4514*5f39d1b3SJooyung Han "st1 {v28.4s}, [x0], #16\n"
4515*5f39d1b3SJooyung Han "st1 {v13.4s}, [x0], #16\n"
4516*5f39d1b3SJooyung Han "st1 {v21.4s}, [x0], #16\n"
4517*5f39d1b3SJooyung Han "st1 {v29.4s}, [x0], #16\n"
4518*5f39d1b3SJooyung Han "st1 {v14.4s}, [x0], #16\n"
4519*5f39d1b3SJooyung Han "st1 {v22.4s}, [x0], #16\n"
4520*5f39d1b3SJooyung Han "st1 {v30.4s}, [x0], #16\n"
4521*5f39d1b3SJooyung Han "st1 {v15.4s}, [x0], #16\n"
4522*5f39d1b3SJooyung Han "st1 {v23.4s}, [x0], #16\n"
4523*5f39d1b3SJooyung Han "st1 {v31.4s}, [x0], #16\n"
4524*5f39d1b3SJooyung Han : // outputs
4525*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
4526*5f39d1b3SJooyung Han [depth] "+r"(depth)
4527*5f39d1b3SJooyung Han : // inputs
4528*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
4529*5f39d1b3SJooyung Han : // clobbers
4530*5f39d1b3SJooyung Han "cc", "memory", "x0", "x18", "v0", "v1", "v2", "v3", "v4", "v5", "v6",
4531*5f39d1b3SJooyung Han "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16",
4532*5f39d1b3SJooyung Han "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26",
4533*5f39d1b3SJooyung Han "v27", "v28", "v29", "v30", "v31");
4534*5f39d1b3SJooyung Han }
4535*5f39d1b3SJooyung Han };
4536*5f39d1b3SJooyung Han
4537*5f39d1b3SJooyung Han #endif // __aarch64__
4538*5f39d1b3SJooyung Han
4539*5f39d1b3SJooyung Han #if defined(__arm__) || defined(__aarch64__)
4540*5f39d1b3SJooyung Han #ifndef __aarch64__
vpaddq_s32(int32x4_t a,int32x4_t b)4541*5f39d1b3SJooyung Han inline int32x4_t vpaddq_s32(int32x4_t a, int32x4_t b) {
4542*5f39d1b3SJooyung Han const int32x2_t c = vpadd_s32(vget_low_s32(a), vget_high_s32(a));
4543*5f39d1b3SJooyung Han const int32x2_t d = vpadd_s32(vget_low_s32(b), vget_high_s32(b));
4544*5f39d1b3SJooyung Han return vcombine_s32(c, d);
4545*5f39d1b3SJooyung Han }
4546*5f39d1b3SJooyung Han #endif
4547*5f39d1b3SJooyung Han
4548*5f39d1b3SJooyung Han // C++ intrinsics-based variant of the deep, int8, fast kernel
4549*5f39d1b3SJooyung Han template <int Cols>
4550*5f39d1b3SJooyung Han struct NEON_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics {
4551*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
4552*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
4553*5f39d1b3SJooyung Han typedef KernelFormat<
4554*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
4555*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<Cols, 16, CellOrder::WidthMajor>, 1> >
4556*5f39d1b3SJooyung Han Format;
RunNEON_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics4557*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4558*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4559*5f39d1b3SJooyung Han int32x4_t acc[4][Cols];
4560*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4561*5f39d1b3SJooyung Han for (int j = 0; j < Cols; j++) {
4562*5f39d1b3SJooyung Han acc[i][j] = vdupq_n_s32(0);
4563*5f39d1b3SJooyung Han }
4564*5f39d1b3SJooyung Han }
4565*5f39d1b3SJooyung Han for (int d = 0; d < depth; d += 16) {
4566*5f39d1b3SJooyung Han int8x16_t lhs[4];
4567*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4568*5f39d1b3SJooyung Han lhs[i] = vld1q_s8(lhs_ptr + 16 * i);
4569*5f39d1b3SJooyung Han }
4570*5f39d1b3SJooyung Han int8x16_t rhs[Cols];
4571*5f39d1b3SJooyung Han for (int i = 0; i < Cols; i++) {
4572*5f39d1b3SJooyung Han rhs[i] = vld1q_s8(rhs_ptr + 16 * i);
4573*5f39d1b3SJooyung Han }
4574*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4575*5f39d1b3SJooyung Han for (int j = 0; j < Cols; j++) {
4576*5f39d1b3SJooyung Han int16x8_t local_acc =
4577*5f39d1b3SJooyung Han vmull_s8(vget_low_s8(lhs[i]), vget_low_s8(rhs[j]));
4578*5f39d1b3SJooyung Han local_acc =
4579*5f39d1b3SJooyung Han vmlal_s8(local_acc, vget_high_s8(lhs[i]), vget_high_s8(rhs[j]));
4580*5f39d1b3SJooyung Han acc[i][j] = vpadalq_s16(acc[i][j], local_acc);
4581*5f39d1b3SJooyung Han }
4582*5f39d1b3SJooyung Han }
4583*5f39d1b3SJooyung Han lhs_ptr += 64;
4584*5f39d1b3SJooyung Han rhs_ptr += 16 * Cols;
4585*5f39d1b3SJooyung Han }
4586*5f39d1b3SJooyung Han for (int i = 0; i < Cols; i++) {
4587*5f39d1b3SJooyung Han int32x4_t acc_2x_0 = vpaddq_s32(acc[0][i], acc[1][i]);
4588*5f39d1b3SJooyung Han int32x4_t acc_2x_1 = vpaddq_s32(acc[2][i], acc[3][i]);
4589*5f39d1b3SJooyung Han int32x4_t acc_4x = vpaddq_s32(acc_2x_0, acc_2x_1);
4590*5f39d1b3SJooyung Han int32x4_t dst_val = vld1q_s32(accum_ptr + 4 * i);
4591*5f39d1b3SJooyung Han dst_val = vaddq_s32(dst_val, acc_4x);
4592*5f39d1b3SJooyung Han vst1q_s32(accum_ptr + 4 * i, dst_val);
4593*5f39d1b3SJooyung Han }
4594*5f39d1b3SJooyung Han }
4595*5f39d1b3SJooyung Han };
4596*5f39d1b3SJooyung Han
4597*5f39d1b3SJooyung Han using NEON_64bit_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics =
4598*5f39d1b3SJooyung Han NEON_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics<4>;
4599*5f39d1b3SJooyung Han
4600*5f39d1b3SJooyung Han using NEON_32bit_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics =
4601*5f39d1b3SJooyung Han NEON_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics<2>;
4602*5f39d1b3SJooyung Han
4603*5f39d1b3SJooyung Han // C++ intrinsics-based variant of the wide, uint8, general kernel
4604*5f39d1b3SJooyung Han template <int RhsCells>
4605*5f39d1b3SJooyung Han struct NEON_GEMM_Uint8Operands_Uint32Accumulators_intrinsics {
4606*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
4607*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
4608*5f39d1b3SJooyung Han typedef KernelFormat<
4609*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 3>,
4610*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, RhsCells> >
4611*5f39d1b3SJooyung Han Format;
RunNEON_GEMM_Uint8Operands_Uint32Accumulators_intrinsics4612*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4613*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4614*5f39d1b3SJooyung Han int32x4_t acc[3][4 * RhsCells];
4615*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4616*5f39d1b3SJooyung Han for (int j = 0; j < 4 * RhsCells; j++) {
4617*5f39d1b3SJooyung Han acc[i][j] = vld1q_s32(accum_ptr + 4 * (i + 3 * j));
4618*5f39d1b3SJooyung Han }
4619*5f39d1b3SJooyung Han }
4620*5f39d1b3SJooyung Han for (int d = 0; d < depth; d += 2) {
4621*5f39d1b3SJooyung Han int16x8_t lhs[3];
4622*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4623*5f39d1b3SJooyung Han lhs[i] = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(lhs_ptr + 8 * i)));
4624*5f39d1b3SJooyung Han }
4625*5f39d1b3SJooyung Han int16x8_t rhs[RhsCells];
4626*5f39d1b3SJooyung Han for (int i = 0; i < RhsCells; i++) {
4627*5f39d1b3SJooyung Han rhs[i] = vreinterpretq_s16_u16(vmovl_u8(vld1_u8(rhs_ptr + 8 * i)));
4628*5f39d1b3SJooyung Han }
4629*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4630*5f39d1b3SJooyung Han for (int j = 0; j < RhsCells; j++) {
4631*5f39d1b3SJooyung Han acc[i][4 * j + 0] = vmlal_lane_s16(
4632*5f39d1b3SJooyung Han acc[i][4 * j + 0], vget_low_s16(lhs[i]), vget_low_s16(rhs[j]), 0);
4633*5f39d1b3SJooyung Han acc[i][4 * j + 1] = vmlal_lane_s16(
4634*5f39d1b3SJooyung Han acc[i][4 * j + 1], vget_low_s16(lhs[i]), vget_low_s16(rhs[j]), 1);
4635*5f39d1b3SJooyung Han acc[i][4 * j + 2] = vmlal_lane_s16(
4636*5f39d1b3SJooyung Han acc[i][4 * j + 2], vget_low_s16(lhs[i]), vget_low_s16(rhs[j]), 2);
4637*5f39d1b3SJooyung Han acc[i][4 * j + 3] = vmlal_lane_s16(
4638*5f39d1b3SJooyung Han acc[i][4 * j + 3], vget_low_s16(lhs[i]), vget_low_s16(rhs[j]), 3);
4639*5f39d1b3SJooyung Han acc[i][4 * j + 0] =
4640*5f39d1b3SJooyung Han vmlal_lane_s16(acc[i][4 * j + 0], vget_high_s16(lhs[i]),
4641*5f39d1b3SJooyung Han vget_high_s16(rhs[j]), 0);
4642*5f39d1b3SJooyung Han acc[i][4 * j + 1] =
4643*5f39d1b3SJooyung Han vmlal_lane_s16(acc[i][4 * j + 1], vget_high_s16(lhs[i]),
4644*5f39d1b3SJooyung Han vget_high_s16(rhs[j]), 1);
4645*5f39d1b3SJooyung Han acc[i][4 * j + 2] =
4646*5f39d1b3SJooyung Han vmlal_lane_s16(acc[i][4 * j + 2], vget_high_s16(lhs[i]),
4647*5f39d1b3SJooyung Han vget_high_s16(rhs[j]), 2);
4648*5f39d1b3SJooyung Han acc[i][4 * j + 3] =
4649*5f39d1b3SJooyung Han vmlal_lane_s16(acc[i][4 * j + 3], vget_high_s16(lhs[i]),
4650*5f39d1b3SJooyung Han vget_high_s16(rhs[j]), 3);
4651*5f39d1b3SJooyung Han }
4652*5f39d1b3SJooyung Han }
4653*5f39d1b3SJooyung Han lhs_ptr += 24;
4654*5f39d1b3SJooyung Han rhs_ptr += 8 * RhsCells;
4655*5f39d1b3SJooyung Han }
4656*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4657*5f39d1b3SJooyung Han for (int j = 0; j < 4 * RhsCells; j++) {
4658*5f39d1b3SJooyung Han vst1q_s32(accum_ptr + 4 * (i + 3 * j), acc[i][j]);
4659*5f39d1b3SJooyung Han }
4660*5f39d1b3SJooyung Han }
4661*5f39d1b3SJooyung Han }
4662*5f39d1b3SJooyung Han };
4663*5f39d1b3SJooyung Han
4664*5f39d1b3SJooyung Han using NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_intrinsics =
4665*5f39d1b3SJooyung Han NEON_GEMM_Uint8Operands_Uint32Accumulators_intrinsics<1>;
4666*5f39d1b3SJooyung Han
4667*5f39d1b3SJooyung Han using NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_intrinsics =
4668*5f39d1b3SJooyung Han NEON_GEMM_Uint8Operands_Uint32Accumulators_intrinsics<2>;
4669*5f39d1b3SJooyung Han
4670*5f39d1b3SJooyung Han template <int RhsCells>
4671*5f39d1b3SJooyung Han struct NEON_GEMM_Float32_WithScalar_intrinsics {
4672*5f39d1b3SJooyung Han typedef float OperandType;
4673*5f39d1b3SJooyung Han typedef float AccumulatorType;
4674*5f39d1b3SJooyung Han typedef KernelFormat<
4675*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, 3>,
4676*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 1, CellOrder::DepthMajor>, RhsCells> >
4677*5f39d1b3SJooyung Han Format;
RunNEON_GEMM_Float32_WithScalar_intrinsics4678*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4679*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4680*5f39d1b3SJooyung Han float32x4_t acc[3][4 * RhsCells];
4681*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4682*5f39d1b3SJooyung Han for (int j = 0; j < 4 * RhsCells; j++) {
4683*5f39d1b3SJooyung Han acc[i][j] = vld1q_f32(accum_ptr + 4 * (i + 3 * j));
4684*5f39d1b3SJooyung Han }
4685*5f39d1b3SJooyung Han }
4686*5f39d1b3SJooyung Han for (int d = 0; d < depth; d++) {
4687*5f39d1b3SJooyung Han float32x4_t lhs[3];
4688*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4689*5f39d1b3SJooyung Han lhs[i] = vld1q_f32(lhs_ptr + 4 * i);
4690*5f39d1b3SJooyung Han }
4691*5f39d1b3SJooyung Han float32x4_t rhs[RhsCells];
4692*5f39d1b3SJooyung Han for (int i = 0; i < RhsCells; i++) {
4693*5f39d1b3SJooyung Han rhs[i] = vld1q_f32(rhs_ptr + 4 * i);
4694*5f39d1b3SJooyung Han }
4695*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4696*5f39d1b3SJooyung Han for (int j = 0; j < RhsCells; j++) {
4697*5f39d1b3SJooyung Han acc[i][4 * j + 0] = vmlaq_lane_f32(acc[i][4 * j + 0], lhs[i],
4698*5f39d1b3SJooyung Han vget_low_f32(rhs[j]), 0);
4699*5f39d1b3SJooyung Han acc[i][4 * j + 1] = vmlaq_lane_f32(acc[i][4 * j + 1], lhs[i],
4700*5f39d1b3SJooyung Han vget_low_f32(rhs[j]), 1);
4701*5f39d1b3SJooyung Han acc[i][4 * j + 2] = vmlaq_lane_f32(acc[i][4 * j + 2], lhs[i],
4702*5f39d1b3SJooyung Han vget_high_f32(rhs[j]), 0);
4703*5f39d1b3SJooyung Han acc[i][4 * j + 3] = vmlaq_lane_f32(acc[i][4 * j + 3], lhs[i],
4704*5f39d1b3SJooyung Han vget_high_f32(rhs[j]), 1);
4705*5f39d1b3SJooyung Han }
4706*5f39d1b3SJooyung Han }
4707*5f39d1b3SJooyung Han lhs_ptr += 12;
4708*5f39d1b3SJooyung Han rhs_ptr += 4 * RhsCells;
4709*5f39d1b3SJooyung Han }
4710*5f39d1b3SJooyung Han for (int i = 0; i < 3; i++) {
4711*5f39d1b3SJooyung Han for (int j = 0; j < 4 * RhsCells; j++) {
4712*5f39d1b3SJooyung Han vst1q_f32(accum_ptr + 4 * (i + 3 * j), acc[i][j]);
4713*5f39d1b3SJooyung Han }
4714*5f39d1b3SJooyung Han }
4715*5f39d1b3SJooyung Han }
4716*5f39d1b3SJooyung Han };
4717*5f39d1b3SJooyung Han
4718*5f39d1b3SJooyung Han using NEON_32bit_GEMM_Float32_WithScalar_intrinsics =
4719*5f39d1b3SJooyung Han NEON_GEMM_Float32_WithScalar_intrinsics<1>;
4720*5f39d1b3SJooyung Han
4721*5f39d1b3SJooyung Han using NEON_64bit_GEMM_Float32_WithScalar_intrinsics =
4722*5f39d1b3SJooyung Han NEON_GEMM_Float32_WithScalar_intrinsics<2>;
4723*5f39d1b3SJooyung Han
4724*5f39d1b3SJooyung Han // C++ intrinsics-based variant of the deep, 7-bit, fast kernel
4725*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits_intrinsics {
4726*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
4727*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
4728*5f39d1b3SJooyung Han typedef KernelFormat<
4729*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
4730*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<2, 16, CellOrder::WidthMajor>, 1> >
4731*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits_intrinsics4732*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4733*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4734*5f39d1b3SJooyung Han int32x4_t acc[4][2];
4735*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4736*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4737*5f39d1b3SJooyung Han acc[i][j] = vdupq_n_s32(0);
4738*5f39d1b3SJooyung Han }
4739*5f39d1b3SJooyung Han }
4740*5f39d1b3SJooyung Han
4741*5f39d1b3SJooyung Han int d = 0;
4742*5f39d1b3SJooyung Han for (; d <= depth - 64; d += 64) {
4743*5f39d1b3SJooyung Han int16x8_t local_acc[4][2];
4744*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4745*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4746*5f39d1b3SJooyung Han local_acc[i][j] = vdupq_n_s16(0);
4747*5f39d1b3SJooyung Han }
4748*5f39d1b3SJooyung Han }
4749*5f39d1b3SJooyung Han
4750*5f39d1b3SJooyung Han // There are not enough registers to fit all lhs and rhs values for 64
4751*5f39d1b3SJooyung Han // depth. Instead, load values for 32 depth at a time.
4752*5f39d1b3SJooyung Han for (int k = 0; k < 2; k++) {
4753*5f39d1b3SJooyung Han int8x16_t lhs[4][2];
4754*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4755*5f39d1b3SJooyung Han lhs[i][0] = vld1q_s8(lhs_ptr + 16 * i + 128 * k);
4756*5f39d1b3SJooyung Han lhs[i][1] = vld1q_s8(lhs_ptr + 64 + 16 * i + 128 * k);
4757*5f39d1b3SJooyung Han }
4758*5f39d1b3SJooyung Han
4759*5f39d1b3SJooyung Han int8x16_t rhs[4];
4760*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4761*5f39d1b3SJooyung Han rhs[i] = vld1q_s8(rhs_ptr + 16 * i + 64 * k);
4762*5f39d1b3SJooyung Han }
4763*5f39d1b3SJooyung Han
4764*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4765*5f39d1b3SJooyung Han if (k == 0) {
4766*5f39d1b3SJooyung Han local_acc[i][0] = vmull_s8(vget_low_s8(lhs[i][0]),
4767*5f39d1b3SJooyung Han vget_low_s8(rhs[0]));
4768*5f39d1b3SJooyung Han local_acc[i][0] = vmlal_s8(local_acc[i][0], vget_low_s8(lhs[i][1]),
4769*5f39d1b3SJooyung Han vget_low_s8(rhs[2]));
4770*5f39d1b3SJooyung Han local_acc[i][1] = vmull_s8(vget_low_s8(lhs[i][0]),
4771*5f39d1b3SJooyung Han vget_low_s8(rhs[1]));
4772*5f39d1b3SJooyung Han local_acc[i][1] = vmlal_s8(local_acc[i][1],
4773*5f39d1b3SJooyung Han vget_low_s8(lhs[i][1]),
4774*5f39d1b3SJooyung Han vget_low_s8(rhs[3]));
4775*5f39d1b3SJooyung Han } else {
4776*5f39d1b3SJooyung Han local_acc[i][0] = vmlal_s8(local_acc[i][0], vget_low_s8(lhs[i][0]),
4777*5f39d1b3SJooyung Han vget_low_s8(rhs[0]));
4778*5f39d1b3SJooyung Han local_acc[i][0] = vmlal_s8(local_acc[i][0], vget_low_s8(lhs[i][1]),
4779*5f39d1b3SJooyung Han vget_low_s8(rhs[2]));
4780*5f39d1b3SJooyung Han local_acc[i][1] = vmlal_s8(local_acc[i][1], vget_low_s8(lhs[i][0]),
4781*5f39d1b3SJooyung Han vget_low_s8(rhs[1]));
4782*5f39d1b3SJooyung Han local_acc[i][1] = vmlal_s8(local_acc[i][1], vget_low_s8(lhs[i][1]),
4783*5f39d1b3SJooyung Han vget_low_s8(rhs[3]));
4784*5f39d1b3SJooyung Han }
4785*5f39d1b3SJooyung Han
4786*5f39d1b3SJooyung Han local_acc[i][0] = vmlal_s8(local_acc[i][0], vget_high_s8(lhs[i][0]),
4787*5f39d1b3SJooyung Han vget_high_s8(rhs[0]));
4788*5f39d1b3SJooyung Han local_acc[i][0] = vmlal_s8(local_acc[i][0], vget_high_s8(lhs[i][1]),
4789*5f39d1b3SJooyung Han vget_high_s8(rhs[2]));
4790*5f39d1b3SJooyung Han local_acc[i][1] = vmlal_s8(local_acc[i][1], vget_high_s8(lhs[i][0]),
4791*5f39d1b3SJooyung Han vget_high_s8(rhs[1]));
4792*5f39d1b3SJooyung Han local_acc[i][1] = vmlal_s8(local_acc[i][1], vget_high_s8(lhs[i][1]),
4793*5f39d1b3SJooyung Han vget_high_s8(rhs[3]));
4794*5f39d1b3SJooyung Han }
4795*5f39d1b3SJooyung Han }
4796*5f39d1b3SJooyung Han
4797*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4798*5f39d1b3SJooyung Han acc[i][0] = vpadalq_s16(acc[i][0], local_acc[i][0]);
4799*5f39d1b3SJooyung Han acc[i][1] = vpadalq_s16(acc[i][1], local_acc[i][1]);
4800*5f39d1b3SJooyung Han }
4801*5f39d1b3SJooyung Han
4802*5f39d1b3SJooyung Han lhs_ptr += 64 * 4;
4803*5f39d1b3SJooyung Han rhs_ptr += 64 * 2;
4804*5f39d1b3SJooyung Han }
4805*5f39d1b3SJooyung Han for (; d <= depth - 16; d += 16) {
4806*5f39d1b3SJooyung Han int8x16_t lhs[4];
4807*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4808*5f39d1b3SJooyung Han lhs[i] = vld1q_s8(lhs_ptr + 16 * i);
4809*5f39d1b3SJooyung Han }
4810*5f39d1b3SJooyung Han int8x16_t rhs[2];
4811*5f39d1b3SJooyung Han for (int i = 0; i < 2; i++) {
4812*5f39d1b3SJooyung Han rhs[i] = vld1q_s8(rhs_ptr + 16 * i);
4813*5f39d1b3SJooyung Han }
4814*5f39d1b3SJooyung Han
4815*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4816*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4817*5f39d1b3SJooyung Han int16x8_t local_acc =
4818*5f39d1b3SJooyung Han vmull_s8(vget_low_s8(lhs[i]), vget_low_s8(rhs[j]));
4819*5f39d1b3SJooyung Han local_acc =
4820*5f39d1b3SJooyung Han vmlal_s8(local_acc, vget_high_s8(lhs[i]), vget_high_s8(rhs[j]));
4821*5f39d1b3SJooyung Han acc[i][j] = vpadalq_s16(acc[i][j], local_acc);
4822*5f39d1b3SJooyung Han }
4823*5f39d1b3SJooyung Han }
4824*5f39d1b3SJooyung Han lhs_ptr += 16 * 4;
4825*5f39d1b3SJooyung Han rhs_ptr += 16 * 2;
4826*5f39d1b3SJooyung Han }
4827*5f39d1b3SJooyung Han for (int i = 0; i < 2; i++) {
4828*5f39d1b3SJooyung Han int32x4_t acc_2x_0 = vpaddq_s32(acc[0][i], acc[1][i]);
4829*5f39d1b3SJooyung Han int32x4_t acc_2x_1 = vpaddq_s32(acc[2][i], acc[3][i]);
4830*5f39d1b3SJooyung Han int32x4_t acc_4x = vpaddq_s32(acc_2x_0, acc_2x_1);
4831*5f39d1b3SJooyung Han int32x4_t dst_val = vld1q_s32(accum_ptr + 4 * i);
4832*5f39d1b3SJooyung Han dst_val = vaddq_s32(dst_val, acc_4x);
4833*5f39d1b3SJooyung Han vst1q_s32(accum_ptr + 4 * i, dst_val);
4834*5f39d1b3SJooyung Han }
4835*5f39d1b3SJooyung Han }
4836*5f39d1b3SJooyung Han };
4837*5f39d1b3SJooyung Han
4838*5f39d1b3SJooyung Han SET_7BIT_RANGES(NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits_intrinsics);
4839*5f39d1b3SJooyung Han
4840*5f39d1b3SJooyung Han // C++ intrinsics-based variant of the deep, 4.25-bit, fast kernel.
4841*5f39d1b3SJooyung Han struct NEON_64bit_GEMM_Int425Operands_intrinsics {
4842*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
4843*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
4844*5f39d1b3SJooyung Han typedef KernelFormat<
4845*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 32, CellOrder::WidthMajor>, 1>,
4846*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<2, 32, CellOrder::WidthMajor>, 1> >
4847*5f39d1b3SJooyung Han Format;
RunNEON_64bit_GEMM_Int425Operands_intrinsics4848*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
4849*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4850*5f39d1b3SJooyung Han int32x4_t acc[4][2];
4851*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4852*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4853*5f39d1b3SJooyung Han acc[i][j] = vdupq_n_s32(0);
4854*5f39d1b3SJooyung Han }
4855*5f39d1b3SJooyung Han }
4856*5f39d1b3SJooyung Han
4857*5f39d1b3SJooyung Han const int num_outer_depth_loop = depth / 512 + 1;
4858*5f39d1b3SJooyung Han int d = 0;
4859*5f39d1b3SJooyung Han for (int od = 0; od < num_outer_depth_loop; od++) {
4860*5f39d1b3SJooyung Han int16x8_t local_acc[4][2];
4861*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4862*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4863*5f39d1b3SJooyung Han local_acc[i][j] = vdupq_n_s16(0);
4864*5f39d1b3SJooyung Han }
4865*5f39d1b3SJooyung Han }
4866*5f39d1b3SJooyung Han for (int k = 0; k < 16 && d <= depth - 32; k++, d += 32) {
4867*5f39d1b3SJooyung Han int8x16_t lhs[8];
4868*5f39d1b3SJooyung Han for (int i = 0; i < 8; i++) {
4869*5f39d1b3SJooyung Han lhs[i] = vld1q_s8(lhs_ptr + 16 * i);
4870*5f39d1b3SJooyung Han }
4871*5f39d1b3SJooyung Han int8x16_t rhs[4];
4872*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4873*5f39d1b3SJooyung Han rhs[i] = vld1q_s8(rhs_ptr + 16 * i);
4874*5f39d1b3SJooyung Han }
4875*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4876*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4877*5f39d1b3SJooyung Han int8x16_t temp_acc = vmulq_s8(lhs[i * 2], rhs[j * 2]);
4878*5f39d1b3SJooyung Han temp_acc = vmlaq_s8(temp_acc, lhs[i * 2 + 1], rhs[j * 2 + 1]);
4879*5f39d1b3SJooyung Han local_acc[i][j] = vpadalq_s8(local_acc[i][j], temp_acc);
4880*5f39d1b3SJooyung Han }
4881*5f39d1b3SJooyung Han }
4882*5f39d1b3SJooyung Han lhs_ptr += 128;
4883*5f39d1b3SJooyung Han rhs_ptr += 64;
4884*5f39d1b3SJooyung Han }
4885*5f39d1b3SJooyung Han
4886*5f39d1b3SJooyung Han for (int i = 0; i < 4; i++) {
4887*5f39d1b3SJooyung Han for (int j = 0; j < 2; j++) {
4888*5f39d1b3SJooyung Han acc[i][j] = vpadalq_s16(acc[i][j], local_acc[i][j]);
4889*5f39d1b3SJooyung Han }
4890*5f39d1b3SJooyung Han }
4891*5f39d1b3SJooyung Han }
4892*5f39d1b3SJooyung Han
4893*5f39d1b3SJooyung Han for (int i = 0; i < 2; i++) {
4894*5f39d1b3SJooyung Han int32x4_t acc_2x_0 = vpaddq_s32(acc[0][i], acc[1][i]);
4895*5f39d1b3SJooyung Han int32x4_t acc_2x_1 = vpaddq_s32(acc[2][i], acc[3][i]);
4896*5f39d1b3SJooyung Han int32x4_t acc_4x = vpaddq_s32(acc_2x_0, acc_2x_1);
4897*5f39d1b3SJooyung Han
4898*5f39d1b3SJooyung Han int32x4_t dst_val = vld1q_s32(accum_ptr + 4 * i);
4899*5f39d1b3SJooyung Han dst_val = vaddq_s32(dst_val, acc_4x);
4900*5f39d1b3SJooyung Han vst1q_s32(accum_ptr + 4 * i, dst_val);
4901*5f39d1b3SJooyung Han }
4902*5f39d1b3SJooyung Han }
4903*5f39d1b3SJooyung Han };
4904*5f39d1b3SJooyung Han
4905*5f39d1b3SJooyung Han SET_425BIT_RANGES(NEON_64bit_GEMM_Int425Operands_intrinsics);
4906*5f39d1b3SJooyung Han
4907*5f39d1b3SJooyung Han #endif // __arm__ || __aarch64__
4908*5f39d1b3SJooyung Han
4909*5f39d1b3SJooyung Han #ifdef __mips
4910*5f39d1b3SJooyung Han // 12x8 depth 2 depth-major kernel.
4911*5f39d1b3SJooyung Han struct MSA_GEMM_12x8_Uint8Operands_Uint32Accumulators1 {
4912*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
4913*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
4914*5f39d1b3SJooyung Han typedef KernelFormat<
4915*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 3>,
4916*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::DepthMajor>, 2> >
4917*5f39d1b3SJooyung Han Format;
RunMSA_GEMM_12x8_Uint8Operands_Uint32Accumulators14918*5f39d1b3SJooyung Han static void Run(OperandType* lhs_ptr, OperandType* rhs_ptr,
4919*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
4920*5f39d1b3SJooyung Han asm volatile(
4921*5f39d1b3SJooyung Han // Load accumulators
4922*5f39d1b3SJooyung Han "ld.w $w0, (0*16)(%[accum_ptr])\n"
4923*5f39d1b3SJooyung Han "ld.w $w4, (1*16)(%[accum_ptr])\n"
4924*5f39d1b3SJooyung Han "ld.w $w8, (2*16)(%[accum_ptr])\n"
4925*5f39d1b3SJooyung Han "ld.w $w1, (3*16)(%[accum_ptr])\n"
4926*5f39d1b3SJooyung Han "ld.w $w5, (4*16)(%[accum_ptr])\n"
4927*5f39d1b3SJooyung Han "ld.w $w9, (5*16)(%[accum_ptr])\n"
4928*5f39d1b3SJooyung Han "ld.w $w2, (6*16)(%[accum_ptr])\n"
4929*5f39d1b3SJooyung Han "ld.w $w6, (7*16)(%[accum_ptr])\n"
4930*5f39d1b3SJooyung Han "ld.w $w10, (8*16)(%[accum_ptr])\n"
4931*5f39d1b3SJooyung Han "ld.w $w3, (9*16)(%[accum_ptr])\n"
4932*5f39d1b3SJooyung Han "ld.w $w7, (10*16)(%[accum_ptr])\n"
4933*5f39d1b3SJooyung Han "ld.w $w11, (11*16)(%[accum_ptr])\n"
4934*5f39d1b3SJooyung Han "ld.w $w12, (12*16)(%[accum_ptr])\n"
4935*5f39d1b3SJooyung Han "ld.w $w16, (13*16)(%[accum_ptr])\n"
4936*5f39d1b3SJooyung Han "ld.w $w20, (14*16)(%[accum_ptr])\n"
4937*5f39d1b3SJooyung Han "ld.w $w13, (15*16)(%[accum_ptr])\n"
4938*5f39d1b3SJooyung Han "ld.w $w17, (16*16)(%[accum_ptr])\n"
4939*5f39d1b3SJooyung Han "ld.w $w21, (17*16)(%[accum_ptr])\n"
4940*5f39d1b3SJooyung Han "ld.w $w14, (18*16)(%[accum_ptr])\n"
4941*5f39d1b3SJooyung Han "ld.w $w18, (19*16)(%[accum_ptr])\n"
4942*5f39d1b3SJooyung Han "ld.w $w22, (20*16)(%[accum_ptr])\n"
4943*5f39d1b3SJooyung Han "ld.w $w15, (21*16)(%[accum_ptr])\n"
4944*5f39d1b3SJooyung Han "ld.w $w19, (22*16)(%[accum_ptr])\n"
4945*5f39d1b3SJooyung Han "ld.w $w23, (23*16)(%[accum_ptr])\n"
4946*5f39d1b3SJooyung Han // Set a temp to all zeroes.
4947*5f39d1b3SJooyung Han "ldi.b $w31, 0\n"
4948*5f39d1b3SJooyung Han
4949*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP ":\n"
4950*5f39d1b3SJooyung Han // Overview of register layout:
4951*5f39d1b3SJooyung Han //
4952*5f39d1b3SJooyung Han // A half of the 2 2x4 cells of Rhs is stored in 16bit in w27-w30
4953*5f39d1b3SJooyung Han // (each register contains 4 replicas of a pair of elements).
4954*5f39d1b3SJooyung Han // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in w24-w26.
4955*5f39d1b3SJooyung Han // A 12x8 block of accumulators is stored in 32bit in w0-w23.
4956*5f39d1b3SJooyung Han //
4957*5f39d1b3SJooyung Han // +------+------+------+------+
4958*5f39d1b3SJooyung Han // Rhs |w27 |w28 |w29 |w30 |
4959*5f39d1b3SJooyung Han // +------+------+------+------+
4960*5f39d1b3SJooyung Han //
4961*5f39d1b3SJooyung Han // | | | | |
4962*5f39d1b3SJooyung Han //
4963*5f39d1b3SJooyung Han // Lhs | | | | |
4964*5f39d1b3SJooyung Han //
4965*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
4966*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
4967*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
4968*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
4969*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
4970*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
4971*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
4972*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
4973*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
4974*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
4975*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
4976*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
4977*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
4978*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
4979*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
4980*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
4981*5f39d1b3SJooyung Han //
4982*5f39d1b3SJooyung Han // Accumulators
4983*5f39d1b3SJooyung Han
4984*5f39d1b3SJooyung Han // Load 3 x 8 bytes of lhs[] with 2 16-byte overlapped loads.
4985*5f39d1b3SJooyung Han "ld.b $w24, 0(%[lhs_ptr])\n"
4986*5f39d1b3SJooyung Han "ld.b $w25, 8(%[lhs_ptr])\n"
4987*5f39d1b3SJooyung Han
4988*5f39d1b3SJooyung Han // Load 4 bytes of rhs[] for the first half of depth 0.
4989*5f39d1b3SJooyung Han "lbu $a0, 0(%[rhs_ptr])\n"
4990*5f39d1b3SJooyung Han "lbu $a1, 1(%[rhs_ptr])\n"
4991*5f39d1b3SJooyung Han "lbu $a2, 2(%[rhs_ptr])\n"
4992*5f39d1b3SJooyung Han "lbu $a3, 3(%[rhs_ptr])\n"
4993*5f39d1b3SJooyung Han // Load 4 bytes of rhs[] for the first half of depth 1.
4994*5f39d1b3SJooyung Han "lbu $v0, 4(%[rhs_ptr])\n"
4995*5f39d1b3SJooyung Han "lbu $v1, 5(%[rhs_ptr])\n"
4996*5f39d1b3SJooyung Han "lbu $t8, 6(%[rhs_ptr])\n"
4997*5f39d1b3SJooyung Han "lbu $t9, 7(%[rhs_ptr])\n"
4998*5f39d1b3SJooyung Han
4999*5f39d1b3SJooyung Han // Zero-extend 8-bit elements of lhs[] to 16 bits.
5000*5f39d1b3SJooyung Han "ilvr.b $w24, $w31, $w24\n"
5001*5f39d1b3SJooyung Han "ilvl.b $w26, $w31, $w25\n"
5002*5f39d1b3SJooyung Han "ilvr.b $w25, $w31, $w25\n"
5003*5f39d1b3SJooyung Han // Interleave depth 0 and depth 1 elements of lhs[] for dpadd_u.w.
5004*5f39d1b3SJooyung Han "ilvl.d $w27, $w31, $w24\n"
5005*5f39d1b3SJooyung Han "ilvl.d $w28, $w31, $w25\n"
5006*5f39d1b3SJooyung Han "ilvl.d $w29, $w31, $w26\n"
5007*5f39d1b3SJooyung Han "ilvr.h $w24, $w27, $w24\n"
5008*5f39d1b3SJooyung Han "ilvr.h $w25, $w28, $w25\n"
5009*5f39d1b3SJooyung Han "ilvr.h $w26, $w29, $w26\n"
5010*5f39d1b3SJooyung Han
5011*5f39d1b3SJooyung Han // Combine and interleave depth 0 and depth 1 elements of rhs[] for dpadd_u.w
5012*5f39d1b3SJooyung Han // (for the first half).
5013*5f39d1b3SJooyung Han "ins $a0, $v0, 16, 8\n"
5014*5f39d1b3SJooyung Han "ins $a1, $v1, 16, 8\n"
5015*5f39d1b3SJooyung Han "ins $a2, $t8, 16, 8\n"
5016*5f39d1b3SJooyung Han "ins $a3, $t9, 16, 8\n"
5017*5f39d1b3SJooyung Han // Make 4 replicas of every pair of rhs[] elements.
5018*5f39d1b3SJooyung Han "fill.w $w27, $a0\n"
5019*5f39d1b3SJooyung Han "fill.w $w28, $a1\n"
5020*5f39d1b3SJooyung Han "fill.w $w29, $a2\n"
5021*5f39d1b3SJooyung Han "fill.w $w30, $a3\n"
5022*5f39d1b3SJooyung Han
5023*5f39d1b3SJooyung Han // Load 4 bytes of rhs[] for the second half of depth 0.
5024*5f39d1b3SJooyung Han "lbu $a0, 8(%[rhs_ptr])\n"
5025*5f39d1b3SJooyung Han "lbu $a1, 9(%[rhs_ptr])\n"
5026*5f39d1b3SJooyung Han "lbu $a2, 10(%[rhs_ptr])\n"
5027*5f39d1b3SJooyung Han "lbu $a3, 11(%[rhs_ptr])\n"
5028*5f39d1b3SJooyung Han // Load 4 bytes of rhs[] for the second half of depth 1.
5029*5f39d1b3SJooyung Han "lbu $v0, 12(%[rhs_ptr])\n"
5030*5f39d1b3SJooyung Han "lbu $v1, 13(%[rhs_ptr])\n"
5031*5f39d1b3SJooyung Han "lbu $t8, 14(%[rhs_ptr])\n"
5032*5f39d1b3SJooyung Han "lbu $t9, 15(%[rhs_ptr])\n"
5033*5f39d1b3SJooyung Han
5034*5f39d1b3SJooyung Han // First half of depths 0 and 1.
5035*5f39d1b3SJooyung Han // Dot-product-(and)-add doubles multiplicand width.
5036*5f39d1b3SJooyung Han "dpadd_u.w $w0, $w24, $w27\n"
5037*5f39d1b3SJooyung Han "dpadd_u.w $w4, $w25, $w27\n"
5038*5f39d1b3SJooyung Han "dpadd_u.w $w8, $w26, $w27\n"
5039*5f39d1b3SJooyung Han "dpadd_u.w $w1, $w24, $w28\n"
5040*5f39d1b3SJooyung Han "dpadd_u.w $w5, $w25, $w28\n"
5041*5f39d1b3SJooyung Han "dpadd_u.w $w9, $w26, $w28\n"
5042*5f39d1b3SJooyung Han "dpadd_u.w $w2, $w24, $w29\n"
5043*5f39d1b3SJooyung Han "dpadd_u.w $w6, $w25, $w29\n"
5044*5f39d1b3SJooyung Han "dpadd_u.w $w10, $w26, $w29\n"
5045*5f39d1b3SJooyung Han "dpadd_u.w $w3, $w24, $w30\n"
5046*5f39d1b3SJooyung Han "dpadd_u.w $w7, $w25, $w30\n"
5047*5f39d1b3SJooyung Han "dpadd_u.w $w11, $w26, $w30\n"
5048*5f39d1b3SJooyung Han
5049*5f39d1b3SJooyung Han // Combine and interleave depth 0 and depth 1 elements of rhs[] for dpadd_u.w
5050*5f39d1b3SJooyung Han // (for the second half).
5051*5f39d1b3SJooyung Han "ins $a0, $v0, 16, 8\n"
5052*5f39d1b3SJooyung Han "ins $a1, $v1, 16, 8\n"
5053*5f39d1b3SJooyung Han "ins $a2, $t8, 16, 8\n"
5054*5f39d1b3SJooyung Han "ins $a3, $t9, 16, 8\n"
5055*5f39d1b3SJooyung Han // Make 4 replicas of every pair of rhs[] elements.
5056*5f39d1b3SJooyung Han "fill.w $w27, $a0\n"
5057*5f39d1b3SJooyung Han "fill.w $w28, $a1\n"
5058*5f39d1b3SJooyung Han "fill.w $w29, $a2\n"
5059*5f39d1b3SJooyung Han "fill.w $w30, $a3\n"
5060*5f39d1b3SJooyung Han
5061*5f39d1b3SJooyung Han // Second half of depths 0 and 1.
5062*5f39d1b3SJooyung Han // Dot-product-(and)-add doubles multiplicand width.
5063*5f39d1b3SJooyung Han "dpadd_u.w $w12, $w24, $w27\n"
5064*5f39d1b3SJooyung Han "dpadd_u.w $w16, $w25, $w27\n"
5065*5f39d1b3SJooyung Han "dpadd_u.w $w20, $w26, $w27\n"
5066*5f39d1b3SJooyung Han "dpadd_u.w $w13, $w24, $w28\n"
5067*5f39d1b3SJooyung Han "dpadd_u.w $w17, $w25, $w28\n"
5068*5f39d1b3SJooyung Han "dpadd_u.w $w21, $w26, $w28\n"
5069*5f39d1b3SJooyung Han "dpadd_u.w $w14, $w24, $w29\n"
5070*5f39d1b3SJooyung Han "dpadd_u.w $w18, $w25, $w29\n"
5071*5f39d1b3SJooyung Han "dpadd_u.w $w22, $w26, $w29\n"
5072*5f39d1b3SJooyung Han "dpadd_u.w $w15, $w24, $w30\n"
5073*5f39d1b3SJooyung Han "dpadd_u.w $w19, $w25, $w30\n"
5074*5f39d1b3SJooyung Han "dpadd_u.w $w23, $w26, $w30\n"
5075*5f39d1b3SJooyung Han
5076*5f39d1b3SJooyung Han "addiu %[depth], -2\n"
5077*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[lhs_ptr], 24\n"
5078*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[rhs_ptr], 16\n"
5079*5f39d1b3SJooyung Han "bnez %[depth]," GEMMLOWP_LABEL_LOOP "b\n"
5080*5f39d1b3SJooyung Han
5081*5f39d1b3SJooyung Han // Store accumulators.
5082*5f39d1b3SJooyung Han "st.w $w0, (0*16)(%[accum_ptr])\n"
5083*5f39d1b3SJooyung Han "st.w $w4, (1*16)(%[accum_ptr])\n"
5084*5f39d1b3SJooyung Han "st.w $w8, (2*16)(%[accum_ptr])\n"
5085*5f39d1b3SJooyung Han "st.w $w1, (3*16)(%[accum_ptr])\n"
5086*5f39d1b3SJooyung Han "st.w $w5, (4*16)(%[accum_ptr])\n"
5087*5f39d1b3SJooyung Han "st.w $w9, (5*16)(%[accum_ptr])\n"
5088*5f39d1b3SJooyung Han "st.w $w2, (6*16)(%[accum_ptr])\n"
5089*5f39d1b3SJooyung Han "st.w $w6, (7*16)(%[accum_ptr])\n"
5090*5f39d1b3SJooyung Han "st.w $w10, (8*16)(%[accum_ptr])\n"
5091*5f39d1b3SJooyung Han "st.w $w3, (9*16)(%[accum_ptr])\n"
5092*5f39d1b3SJooyung Han "st.w $w7, (10*16)(%[accum_ptr])\n"
5093*5f39d1b3SJooyung Han "st.w $w11, (11*16)(%[accum_ptr])\n"
5094*5f39d1b3SJooyung Han "st.w $w12, (12*16)(%[accum_ptr])\n"
5095*5f39d1b3SJooyung Han "st.w $w16, (13*16)(%[accum_ptr])\n"
5096*5f39d1b3SJooyung Han "st.w $w20, (14*16)(%[accum_ptr])\n"
5097*5f39d1b3SJooyung Han "st.w $w13, (15*16)(%[accum_ptr])\n"
5098*5f39d1b3SJooyung Han "st.w $w17, (16*16)(%[accum_ptr])\n"
5099*5f39d1b3SJooyung Han "st.w $w21, (17*16)(%[accum_ptr])\n"
5100*5f39d1b3SJooyung Han "st.w $w14, (18*16)(%[accum_ptr])\n"
5101*5f39d1b3SJooyung Han "st.w $w18, (19*16)(%[accum_ptr])\n"
5102*5f39d1b3SJooyung Han "st.w $w22, (20*16)(%[accum_ptr])\n"
5103*5f39d1b3SJooyung Han "st.w $w15, (21*16)(%[accum_ptr])\n"
5104*5f39d1b3SJooyung Han "st.w $w19, (22*16)(%[accum_ptr])\n"
5105*5f39d1b3SJooyung Han "st.w $w23, (23*16)(%[accum_ptr])\n"
5106*5f39d1b3SJooyung Han : // outputs
5107*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
5108*5f39d1b3SJooyung Han [depth] "+r"(depth)
5109*5f39d1b3SJooyung Han : // inputs
5110*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
5111*5f39d1b3SJooyung Han : // clobbers
5112*5f39d1b3SJooyung Han "memory",
5113*5f39d1b3SJooyung Han "v0", "v1",
5114*5f39d1b3SJooyung Han "a0", "a1", "a2", "a3",
5115*5f39d1b3SJooyung Han "t8", "t9",
5116*5f39d1b3SJooyung Han "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
5117*5f39d1b3SJooyung Han "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
5118*5f39d1b3SJooyung Han "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
5119*5f39d1b3SJooyung Han "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31");
5120*5f39d1b3SJooyung Han }
5121*5f39d1b3SJooyung Han };
5122*5f39d1b3SJooyung Han
5123*5f39d1b3SJooyung Han // 12x8 depth 2 width-major kernel.
5124*5f39d1b3SJooyung Han // Does less shuffling and replication than the kernel above.
5125*5f39d1b3SJooyung Han struct MSA_GEMM_12x8_Uint8Operands_Uint32Accumulators2 {
5126*5f39d1b3SJooyung Han typedef std::uint8_t OperandType;
5127*5f39d1b3SJooyung Han typedef std::uint32_t AccumulatorType;
5128*5f39d1b3SJooyung Han typedef KernelFormat<
5129*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::WidthMajor>, 3>,
5130*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 2, CellOrder::WidthMajor>, 2> >
5131*5f39d1b3SJooyung Han Format;
RunMSA_GEMM_12x8_Uint8Operands_Uint32Accumulators25132*5f39d1b3SJooyung Han static void Run(OperandType* lhs_ptr, OperandType* rhs_ptr,
5133*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
5134*5f39d1b3SJooyung Han asm volatile(
5135*5f39d1b3SJooyung Han // Load accumulators
5136*5f39d1b3SJooyung Han "ld.w $w0, (0*16)(%[accum_ptr])\n"
5137*5f39d1b3SJooyung Han "ld.w $w4, (1*16)(%[accum_ptr])\n"
5138*5f39d1b3SJooyung Han "ld.w $w8, (2*16)(%[accum_ptr])\n"
5139*5f39d1b3SJooyung Han "ld.w $w1, (3*16)(%[accum_ptr])\n"
5140*5f39d1b3SJooyung Han "ld.w $w5, (4*16)(%[accum_ptr])\n"
5141*5f39d1b3SJooyung Han "ld.w $w9, (5*16)(%[accum_ptr])\n"
5142*5f39d1b3SJooyung Han "ld.w $w2, (6*16)(%[accum_ptr])\n"
5143*5f39d1b3SJooyung Han "ld.w $w6, (7*16)(%[accum_ptr])\n"
5144*5f39d1b3SJooyung Han "ld.w $w10, (8*16)(%[accum_ptr])\n"
5145*5f39d1b3SJooyung Han "ld.w $w3, (9*16)(%[accum_ptr])\n"
5146*5f39d1b3SJooyung Han "ld.w $w7, (10*16)(%[accum_ptr])\n"
5147*5f39d1b3SJooyung Han "ld.w $w11, (11*16)(%[accum_ptr])\n"
5148*5f39d1b3SJooyung Han "ld.w $w12, (12*16)(%[accum_ptr])\n"
5149*5f39d1b3SJooyung Han "ld.w $w16, (13*16)(%[accum_ptr])\n"
5150*5f39d1b3SJooyung Han "ld.w $w20, (14*16)(%[accum_ptr])\n"
5151*5f39d1b3SJooyung Han "ld.w $w13, (15*16)(%[accum_ptr])\n"
5152*5f39d1b3SJooyung Han "ld.w $w17, (16*16)(%[accum_ptr])\n"
5153*5f39d1b3SJooyung Han "ld.w $w21, (17*16)(%[accum_ptr])\n"
5154*5f39d1b3SJooyung Han "ld.w $w14, (18*16)(%[accum_ptr])\n"
5155*5f39d1b3SJooyung Han "ld.w $w18, (19*16)(%[accum_ptr])\n"
5156*5f39d1b3SJooyung Han "ld.w $w22, (20*16)(%[accum_ptr])\n"
5157*5f39d1b3SJooyung Han "ld.w $w15, (21*16)(%[accum_ptr])\n"
5158*5f39d1b3SJooyung Han "ld.w $w19, (22*16)(%[accum_ptr])\n"
5159*5f39d1b3SJooyung Han "ld.w $w23, (23*16)(%[accum_ptr])\n"
5160*5f39d1b3SJooyung Han
5161*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP
5162*5f39d1b3SJooyung Han ":\n"
5163*5f39d1b3SJooyung Han // Overview of register layout:
5164*5f39d1b3SJooyung Han //
5165*5f39d1b3SJooyung Han // A half of the 2 2x4 cells of Rhs is stored in 16bit in w28-w31
5166*5f39d1b3SJooyung Han // (each register contains 4 replicas of a pair of elements).
5167*5f39d1b3SJooyung Han // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in w24-w26.
5168*5f39d1b3SJooyung Han // A 12x8 block of accumulators is stored in 32bit in w0-w23.
5169*5f39d1b3SJooyung Han //
5170*5f39d1b3SJooyung Han // +------+------+------+------+
5171*5f39d1b3SJooyung Han // Rhs |w28 |w29 |w30 |w31 |
5172*5f39d1b3SJooyung Han // +------+------+------+------+
5173*5f39d1b3SJooyung Han //
5174*5f39d1b3SJooyung Han // | | | | |
5175*5f39d1b3SJooyung Han //
5176*5f39d1b3SJooyung Han // Lhs | | | | |
5177*5f39d1b3SJooyung Han //
5178*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
5179*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
5180*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
5181*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
5182*5f39d1b3SJooyung Han // |w24| |w0/12 |w1/13 |w2/14 |w3/15 |
5183*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
5184*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
5185*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
5186*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
5187*5f39d1b3SJooyung Han // |w25| |w4/16 |w5/17 |w6/18 |w7/19 |
5188*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
5189*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
5190*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
5191*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
5192*5f39d1b3SJooyung Han // |w26| |w8/20 |w9/21 |w10/22|w11/23|
5193*5f39d1b3SJooyung Han // +---+ - - - - +------+------+------+------+
5194*5f39d1b3SJooyung Han //
5195*5f39d1b3SJooyung Han // Accumulators
5196*5f39d1b3SJooyung Han
5197*5f39d1b3SJooyung Han // Load 3 x 8 bytes of lhs[] with 2 16-byte overlapped loads.
5198*5f39d1b3SJooyung Han "ld.b $w24, 0(%[lhs_ptr])\n"
5199*5f39d1b3SJooyung Han "ld.b $w25, 8(%[lhs_ptr])\n"
5200*5f39d1b3SJooyung Han
5201*5f39d1b3SJooyung Han // Load 2 x 8 bytes of rhs[].
5202*5f39d1b3SJooyung Han "ld.b $w27, 0(%[rhs_ptr])\n"
5203*5f39d1b3SJooyung Han
5204*5f39d1b3SJooyung Han // Zero-extend 8-bit elements of lhs[] to 16 bits.
5205*5f39d1b3SJooyung Han "ldi.b $w31, 0\n"
5206*5f39d1b3SJooyung Han "ilvr.b $w24, $w31, $w24\n"
5207*5f39d1b3SJooyung Han "ilvl.b $w26, $w31, $w25\n"
5208*5f39d1b3SJooyung Han "ilvr.b $w25, $w31, $w25\n"
5209*5f39d1b3SJooyung Han
5210*5f39d1b3SJooyung Han // First half of depths 0 and 1.
5211*5f39d1b3SJooyung Han // Zero-extend 8-bit elements of rhs[] to 16 bits.
5212*5f39d1b3SJooyung Han "ilvr.b $w31, $w31, $w27\n"
5213*5f39d1b3SJooyung Han // Make 4 replicas of every pair of rhs[] elements.
5214*5f39d1b3SJooyung Han "splati.w $w28, $w31[0]\n"
5215*5f39d1b3SJooyung Han "splati.w $w29, $w31[1]\n"
5216*5f39d1b3SJooyung Han "splati.w $w30, $w31[2]\n"
5217*5f39d1b3SJooyung Han "splati.w $w31, $w31[3]\n"
5218*5f39d1b3SJooyung Han // Dot-product-(and)-add doubles multiplicand width.
5219*5f39d1b3SJooyung Han "dpadd_u.w $w0, $w24, $w28\n"
5220*5f39d1b3SJooyung Han "dpadd_u.w $w4, $w25, $w28\n"
5221*5f39d1b3SJooyung Han "dpadd_u.w $w8, $w26, $w28\n"
5222*5f39d1b3SJooyung Han "dpadd_u.w $w1, $w24, $w29\n"
5223*5f39d1b3SJooyung Han "dpadd_u.w $w5, $w25, $w29\n"
5224*5f39d1b3SJooyung Han "dpadd_u.w $w9, $w26, $w29\n"
5225*5f39d1b3SJooyung Han "dpadd_u.w $w2, $w24, $w30\n"
5226*5f39d1b3SJooyung Han "dpadd_u.w $w6, $w25, $w30\n"
5227*5f39d1b3SJooyung Han "dpadd_u.w $w10, $w26, $w30\n"
5228*5f39d1b3SJooyung Han "dpadd_u.w $w3, $w24, $w31\n"
5229*5f39d1b3SJooyung Han "dpadd_u.w $w7, $w25, $w31\n"
5230*5f39d1b3SJooyung Han "dpadd_u.w $w11, $w26, $w31\n"
5231*5f39d1b3SJooyung Han
5232*5f39d1b3SJooyung Han // Second half of depths 0 and 1.
5233*5f39d1b3SJooyung Han // Zero-extend 8-bit elements of rhs[] to 16 bits.
5234*5f39d1b3SJooyung Han "ldi.b $w31, 0\n"
5235*5f39d1b3SJooyung Han "ilvl.b $w31, $w31, $w27\n"
5236*5f39d1b3SJooyung Han // Make 4 replicas of every pair of rhs[] elements.
5237*5f39d1b3SJooyung Han "splati.w $w28, $w31[0]\n"
5238*5f39d1b3SJooyung Han "splati.w $w29, $w31[1]\n"
5239*5f39d1b3SJooyung Han "splati.w $w30, $w31[2]\n"
5240*5f39d1b3SJooyung Han "splati.w $w31, $w31[3]\n"
5241*5f39d1b3SJooyung Han // Dot-product-(and)-add doubles multiplicand width.
5242*5f39d1b3SJooyung Han "dpadd_u.w $w12, $w24, $w28\n"
5243*5f39d1b3SJooyung Han "dpadd_u.w $w16, $w25, $w28\n"
5244*5f39d1b3SJooyung Han "dpadd_u.w $w20, $w26, $w28\n"
5245*5f39d1b3SJooyung Han "dpadd_u.w $w13, $w24, $w29\n"
5246*5f39d1b3SJooyung Han "dpadd_u.w $w17, $w25, $w29\n"
5247*5f39d1b3SJooyung Han "dpadd_u.w $w21, $w26, $w29\n"
5248*5f39d1b3SJooyung Han "dpadd_u.w $w14, $w24, $w30\n"
5249*5f39d1b3SJooyung Han "dpadd_u.w $w18, $w25, $w30\n"
5250*5f39d1b3SJooyung Han "dpadd_u.w $w22, $w26, $w30\n"
5251*5f39d1b3SJooyung Han "dpadd_u.w $w15, $w24, $w31\n"
5252*5f39d1b3SJooyung Han "dpadd_u.w $w19, $w25, $w31\n"
5253*5f39d1b3SJooyung Han "dpadd_u.w $w23, $w26, $w31\n"
5254*5f39d1b3SJooyung Han
5255*5f39d1b3SJooyung Han "addiu %[depth], -2\n" GEMMLOWP_MIPS_XADDIU
5256*5f39d1b3SJooyung Han " %[lhs_ptr], 24\n" GEMMLOWP_MIPS_XADDIU
5257*5f39d1b3SJooyung Han " %[rhs_ptr], 16\n"
5258*5f39d1b3SJooyung Han "bnez %[depth]," GEMMLOWP_LABEL_LOOP
5259*5f39d1b3SJooyung Han "b\n"
5260*5f39d1b3SJooyung Han
5261*5f39d1b3SJooyung Han // Store accumulators.
5262*5f39d1b3SJooyung Han "st.w $w0, (0*16)(%[accum_ptr])\n"
5263*5f39d1b3SJooyung Han "st.w $w4, (1*16)(%[accum_ptr])\n"
5264*5f39d1b3SJooyung Han "st.w $w8, (2*16)(%[accum_ptr])\n"
5265*5f39d1b3SJooyung Han "st.w $w1, (3*16)(%[accum_ptr])\n"
5266*5f39d1b3SJooyung Han "st.w $w5, (4*16)(%[accum_ptr])\n"
5267*5f39d1b3SJooyung Han "st.w $w9, (5*16)(%[accum_ptr])\n"
5268*5f39d1b3SJooyung Han "st.w $w2, (6*16)(%[accum_ptr])\n"
5269*5f39d1b3SJooyung Han "st.w $w6, (7*16)(%[accum_ptr])\n"
5270*5f39d1b3SJooyung Han "st.w $w10, (8*16)(%[accum_ptr])\n"
5271*5f39d1b3SJooyung Han "st.w $w3, (9*16)(%[accum_ptr])\n"
5272*5f39d1b3SJooyung Han "st.w $w7, (10*16)(%[accum_ptr])\n"
5273*5f39d1b3SJooyung Han "st.w $w11, (11*16)(%[accum_ptr])\n"
5274*5f39d1b3SJooyung Han "st.w $w12, (12*16)(%[accum_ptr])\n"
5275*5f39d1b3SJooyung Han "st.w $w16, (13*16)(%[accum_ptr])\n"
5276*5f39d1b3SJooyung Han "st.w $w20, (14*16)(%[accum_ptr])\n"
5277*5f39d1b3SJooyung Han "st.w $w13, (15*16)(%[accum_ptr])\n"
5278*5f39d1b3SJooyung Han "st.w $w17, (16*16)(%[accum_ptr])\n"
5279*5f39d1b3SJooyung Han "st.w $w21, (17*16)(%[accum_ptr])\n"
5280*5f39d1b3SJooyung Han "st.w $w14, (18*16)(%[accum_ptr])\n"
5281*5f39d1b3SJooyung Han "st.w $w18, (19*16)(%[accum_ptr])\n"
5282*5f39d1b3SJooyung Han "st.w $w22, (20*16)(%[accum_ptr])\n"
5283*5f39d1b3SJooyung Han "st.w $w15, (21*16)(%[accum_ptr])\n"
5284*5f39d1b3SJooyung Han "st.w $w19, (22*16)(%[accum_ptr])\n"
5285*5f39d1b3SJooyung Han "st.w $w23, (23*16)(%[accum_ptr])\n"
5286*5f39d1b3SJooyung Han : // outputs
5287*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
5288*5f39d1b3SJooyung Han [depth] "+r"(depth)
5289*5f39d1b3SJooyung Han : // inputs
5290*5f39d1b3SJooyung Han [accum_ptr] "r"(accum_ptr)
5291*5f39d1b3SJooyung Han : // clobbers
5292*5f39d1b3SJooyung Han "memory", "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8",
5293*5f39d1b3SJooyung Han "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17",
5294*5f39d1b3SJooyung Han "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26",
5295*5f39d1b3SJooyung Han "$f27", "$f28", "$f29", "$f30", "$f31");
5296*5f39d1b3SJooyung Han }
5297*5f39d1b3SJooyung Han };
5298*5f39d1b3SJooyung Han
5299*5f39d1b3SJooyung Han // 4x4 depth 16 width-major kernel operating on int8 operands.
5300*5f39d1b3SJooyung Han // It is assumed that one of the two int8 operands only takes values
5301*5f39d1b3SJooyung Han // in [-127, 127], while the other may freely range in [-128, 127].
5302*5f39d1b3SJooyung Han // The issue with both operands taking the value -128 is that:
5303*5f39d1b3SJooyung Han // -128*-128 + -128*-128 == -32768 overflows int16.
5304*5f39d1b3SJooyung Han // Every other expression a*b + c*d, for any int8 a,b,c,d, fits in int16
5305*5f39d1b3SJooyung Han // range. That is the basic idea of this kernel.
5306*5f39d1b3SJooyung Han struct MSA_GEMM_Int8Operands_AccumTwoWithin16Bits {
5307*5f39d1b3SJooyung Han typedef std::int8_t OperandType;
5308*5f39d1b3SJooyung Han typedef std::int32_t AccumulatorType;
5309*5f39d1b3SJooyung Han typedef KernelFormat<
5310*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
5311*5f39d1b3SJooyung Han KernelSideFormat<CellFormat<4, 16, CellOrder::WidthMajor>, 1> >
5312*5f39d1b3SJooyung Han Format;
RunMSA_GEMM_Int8Operands_AccumTwoWithin16Bits5313*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
5314*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
5315*5f39d1b3SJooyung Han std::size_t start_depth = 123;
5316*5f39d1b3SJooyung Han std::size_t run_depth = depth;
5317*5f39d1b3SJooyung Han std::size_t dst_col_stride = 4;
5318*5f39d1b3SJooyung Han AccumulatorType* dst_ptr = accum_ptr;
5319*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_AFTER_LOOP_LAST16 "1"
5320*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_LOOP "2"
5321*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES "3"
5322*5f39d1b3SJooyung Han #define GEMMLOWP_LABEL_STORE "4"
5323*5f39d1b3SJooyung Han asm volatile(
5324*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[run_depth], -16\n"
5325*5f39d1b3SJooyung Han // Load lhs[] and rhs[], zero out internal accumulators.
5326*5f39d1b3SJooyung Han "ld.b $w16, 0(%[lhs_ptr])\n"
5327*5f39d1b3SJooyung Han "ldi.b $w0, 0\n"
5328*5f39d1b3SJooyung Han "ld.b $w20, 0(%[rhs_ptr])\n"
5329*5f39d1b3SJooyung Han "ldi.b $w1, 0\n"
5330*5f39d1b3SJooyung Han "ld.b $w17, 16(%[lhs_ptr])\n"
5331*5f39d1b3SJooyung Han "ldi.b $w2, 0\n"
5332*5f39d1b3SJooyung Han "ld.b $w21, 16(%[rhs_ptr])\n"
5333*5f39d1b3SJooyung Han "ldi.b $w3, 0\n"
5334*5f39d1b3SJooyung Han "ld.b $w18, 32(%[lhs_ptr])\n"
5335*5f39d1b3SJooyung Han "ldi.b $w4, 0\n"
5336*5f39d1b3SJooyung Han "ld.b $w19, 48(%[lhs_ptr])\n"
5337*5f39d1b3SJooyung Han "ldi.b $w5, 0\n"
5338*5f39d1b3SJooyung Han "ld.b $w22, 32(%[rhs_ptr])\n"
5339*5f39d1b3SJooyung Han "ldi.b $w6, 0\n"
5340*5f39d1b3SJooyung Han "ld.b $w23, 48(%[rhs_ptr])\n"
5341*5f39d1b3SJooyung Han "ldi.b $w7, 0\n"
5342*5f39d1b3SJooyung Han "ldi.b $w8, 0\n"
5343*5f39d1b3SJooyung Han "ldi.b $w9, 0\n"
5344*5f39d1b3SJooyung Han "ldi.b $w10, 0\n"
5345*5f39d1b3SJooyung Han "ldi.b $w11, 0\n"
5346*5f39d1b3SJooyung Han "ldi.b $w12, 0\n"
5347*5f39d1b3SJooyung Han "ldi.b $w13, 0\n"
5348*5f39d1b3SJooyung Han "ldi.b $w14, 0\n"
5349*5f39d1b3SJooyung Han "ldi.b $w15, 0\n"
5350*5f39d1b3SJooyung Han "ldi.h $w31, 1\n"
5351*5f39d1b3SJooyung Han // If the loop depth is only 16, then we can skip the general loop
5352*5f39d1b3SJooyung Han // and go straight to the final part of the code.
5353*5f39d1b3SJooyung Han "beqz %[run_depth], " GEMMLOWP_LABEL_AFTER_LOOP_LAST16 "f\n"
5354*5f39d1b3SJooyung Han
5355*5f39d1b3SJooyung Han GEMMLOWP_LABEL_LOOP ":\n"
5356*5f39d1b3SJooyung Han // Overview of register layout:
5357*5f39d1b3SJooyung Han //
5358*5f39d1b3SJooyung Han // A 4x16 block of Rhs is stored in 8 bit in w16-w19.
5359*5f39d1b3SJooyung Han // A 4x16 block of Lhs is stored in 8 bit in w20-w23.
5360*5f39d1b3SJooyung Han //
5361*5f39d1b3SJooyung Han // A 4x4 block of accumulators is stored in w0-w15 (as 4x32 bit
5362*5f39d1b3SJooyung Han // components which need to be horizontally added at the end).
5363*5f39d1b3SJooyung Han //
5364*5f39d1b3SJooyung Han // Dot products of Lhs and Rhs are 16-bit values, which can't
5365*5f39d1b3SJooyung Han // immediately be accumulated in 32-bit accumulators by that
5366*5f39d1b3SJooyung Han // same instruction that calculates them.
5367*5f39d1b3SJooyung Han // For example, "dotp_s.h $w25, $w16, $w20" produces 8 16-bit
5368*5f39d1b3SJooyung Han // sums in w25 (note, the 16 sums have already been reduced to 8
5369*5f39d1b3SJooyung Han // by the horizontal addition of the dotp instruction).
5370*5f39d1b3SJooyung Han // They are then sign-extended to 32 bits, horizontally added
5371*5f39d1b3SJooyung Han // (again) to form 4 32-bit sums and then they are finally added
5372*5f39d1b3SJooyung Han // to the 32-bit accumulators, all by "dpadd_s.w $w0, $w25, $w31".
5373*5f39d1b3SJooyung Han //
5374*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
5375*5f39d1b3SJooyung Han // Rhs | w20 | w21 | w22 | w23 |
5376*5f39d1b3SJooyung Han // +-----+-----+-----+-----+
5377*5f39d1b3SJooyung Han //
5378*5f39d1b3SJooyung Han // | | | | |
5379*5f39d1b3SJooyung Han //
5380*5f39d1b3SJooyung Han // Lhs | | | | |
5381*5f39d1b3SJooyung Han //
5382*5f39d1b3SJooyung Han // +---+ - - - - +-----+-----+-----+-----+
5383*5f39d1b3SJooyung Han // |w16| | w0 | w4 | w8 | w12 |
5384*5f39d1b3SJooyung Han // |w17| | w1 | w5 | w9 | w13 |
5385*5f39d1b3SJooyung Han // |w18| | w2 | w6 | w10 | w14 |
5386*5f39d1b3SJooyung Han // |w19| | w3 | w7 | w11 | w15 |
5387*5f39d1b3SJooyung Han // +---+ - - - - +-----+-----+-----+-----+
5388*5f39d1b3SJooyung Han //
5389*5f39d1b3SJooyung Han // Accumulators
5390*5f39d1b3SJooyung Han
5391*5f39d1b3SJooyung Han // Calculate the results for 16 depths and load
5392*5f39d1b3SJooyung Han // lhs[] and rhs[] for the next iteration.
5393*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[lhs_ptr], 64\n"
5394*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[rhs_ptr], 64\n"
5395*5f39d1b3SJooyung Han GEMMLOWP_MIPS_XADDIU " %[run_depth], -16\n"
5396*5f39d1b3SJooyung Han
5397*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5398*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5399*5f39d1b3SJooyung Han "dotp_s.h $w25, $w16, $w20\n"
5400*5f39d1b3SJooyung Han "dotp_s.h $w26, $w17, $w20\n"
5401*5f39d1b3SJooyung Han "dotp_s.h $w27, $w16, $w21\n"
5402*5f39d1b3SJooyung Han "dotp_s.h $w28, $w17, $w21\n"
5403*5f39d1b3SJooyung Han "dotp_s.h $w29, $w18, $w20\n"
5404*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5405*5f39d1b3SJooyung Han // accumulators.
5406*5f39d1b3SJooyung Han "dpadd_s.w $w0, $w25, $w31\n"
5407*5f39d1b3SJooyung Han "dpadd_s.w $w1, $w26, $w31\n"
5408*5f39d1b3SJooyung Han "dpadd_s.w $w4, $w27, $w31\n"
5409*5f39d1b3SJooyung Han "dpadd_s.w $w5, $w28, $w31\n"
5410*5f39d1b3SJooyung Han "dpadd_s.w $w2, $w29, $w31\n"
5411*5f39d1b3SJooyung Han
5412*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5413*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5414*5f39d1b3SJooyung Han "dotp_s.h $w24, $w16, $w22\n"
5415*5f39d1b3SJooyung Han "dotp_s.h $w25, $w19, $w20\n"
5416*5f39d1b3SJooyung Han "dotp_s.h $w26, $w16, $w23\n"
5417*5f39d1b3SJooyung Han "dotp_s.h $w27, $w17, $w22\n"
5418*5f39d1b3SJooyung Han "ld.b $w20, 0(%[rhs_ptr])\n"
5419*5f39d1b3SJooyung Han "dotp_s.h $w28, $w17, $w23\n"
5420*5f39d1b3SJooyung Han "ld.b $w16, 0(%[lhs_ptr])\n"
5421*5f39d1b3SJooyung Han "dotp_s.h $w29, $w18, $w21\n"
5422*5f39d1b3SJooyung Han "ld.b $w17, 16(%[lhs_ptr])\n"
5423*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5424*5f39d1b3SJooyung Han // accumulators.
5425*5f39d1b3SJooyung Han "dpadd_s.w $w8, $w24, $w31\n"
5426*5f39d1b3SJooyung Han "dpadd_s.w $w3, $w25, $w31\n"
5427*5f39d1b3SJooyung Han "dpadd_s.w $w12, $w26, $w31\n"
5428*5f39d1b3SJooyung Han "dpadd_s.w $w9, $w27, $w31\n"
5429*5f39d1b3SJooyung Han "dpadd_s.w $w13, $w28, $w31\n"
5430*5f39d1b3SJooyung Han "dpadd_s.w $w6, $w29, $w31\n"
5431*5f39d1b3SJooyung Han
5432*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5433*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5434*5f39d1b3SJooyung Han "dotp_s.h $w25, $w19, $w21\n"
5435*5f39d1b3SJooyung Han "dotp_s.h $w26, $w18, $w22\n"
5436*5f39d1b3SJooyung Han "dotp_s.h $w27, $w18, $w23\n"
5437*5f39d1b3SJooyung Han "ld.b $w21, 16(%[rhs_ptr])\n"
5438*5f39d1b3SJooyung Han "dotp_s.h $w28, $w19, $w22\n"
5439*5f39d1b3SJooyung Han "ld.b $w18, 32(%[lhs_ptr])\n"
5440*5f39d1b3SJooyung Han "dotp_s.h $w29, $w19, $w23\n"
5441*5f39d1b3SJooyung Han "ld.b $w22, 32(%[rhs_ptr])\n"
5442*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5443*5f39d1b3SJooyung Han // accumulators.
5444*5f39d1b3SJooyung Han "dpadd_s.w $w7, $w25, $w31\n"
5445*5f39d1b3SJooyung Han "ld.b $w19, 48(%[lhs_ptr])\n"
5446*5f39d1b3SJooyung Han "dpadd_s.w $w10, $w26, $w31\n"
5447*5f39d1b3SJooyung Han "ld.b $w23, 48(%[rhs_ptr])\n"
5448*5f39d1b3SJooyung Han "dpadd_s.w $w14, $w27, $w31\n"
5449*5f39d1b3SJooyung Han "dpadd_s.w $w11, $w28, $w31\n"
5450*5f39d1b3SJooyung Han "dpadd_s.w $w15, $w29, $w31\n"
5451*5f39d1b3SJooyung Han
5452*5f39d1b3SJooyung Han "bnez %[run_depth], " GEMMLOWP_LABEL_LOOP "b\n"
5453*5f39d1b3SJooyung Han
5454*5f39d1b3SJooyung Han GEMMLOWP_LABEL_AFTER_LOOP_LAST16 ":\n"
5455*5f39d1b3SJooyung Han // Calculate the results for the last 16 depths.
5456*5f39d1b3SJooyung Han
5457*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5458*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5459*5f39d1b3SJooyung Han "dotp_s.h $w25, $w16, $w20\n"
5460*5f39d1b3SJooyung Han "dotp_s.h $w26, $w17, $w20\n"
5461*5f39d1b3SJooyung Han "dotp_s.h $w27, $w16, $w21\n"
5462*5f39d1b3SJooyung Han "dotp_s.h $w28, $w17, $w21\n"
5463*5f39d1b3SJooyung Han "dotp_s.h $w29, $w18, $w20\n"
5464*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5465*5f39d1b3SJooyung Han // accumulators.
5466*5f39d1b3SJooyung Han "dpadd_s.w $w0, $w25, $w31\n"
5467*5f39d1b3SJooyung Han "dpadd_s.w $w1, $w26, $w31\n"
5468*5f39d1b3SJooyung Han "dpadd_s.w $w4, $w27, $w31\n"
5469*5f39d1b3SJooyung Han "dpadd_s.w $w5, $w28, $w31\n"
5470*5f39d1b3SJooyung Han "dpadd_s.w $w2, $w29, $w31\n"
5471*5f39d1b3SJooyung Han
5472*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5473*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5474*5f39d1b3SJooyung Han "dotp_s.h $w24, $w16, $w22\n"
5475*5f39d1b3SJooyung Han "dotp_s.h $w25, $w19, $w20\n"
5476*5f39d1b3SJooyung Han "dotp_s.h $w26, $w16, $w23\n"
5477*5f39d1b3SJooyung Han "dotp_s.h $w27, $w17, $w22\n"
5478*5f39d1b3SJooyung Han "dotp_s.h $w28, $w17, $w23\n"
5479*5f39d1b3SJooyung Han "dotp_s.h $w29, $w18, $w21\n"
5480*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5481*5f39d1b3SJooyung Han // accumulators.
5482*5f39d1b3SJooyung Han "dpadd_s.w $w8, $w24, $w31\n"
5483*5f39d1b3SJooyung Han "dpadd_s.w $w3, $w25, $w31\n"
5484*5f39d1b3SJooyung Han "dpadd_s.w $w12, $w26, $w31\n"
5485*5f39d1b3SJooyung Han "dpadd_s.w $w9, $w27, $w31\n"
5486*5f39d1b3SJooyung Han "dpadd_s.w $w13, $w28, $w31\n"
5487*5f39d1b3SJooyung Han "dpadd_s.w $w6, $w29, $w31\n"
5488*5f39d1b3SJooyung Han
5489*5f39d1b3SJooyung Han // Dot product: multiply-add pairs of adjacent int8 elements.
5490*5f39d1b3SJooyung Han // Each dot product takes 16*2 int8 values in and produces 8 int16 sums.
5491*5f39d1b3SJooyung Han "dotp_s.h $w25, $w19, $w21\n"
5492*5f39d1b3SJooyung Han "dotp_s.h $w26, $w18, $w22\n"
5493*5f39d1b3SJooyung Han "dotp_s.h $w27, $w18, $w23\n"
5494*5f39d1b3SJooyung Han "dotp_s.h $w28, $w19, $w22\n"
5495*5f39d1b3SJooyung Han "dotp_s.h $w29, $w19, $w23\n"
5496*5f39d1b3SJooyung Han // Horizontal add of pairs of adjacent int16 sums into internal int32
5497*5f39d1b3SJooyung Han // accumulators.
5498*5f39d1b3SJooyung Han "dpadd_s.w $w7, $w25, $w31\n"
5499*5f39d1b3SJooyung Han "dpadd_s.w $w10, $w26, $w31\n"
5500*5f39d1b3SJooyung Han "dpadd_s.w $w14, $w27, $w31\n"
5501*5f39d1b3SJooyung Han "dpadd_s.w $w11, $w28, $w31\n"
5502*5f39d1b3SJooyung Han "dpadd_s.w $w15, $w29, $w31\n"
5503*5f39d1b3SJooyung Han
5504*5f39d1b3SJooyung Han // Horizontal-add internal accumulators.
5505*5f39d1b3SJooyung Han "hadd_s.d $w0, $w0, $w0\n"
5506*5f39d1b3SJooyung Han "hadd_s.d $w1, $w1, $w1\n"
5507*5f39d1b3SJooyung Han "hadd_s.d $w2, $w2, $w2\n"
5508*5f39d1b3SJooyung Han "hadd_s.d $w3, $w3, $w3\n"
5509*5f39d1b3SJooyung Han "hadd_s.d $w4, $w4, $w4\n"
5510*5f39d1b3SJooyung Han "hadd_s.d $w5, $w5, $w5\n"
5511*5f39d1b3SJooyung Han "hadd_s.d $w6, $w6, $w6\n"
5512*5f39d1b3SJooyung Han "hadd_s.d $w7, $w7, $w7\n"
5513*5f39d1b3SJooyung Han "hadd_s.d $w8, $w8, $w8\n"
5514*5f39d1b3SJooyung Han "hadd_s.d $w9, $w9, $w9\n"
5515*5f39d1b3SJooyung Han "hadd_s.d $w10, $w10, $w10\n"
5516*5f39d1b3SJooyung Han "hadd_s.d $w11, $w11, $w11\n"
5517*5f39d1b3SJooyung Han "hadd_s.d $w12, $w12, $w12\n"
5518*5f39d1b3SJooyung Han "hadd_s.d $w13, $w13, $w13\n"
5519*5f39d1b3SJooyung Han "hadd_s.d $w14, $w14, $w14\n"
5520*5f39d1b3SJooyung Han "hadd_s.d $w15, $w15, $w15\n"
5521*5f39d1b3SJooyung Han "pckev.w $w0, $w1, $w0\n"
5522*5f39d1b3SJooyung Han "pckev.w $w2, $w3, $w2\n"
5523*5f39d1b3SJooyung Han "pckev.w $w4, $w5, $w4\n"
5524*5f39d1b3SJooyung Han "pckev.w $w6, $w7, $w6\n"
5525*5f39d1b3SJooyung Han "pckev.w $w8, $w9, $w8\n"
5526*5f39d1b3SJooyung Han "pckev.w $w10, $w11, $w10\n"
5527*5f39d1b3SJooyung Han "pckev.w $w12, $w13, $w12\n"
5528*5f39d1b3SJooyung Han "pckev.w $w14, $w15, $w14\n"
5529*5f39d1b3SJooyung Han "hadd_s.d $w0, $w0, $w0\n"
5530*5f39d1b3SJooyung Han "hadd_s.d $w2, $w2, $w2\n"
5531*5f39d1b3SJooyung Han "hadd_s.d $w4, $w4, $w4\n"
5532*5f39d1b3SJooyung Han "hadd_s.d $w6, $w6, $w6\n"
5533*5f39d1b3SJooyung Han "hadd_s.d $w8, $w8, $w8\n"
5534*5f39d1b3SJooyung Han "hadd_s.d $w10, $w10, $w10\n"
5535*5f39d1b3SJooyung Han "hadd_s.d $w12, $w12, $w12\n"
5536*5f39d1b3SJooyung Han "hadd_s.d $w14, $w14, $w14\n"
5537*5f39d1b3SJooyung Han // 4 more pckev instructions follow in both paths below.
5538*5f39d1b3SJooyung Han
5539*5f39d1b3SJooyung Han // Check if start_depth==0 to decide whether we will load
5540*5f39d1b3SJooyung Han // existing accumulators from memory.
5541*5f39d1b3SJooyung Han "bnez %[start_depth], " GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES "f\n"
5542*5f39d1b3SJooyung Han
5543*5f39d1b3SJooyung Han "pckev.w $w0, $w2, $w0\n"
5544*5f39d1b3SJooyung Han "pckev.w $w1, $w6, $w4\n"
5545*5f39d1b3SJooyung Han "pckev.w $w2, $w10, $w8\n"
5546*5f39d1b3SJooyung Han "pckev.w $w3, $w14, $w12\n"
5547*5f39d1b3SJooyung Han
5548*5f39d1b3SJooyung Han "b " GEMMLOWP_LABEL_STORE "f\n"
5549*5f39d1b3SJooyung Han
5550*5f39d1b3SJooyung Han GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES ":\n"
5551*5f39d1b3SJooyung Han // Load accumulators from memory.
5552*5f39d1b3SJooyung Han "ld.w $w16, 0(%[dst_ptr0])\n"
5553*5f39d1b3SJooyung Han "pckev.w $w0, $w2, $w0\n"
5554*5f39d1b3SJooyung Han "ld.w $w17, 0(%[dst_ptr1])\n"
5555*5f39d1b3SJooyung Han "pckev.w $w1, $w6, $w4\n"
5556*5f39d1b3SJooyung Han "ld.w $w18, 0(%[dst_ptr2])\n"
5557*5f39d1b3SJooyung Han "pckev.w $w2, $w10, $w8\n"
5558*5f39d1b3SJooyung Han "ld.w $w19, 0(%[dst_ptr3])\n"
5559*5f39d1b3SJooyung Han "pckev.w $w3, $w14, $w12\n"
5560*5f39d1b3SJooyung Han
5561*5f39d1b3SJooyung Han // Add them to internal accumulators.
5562*5f39d1b3SJooyung Han "addv.w $w0, $w0, $w16\n"
5563*5f39d1b3SJooyung Han "addv.w $w1, $w1, $w17\n"
5564*5f39d1b3SJooyung Han "addv.w $w2, $w2, $w18\n"
5565*5f39d1b3SJooyung Han "addv.w $w3, $w3, $w19\n"
5566*5f39d1b3SJooyung Han
5567*5f39d1b3SJooyung Han GEMMLOWP_LABEL_STORE ":\n"
5568*5f39d1b3SJooyung Han // Store accumulators.
5569*5f39d1b3SJooyung Han "st.w $w0, 0(%[dst_ptr0])\n"
5570*5f39d1b3SJooyung Han "st.w $w1, 0(%[dst_ptr1])\n"
5571*5f39d1b3SJooyung Han "st.w $w2, 0(%[dst_ptr2])\n"
5572*5f39d1b3SJooyung Han "st.w $w3, 0(%[dst_ptr3])\n"
5573*5f39d1b3SJooyung Han : // outputs
5574*5f39d1b3SJooyung Han [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
5575*5f39d1b3SJooyung Han [run_depth] "+r"(run_depth)
5576*5f39d1b3SJooyung Han : // inputs
5577*5f39d1b3SJooyung Han [dst_ptr0] "r"(dst_ptr), [dst_ptr1] "r"(dst_ptr + dst_col_stride),
5578*5f39d1b3SJooyung Han [dst_ptr2] "r"(dst_ptr + dst_col_stride * 2),
5579*5f39d1b3SJooyung Han [dst_ptr3] "r"(dst_ptr + dst_col_stride * 3),
5580*5f39d1b3SJooyung Han [start_depth] "r"(start_depth)
5581*5f39d1b3SJooyung Han : // clobbers
5582*5f39d1b3SJooyung Han "memory", "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8",
5583*5f39d1b3SJooyung Han "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17",
5584*5f39d1b3SJooyung Han "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26",
5585*5f39d1b3SJooyung Han "$f27", "$f28", "$f29", "$f30", "$f31");
5586*5f39d1b3SJooyung Han #undef GEMMLOWP_LABEL_LOOP
5587*5f39d1b3SJooyung Han #undef GEMMLOWP_LABEL_AFTER_LOOP_LAST16
5588*5f39d1b3SJooyung Han #undef GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
5589*5f39d1b3SJooyung Han #undef GEMMLOWP_LABEL_STORE
5590*5f39d1b3SJooyung Han }
5591*5f39d1b3SJooyung Han };
5592*5f39d1b3SJooyung Han #endif // __mips
5593*5f39d1b3SJooyung Han
5594*5f39d1b3SJooyung Han // BEGIN code copied from gemmlowp/internal/kernel_reference.h
5595*5f39d1b3SJooyung Han
5596*5f39d1b3SJooyung Han // This kernel is templatized in an arbitrary Format template parameter,
5597*5f39d1b3SJooyung Han // allowing it to have any arbitrary format.
5598*5f39d1b3SJooyung Han template <typename tOperandType, typename tAccumulatorType, typename tFormat>
5599*5f39d1b3SJooyung Han struct ReferenceKernel {
5600*5f39d1b3SJooyung Han typedef tOperandType OperandType;
5601*5f39d1b3SJooyung Han typedef tAccumulatorType AccumulatorType;
5602*5f39d1b3SJooyung Han typedef tFormat Format;
5603*5f39d1b3SJooyung Han
RunReferenceKernel5604*5f39d1b3SJooyung Han static void Run(const OperandType* lhs_ptr, const OperandType* rhs_ptr,
5605*5f39d1b3SJooyung Han AccumulatorType* accum_ptr, int depth) {
5606*5f39d1b3SJooyung Han const int depth_cells = static_cast<int>(depth / Format::kDepth);
5607*5f39d1b3SJooyung Han
5608*5f39d1b3SJooyung Han // The outer loop is over the depth dimension.
5609*5f39d1b3SJooyung Han for (int dc = 0; dc < depth_cells; dc++) {
5610*5f39d1b3SJooyung Han // The next two loops are over cells of the Lhs (stacked vertically),
5611*5f39d1b3SJooyung Han // and over cells of the Rhs (stacked horizontally).
5612*5f39d1b3SJooyung Han for (int rc = 0; rc < Format::Lhs::kCells; rc++) {
5613*5f39d1b3SJooyung Han const OperandType* lhs_cell_ptr =
5614*5f39d1b3SJooyung Han lhs_ptr + (dc * Format::Lhs::kCells + rc) *
5615*5f39d1b3SJooyung Han Format::Lhs::Cell::kWidth * Format::kDepth;
5616*5f39d1b3SJooyung Han for (int cc = 0; cc < Format::Rhs::kCells; cc++) {
5617*5f39d1b3SJooyung Han const OperandType* rhs_cell_ptr =
5618*5f39d1b3SJooyung Han rhs_ptr + (dc * Format::Rhs::kCells + cc) *
5619*5f39d1b3SJooyung Han Format::Rhs::Cell::kWidth * Format::kDepth;
5620*5f39d1b3SJooyung Han
5621*5f39d1b3SJooyung Han // Now we are inside one cell of the Lhs and inside one cell
5622*5f39d1b3SJooyung Han // of the Rhs, so the remaining inner loops are just
5623*5f39d1b3SJooyung Han // traditional three loops of matrix multiplication.
5624*5f39d1b3SJooyung Han for (int di = 0; di < Format::kDepth; di++) {
5625*5f39d1b3SJooyung Han for (int ri = 0; ri < Format::Lhs::Cell::kWidth; ri++) {
5626*5f39d1b3SJooyung Han for (int ci = 0; ci < Format::Rhs::Cell::kWidth; ci++) {
5627*5f39d1b3SJooyung Han const OperandType* lhs_coeff_ptr =
5628*5f39d1b3SJooyung Han lhs_cell_ptr +
5629*5f39d1b3SJooyung Han OffsetIntoCell<typename Format::Lhs::Cell>(ri, di);
5630*5f39d1b3SJooyung Han const OperandType* rhs_coeff_ptr =
5631*5f39d1b3SJooyung Han rhs_cell_ptr +
5632*5f39d1b3SJooyung Han OffsetIntoCell<typename Format::Rhs::Cell>(ci, di);
5633*5f39d1b3SJooyung Han AccumulatorType* accumulator_coeff_ptr =
5634*5f39d1b3SJooyung Han accum_ptr + (ri + rc * Format::Lhs::Cell::kWidth) +
5635*5f39d1b3SJooyung Han (ci + cc * Format::Rhs::Cell::kWidth) * Format::kRows;
5636*5f39d1b3SJooyung Han *accumulator_coeff_ptr += AccumulatorType(*lhs_coeff_ptr) *
5637*5f39d1b3SJooyung Han AccumulatorType(*rhs_coeff_ptr);
5638*5f39d1b3SJooyung Han }
5639*5f39d1b3SJooyung Han }
5640*5f39d1b3SJooyung Han }
5641*5f39d1b3SJooyung Han }
5642*5f39d1b3SJooyung Han }
5643*5f39d1b3SJooyung Han }
5644*5f39d1b3SJooyung Han }
5645*5f39d1b3SJooyung Han };
5646*5f39d1b3SJooyung Han
5647*5f39d1b3SJooyung Han // END code copied from gemmlowp/internal/kernel_reference.h
5648*5f39d1b3SJooyung Han
5649*5f39d1b3SJooyung Han template <typename DataType>
5650*5f39d1b3SJooyung Han class CacheLineAlignedBuffer {
5651*5f39d1b3SJooyung Han public:
CacheLineAlignedBuffer(std::size_t size)5652*5f39d1b3SJooyung Han CacheLineAlignedBuffer(std::size_t size) : size_(size) {
5653*5f39d1b3SJooyung Han data_ = nullptr;
5654*5f39d1b3SJooyung Han // Adds a few bytes of padding here, because the 64-bit 'A57' kernel
5655*5f39d1b3SJooyung Han // reads one iteration past the end the buffer, causing a crash on iOS.
5656*5f39d1b3SJooyung Han int res = posix_memalign(reinterpret_cast<void**>(&data_), kCacheLineSize,
5657*5f39d1b3SJooyung Han size_ * sizeof(DataType) + 16);
5658*5f39d1b3SJooyung Han (void)res;
5659*5f39d1b3SJooyung Han }
5660*5f39d1b3SJooyung Han
~CacheLineAlignedBuffer()5661*5f39d1b3SJooyung Han ~CacheLineAlignedBuffer() { free(data_); }
5662*5f39d1b3SJooyung Han
data() const5663*5f39d1b3SJooyung Han const DataType* data() const { return data_; }
data()5664*5f39d1b3SJooyung Han DataType* data() { return data_; }
5665*5f39d1b3SJooyung Han
size() const5666*5f39d1b3SJooyung Han std::size_t size() const { return size_; }
5667*5f39d1b3SJooyung Han
5668*5f39d1b3SJooyung Han private:
5669*5f39d1b3SJooyung Han const std::size_t size_;
5670*5f39d1b3SJooyung Han DataType* data_;
5671*5f39d1b3SJooyung Han };
5672*5f39d1b3SJooyung Han
5673*5f39d1b3SJooyung Han template <typename DataType>
FillRandom(CacheLineAlignedBuffer<DataType> * buffer,DataType min,DataType max)5674*5f39d1b3SJooyung Han void FillRandom(CacheLineAlignedBuffer<DataType>* buffer, DataType min,
5675*5f39d1b3SJooyung Han DataType max) {
5676*5f39d1b3SJooyung Han static std::mt19937 generator(0);
5677*5f39d1b3SJooyung Han std::uniform_real_distribution<float> dist(min, max);
5678*5f39d1b3SJooyung Han for (std::size_t i = 0; i < buffer->size(); i++) {
5679*5f39d1b3SJooyung Han buffer->data()[i] = DataType(dist(generator));
5680*5f39d1b3SJooyung Han }
5681*5f39d1b3SJooyung Han }
5682*5f39d1b3SJooyung Han
5683*5f39d1b3SJooyung Han template <typename DataType>
FillZero(CacheLineAlignedBuffer<DataType> * buffer)5684*5f39d1b3SJooyung Han void FillZero(CacheLineAlignedBuffer<DataType>* buffer) {
5685*5f39d1b3SJooyung Han for (std::size_t i = 0; i < buffer->size(); i++) {
5686*5f39d1b3SJooyung Han buffer->data()[i] = DataType(0);
5687*5f39d1b3SJooyung Han }
5688*5f39d1b3SJooyung Han }
5689*5f39d1b3SJooyung Han
5690*5f39d1b3SJooyung Han template <typename DataType>
Copy(CacheLineAlignedBuffer<DataType> * dst,const CacheLineAlignedBuffer<DataType> & src)5691*5f39d1b3SJooyung Han void Copy(CacheLineAlignedBuffer<DataType>* dst,
5692*5f39d1b3SJooyung Han const CacheLineAlignedBuffer<DataType>& src) {
5693*5f39d1b3SJooyung Han assert(dst->size() == src.size());
5694*5f39d1b3SJooyung Han memcpy(dst->data(), src.data(), src.size() * sizeof(DataType));
5695*5f39d1b3SJooyung Han }
5696*5f39d1b3SJooyung Han
5697*5f39d1b3SJooyung Han template <typename DataType>
PrintMatrix(int rows,int cols,int rowstride,int colstride,const DataType * data)5698*5f39d1b3SJooyung Han void PrintMatrix(int rows, int cols, int rowstride, int colstride,
5699*5f39d1b3SJooyung Han const DataType* data) {
5700*5f39d1b3SJooyung Han for (int r = 0; r < rows; r++) {
5701*5f39d1b3SJooyung Han for (int c = 0; c < cols; c++) {
5702*5f39d1b3SJooyung Han std::cerr << double(data[r * rowstride + c * colstride]) << " ";
5703*5f39d1b3SJooyung Han }
5704*5f39d1b3SJooyung Han std::cerr << std::endl;
5705*5f39d1b3SJooyung Han }
5706*5f39d1b3SJooyung Han std::cerr << std::endl;
5707*5f39d1b3SJooyung Han }
5708*5f39d1b3SJooyung Han
5709*5f39d1b3SJooyung Han template <typename DataType>
approx_equals(DataType a,DataType b)5710*5f39d1b3SJooyung Han bool approx_equals(DataType a, DataType b) {
5711*5f39d1b3SJooyung Han return a == b;
5712*5f39d1b3SJooyung Han }
5713*5f39d1b3SJooyung Han
5714*5f39d1b3SJooyung Han template <>
approx_equals(float a,float b)5715*5f39d1b3SJooyung Han bool approx_equals(float a, float b) {
5716*5f39d1b3SJooyung Han if (!a && !b) {
5717*5f39d1b3SJooyung Han return true;
5718*5f39d1b3SJooyung Han }
5719*5f39d1b3SJooyung Han // 1e-1 is very coarse accuracy, we should switch to an overall L2 metric
5720*5f39d1b3SJooyung Han // and tighten the tolerance on that metric.
5721*5f39d1b3SJooyung Han return std::abs(a - b) < 1e-1f * std::min(std::abs(a), std::abs(b));
5722*5f39d1b3SJooyung Han }
5723*5f39d1b3SJooyung Han
5724*5f39d1b3SJooyung Han template <typename Kernel>
test_kernel(int depth,const char * kernel_name)5725*5f39d1b3SJooyung Han void test_kernel(int depth, const char* kernel_name) {
5726*5f39d1b3SJooyung Han typedef typename Kernel::OperandType OperandType;
5727*5f39d1b3SJooyung Han typedef typename Kernel::AccumulatorType AccumulatorType;
5728*5f39d1b3SJooyung Han typedef typename Kernel::Format Format;
5729*5f39d1b3SJooyung Han static const int kLhsWidth = Format::Lhs::kWidth;
5730*5f39d1b3SJooyung Han static const int kRhsWidth = Format::Rhs::kWidth;
5731*5f39d1b3SJooyung Han
5732*5f39d1b3SJooyung Han typedef ReferenceKernel<OperandType, AccumulatorType, Format> ReferenceKernel;
5733*5f39d1b3SJooyung Han
5734*5f39d1b3SJooyung Han CacheLineAlignedBuffer<OperandType> lhs(kLhsWidth * depth);
5735*5f39d1b3SJooyung Han CacheLineAlignedBuffer<OperandType> rhs(kRhsWidth * depth);
5736*5f39d1b3SJooyung Han CacheLineAlignedBuffer<AccumulatorType> accum_initial(kLhsWidth * kRhsWidth);
5737*5f39d1b3SJooyung Han CacheLineAlignedBuffer<AccumulatorType> accum(kLhsWidth * kRhsWidth);
5738*5f39d1b3SJooyung Han CacheLineAlignedBuffer<AccumulatorType> accum_reference(kLhsWidth *
5739*5f39d1b3SJooyung Han kRhsWidth);
5740*5f39d1b3SJooyung Han
5741*5f39d1b3SJooyung Han FillRandom(&lhs, KernelOperandRanges<Kernel>::LhsMin(),
5742*5f39d1b3SJooyung Han KernelOperandRanges<Kernel>::LhsMax());
5743*5f39d1b3SJooyung Han FillRandom(&rhs, KernelOperandRanges<Kernel>::RhsMin(),
5744*5f39d1b3SJooyung Han KernelOperandRanges<Kernel>::RhsMax());
5745*5f39d1b3SJooyung Han FillRandom(&accum_initial,
5746*5f39d1b3SJooyung Han std::is_signed<AccumulatorType>::value
5747*5f39d1b3SJooyung Han ? AccumulatorType(-100)
5748*5f39d1b3SJooyung Han : AccumulatorType(0),
5749*5f39d1b3SJooyung Han AccumulatorType(100));
5750*5f39d1b3SJooyung Han
5751*5f39d1b3SJooyung Han Copy(&accum, accum_initial);
5752*5f39d1b3SJooyung Han Copy(&accum_reference, accum_initial);
5753*5f39d1b3SJooyung Han
5754*5f39d1b3SJooyung Han ReferenceKernel::Run(lhs.data(), rhs.data(), accum_reference.data(), depth);
5755*5f39d1b3SJooyung Han Kernel::Run(lhs.data(), rhs.data(), accum.data(), depth);
5756*5f39d1b3SJooyung Han
5757*5f39d1b3SJooyung Han for (int l = 0; l < kLhsWidth; l++) {
5758*5f39d1b3SJooyung Han for (int r = 0; r < kRhsWidth; r++) {
5759*5f39d1b3SJooyung Han const int index = l + kLhsWidth * r;
5760*5f39d1b3SJooyung Han if (!approx_equals(accum.data()[index], accum_reference.data()[index])) {
5761*5f39d1b3SJooyung Han std::cerr << "Arithmetic error in kernel:" << std::endl
5762*5f39d1b3SJooyung Han << " " << kernel_name << std::endl
5763*5f39d1b3SJooyung Han << "Wrong accumulator for depth=" << depth << ", "
5764*5f39d1b3SJooyung Han << "at l = " << l << ", r = " << r << std::endl;
5765*5f39d1b3SJooyung Han std::cerr << "reference value: " << accum_reference.data()[index]
5766*5f39d1b3SJooyung Han << std::endl;
5767*5f39d1b3SJooyung Han std::cerr << "actual value: " << accum.data()[index] << std::endl;
5768*5f39d1b3SJooyung Han if (depth <= 16) {
5769*5f39d1b3SJooyung Han std::cerr << "LHS matrix:" << std::endl;
5770*5f39d1b3SJooyung Han PrintMatrix(kLhsWidth, depth, 1, kLhsWidth, lhs.data());
5771*5f39d1b3SJooyung Han std::cerr << "RHS matrix:" << std::endl;
5772*5f39d1b3SJooyung Han PrintMatrix(depth, kRhsWidth, kRhsWidth, 1, rhs.data());
5773*5f39d1b3SJooyung Han std::cerr << "Initial Accumulator matrix:" << std::endl;
5774*5f39d1b3SJooyung Han PrintMatrix(kLhsWidth, kRhsWidth, 1, kLhsWidth, accum_initial.data());
5775*5f39d1b3SJooyung Han std::cerr << "Reference Accumulator matrix:" << std::endl;
5776*5f39d1b3SJooyung Han PrintMatrix(kLhsWidth, kRhsWidth, 1, kLhsWidth,
5777*5f39d1b3SJooyung Han accum_reference.data());
5778*5f39d1b3SJooyung Han std::cerr << "Actual Accumulator matrix:" << std::endl;
5779*5f39d1b3SJooyung Han PrintMatrix(kLhsWidth, kRhsWidth, 1, kLhsWidth, accum.data());
5780*5f39d1b3SJooyung Han }
5781*5f39d1b3SJooyung Han abort();
5782*5f39d1b3SJooyung Han }
5783*5f39d1b3SJooyung Han }
5784*5f39d1b3SJooyung Han }
5785*5f39d1b3SJooyung Han }
5786*5f39d1b3SJooyung Han
5787*5f39d1b3SJooyung Han template <typename Kernel>
ops(int depth)5788*5f39d1b3SJooyung Han int ops(int depth) {
5789*5f39d1b3SJooyung Han // 2x the number of multiply-accumulate scalar ops.
5790*5f39d1b3SJooyung Han return 2 * Kernel::Format::Lhs::kWidth * Kernel::Format::Rhs::kWidth * depth;
5791*5f39d1b3SJooyung Han }
5792*5f39d1b3SJooyung Han
5793*5f39d1b3SJooyung Han template <unsigned Modulus, typename Integer>
RoundDown(Integer i)5794*5f39d1b3SJooyung Han Integer RoundDown(Integer i) {
5795*5f39d1b3SJooyung Han return i - (i % Modulus);
5796*5f39d1b3SJooyung Han }
5797*5f39d1b3SJooyung Han
CacheSizeInKB()5798*5f39d1b3SJooyung Han int CacheSizeInKB() {
5799*5f39d1b3SJooyung Han static const char* cache_size_k_env = getenv("CACHE_SIZE_KB");
5800*5f39d1b3SJooyung Han static const int cache_size_k =
5801*5f39d1b3SJooyung Han cache_size_k_env ? atoi(cache_size_k_env) : kDefaultCacheSizeK;
5802*5f39d1b3SJooyung Han return cache_size_k;
5803*5f39d1b3SJooyung Han }
5804*5f39d1b3SJooyung Han
5805*5f39d1b3SJooyung Han template <typename Kernel>
BenchmarkDepthToFitInCache()5806*5f39d1b3SJooyung Han int BenchmarkDepthToFitInCache() {
5807*5f39d1b3SJooyung Han const int cache_size_bytes = 1024 * CacheSizeInKB();
5808*5f39d1b3SJooyung Han
5809*5f39d1b3SJooyung Han // Subtract the typical size of a few cache lines, so
5810*5f39d1b3SJooyung Han // we don't need to worry too hard about e.g. some stack data.
5811*5f39d1b3SJooyung Han const int conservative_cache_size_bytes =
5812*5f39d1b3SJooyung Han cache_size_bytes - 2 * kCacheLineSize;
5813*5f39d1b3SJooyung Han
5814*5f39d1b3SJooyung Han // We will subtract the memory occupied by accumulators.
5815*5f39d1b3SJooyung Han typedef typename Kernel::AccumulatorType AccumulatorType;
5816*5f39d1b3SJooyung Han const int kAccumulatorBytes = sizeof(AccumulatorType) *
5817*5f39d1b3SJooyung Han Kernel::Format::Lhs::kWidth *
5818*5f39d1b3SJooyung Han Kernel::Format::Rhs::kWidth;
5819*5f39d1b3SJooyung Han
5820*5f39d1b3SJooyung Han // Compute the depth.
5821*5f39d1b3SJooyung Han typedef typename Kernel::OperandType OperandType;
5822*5f39d1b3SJooyung Han const int kBytesPerUnitOfDepth =
5823*5f39d1b3SJooyung Han sizeof(OperandType) *
5824*5f39d1b3SJooyung Han (Kernel::Format::Lhs::kWidth + Kernel::Format::Rhs::kWidth);
5825*5f39d1b3SJooyung Han const int unrounded_depth =
5826*5f39d1b3SJooyung Han (conservative_cache_size_bytes - kAccumulatorBytes) /
5827*5f39d1b3SJooyung Han kBytesPerUnitOfDepth;
5828*5f39d1b3SJooyung Han
5829*5f39d1b3SJooyung Han // Cap depth, to avoid unfairly favoring narrower kernels
5830*5f39d1b3SJooyung Han const int kMaxDepth = 1024;
5831*5f39d1b3SJooyung Han const int clamped_unrounded_depth = std::min(kMaxDepth, unrounded_depth);
5832*5f39d1b3SJooyung Han
5833*5f39d1b3SJooyung Han // Round depth down to a multiple of cache line size, which helps because
5834*5f39d1b3SJooyung Han // our kernels may crash if depth is not a multiple of the number of
5835*5f39d1b3SJooyung Han // depth level that they want to
5836*5f39d1b3SJooyung Han // handle at each loop iteration, and we don't want to require kernels
5837*5f39d1b3SJooyung Han // to be more complex. Currently all kernels process 1, 2 or 8 levels of
5838*5f39d1b3SJooyung Han // depth at a time. The main reason why that might increase in the future
5839*5f39d1b3SJooyung Han // is if registers get wider, but I don't suppose that register could
5840*5f39d1b3SJooyung Han // ever get wider than cache lines.
5841*5f39d1b3SJooyung Han return RoundDown<kCacheLineSize>(clamped_unrounded_depth);
5842*5f39d1b3SJooyung Han }
5843*5f39d1b3SJooyung Han
current_time_in_seconds()5844*5f39d1b3SJooyung Han double current_time_in_seconds() {
5845*5f39d1b3SJooyung Han timespec t;
5846*5f39d1b3SJooyung Han clock_gettime(CLOCK_REALTIME, &t);
5847*5f39d1b3SJooyung Han return t.tv_sec + 1e-9 * t.tv_nsec;
5848*5f39d1b3SJooyung Han }
5849*5f39d1b3SJooyung Han
5850*5f39d1b3SJooyung Han template <typename Kernel>
benchmark(int depth)5851*5f39d1b3SJooyung Han double benchmark(int depth) {
5852*5f39d1b3SJooyung Han // Minimum duration for this benchmark to run. If the workload finishes
5853*5f39d1b3SJooyung Han // sooner, we retry with double the number of iterations.
5854*5f39d1b3SJooyung Han static const double min_benchmark_time_in_seconds = 1.0;
5855*5f39d1b3SJooyung Han
5856*5f39d1b3SJooyung Han typedef typename Kernel::OperandType OperandType;
5857*5f39d1b3SJooyung Han typedef typename Kernel::AccumulatorType AccumulatorType;
5858*5f39d1b3SJooyung Han
5859*5f39d1b3SJooyung Han CacheLineAlignedBuffer<OperandType> lhs(Kernel::Format::Lhs::kWidth * depth);
5860*5f39d1b3SJooyung Han CacheLineAlignedBuffer<OperandType> rhs(Kernel::Format::Rhs::kWidth * depth);
5861*5f39d1b3SJooyung Han CacheLineAlignedBuffer<AccumulatorType> accum(Kernel::Format::Lhs::kWidth *
5862*5f39d1b3SJooyung Han Kernel::Format::Rhs::kWidth);
5863*5f39d1b3SJooyung Han
5864*5f39d1b3SJooyung Han for (std::uint64_t iters_at_a_time = 1;; iters_at_a_time *= 2) {
5865*5f39d1b3SJooyung Han const double t_start = current_time_in_seconds();
5866*5f39d1b3SJooyung Han for (std::uint64_t i = 0; i < iters_at_a_time; i++) {
5867*5f39d1b3SJooyung Han Kernel::Run(lhs.data(), rhs.data(), accum.data(), depth);
5868*5f39d1b3SJooyung Han }
5869*5f39d1b3SJooyung Han const double t_end = current_time_in_seconds();
5870*5f39d1b3SJooyung Han const double elapsed = t_end - t_start;
5871*5f39d1b3SJooyung Han if (elapsed > min_benchmark_time_in_seconds) {
5872*5f39d1b3SJooyung Han return iters_at_a_time * ops<Kernel>(depth) / elapsed;
5873*5f39d1b3SJooyung Han }
5874*5f39d1b3SJooyung Han }
5875*5f39d1b3SJooyung Han }
5876*5f39d1b3SJooyung Han
5877*5f39d1b3SJooyung Han template <typename Kernel>
benchmark_and_print_results(const char * kernel_name)5878*5f39d1b3SJooyung Han void benchmark_and_print_results(const char* kernel_name) {
5879*5f39d1b3SJooyung Han if (getenv("BENCHMARK_KERNEL")) {
5880*5f39d1b3SJooyung Han if (strcmp(getenv("BENCHMARK_KERNEL"), kernel_name)) {
5881*5f39d1b3SJooyung Han return;
5882*5f39d1b3SJooyung Han }
5883*5f39d1b3SJooyung Han }
5884*5f39d1b3SJooyung Han const int kKernelDepth = Kernel::Format::kDepth;
5885*5f39d1b3SJooyung Han for (int depth = kKernelDepth; depth <= 1024; depth += kKernelDepth) {
5886*5f39d1b3SJooyung Han test_kernel<Kernel>(depth, kernel_name);
5887*5f39d1b3SJooyung Han }
5888*5f39d1b3SJooyung Han
5889*5f39d1b3SJooyung Han if (getenv("BENCHMARK_ALL_DEPTHS")) {
5890*5f39d1b3SJooyung Han for (int depth = kKernelDepth;
5891*5f39d1b3SJooyung Han depth <= BenchmarkDepthToFitInCache<Kernel>(); depth *= 2) {
5892*5f39d1b3SJooyung Han std::cout << kernel_name << "," << depth << ","
5893*5f39d1b3SJooyung Han << benchmark<Kernel>(depth) * 1e-9f << std::endl;
5894*5f39d1b3SJooyung Han }
5895*5f39d1b3SJooyung Han } else {
5896*5f39d1b3SJooyung Han const int depth = BenchmarkDepthToFitInCache<Kernel>();
5897*5f39d1b3SJooyung Han std::cout << kernel_name << "," << benchmark<Kernel>(depth) * 1e-9f
5898*5f39d1b3SJooyung Han << std::endl;
5899*5f39d1b3SJooyung Han }
5900*5f39d1b3SJooyung Han }
5901*5f39d1b3SJooyung Han
5902*5f39d1b3SJooyung Han #define BENCHMARK(Kernel) \
5903*5f39d1b3SJooyung Han do { \
5904*5f39d1b3SJooyung Han benchmark_and_print_results<Kernel>(#Kernel); \
5905*5f39d1b3SJooyung Han } while (false)
5906*5f39d1b3SJooyung Han
main()5907*5f39d1b3SJooyung Han int main() {
5908*5f39d1b3SJooyung Han if (getenv("BENCHMARK_ALL_DEPTHS")) {
5909*5f39d1b3SJooyung Han std::cout << "kernel,depth,Gop/s" << std::endl;
5910*5f39d1b3SJooyung Han } else {
5911*5f39d1b3SJooyung Han std::cout << "kernel,Gop/s" << std::endl;
5912*5f39d1b3SJooyung Han }
5913*5f39d1b3SJooyung Han
5914*5f39d1b3SJooyung Han #ifdef __arm__
5915*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Int8Operands_AccumTwoWithin16Bits);
5916*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics);
5917*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators);
5918*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_intrinsics);
5919*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand);
5920*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Int32_WithScalar);
5921*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_MLA_WithVectorDuplicatingScalar);
5922*5f39d1b3SJooyung Han #ifdef __ARM_FEATURE_FMA
5923*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_FMA_WithVectorDuplicatingScalar);
5924*5f39d1b3SJooyung Han #endif
5925*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_MLA_WithScalar);
5926*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_WithScalar_intrinsics);
5927*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_WithScalar_A53);
5928*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_WithScalar_A53_depth2);
5929*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_MLA_Rotating);
5930*5f39d1b3SJooyung Han #ifdef __ARM_FEATURE_FMA
5931*5f39d1b3SJooyung Han BENCHMARK(NEON_32bit_GEMM_Float32_FMA_Rotating);
5932*5f39d1b3SJooyung Han #endif
5933*5f39d1b3SJooyung Han #endif
5934*5f39d1b3SJooyung Han
5935*5f39d1b3SJooyung Han #ifdef __aarch64__
5936*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int425Operands);
5937*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int425Operands_intrinsics);
5938*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits);
5939*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int7Operands_AccumEightWithin16Bits_intrinsics);
5940*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int8Operands_AccumTwoWithin16Bits);
5941*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int8Operands_AccumTwoWithin16Bits_intrinsics);
5942*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators);
5943*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_intrinsics);
5944*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_noexpand_A57);
5945*5f39d1b3SJooyung Han #ifdef __ARM_FEATURE_DOTPROD
5946*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct);
5947*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_A55r1);
5948*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Uint8Operands_Uint32Accumulators_dotproduct_narrow);
5949*5f39d1b3SJooyung Han #endif
5950*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Int32_WithScalar);
5951*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithVectorDuplicatingScalar);
5952*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithScalar);
5953*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithScalar_intrinsics);
5954*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithScalar_A57);
5955*5f39d1b3SJooyung Han #ifndef __APPLE__
5956*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithScalar_A53);
5957*5f39d1b3SJooyung Han #endif
5958*5f39d1b3SJooyung Han BENCHMARK(NEON_64bit_GEMM_Float32_WithScalar_A55r1);
5959*5f39d1b3SJooyung Han #endif
5960*5f39d1b3SJooyung Han
5961*5f39d1b3SJooyung Han #ifdef __mips
5962*5f39d1b3SJooyung Han BENCHMARK(MSA_GEMM_12x8_Uint8Operands_Uint32Accumulators1);
5963*5f39d1b3SJooyung Han BENCHMARK(MSA_GEMM_12x8_Uint8Operands_Uint32Accumulators2);
5964*5f39d1b3SJooyung Han BENCHMARK(MSA_GEMM_Int8Operands_AccumTwoWithin16Bits);
5965*5f39d1b3SJooyung Han #endif
5966*5f39d1b3SJooyung Han
5967*5f39d1b3SJooyung Han return 0;
5968*5f39d1b3SJooyung Han }
5969