diff options
author | Frank Barchard <fbarchard@google.com> | 2022-05-17 10:40:45 -0700 |
---|---|---|
committer | XNNPACK Team <xnnpack-github-robot@google.com> | 2022-05-17 10:41:53 -0700 |
commit | ed4e2032a4c3c98ad21d4e27c5a98879385ffd65 (patch) | |
tree | fcde32931e047ec84bcf70d12ebc98729ce90a1f | |
parent | ddeeed7df85a0cd0297cea6901590b2ac1695404 (diff) | |
download | XNNPACK-ed4e2032a4c3c98ad21d4e27c5a98879385ffd65.tar.gz |
4X8 IGEMM for Cortex A7/32/A35
- Adapted from A53 IGEMM but with NEON loads.
PiperOrigin-RevId: 449258615
-rw-r--r-- | BUILD.bazel | 8 | ||||
-rwxr-xr-x | CMakeLists.txt | 8 | ||||
-rw-r--r-- | bench/qs8-gemm-e2e.cc | 12 | ||||
-rw-r--r-- | bench/qu8-gemm-e2e.cc | 8 | ||||
-rwxr-xr-x | scripts/generate-qs8-igemm.sh | 10 | ||||
-rw-r--r-- | src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S | 581 | ||||
-rw-r--r-- | src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S | 588 | ||||
-rw-r--r-- | src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S | 572 | ||||
-rw-r--r-- | src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S | 579 | ||||
-rw-r--r-- | src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in | 784 | ||||
-rw-r--r-- | src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S | 577 | ||||
-rw-r--r-- | src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S | 584 | ||||
-rw-r--r-- | src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S | 578 | ||||
-rw-r--r-- | src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S | 585 | ||||
-rw-r--r-- | src/xnnpack/igemm.h | 8 |
15 files changed, 5472 insertions, 10 deletions
diff --git a/BUILD.bazel b/BUILD.bazel index b94809a3f..29b9982ca 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -7878,6 +7878,14 @@ AARCH32_ASM_MICROKERNEL_SRCS = [ "src/f32-gemm/4x4-aarch32-vfp-ld64.S", "src/f32-gemm/4x4-minmax-aarch32-vfp-ld64.S", "src/f32-gemm/4x8-minmax-aarch32-neon-cortex-a55.S", + "src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S", + "src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S", + "src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S", + "src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S", + "src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S", + "src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S", + "src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S", + "src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S", "src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a7.S", "src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a53.S", "src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a75.S", diff --git a/CMakeLists.txt b/CMakeLists.txt index e14fd789a..269316849 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6392,6 +6392,14 @@ SET(AARCH32_ASM_MICROKERNEL_SRCS src/f32-gemm/4x4-aarch32-vfp-ld64.S src/f32-gemm/4x4-minmax-aarch32-vfp-ld64.S src/f32-gemm/4x8-minmax-aarch32-neon-cortex-a55.S + src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S + src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S + src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S + src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S + src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S + src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S + src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S + src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a7.S src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a53.S src/f32-igemm/gen/4x8-minmax-aarch32-neon-cortex-a75.S diff --git a/bench/qs8-gemm-e2e.cc b/bench/qs8-gemm-e2e.cc index d419d251f..e45767501 100644 --- a/bench/qs8-gemm-e2e.cc +++ b/bench/qs8-gemm-e2e.cc @@ -102,7 +102,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53, xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53, - xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, @@ -112,7 +112,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53, xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53, - xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, @@ -121,7 +121,7 @@ static void GEMMEnd2EndBenchmark( static void qs8_gemm_4x8__aarch32_neon_mlal_lane_cortex_a7(benchmark::State& state, models::ExecutionPlanFactory model) { GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7, - xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64, + xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, @@ -131,7 +131,7 @@ static void GEMMEnd2EndBenchmark( static void qs8_gemm_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7(benchmark::State& state, models::ExecutionPlanFactory model) { GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7, - xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64, + xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, @@ -142,7 +142,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64, xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64, - xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, @@ -152,7 +152,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qs8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64, xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64, - xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qs8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qs8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qs8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, diff --git a/bench/qu8-gemm-e2e.cc b/bench/qu8-gemm-e2e.cc index 91f0005ab..838b75816 100644 --- a/bench/qu8-gemm-e2e.cc +++ b/bench/qu8-gemm-e2e.cc @@ -82,7 +82,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qu8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53, xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53, - xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qu8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qu8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, @@ -92,7 +92,7 @@ static void GEMMEnd2EndBenchmark( GEMMEnd2EndBenchmark(state, model, xnn_qu8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53, xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53, - xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, + xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qu8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qu8_conv_minmax_rndnu_neon_params, 4 /* mr */, 8 /* nr */, 0 /* log2_kr */, 0 /* log2_sr */, @@ -101,7 +101,7 @@ static void GEMMEnd2EndBenchmark( static void qu8_gemm_4x8__aarch32_neon_mlal_lane_cortex_a7(benchmark::State& state, models::ExecutionPlanFactory model) { GEMMEnd2EndBenchmark(state, model, xnn_qu8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7, - xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64, + xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_cortex_a7, xnn_qu8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qu8_conv_minmax_rndnu_neon_params, @@ -111,7 +111,7 @@ static void GEMMEnd2EndBenchmark( static void qu8_gemm_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7(benchmark::State& state, models::ExecutionPlanFactory model) { GEMMEnd2EndBenchmark(state, model, xnn_qu8_gemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7, - xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64, + xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qu8_gemm_minmax_rndnu_ukernel_1x8__aarch32_neon_mlal_lane_prfm_cortex_a7, xnn_qu8_igemm_minmax_rndnu_ukernel_1x8__neon_mlal_lane, xnn_init_qu8_conv_minmax_rndnu_neon_params, diff --git a/scripts/generate-qs8-igemm.sh b/scripts/generate-qs8-igemm.sh index 05a82eff8..20f8d3593 100755 --- a/scripts/generate-qs8-igemm.sh +++ b/scripts/generate-qs8-igemm.sh @@ -701,6 +701,13 @@ tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in -D PREFETC tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in -D PREFETCH=0 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=1 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a53.S & tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in -D PREFETCH=1 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=1 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a53.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=0 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QS8 -D ARMV8=0 -o src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=1 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QS8 -D ARMV8=0 -o src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=0 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=0 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=1 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=0 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=0 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=1 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=1 -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -D ARMV8=1 -o src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S & + ### QU8 micro-kernels tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-ld64.S.in -D PREFETCH=0 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-ld64.S & tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-ld64.S.in -D PREFETCH=1 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-ld64.S & @@ -708,6 +715,9 @@ tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-ld64.S.in -D PREFETC tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in -D PREFETCH=0 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a53.S & tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in -D PREFETCH=1 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a53.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=0 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S & +tools/xngen src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in -D PREFETCH=1 -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QU8 -D ARMV8=0 -o src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S & + ### C4 micro-kernels tools/xngen src/qs8-igemm/4x8c4-aarch32-neondot-ld64.S.in -D REQUANTIZATION=RNDNU -D CHANNELWISE=0 -D DATATYPE=QS8 -o src/qs8-igemm/gen/4x8c4-minmax-rndnu-aarch32-neondot-ld64.S & tools/xngen src/qs8-igemm/4x8c4-aarch32-neondot-ld64.S.in -D REQUANTIZATION=FP32 -D CHANNELWISE=1 -D DATATYPE=QC8 -o src/qc8-igemm/gen/4x8c4-minmax-fp32-aarch32-neondot-ld64.S & diff --git a/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S new file mode 100644 index 000000000..177bbb739 --- /dev/null +++ b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-cortex-a7.S @@ -0,0 +1,581 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 10 bytes +// struct { +// float magic_bias; d12[0] +// int32_t magic_bias_less_output_zero_point; d12[1] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } xnn_qs8_minmax_params.neon; + +BEGIN_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLDM r5!, {d12} // QC8 neon params + VLD1.16 {d13[]}, [r5] + + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VDUP.32 q2, d12[0] // magic_bias + VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VADD.F32 q8, q8, q2 // magic_bias + VADD.F32 q9, q9, q2 + VADD.F32 q10, q10, q2 + VADD.F32 q11, q11, q2 + VADD.F32 q12, q12, q2 + VADD.F32 q13, q13, q2 + VADD.F32 q14, q14, q2 + VADD.F32 q15, q15, q2 + + VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point + VQSUB.S32 q9, q9, q3 + VQSUB.S32 q10, q10, q3 + VQSUB.S32 q11, q11, q3 + VQSUB.S32 q12, q12, q3 + VQSUB.S32 q13, q13, q3 + VQSUB.S32 q14, q14, q3 + VQSUB.S32 q15, q15, q3 + + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S new file mode 100644 index 000000000..57e8db33c --- /dev/null +++ b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neon-mlal-lane-prfm-cortex-a7.S @@ -0,0 +1,588 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 10 bytes +// struct { +// float magic_bias; d12[0] +// int32_t magic_bias_less_output_zero_point; d12[1] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } xnn_qs8_minmax_params.neon; + +BEGIN_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLDM r5!, {d12} // QC8 neon params + VLD1.16 {d13[]}, [r5] + + PLD [r9, 64] // Prefetch B + PLD [r9, 128] + PLD [r9, 192] + PLD [r9, 256] + PLD [r9, 320] + PLD [r9, 384] + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + PLD [r9, 448] + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VDUP.32 q2, d12[0] // magic_bias + VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VADD.F32 q8, q8, q2 // magic_bias + VADD.F32 q9, q9, q2 + VADD.F32 q10, q10, q2 + VADD.F32 q11, q11, q2 + VADD.F32 q12, q12, q2 + VADD.F32 q13, q13, q2 + VADD.F32 q14, q14, q2 + VADD.F32 q15, q15, q2 + + VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point + VQSUB.S32 q9, q9, q3 + VQSUB.S32 q10, q10, q3 + VQSUB.S32 q11, q11, q3 + VQSUB.S32 q12, q12, q3 + VQSUB.S32 q13, q13, q3 + VQSUB.S32 q14, q14, q3 + VQSUB.S32 q15, q15, q3 + + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S new file mode 100644 index 000000000..9fa909c24 --- /dev/null +++ b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-cortex-a35.S @@ -0,0 +1,572 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_cortex_a35( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 4 bytes +// struct { +// int16_t output_zero_point; d13[2] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } xnn_qs8_minmax_params.neonv8; + +BEGIN_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_cortex_a35 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLD1.32 {d13[]}, [r5] // QC8 neonv8 params + + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VCVTN.S32.F32 q8, q8 + VCVTN.S32.F32 q9, q9 + VCVTN.S32.F32 q10, q10 + VCVTN.S32.F32 q11, q11 + VCVTN.S32.F32 q12, q12 + VCVTN.S32.F32 q13, q13 + VCVTN.S32.F32 q14, q14 + VCVTN.S32.F32 q15, q15 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_cortex_a35 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S new file mode 100644 index 000000000..21c5cbbe2 --- /dev/null +++ b/src/qc8-igemm/gen/4x8-minmax-fp32-aarch32-neonv8-mlal-lane-prfm-cortex-a35.S @@ -0,0 +1,579 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a35( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 4 bytes +// struct { +// int16_t output_zero_point; d13[2] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } xnn_qs8_minmax_params.neonv8; + +BEGIN_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a35 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLD1.32 {d13[]}, [r5] // QC8 neonv8 params + + PLD [r9, 64] // Prefetch B + PLD [r9, 128] + PLD [r9, 192] + PLD [r9, 256] + PLD [r9, 320] + PLD [r9, 384] + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + PLD [r9, 448] + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VCVTN.S32.F32 q8, q8 + VCVTN.S32.F32 q9, q9 + VCVTN.S32.F32 q10, q10 + VCVTN.S32.F32 q11, q11 + VCVTN.S32.F32 q12, q12 + VCVTN.S32.F32 q13, q13 + VCVTN.S32.F32 q14, q14 + VCVTN.S32.F32 q15, q15 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a35 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in b/src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in new file mode 100644 index 000000000..5d2bc52c5 --- /dev/null +++ b/src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in @@ -0,0 +1,784 @@ +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +$assert REQUANTIZATION in ["FP32", "RNDNU"] +$assert not CHANNELWISE or REQUANTIZATION == "FP32" +$assert DATATYPE in ["QC8", "QS8", "QU8"] +$assert DATATYPE != "QC8" or REQUANTIZATION == "FP32" + +#include <xnnpack/assembly.h> + +.syntax unified + +$PARAMS_UNION = "xnn_qs8_minmax_params" if CHANNELWISE else "xnn_qs8_conv_minmax_params" +$ISA = "neonv8" if ARMV8 else "neon" +$CPU = "a35" if ARMV8 else "a7" +$XMIN = "VMIN.U8" if DATATYPE == "QU8" else "VMIN.S8" +$XMAX = "VMAX.U8" if DATATYPE == "QU8" else "VMAX.S8" +$XXTL = "VMOVL.U8" if DATATYPE == "QU8" else "VMOVL.S8" +$SQXTXN = "VQMOVUN.S16" if DATATYPE == "QU8" else "VQMOVN.S16" +$XINT8_T = "uint8_t" if DATATYPE == "QU8" else "int8_t" +// void xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU}( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const ${XINT8_T}**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// ${XINT8_T}*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const ${XINT8_T}* zero, sp + 128 -> (r7) +// ${PARAMS_UNION}*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +$if REQUANTIZATION == "RNDNU" and DATATYPE != "QU8": + // params structure is 16 bytes + // struct { + // int32_t right_pre_shift; d12[0] + // int32_t multiplier; d12[1] + // int32_t right_post_shift; d13[0] + // int16_t output_zero_point; d13[2] + // int8_t output_min; d13[6] + // int8_t output_max; d13[7] + // } rndnu_neon; +$elif REQUANTIZATION == "RNDNU" and DATATYPE == "QU8": + // params structure is 20 bytes + // struct { + // uint8_t kernel_zero_point[4]; d14 + // int32_t right_pre_shift; d12[0] + // int32_t multiplier; d12[1] + // int32_t right_post_shift; d13[0] + // int16_t output_zero_point; d13[2] + // uint8_t output_min; d13[6] + // uint8_t output_max; d13[7] + // } rndnu_neon; +$elif DATATYPE == "QC8" and not ARMV8: + // params structure is 10 bytes + // struct { + // float magic_bias; d12[0] + // int32_t magic_bias_less_output_zero_point; d12[1] + // int8_t output_min; d13[6] + // int8_t output_max; d13[7] + // } xnn_qs8_minmax_params.neon; +$else: + // params structure is 4 bytes + // struct { + // int16_t output_zero_point; d13[2] + // int8_t output_min; d13[6] + // int8_t output_max; d13[7] + // } xnn_qs8_minmax_params.neonv8; + +BEGIN_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU} + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + $if DATATYPE == "QU8": + VPUSH {d8-d14} // +56 = 104 + $else: + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + $if DATATYPE == "QU8": + VLD1.32 {d14[]}, [r5]! // QU8 kernel_zero_point + $if REQUANTIZATION == "RNDNU": + VLDM r5, {d12-d13} // RNDNU params + $elif DATATYPE == "QC8" and ARMV8: + VLD1.32 {d13[]}, [r5] // QC8 neonv8 params + $elif DATATYPE == "QC8" and not ARMV8: + VLDM r5!, {d12} // QC8 neon params + VLD1.16 {d13[]}, [r5] + + $if PREFETCH: + PLD [r9, 64] // Prefetch B + PLD [r9, 128] + PLD [r9, 192] + PLD [r9, 256] + PLD [r9, 320] + PLD [r9, 384] + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + ${XXTL} q0, d0 + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + $if PREFETCH: + PLD [r9, 448] + ${XXTL} q1, d2 + ${XXTL} q2, d4 + ${XXTL} q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + ${XXTL} q0, d0 + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + ${XXTL} q1, d2 + ${XXTL} q2, d4 + ${XXTL} q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + $if DATATYPE == "QU8": + VSUBL.U8 q5, d10, d14 + $else: + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + $if REQUANTIZATION == "RNDNU": + # RNDNU quantization + VDUP.32 q0, d12[0] // right_pre_shift + + VQSHL.S32 q8, q8, q0 + VQSHL.S32 q9, q9, q0 + VQSHL.S32 q10, q10, q0 + VQSHL.S32 q11, q11, q0 + VQSHL.S32 q12, q12, q0 + VQSHL.S32 q13, q13, q0 + VQSHL.S32 q14, q14, q0 + VQSHL.S32 q15, q15, q0 + + VDUP.32 q2, d13[0] // right_post_shift + + VQDMULH.S32 q8, q8, d12[1] // multiplier + VQDMULH.S32 q9, q9, d12[1] + VQDMULH.S32 q10, q10, d12[1] + VQDMULH.S32 q11, q11, d12[1] + VQDMULH.S32 q12, q12, d12[1] + VQDMULH.S32 q13, q13, d12[1] + VQDMULH.S32 q14, q14, d12[1] + VQDMULH.S32 q15, q15, d12[1] + + VRSHL.S32 q8, q8, q2 + VRSHL.S32 q9, q9, q2 + VRSHL.S32 q10, q10, q2 + VRSHL.S32 q11, q11, q2 + VRSHL.S32 q12, q12, q2 + VRSHL.S32 q13, q13, q2 + VRSHL.S32 q14, q14, q2 + VRSHL.S32 q15, q15, q2 + $elif DATATYPE == "QC8" and ARMV8: + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VCVTN.S32.F32 q8, q8 + VCVTN.S32.F32 q9, q9 + VCVTN.S32.F32 q10, q10 + VCVTN.S32.F32 q11, q11 + VCVTN.S32.F32 q12, q12 + VCVTN.S32.F32 q13, q13 + VCVTN.S32.F32 q14, q14 + VCVTN.S32.F32 q15, q15 + $elif DATATYPE == "QC8" and not ARMV8: + # QC8 FP32 quantization + VLD1.8 {q0-q1}, [r9]! + + VDUP.32 q2, d12[0] // magic_bias + VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point + + VCVT.F32.S32 q8, q8 + VCVT.F32.S32 q9, q9 + VCVT.F32.S32 q10, q10 + VCVT.F32.S32 q11, q11 + VCVT.F32.S32 q12, q12 + VCVT.F32.S32 q13, q13 + VCVT.F32.S32 q14, q14 + VCVT.F32.S32 q15, q15 + + VMUL.F32 q8, q8, q0 // multiplier + VMUL.F32 q9, q9, q1 + VMUL.F32 q10, q10, q0 + VMUL.F32 q11, q11, q1 + VMUL.F32 q12, q12, q0 + VMUL.F32 q13, q13, q1 + VMUL.F32 q14, q14, q0 + VMUL.F32 q15, q15, q1 + + VADD.F32 q8, q8, q2 // magic_bias + VADD.F32 q9, q9, q2 + VADD.F32 q10, q10, q2 + VADD.F32 q11, q11, q2 + VADD.F32 q12, q12, q2 + VADD.F32 q13, q13, q2 + VADD.F32 q14, q14, q2 + VADD.F32 q15, q15, q2 + + VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point + VQSUB.S32 q9, q9, q3 + VQSUB.S32 q10, q10, q3 + VQSUB.S32 q11, q11, q3 + VQSUB.S32 q12, q12, q3 + VQSUB.S32 q13, q13, q3 + VQSUB.S32 q14, q14, q3 + VQSUB.S32 q15, q15, q3 + + $if DATATYPE != "QC8" or ARMV8: + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + $if DATATYPE != "QC8" or ARMV8: + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + ${SQXTXN} d0, q8 + ${SQXTXN} d1, q9 + ${SQXTXN} d2, q10 + ${SQXTXN} d3, q11 + + VDUP.8 q13, d13[7] // output_max + + ${XMAX} q0, q0, q12 + ${XMAX} q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + ${XMIN} q0, q0, q13 + ${XMIN} q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + $if DATATYPE == "QU8": + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + $else: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + ${XXTL} q0, d0 + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + ${XXTL} q1, d2 + ${XXTL} q2, d4 + ${XXTL} q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + $if DATATYPE == "QU8": + VSUBL.U8 q4, d8, d14 + $else: + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + $if DATATYPE == "QU8": + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + $else: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU} + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S b/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S new file mode 100644 index 000000000..77c0e8c4b --- /dev/null +++ b/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S @@ -0,0 +1,577 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_conv_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 16 bytes +// struct { +// int32_t right_pre_shift; d12[0] +// int32_t multiplier; d12[1] +// int32_t right_post_shift; d13[0] +// int16_t output_zero_point; d13[2] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } rndnu_neon; + +BEGIN_FUNCTION xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLDM r5, {d12-d13} // RNDNU params + + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # RNDNU quantization + VDUP.32 q0, d12[0] // right_pre_shift + + VQSHL.S32 q8, q8, q0 + VQSHL.S32 q9, q9, q0 + VQSHL.S32 q10, q10, q0 + VQSHL.S32 q11, q11, q0 + VQSHL.S32 q12, q12, q0 + VQSHL.S32 q13, q13, q0 + VQSHL.S32 q14, q14, q0 + VQSHL.S32 q15, q15, q0 + + VDUP.32 q2, d13[0] // right_post_shift + + VQDMULH.S32 q8, q8, d12[1] // multiplier + VQDMULH.S32 q9, q9, d12[1] + VQDMULH.S32 q10, q10, d12[1] + VQDMULH.S32 q11, q11, d12[1] + VQDMULH.S32 q12, q12, d12[1] + VQDMULH.S32 q13, q13, d12[1] + VQDMULH.S32 q14, q14, d12[1] + VQDMULH.S32 q15, q15, d12[1] + + VRSHL.S32 q8, q8, q2 + VRSHL.S32 q9, q9, q2 + VRSHL.S32 q10, q10, q2 + VRSHL.S32 q11, q11, q2 + VRSHL.S32 q12, q12, q2 + VRSHL.S32 q13, q13, q2 + VRSHL.S32 q14, q14, q2 + VRSHL.S32 q15, q15, q2 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S b/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S new file mode 100644 index 000000000..f5eb16979 --- /dev/null +++ b/src/qs8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S @@ -0,0 +1,584 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const int8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// int8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const int8_t* zero, sp + 128 -> (r7) +// xnn_qs8_conv_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 16 bytes +// struct { +// int32_t right_pre_shift; d12[0] +// int32_t multiplier; d12[1] +// int32_t right_post_shift; d13[0] +// int16_t output_zero_point; d13[2] +// int8_t output_min; d13[6] +// int8_t output_max; d13[7] +// } rndnu_neon; + +BEGIN_FUNCTION xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + SUB sp, sp, 8 // +8 + VPUSH {d8-d13} // +48 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLDM r5, {d12-d13} // RNDNU params + + PLD [r9, 64] // Prefetch B + PLD [r9, 128] + PLD [r9, 192] + PLD [r9, 256] + PLD [r9, 320] + PLD [r9, 384] + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + PLD [r9, 448] + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VMOVL.S8 q4, d8 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMOVL.S8 q5, d10 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # RNDNU quantization + VDUP.32 q0, d12[0] // right_pre_shift + + VQSHL.S32 q8, q8, q0 + VQSHL.S32 q9, q9, q0 + VQSHL.S32 q10, q10, q0 + VQSHL.S32 q11, q11, q0 + VQSHL.S32 q12, q12, q0 + VQSHL.S32 q13, q13, q0 + VQSHL.S32 q14, q14, q0 + VQSHL.S32 q15, q15, q0 + + VDUP.32 q2, d13[0] // right_post_shift + + VQDMULH.S32 q8, q8, d12[1] // multiplier + VQDMULH.S32 q9, q9, d12[1] + VQDMULH.S32 q10, q10, d12[1] + VQDMULH.S32 q11, q11, d12[1] + VQDMULH.S32 q12, q12, d12[1] + VQDMULH.S32 q13, q13, d12[1] + VQDMULH.S32 q14, q14, d12[1] + VQDMULH.S32 q15, q15, d12[1] + + VRSHL.S32 q8, q8, q2 + VRSHL.S32 q9, q9, q2 + VRSHL.S32 q10, q10, q2 + VRSHL.S32 q11, q11, q2 + VRSHL.S32 q12, q12, q2 + VRSHL.S32 q13, q13, q2 + VRSHL.S32 q14, q14, q2 + VRSHL.S32 q15, q15, q2 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVN.S16 d0, q8 + VQMOVN.S16 d1, q9 + VQMOVN.S16 d2, q10 + VQMOVN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.S8 q0, q0, q12 + VMAX.S8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.S8 q0, q0, q13 + VMIN.S8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.S8 q0, d0 + VMOVL.S8 q4, d8 + VMOVL.S8 q1, d2 + VMOVL.S8 q2, d4 + VMOVL.S8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VMOVL.S8 q4, d8 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d13} + ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S b/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S new file mode 100644 index 000000000..aa3322456 --- /dev/null +++ b/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-cortex-a7.S @@ -0,0 +1,578 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const uint8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// uint8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const uint8_t* zero, sp + 128 -> (r7) +// xnn_qs8_conv_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 20 bytes +// struct { +// uint8_t kernel_zero_point[4]; d14 +// int32_t right_pre_shift; d12[0] +// int32_t multiplier; d12[1] +// int32_t right_post_shift; d13[0] +// int16_t output_zero_point; d13[2] +// uint8_t output_min; d13[6] +// uint8_t output_max; d13[7] +// } rndnu_neon; + +BEGIN_FUNCTION xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + VPUSH {d8-d14} // +56 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLD1.32 {d14[]}, [r5]! // QU8 kernel_zero_point + VLDM r5, {d12-d13} // RNDNU params + + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # RNDNU quantization + VDUP.32 q0, d12[0] // right_pre_shift + + VQSHL.S32 q8, q8, q0 + VQSHL.S32 q9, q9, q0 + VQSHL.S32 q10, q10, q0 + VQSHL.S32 q11, q11, q0 + VQSHL.S32 q12, q12, q0 + VQSHL.S32 q13, q13, q0 + VQSHL.S32 q14, q14, q0 + VQSHL.S32 q15, q15, q0 + + VDUP.32 q2, d13[0] // right_post_shift + + VQDMULH.S32 q8, q8, d12[1] // multiplier + VQDMULH.S32 q9, q9, d12[1] + VQDMULH.S32 q10, q10, d12[1] + VQDMULH.S32 q11, q11, d12[1] + VQDMULH.S32 q12, q12, d12[1] + VQDMULH.S32 q13, q13, d12[1] + VQDMULH.S32 q14, q14, d12[1] + VQDMULH.S32 q15, q15, d12[1] + + VRSHL.S32 q8, q8, q2 + VRSHL.S32 q9, q9, q2 + VRSHL.S32 q10, q10, q2 + VRSHL.S32 q11, q11, q2 + VRSHL.S32 q12, q12, q2 + VRSHL.S32 q13, q13, q2 + VRSHL.S32 q14, q14, q2 + VRSHL.S32 q15, q15, q2 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVUN.S16 d0, q8 + VQMOVUN.S16 d1, q9 + VQMOVUN.S16 d2, q10 + VQMOVUN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.U8 q0, q0, q12 + VMAX.U8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.U8 q0, q0, q13 + VMIN.U8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S b/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S new file mode 100644 index 000000000..9e354fa62 --- /dev/null +++ b/src/qu8-igemm/gen/4x8-minmax-rndnu-aarch32-neon-mlal-lane-prfm-cortex-a7.S @@ -0,0 +1,585 @@ +// Auto-generated file. Do not edit! +// Template: src/qs8-igemm/4x8-aarch32-neon-mlal-lane-cortex-a7.S.in +// Generator: tools/xngen +// +// Copyright 2021 Google LLC +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + + +#include <xnnpack/assembly.h> + +.syntax unified + +// void xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7( +// size_t mr, (r0) +// size_t nc, r1 -> sp + 56 +// size_t kc, (r2) -> r5 -> sp + 60 +// size_t ks, (r3) -> sp + 64 -> r14 +// const uint8_t**restrict a, sp + 104 -> r2 +// const void*restrict w, sp + 108 -> r9 +// uint8_t*restrict c, sp + 112 -> r11 +// size_t cm_stride, sp + 116 -> (r6) +// size_t cn_stride, sp + 120 -> (r7) +// size_t a_offset, sp + 124 -> (r5) +// const uint8_t* zero, sp + 128 -> (r7) +// xnn_qs8_conv_minmax_params*params); sp + 132 -> (r5) + +// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. + +// Register usage +// A0 r3 d0-d1 q0 +// A1 r12 d2-d3 q1 +// A2 r10 d4-d5 q2 +// A3 r0 d6-d7 q3 + +// B r9 d8-d9 q4 q5 + +// C0 r11 d16-d17 q8 d18-d19 q9 +// C1 r4 d20-d21 q10 d22-d23 q11 +// C2 r8 d24-d25 q12 d26-d27 q13 +// C3 r6 d28-d29 q14 d30-d31 q15 + +// Unused d15 + +// params structure is 20 bytes +// struct { +// uint8_t kernel_zero_point[4]; d14 +// int32_t right_pre_shift; d12[0] +// int32_t multiplier; d12[1] +// int32_t right_post_shift; d13[0] +// int16_t output_zero_point; d13[2] +// uint8_t output_min; d13[6] +// uint8_t output_max; d13[7] +// } rndnu_neon; + +BEGIN_FUNCTION xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + # Push 104 bytes + # r1, r2 will be reloaded in outer loop. r3 is ks + PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 + VPUSH {d8-d14} // +56 = 104 + + LDR r11, [sp, 112] // c + LDR r6, [sp, 116] // cm_stride + LDR r2, [sp, 104] // a + LDR r9, [sp, 108] // w + LDR r5, [sp, 132] // params + MOV r14, r3 // p = ks + + # Clamp C pointers + CMP r0, 2 // if mr >= 2 + ADD r4, r11, r6 // c1 = c0 + cm_stride + MOVLO r4, r11 // c1 + // if mr > 2 + ADD r8, r4, r6 // c2 = c1 + cm_stride + MOVLS r8, r4 // c2 + CMP r0, 4 // if mr >=4 + ADD r6, r8, r6 // c3 = c2 + cm_stride + MOVLO r6, r8 // c3 + + # Load params values + VLD1.32 {d14[]}, [r5]! // QU8 kernel_zero_point + VLDM r5, {d12-d13} // RNDNU params + + PLD [r9, 64] // Prefetch B + PLD [r9, 128] + PLD [r9, 192] + PLD [r9, 256] + PLD [r9, 320] + PLD [r9, 384] + + .p2align 3 +0: + # Load initial bias from w into accumulators + VLDM r9!, {d16-d19} // Bias + VMOV q10, q8 + VMOV q11, q9 + STR r1, [sp, 56] // save nc + VMOV q12, q8 + VMOV q13, q9 + VMOV q14, q8 + VMOV q15, q9 + + .p2align 3 +1: + # Load next 4 A pointers + LDR r3, [r2, 0] + LDR r12, [r2, 4] + LDR r10, [r2, 8] + LDR r0, [r2, 12] + + # Add a_offset + LDR r5, [sp, 124] // a_offset + LDR r7, [sp, 128] // zero + ADD r2, r2, 16 + CMP r3, r7 // if a0 == zero + ADD r3, r3, r5 // a0 += a_offset + MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset + CMP r12, r7 // if a1 == zero + ADD r12, r12, r5 // a1 += a_offset + MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset + CMP r10, r7 // if a2 == zero + ADD r10, r10, r5 // a2 += a_offset + MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset + CMP r0, r7 // if a3 == zero + ADD r0, r0, r5 // a3 += a_offset + LDR r5, [sp, 60] // kc + MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset + SUBS r5, r5, 8 // kc - 8 + BLO 5f // less than 8 channels? + + // Prologue - load 4A's and B0 + VLD1.8 {d0}, [r3]! // A0 + VLD1.8 {d2}, [r12]! // A1 + VLD1.8 {d4}, [r10]! // A2 + VLD1.8 {d6}, [r0]! // A3 + VLD1.8 {d8}, [r9]! // B0 + + SUBS r5, r5, 8 // k = k - 8 + BLO 3f // less than 8 channels? + + // Main loop - 8 bytes + // 64 bytes for weights. + // 5 VMOVL = 4 A and 1 B = 5 cycles + // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles + // 1 blocks with VLD B, VMLA = 9 cycles + // total = 84 cycles + .p2align 3 +2: + // Extend - 5 cycles + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + PLD [r9, 448] + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + + // BLOCK 0 - 10 cycles + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + // BLOCK 1 - 10 cycles + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + // BLOCK 2 - 10 cycles + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + // BLOCK 3 - 10 cycles + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VLD1.8 {d0}, [r3]! // A0 + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + // BLOCK 4 - 10 cycles + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VLD1.8 {d2}, [r12]! // A1 + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + // BLOCK 5 - 10 cycles + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VLD1.8 {d4}, [r10]! // A2 + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + // BLOCK 6 - 10 cycles + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VLD1.8 {d6}, [r0]! // A3 + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + // BLOCK 7 - 9 cycles + VLD1.8 {d8}, [r9]! // B0 + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + SUBS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + BHS 2b + + // Epilogue + + .p2align 3 +3: + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + + VLD1.8 {d10}, [r9]! // B1 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + + VLD1.8 {d8}, [r9]! // B2 + VMLAL.S16 q8, d10, d0[1] + VMLAL.S16 q9, d11, d0[1] + VMLAL.S16 q10, d10, d2[1] + VMLAL.S16 q11, d11, d2[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[1] + VMLAL.S16 q13, d11, d4[1] + VMLAL.S16 q14, d10, d6[1] + VMLAL.S16 q15, d11, d6[1] + + VLD1.8 {d10}, [r9]! // B3 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + + VLD1.8 {d8}, [r9]! // B4 + VMLAL.S16 q8, d10, d0[3] + VMLAL.S16 q9, d11, d0[3] + VMLAL.S16 q10, d10, d2[3] + VMLAL.S16 q11, d11, d2[3] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d4[3] + VMLAL.S16 q13, d11, d4[3] + VMLAL.S16 q14, d10, d6[3] + VMLAL.S16 q15, d11, d6[3] + + VLD1.8 {d10}, [r9]! // B5 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + + VLD1.8 {d8}, [r9]! // B6 + VMLAL.S16 q8, d10, d1[1] + VMLAL.S16 q9, d11, d1[1] + VMLAL.S16 q10, d10, d3[1] + VMLAL.S16 q11, d11, d3[1] + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q12, d10, d5[1] + VMLAL.S16 q13, d11, d5[1] + VMLAL.S16 q14, d10, d7[1] + VMLAL.S16 q15, d11, d7[1] + + VLD1.8 {d10}, [r9]! // B7 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VSUBL.U8 q5, d10, d14 + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + + VMLAL.S16 q8, d10, d1[3] + VMLAL.S16 q9, d11, d1[3] + VMLAL.S16 q10, d10, d3[3] + VMLAL.S16 q11, d11, d3[3] + VMLAL.S16 q12, d10, d5[3] + VMLAL.S16 q13, d11, d5[3] + ADDS r5, r5, 8 + VMLAL.S16 q14, d10, d7[3] + VMLAL.S16 q15, d11, d7[3] + + # Is there a remainder?- 1-7 bytes of A + BNE 6f + +4: + # ks loop + SUBS r14, r14, 16 // ks -= MR * sizeof(void*) + BHI 1b + + LDR r7, [sp, 120] // cn_stride + LDR r14, [sp, 64] // p = ks + + # RNDNU quantization + VDUP.32 q0, d12[0] // right_pre_shift + + VQSHL.S32 q8, q8, q0 + VQSHL.S32 q9, q9, q0 + VQSHL.S32 q10, q10, q0 + VQSHL.S32 q11, q11, q0 + VQSHL.S32 q12, q12, q0 + VQSHL.S32 q13, q13, q0 + VQSHL.S32 q14, q14, q0 + VQSHL.S32 q15, q15, q0 + + VDUP.32 q2, d13[0] // right_post_shift + + VQDMULH.S32 q8, q8, d12[1] // multiplier + VQDMULH.S32 q9, q9, d12[1] + VQDMULH.S32 q10, q10, d12[1] + VQDMULH.S32 q11, q11, d12[1] + VQDMULH.S32 q12, q12, d12[1] + VQDMULH.S32 q13, q13, d12[1] + VQDMULH.S32 q14, q14, d12[1] + VQDMULH.S32 q15, q15, d12[1] + + VRSHL.S32 q8, q8, q2 + VRSHL.S32 q9, q9, q2 + VRSHL.S32 q10, q10, q2 + VRSHL.S32 q11, q11, q2 + VRSHL.S32 q12, q12, q2 + VRSHL.S32 q13, q13, q2 + VRSHL.S32 q14, q14, q2 + VRSHL.S32 q15, q15, q2 + + VDUP.16 q0, d13[2] // output_zero_point + + VQMOVN.S32 d16, q8 + VQMOVN.S32 d17, q9 + VQMOVN.S32 d18, q10 + VQMOVN.S32 d19, q11 + VQMOVN.S32 d20, q12 + VQMOVN.S32 d21, q13 + VQMOVN.S32 d22, q14 + VQMOVN.S32 d23, q15 + + VQADD.S16 q8, q8, q0 + VQADD.S16 q9, q9, q0 + VQADD.S16 q10, q10, q0 + VQADD.S16 q11, q11, q0 + + LDR r1, [sp, 56] // restore nc + VDUP.8 q12, d13[6] // output_min + + VQMOVUN.S16 d0, q8 + VQMOVUN.S16 d1, q9 + VQMOVUN.S16 d2, q10 + VQMOVUN.S16 d3, q11 + + VDUP.8 q13, d13[7] // output_max + + VMAX.U8 q0, q0, q12 + VMAX.U8 q1, q1, q12 + + SUBS r1, r1, 8 // nc -= 8 + + VMIN.U8 q0, q0, q13 + VMIN.U8 q1, q1, q13 + + # Store full 4 x 8 + BLO 7f + VST1.8 {d3}, [r6], r7 + VST1.8 {d2}, [r8], r7 + VST1.8 {d1}, [r4], r7 + VST1.8 {d0}, [r11], r7 + SUB r2, r2, r14 // a -= ks + BHI 0b + + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + + # Remainder- 1 to 7 bytes of A + .p2align 3 +5: + AND r5, r5, 7 // kc remainder 1 to 7 +6: + VLD1.8 {d0}, [r3] + VLD1.8 {d8}, [r9]! + VLD1.8 {d2}, [r12] + VLD1.8 {d4}, [r10] + VLD1.8 {d6}, [r0] + + VMOVL.U8 q0, d0 + VSUBL.U8 q4, d8, d14 + VMOVL.U8 q1, d2 + VMOVL.U8 q2, d4 + VMOVL.U8 q3, d6 + VMLAL.S16 q8, d8, d0[0] + VMLAL.S16 q9, d9, d0[0] + VMLAL.S16 q10, d8, d2[0] + VMLAL.S16 q11, d9, d2[0] + VMLAL.S16 q12, d8, d4[0] + VMLAL.S16 q13, d9, d4[0] + VMLAL.S16 q14, d8, d6[0] + VMLAL.S16 q15, d9, d6[0] + CMP r5, 2 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[1] + VMLAL.S16 q9, d9, d0[1] + VMLAL.S16 q10, d8, d2[1] + VMLAL.S16 q11, d9, d2[1] + VMLAL.S16 q12, d8, d4[1] + VMLAL.S16 q13, d9, d4[1] + VMLAL.S16 q14, d8, d6[1] + VMLAL.S16 q15, d9, d6[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[2] + VMLAL.S16 q9, d9, d0[2] + VMLAL.S16 q10, d8, d2[2] + VMLAL.S16 q11, d9, d2[2] + VMLAL.S16 q12, d8, d4[2] + VMLAL.S16 q13, d9, d4[2] + VMLAL.S16 q14, d8, d6[2] + VMLAL.S16 q15, d9, d6[2] + CMP r5, 4 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d0[3] + VMLAL.S16 q9, d9, d0[3] + VMLAL.S16 q10, d8, d2[3] + VMLAL.S16 q11, d9, d2[3] + VMLAL.S16 q12, d8, d4[3] + VMLAL.S16 q13, d9, d4[3] + VMLAL.S16 q14, d8, d6[3] + VMLAL.S16 q15, d9, d6[3] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[0] + VMLAL.S16 q9, d9, d1[0] + VMLAL.S16 q10, d8, d3[0] + VMLAL.S16 q11, d9, d3[0] + VMLAL.S16 q12, d8, d5[0] + VMLAL.S16 q13, d9, d5[0] + VMLAL.S16 q14, d8, d7[0] + VMLAL.S16 q15, d9, d7[0] + CMP r5, 6 + BLO 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[1] + VMLAL.S16 q9, d9, d1[1] + VMLAL.S16 q10, d8, d3[1] + VMLAL.S16 q11, d9, d3[1] + VMLAL.S16 q12, d8, d5[1] + VMLAL.S16 q13, d9, d5[1] + VMLAL.S16 q14, d8, d7[1] + VMLAL.S16 q15, d9, d7[1] + BEQ 4b + + VLD1.8 {d8}, [r9]! + VSUBL.U8 q4, d8, d14 + VMLAL.S16 q8, d8, d1[2] + VMLAL.S16 q9, d9, d1[2] + VMLAL.S16 q10, d8, d3[2] + VMLAL.S16 q11, d9, d3[2] + VMLAL.S16 q12, d8, d5[2] + VMLAL.S16 q13, d9, d5[2] + VMLAL.S16 q14, d8, d7[2] + VMLAL.S16 q15, d9, d7[2] + B 4b + + # Store odd width + .p2align 3 +7: + TST r1, 4 + BEQ 8f + VST1.32 {d3[0]}, [r6]! + VST1.32 {d2[0]}, [r8]! + VST1.32 {d1[0]}, [r4]! + VST1.32 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 4 + VEXT.8 q1, q1, q1, 4 +8: + TST r1, 2 + BEQ 9f + VST1.16 {d3[0]}, [r6]! + VST1.16 {d2[0]}, [r8]! + VST1.16 {d1[0]}, [r4]! + VST1.16 {d0[0]}, [r11]! + VEXT.8 q0, q0, q0, 2 + VEXT.8 q1, q1, q1, 2 + +9: + TST r1, 1 + BEQ 10f + VST1.8 {d3[0]}, [r6] + VST1.8 {d2[0]}, [r8] + VST1.8 {d1[0]}, [r4] + VST1.8 {d0[0]}, [r11] + +10: + VPOP {d8-d14} + ADD sp, sp, 12 // skip r1, r2, r3 + POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} + +END_FUNCTION xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7 + +#ifdef __ELF__ +.section ".note.GNU-stack","",%progbits +#endif diff --git a/src/xnnpack/igemm.h b/src/xnnpack/igemm.h index 45ecb333e..95b998828 100644 --- a/src/xnnpack/igemm.h +++ b/src/xnnpack/igemm.h @@ -493,8 +493,10 @@ DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_fp32_ukernel_1x16 DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_fp32_ukernel_2x16c4__neondot) DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_fp32_ukernel_4x16c4__neondot) +DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7) DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53) DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64) +DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7) DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53) DECLARE_QU8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qu8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64) @@ -1045,8 +1047,10 @@ DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_fp32_ukernel_4x16 DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8c4__aarch32_neondot_cortex_a55) DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8c4__aarch32_neondot_ld64) +DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7) DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53) DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_ld64) +DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7) DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53) DECLARE_QS8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qs8_igemm_minmax_rndnu_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64) @@ -1396,12 +1400,16 @@ DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x16 DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8c4__aarch32_neondot_cortex_a55) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8c4__aarch32_neondot_ld64) +DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a7) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_cortex_a53) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_ld64) +DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a7) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_cortex_a53) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neon_mlal_lane_prfm_ld64) +DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_cortex_a35) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_cortex_a53) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_ld64) +DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a35) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a53) DECLARE_QC8_IGEMM_MINMAX_UKERNEL_FUNCTION(xnn_qc8_igemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_ld64) |