blob: 503aa7e83798e6f608296822e5cc701bd0414cac [file] [log] [blame]
Dmitry Savenkod7295b72017-11-20 22:00:08 +07001/*
giuros016d109962019-01-07 17:47:19 +00002 * Copyright (c) 2017-2019 ARM Limited.
Dmitry Savenkod7295b72017-11-20 22:00:08 +07003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include "helpers_asymm.h"
26
Gian Marco Iodice4b908652018-10-18 10:21:02 +010027#if defined(WEIGHTS_OFFSET) && defined(INPUT_OFFSET) && defined(K_OFFSET) && ((defined(OUTPUT_OFFSET) && defined(OUTPUT_MULTIPLIER) && defined(OUTPUT_SHIFT)) || defined(REAL_MULTIPLIER))
Giorgio Arena287b5702018-02-16 11:01:04 +000028
Giorgio Arena99ac60b2018-02-16 15:17:23 +000029#if defined(FUSED_ACTIVATION)
30#define DATA_TYPE uchar
Giorgio Arenadfca60b2018-01-31 10:30:59 +000031#ifndef VEC_SIZE
Giorgio Arena99ac60b2018-02-16 15:17:23 +000032#define VEC_SIZE 8
Giorgio Arenadfca60b2018-01-31 10:30:59 +000033#endif /* VEC_SIZE */
Giorgio Arena99ac60b2018-02-16 15:17:23 +000034#include "activation_layer_qa8.cl"
35#define ACTIVATION_FUNC(x) PERFORM_ACTIVATION_QA8(FUSED_ACTIVATION, x)
36#else /* defined(FUSED_ACTIVATION) */
37#define ACTIVATION_FUNC(x) (x)
38#endif /* defined(FUSED_ACTIVATION) */
39
Georgios Pinitasdaa38552018-08-28 17:43:18 +010040#if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8)
41#if defined(ARM_COMPUTE_OPENCL_DOT8_ACC_ENABLED) && defined(cl_arm_integer_dot_product_accumulate_int8)
Gian Marco Iodice4b908652018-10-18 10:21:02 +010042#define ARM_DOT(x, y, val) val = arm_dot_acc((x), (y), val);
Georgios Pinitasdaa38552018-08-28 17:43:18 +010043#else // defined(ARM_COMPUTE_OPENCL_DOT8_ACC_ENABLED) && defined(cl_arm_integer_dot_product_accumulate_int8)
Gian Marco Iodice4b908652018-10-18 10:21:02 +010044#define ARM_DOT(x, y, val) val += arm_dot((x), (y));
Georgios Pinitasdaa38552018-08-28 17:43:18 +010045#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ACC_ENABLED) && defined(cl_arm_integer_dot_product_accumulate_int8)
46#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8)
Giorgio Arenaeff8d952018-07-02 15:29:57 +010047
Georgios Pinitase55b40a2018-09-13 17:20:04 +010048#if defined(CONV_STRIDE_Y) && defined(CONV_STRIDE_X) && defined(DEPTH_MULTIPLIER) && defined(DST_CHANNELS)
Giorgio Arenadfca60b2018-01-31 10:30:59 +000049
Giorgio Arena287b5702018-02-16 11:01:04 +000050#if CONV_STRIDE_X > 3
51#error "Stride X not supported"
52#endif /* CONV_STRIDE_X > 3 */
Dmitry Savenkod7295b72017-11-20 22:00:08 +070053
Georgios Pinitasdaa38552018-08-28 17:43:18 +010054#if !(defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8))
Giorgio Arenaeff8d952018-07-02 15:29:57 +010055
Dmitry Savenkod7295b72017-11-20 22:00:08 +070056#if CONV_STRIDE_X == 1
Giorgio Arena287b5702018-02-16 11:01:04 +000057#define GET_VALUES(first_value, left, middle, right) \
58 ({ \
59 int8 temp0 = CONVERT(vload8(0, first_value), int8); \
60 int2 temp1 = CONVERT(vload2(0, (first_value + 8 * sizeof(uchar))), int2); \
61 \
62 left = CONVERT(temp0.s01234567, int8); \
63 middle = CONVERT((int8)(temp0.s1234, temp0.s567, temp1.s0), int8); \
64 right = CONVERT((int8)(temp0.s2345, temp0.s67, temp1.s01), int8); \
65 })
Dmitry Savenkod7295b72017-11-20 22:00:08 +070066#elif CONV_STRIDE_X == 2
Giorgio Arena287b5702018-02-16 11:01:04 +000067#define GET_VALUES(first_value, left, middle, right) \
68 ({ \
69 int16 temp0 = CONVERT(vload16(0, first_value), int16); \
70 int temp1 = CONVERT(*(first_value + 16 * sizeof(uchar)), int); \
71 \
72 left = CONVERT(temp0.s02468ace, int8); \
73 middle = CONVERT(temp0.s13579bdf, int8); \
74 right = CONVERT((int8)(temp0.s2468, temp0.sace, temp1), int8); \
75 })
Dmitry Savenkod7295b72017-11-20 22:00:08 +070076#else /* CONV_STRIDE_X */
Giorgio Arena287b5702018-02-16 11:01:04 +000077#define GET_VALUES(first_value, left, middle, right) \
78 ({ \
79 int16 temp0 = CONVERT(vload16(0, first_value), int16); \
80 int8 temp1 = CONVERT(vload8(0, (first_value + 16 * sizeof(uchar))), int8); \
81 \
82 left = CONVERT((int8)(temp0.s0369, temp0.scf, temp1.s25), int8); \
83 middle = CONVERT((int8)(temp0.s147a, temp0.sd, temp1.s036), int8); \
84 right = CONVERT((int8)(temp0.s258b, temp0.se, temp1.s147), int8); \
85 })
Dmitry Savenkod7295b72017-11-20 22:00:08 +070086#endif /* CONV_STRIDE_X */
87
Giorgio Arenadfca60b2018-01-31 10:30:59 +000088/** This function computes the depthwise convolution quantized.
Anthony Barbierf202e502017-11-23 18:02:04 +000089 *
Georgios Pinitas37044642018-10-30 14:53:25 +000090 * @param[in] src_ptr Pointer to the source tensor. Supported data types: QASYMM8
91 * @param[in] src_stride_x Stride of the source tensor in X dimension (in bytes)
Anthony Barbierf202e502017-11-23 18:02:04 +000092 * @param[in] src_step_x src_stride_x * number of elements along X processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +000093 * @param[in] src_stride_y Stride of the source tensor in Y dimension (in bytes)
Anthony Barbierf202e502017-11-23 18:02:04 +000094 * @param[in] src_step_y src_stride_y * number of elements along Y processed per workitem(in bytes)
Anthony Barbierf202e502017-11-23 18:02:04 +000095 * @param[in] src_stride_z Stride of the source tensor in Z dimension (in bytes)
96 * @param[in] src_step_z src_stride_z * number of elements along Y processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +000097 * @param[in] src_offset_first_element_in_bytes The offset of the first element in the source tensor
Anthony Barbierf202e502017-11-23 18:02:04 +000098 * @param[in] dst_ptr Pointer to the destination tensor. Supported data types: QASYMM8
99 * @param[in] dst_stride_x Stride of the destination tensor in X dimension (in bytes)
100 * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes)
101 * @param[in] dst_stride_y Stride of the destination tensor in Y dimension (in bytes)
102 * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes)
103 * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes)
104 * @param[in] dst_step_z dst_stride_z * number of elements along Y processed per workitem(in bytes)
105 * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination tensor
106 * @param[in] weights_ptr Pointer to the weights tensor. Supported data types: QASYMM8
107 * @param[in] weights_stride_x Stride of the weights tensor in X dimension (in bytes)
108 * @param[in] weights_step_x weights_stride_x * number of elements along X processed per workitem(in bytes)
109 * @param[in] weights_stride_y Stride of the weights tensor in Y dimension (in bytes)
110 * @param[in] weights_step_y weights_stride_y * number of elements along Y processed per workitem(in bytes)
111 * @param[in] weights_stride_z Stride of the weights tensor in Z dimension (in bytes)
112 * @param[in] weights_step_z weights_stride_z * number of elements along Y processed per workitem(in bytes)
113 * @param[in] weights_offset_first_element_in_bytes The offset of the first element in the weights tensor
114 * @param[in] biases_ptr (Optional) Pointer to the biases vector. Supported data types: QASYMM8
115 * @param[in] biases_stride_x (Optional) Stride of the biases vector in X dimension (in bytes)
116 * @param[in] biases_step_x (Optional) biases_stride_x * number of elements along X processed per workitem(in bytes)
117 * @param[in] biases_offset_first_element_in_bytes (Optional) The offset of the first element in the biases vector
Anthony Barbierf202e502017-11-23 18:02:04 +0000118 */
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700119
Pablo Tello47104362019-02-27 13:32:51 +0000120__kernel void dwc_3x3_native_qasymm8_nchw(
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700121 TENSOR3D_DECLARATION(src),
122 TENSOR3D_DECLARATION(dst),
Georgios Pinitas5b2191e2018-02-22 12:56:51 +0000123 TENSOR3D_DECLARATION(weights)
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700124#if defined(HAS_BIAS)
Georgios Pinitas5b2191e2018-02-22 12:56:51 +0000125 ,
Giorgio Arena287b5702018-02-16 11:01:04 +0000126 VECTOR_DECLARATION(biases)
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700127#endif //defined(HAS_BIAS)
Giorgio Arena287b5702018-02-16 11:01:04 +0000128)
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700129{
130 Image src = CONVERT_TENSOR3D_TO_IMAGE_STRUCT(src);
131 Image dst = CONVERT_TENSOR3D_TO_IMAGE_STRUCT(dst);
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100132 Tensor3D weights = CONVERT_TO_TENSOR3D_STRUCT_NO_STEP(weights);
133
134 // Extract channel and linearized batch indices
135 const int channel = get_global_id(2) % DST_CHANNELS;
136 const int batch = get_global_id(2) / DST_CHANNELS;
137
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700138#if defined(HAS_BIAS)
139 Vector biases = CONVERT_TO_VECTOR_STRUCT_NO_STEP(biases);
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700140
Georgios Pinitas728d3cf2018-09-21 13:41:35 +0100141 int bias_value = *((__global int *)(vector_offset(&biases, channel)));
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700142#endif //defined(HAS_BIAS)
143
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100144 // Load relevant input and weights data (Accounts depth multiplier when indexing input, OFM = IFM * DEPTH_MULTIPLIER)
145 src.ptr -= batch * (DST_CHANNELS / DEPTH_MULTIPLIER) * (DEPTH_MULTIPLIER - 1) * src_step_z + (channel - (channel / DEPTH_MULTIPLIER)) * src_step_z;
146 __global uchar *weights_addr = weights.ptr + get_global_id(0) * weights_step_x + get_global_id(1) * weights_step_y + channel * weights_step_z;
Giorgio Arena76572242018-04-04 17:44:26 +0100147
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100148 uchar3 w0 = vload3(0, weights_addr + 0 * weights_stride_y);
149 uchar3 w1 = vload3(0, weights_addr + 1 * weights_stride_y);
150 uchar3 w2 = vload3(0, weights_addr + 2 * weights_stride_y);
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700151
Giorgio Arena287b5702018-02-16 11:01:04 +0000152 int8 values0 = 0;
153 int8 sum0 = 0;
154#if CONV_STRIDE_Y == 1
155 int8 values1 = 0;
156 int8 sum1 = 0;
157#endif /* CONV_STRIDE_Y */
158
159 // Row0
160 int8 left, middle, right;
161 GET_VALUES(src.ptr + 0 * src_stride_y, left, middle, right);
162 values0 += left * (int8)(w0.s0);
163 values0 += middle * (int8)(w0.s1);
164 values0 += right * (int8)(w0.s2);
165
166#if WEIGHTS_OFFSET != 0
167 sum0 += left + middle + right;
168#endif /* WEIGHTS_OFFSET != 0 */
169
170 // Row1
171 GET_VALUES(src.ptr + 1 * src_stride_y, left, middle, right);
172 values0 += left * (int8)(w1.s0);
173 values0 += middle * (int8)(w1.s1);
174 values0 += right * (int8)(w1.s2);
175#if CONV_STRIDE_Y == 1
176 values1 += left * (int8)(w0.s0);
177 values1 += middle * (int8)(w0.s1);
178 values1 += right * (int8)(w0.s2);
179#endif /* CONV_STRIDE_Y == 1 */
180
181#if WEIGHTS_OFFSET != 0
182 int8 tmp = left + middle + right;
183 sum0 += tmp;
184#if CONV_STRIDE_Y == 1
185 sum1 += tmp;
186#endif /* CONV_STRIDE_Y == 1 */
187#endif /* WEIGHTS_OFFSET != 0 */
188
189 // Row2
190 GET_VALUES(src.ptr + 2 * src_stride_y, left, middle, right);
191 values0 += left * (int8)(w2.s0);
192 values0 += middle * (int8)(w2.s1);
193 values0 += right * (int8)(w2.s2);
194#if CONV_STRIDE_Y == 1
195 values1 += left * (int8)(w1.s0);
196 values1 += middle * (int8)(w1.s1);
197 values1 += right * (int8)(w1.s2);
198#endif /* CONV_STRIDE_Y == 1 */
199
200#if WEIGHTS_OFFSET != 0
201 tmp = left + middle + right;
202 sum0 += tmp;
203#if CONV_STRIDE_Y == 1
204 sum1 += tmp;
205#endif /* CONV_STRIDE_Y == 1 */
206#endif /* WEIGHTS_OFFSET != 0 */
207
208#if CONV_STRIDE_Y == 1
209 // Row3
210 GET_VALUES(src.ptr + 3 * src_stride_y, left, middle, right);
211 values1 += left * (int8)(w2.s0);
212 values1 += middle * (int8)(w2.s1);
213 values1 += right * (int8)(w2.s2);
214
215#if WEIGHTS_OFFSET != 0
216 sum1 += left + middle + right;
217#endif /* WEIGHTS_OFFSET != 0 */
218#endif /* CONV_STRIDE_Y == 1 */
219
220#if defined(HAS_BIAS)
221 values0 += (int8)(bias_value);
222#if CONV_STRIDE_Y == 1
223 values1 += (int8)(bias_value);
224#endif /* CONV_STRIDE_Y == 1 */
225#endif //defined(HAS_BIAS)
226
227#if WEIGHTS_OFFSET != 0
228 values0 += sum0 * (int8)(WEIGHTS_OFFSET);
229#if CONV_STRIDE_Y == 1
230 values1 += sum1 * (int8)(WEIGHTS_OFFSET);
231#endif /* CONV_STRIDE_Y == 1 */
232#endif /* WEIGHTS_OFFSET != 0 */
233
234#if INPUT_OFFSET != 0
235 ushort sum_weights = 0;
236 ushort3 tmp_we = convert_ushort3(w0) + convert_ushort3(w1) + convert_ushort3(w2);
237 sum_weights += tmp_we.s0 + tmp_we.s1 + tmp_we.s2;
238 values0 += sum_weights * (int8)(INPUT_OFFSET);
239#if CONV_STRIDE_Y == 1
240 values1 += sum_weights * (int8)(INPUT_OFFSET);
241#endif /* CONV_STRIDE_Y == 1 */
242#endif /* INPUT_OFFSET != 0 */
243
244#if K_OFFSET != 0
245 values0 += (int8)(K_OFFSET);
246#if CONV_STRIDE_Y == 1
247 values1 += (int8)(K_OFFSET);
248#endif /* CONV_STRIDE_Y == 1 */
249#endif /* K_OFFSET != 0 */
250
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100251#if defined(REAL_MULTIPLIER)
252
253 values0 = CONVERT(round(CONVERT(values0, float8) * (float8)REAL_MULTIPLIER), int8);
254
255#else // defined(REAL_MULTIPLIER)
256
Pablo Tello47104362019-02-27 13:32:51 +0000257 values0 = ASYMM_MULT_BY_QUANT_MULTIPLIER_LESS_THAN_ONE(values0, OUTPUT_MULTIPLIER, OUTPUT_SHIFT, 8);
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100258
259#endif // defined(REAL_MULTIPLIER)
260
Giorgio Arena287b5702018-02-16 11:01:04 +0000261 values0 += (int8)OUTPUT_OFFSET;
262 uchar8 res0 = convert_uchar8_sat(values0);
263 res0 = max(res0, (uchar8)0);
264 res0 = min(res0, (uchar8)255);
265
Giorgio Arena99ac60b2018-02-16 15:17:23 +0000266 vstore8(ACTIVATION_FUNC(res0), 0, dst.ptr);
Giorgio Arena287b5702018-02-16 11:01:04 +0000267#if CONV_STRIDE_Y == 1
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100268#if defined(REAL_MULTIPLIER)
269
270 values1 = CONVERT(round(CONVERT(values1, float8) * (float8)REAL_MULTIPLIER), int8);
271
272#else // defined(REAL_MULTIPLIER)
Giorgio Arena287b5702018-02-16 11:01:04 +0000273
Pablo Tello47104362019-02-27 13:32:51 +0000274 values1 = ASYMM_MULT_BY_QUANT_MULTIPLIER_LESS_THAN_ONE(values1, OUTPUT_MULTIPLIER, OUTPUT_SHIFT, 8);
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100275
276#endif // defined(REAL_MULTIPLIER)
277
Giorgio Arena287b5702018-02-16 11:01:04 +0000278 values1 += (int8)OUTPUT_OFFSET;
279 uchar8 res1 = convert_uchar8_sat(values1);
280 res1 = max(res1, (uchar8)0);
281 res1 = min(res1, (uchar8)255);
282
Giorgio Arena99ac60b2018-02-16 15:17:23 +0000283 vstore8(ACTIVATION_FUNC(res1), 0, dst.ptr + dst_stride_y);
Giorgio Arena287b5702018-02-16 11:01:04 +0000284#endif /* CONV_STRIDE_Y == 1 */
Dmitry Savenkod7295b72017-11-20 22:00:08 +0700285}
Giorgio Arena287b5702018-02-16 11:01:04 +0000286
Georgios Pinitasdaa38552018-08-28 17:43:18 +0100287#else // !(defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8))
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000288
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100289#if CONV_STRIDE_X == 1
290#define GET_VALUES(first_value, left, middle, right) \
291 ({ \
292 uchar8 temp0 = vload8(0, first_value); \
293 uchar2 temp1 = vload2(0, (first_value + 8 * sizeof(uchar))); \
294 \
295 left = temp0.s01234567; \
296 middle = (uchar8)(temp0.s1234, temp0.s567, temp1.s0); \
297 right = (uchar8)(temp0.s2345, temp0.s67, temp1.s01); \
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000298 })
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100299#elif CONV_STRIDE_X == 2
300#define GET_VALUES(first_value, left, middle, right) \
301 ({ \
302 uchar16 temp0 = vload16(0, first_value); \
303 uchar temp1 = *(first_value + 16 * sizeof(uchar)); \
304 \
305 left = temp0.s02468ace; \
306 middle = temp0.s13579bdf; \
307 right = (uchar8)(temp0.s2468, temp0.sace, temp1); \
308 })
309#else /* CONV_STRIDE_X */
310#define GET_VALUES(first_value, left, middle, right) \
311 ({ \
312 uchar16 temp0 = vload16(0, first_value); \
313 uchar8 temp1 = vload8(0, (first_value + 16 * sizeof(uchar))); \
314 \
315 left = (uchar8)(temp0.s0369, temp0.scf, temp1.s25); \
316 middle = (uchar8)(temp0.s147a, temp0.sd, temp1.s036); \
317 right = (uchar8)(temp0.s258b, temp0.se, temp1.s147); \
318 })
319#endif /* CONV_STRIDE_X */
320/** This function computes the depthwise convolution quantized using dot product when the data layout is NCHW.
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000321 *
Georgios Pinitas37044642018-10-30 14:53:25 +0000322 * @param[in] src_ptr Pointer to the source tensor. Supported data types: QASYMM8
323 * @param[in] src_stride_x Stride of the source tensor in X dimension (in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000324 * @param[in] src_step_x src_stride_x * number of elements along X processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000325 * @param[in] src_stride_y Stride of the source tensor in Y dimension (in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000326 * @param[in] src_step_y src_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000327 * @param[in] src_stride_z Stride of the source tensor in Z dimension (in bytes)
328 * @param[in] src_step_z src_stride_z * number of elements along Y processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000329 * @param[in] src_offset_first_element_in_bytes The offset of the first element in the source tensor
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000330 * @param[in] dst_ptr Pointer to the destination tensor. Supported data types: QASYMM8
331 * @param[in] dst_stride_x Stride of the destination tensor in X dimension (in bytes)
332 * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes)
333 * @param[in] dst_stride_y Stride of the destination tensor in Y dimension (in bytes)
334 * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes)
335 * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes)
336 * @param[in] dst_step_z dst_stride_z * number of elements along Y processed per workitem(in bytes)
337 * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination tensor
338 * @param[in] weights_ptr Pointer to the weights tensor. Supported data types: QASYMM8
339 * @param[in] weights_stride_x Stride of the weights tensor in X dimension (in bytes)
340 * @param[in] weights_step_x weights_stride_x * number of elements along X processed per workitem(in bytes)
341 * @param[in] weights_stride_y Stride of the weights tensor in Y dimension (in bytes)
342 * @param[in] weights_step_y weights_stride_y * number of elements along Y processed per workitem(in bytes)
343 * @param[in] weights_stride_z Stride of the weights tensor in Z dimension (in bytes)
344 * @param[in] weights_step_z weights_stride_z * number of elements along Y processed per workitem(in bytes)
345 * @param[in] weights_offset_first_element_in_bytes The offset of the first element in the weights tensor
346 * @param[in] biases_ptr (Optional) Pointer to the biases vector. Supported data types: QASYMM8
347 * @param[in] biases_stride_x (Optional) Stride of the biases vector in X dimension (in bytes)
348 * @param[in] biases_step_x (Optional) biases_stride_x * number of elements along X processed per workitem(in bytes)
349 * @param[in] biases_offset_first_element_in_bytes (Optional) The offset of the first element in the biases vector
350 */
351
Pablo Tello47104362019-02-27 13:32:51 +0000352__kernel void dwc_3x3_native_qasymm8_dot8_nchw(
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100353 TENSOR3D_DECLARATION(src),
354 TENSOR3D_DECLARATION(dst),
355 TENSOR3D_DECLARATION(weights)
356#if defined(HAS_BIAS)
357 ,
358 VECTOR_DECLARATION(biases)
359#endif //defined(HAS_BIAS)
360)
361{
362 Image src = CONVERT_TENSOR3D_TO_IMAGE_STRUCT(src);
363 Image dst = CONVERT_TENSOR3D_TO_IMAGE_STRUCT(dst);
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100364 Tensor3D weights = CONVERT_TO_TENSOR3D_STRUCT_NO_STEP(weights);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100365
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100366 // Extract channel and linearized batch indices
367 const int channel = get_global_id(2) % DST_CHANNELS;
368 const int batch = get_global_id(2) / DST_CHANNELS;
369
370#if defined(HAS_BIAS)
371 Vector biases = CONVERT_TO_VECTOR_STRUCT_NO_STEP(biases);
372
373 const int bias_value = *((__global int *)(vector_offset(&biases, channel)));
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100374#endif //defined(HAS_BIAS)
375
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100376 // Load relevant input and weights data (Accounts depth multiplier when indexing input, OFM = IFM * DEPTH_MULTIPLIER)
377 src.ptr -= batch * (DST_CHANNELS / DEPTH_MULTIPLIER) * (DEPTH_MULTIPLIER - 1) * src_step_z + (channel - (channel / DEPTH_MULTIPLIER)) * src_step_z;
378 __global uchar *weights_addr = weights.ptr + get_global_id(0) * weights_step_x + get_global_id(1) * weights_step_y + channel * weights_step_z;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100379
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100380 uchar3 w0 = vload3(0, weights_addr + 0 * weights_stride_y);
381 uchar3 w1 = vload3(0, weights_addr + 1 * weights_stride_y);
382 uchar3 w2 = vload3(0, weights_addr + 2 * weights_stride_y);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100383
384 uchar8 left0, middle0, right0;
385 uchar8 left1, middle1, right1;
386 uchar8 left2, middle2, right2;
387
388 int8 values0 = 0;
389 int8 sum0 = 0;
390
391 GET_VALUES(src.ptr + 0 * src_stride_y, left0, middle0, right0);
392 GET_VALUES(src.ptr + 1 * src_stride_y, left1, middle1, right1);
393 GET_VALUES(src.ptr + 2 * src_stride_y, left2, middle2, right2);
394
395#if WEIGHTS_OFFSET != 0
396 sum0 += convert_int8(left0) + convert_int8(middle0) + convert_int8(right0);
397 sum0 += convert_int8(left1) + convert_int8(middle1) + convert_int8(right1);
398 sum0 += convert_int8(left2) + convert_int8(middle2) + convert_int8(right2);
399#endif /* WEIGHTS_OFFSET != 0 */
400
401#if CONV_STRIDE_Y == 1
402 // If conv_stride_y is equals to 1, we compute two output rows
403
404 uchar8 left3, middle3, right3;
405 int8 values1 = 0;
406 int8 sum1 = 0;
407
408 GET_VALUES(src.ptr + 3 * src_stride_y, left3, middle3, right3);
409
410#if WEIGHTS_OFFSET != 0
411 sum1 += convert_int8(left1) + convert_int8(middle1) + convert_int8(right1);
412 sum1 += convert_int8(left2) + convert_int8(middle2) + convert_int8(right2);
413 sum1 += convert_int8(left3) + convert_int8(middle3) + convert_int8(right3);
414#endif /* WEIGHTS_OFFSET != 0 */
415#endif // CONV_STRIDE_Y == 1
416
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100417 ARM_DOT((uchar4)(left0.s0, middle0.s0, right0.s0, left1.s0), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s0);
418 ARM_DOT((uchar4)(middle1.s0, right1.s0, left2.s0, middle2.s0), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s0);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100419 values0.s0 += right2.s0 * w2.s2;
420
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100421 ARM_DOT((uchar4)(left0.s1, middle0.s1, right0.s1, left1.s1), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s1);
422 ARM_DOT((uchar4)(middle1.s1, right1.s1, left2.s1, middle2.s1), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s1);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100423 values0.s1 += right2.s1 * w2.s2;
424
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100425 ARM_DOT((uchar4)(left0.s2, middle0.s2, right0.s2, left1.s2), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s2);
426 ARM_DOT((uchar4)(middle1.s2, right1.s2, left2.s2, middle2.s2), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s2);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100427 values0.s2 += right2.s2 * w2.s2;
428
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100429 ARM_DOT((uchar4)(left0.s3, middle0.s3, right0.s3, left1.s3), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s3);
430 ARM_DOT((uchar4)(middle1.s3, right1.s3, left2.s3, middle2.s3), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s3);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100431 values0.s3 += right2.s3 * w2.s2;
432
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100433 ARM_DOT((uchar4)(left0.s4, middle0.s4, right0.s4, left1.s4), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s4);
434 ARM_DOT((uchar4)(middle1.s4, right1.s4, left2.s4, middle2.s4), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s4);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100435 values0.s4 += right2.s4 * w2.s2;
436
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100437 ARM_DOT((uchar4)(left0.s5, middle0.s5, right0.s5, left1.s5), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s5);
438 ARM_DOT((uchar4)(middle1.s5, right1.s5, left2.s5, middle2.s5), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s5);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100439 values0.s5 += right2.s5 * w2.s2;
440
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100441 ARM_DOT((uchar4)(left0.s6, middle0.s6, right0.s6, left1.s6), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s6);
442 ARM_DOT((uchar4)(middle1.s6, right1.s6, left2.s6, middle2.s6), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s6);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100443 values0.s6 += right2.s6 * w2.s2;
444
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100445 ARM_DOT((uchar4)(left0.s7, middle0.s7, right0.s7, left1.s7), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values0.s7);
446 ARM_DOT((uchar4)(middle1.s7, right1.s7, left2.s7, middle2.s7), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values0.s7);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100447 values0.s7 += right2.s7 * w2.s2;
448
449#if CONV_STRIDE_Y == 1
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100450 ARM_DOT((uchar4)(left1.s0, middle1.s0, right1.s0, left2.s0), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s0);
451 ARM_DOT((uchar4)(middle2.s0, right2.s0, left3.s0, middle3.s0), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s0);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100452 values1.s0 += right3.s0 * w2.s2;
453
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100454 ARM_DOT((uchar4)(left1.s1, middle1.s1, right1.s1, left2.s1), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s1);
455 ARM_DOT((uchar4)(middle2.s1, right2.s1, left3.s1, middle3.s1), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s1);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100456 values1.s1 += right3.s1 * w2.s2;
457
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100458 ARM_DOT((uchar4)(left1.s2, middle1.s2, right1.s2, left2.s2), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s2);
459 ARM_DOT((uchar4)(middle2.s2, right2.s2, left3.s2, middle3.s2), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s2);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100460 values1.s2 += right3.s2 * w2.s2;
461
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100462 ARM_DOT((uchar4)(left1.s3, middle1.s3, right1.s3, left2.s3), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s3);
463 ARM_DOT((uchar4)(middle2.s3, right2.s3, left3.s3, middle3.s3), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s3);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100464 values1.s3 += right3.s3 * w2.s2;
465
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100466 ARM_DOT((uchar4)(left1.s4, middle1.s4, right1.s4, left2.s4), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s4);
467 ARM_DOT((uchar4)(middle2.s4, right2.s4, left3.s4, middle3.s4), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s4);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100468 values1.s4 += right3.s4 * w2.s2;
469
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100470 ARM_DOT((uchar4)(left1.s5, middle1.s5, right1.s5, left2.s5), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s5);
471 ARM_DOT((uchar4)(middle2.s5, right2.s5, left3.s5, middle3.s5), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s5);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100472 values1.s5 += right3.s5 * w2.s2;
473
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100474 ARM_DOT((uchar4)(left1.s6, middle1.s6, right1.s6, left2.s6), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s6);
475 ARM_DOT((uchar4)(middle2.s6, right2.s6, left3.s6, middle3.s6), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s6);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100476 values1.s6 += right3.s6 * w2.s2;
477
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100478 ARM_DOT((uchar4)(left1.s7, middle1.s7, right1.s7, left2.s7), (uchar4)(w0.s0, w0.s1, w0.s2, w1.s0), values1.s7);
479 ARM_DOT((uchar4)(middle2.s7, right2.s7, left3.s7, middle3.s7), (uchar4)(w1.s1, w1.s2, w2.s0, w2.s1), values1.s7);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100480 values1.s7 += right3.s7 * w2.s2;
481#endif // CONV_STRIDE_Y == 1
482
483#if defined(HAS_BIAS)
484 values0 += (int8)(bias_value);
485#if CONV_STRIDE_Y == 1
486 values1 += (int8)(bias_value);
487#endif /* CONV_STRIDE_Y == 1 */
488#endif //defined(HAS_BIAS)
489
490#if WEIGHTS_OFFSET != 0
491 values0 += sum0 * (int8)(WEIGHTS_OFFSET);
492#if CONV_STRIDE_Y == 1
493 values1 += sum1 * (int8)(WEIGHTS_OFFSET);
494#endif /* CONV_STRIDE_Y == 1 */
495#endif /* WEIGHTS_OFFSET != 0 */
496
497#if INPUT_OFFSET != 0
498 ushort sum_weights = 0;
499 ushort3 tmp_we = convert_ushort3(w0) + convert_ushort3(w1) + convert_ushort3(w2);
500 sum_weights += tmp_we.s0 + tmp_we.s1 + tmp_we.s2;
501 values0 += sum_weights * (int8)(INPUT_OFFSET);
502#if CONV_STRIDE_Y == 1
503 values1 += sum_weights * (int8)(INPUT_OFFSET);
504#endif /* CONV_STRIDE_Y == 1 */
505#endif /* INPUT_OFFSET != 0 */
506
507#if K_OFFSET != 0
508 values0 += (int8)(K_OFFSET);
509#if CONV_STRIDE_Y == 1
510 values1 += (int8)(K_OFFSET);
511#endif /* CONV_STRIDE_Y == 1 */
512#endif /* K_OFFSET != 0 */
513
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100514#if defined(REAL_MULTIPLIER)
515
516 values0 = CONVERT(round(CONVERT(values0, float8) * (float8)REAL_MULTIPLIER), int8);
517
518#else // defined(REAL_MULTIPLIER)
519
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100520 values0 = ASYMM_MULT_BY_QUANT_MULTIPLIER_LESS_THAN_ONE(values0, OUTPUT_MULTIPLIER, OUTPUT_SHIFT, 8);
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100521
522#endif // defined(REAL_MULTIPLIER)
523
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100524 values0 += (int8)OUTPUT_OFFSET;
525 uchar8 res0 = convert_uchar8_sat(values0);
526 res0 = max(res0, (uchar8)0);
527 res0 = min(res0, (uchar8)255);
528
529 vstore8(ACTIVATION_FUNC(res0), 0, dst.ptr);
530#if CONV_STRIDE_Y == 1
531
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100532#if defined(REAL_MULTIPLIER)
533
534 values1 = CONVERT(round(CONVERT(values1, float8) * (float8)REAL_MULTIPLIER), int8);
535
536#else // defined(REAL_MULTIPLIER)
537
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100538 values1 = ASYMM_MULT_BY_QUANT_MULTIPLIER_LESS_THAN_ONE(values1, OUTPUT_MULTIPLIER, OUTPUT_SHIFT, 8);
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100539
540#endif // defined(REAL_MULTIPLIER)
541
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100542 values1 += (int8)OUTPUT_OFFSET;
543 uchar8 res1 = convert_uchar8_sat(values1);
544 res1 = max(res1, (uchar8)0);
545 res1 = min(res1, (uchar8)255);
546
547 vstore8(ACTIVATION_FUNC(res1), 0, dst.ptr + dst_stride_y);
548#endif /* CONV_STRIDE_Y == 1 */
549}
550
Georgios Pinitasdaa38552018-08-28 17:43:18 +0100551#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100552
Georgios Pinitase55b40a2018-09-13 17:20:04 +0100553#endif /* defined(CONV_STRIDE_Y) && defined(CONV_STRIDE_X) && defined(DEPTH_MULTIPLIER) && defined(DST_CHANNELS) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100554
555#if defined(VEC_SIZE) && defined(SRC_DIM_1) && defined(SRC_DIM_2) && defined(CONV_PAD_TOP) && defined(CONV_PAD_LEFT)
556
557#define asymm_mult_by_quant_multiplier_less_than_one(x, y, z) ASYMM_MULT_BY_QUANT_MULTIPLIER_LESS_THAN_ONE(x, y, z, VEC_SIZE)
558
559#define VEC_INT VEC_DATA_TYPE(int, VEC_SIZE)
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100560#define VEC_FLOAT VEC_DATA_TYPE(float, VEC_SIZE)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100561#define VEC_UCHAR VEC_DATA_TYPE(uchar, VEC_SIZE)
562#define VEC_USHORT VEC_DATA_TYPE(ushort, VEC_SIZE)
563
564#define MULTIPLY_ADD(x, y, acc) acc += CONVERT(CONVERT(x, VEC_USHORT) * CONVERT(y, VEC_USHORT), VEC_INT)
565
566#if WEIGHTS_OFFSET != 0
567#define MULTIPLY_ADD_ACCUMULATE(x, y, acc, sum) \
568 ({ \
569 sum += CONVERT(x, VEC_INT); \
570 MULTIPLY_ADD(x, y, acc); \
571 })
572#else /* WEIGHTS_OFFSET != 0 */
573#define MULTIPLY_ADD_ACCUMULATE(x, y, acc, sum) MULTIPLY_ADD(x, y, acc)
574#endif /* WEIGHTS_OFFSET != 0 */
575
Georgios Pinitasdaa38552018-08-28 17:43:18 +0100576#if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8)
giuros016d109962019-01-07 17:47:19 +0000577#define DOT_PRODUCT(acc, val0, val1, val2, val3, val4, val5, val6, val7, val8, w0, w1) \
578 ({ \
579 ARM_DOT((uchar4)(val0, val1, val2, val3), w0.s0123, acc); \
580 ARM_DOT((uchar4)(val4, val5, val6, val7), w0.s4567, acc); \
581 acc += val8 * w1; \
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100582 })
583
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100584#define DOT_PRODUCT_REDUCTION(sum, val0, val1, val2, val3, val4, val5, val6, val7, val8) \
585 ({ \
giuros016d109962019-01-07 17:47:19 +0000586 sum = val0; \
587 ARM_DOT((uchar4)(val1, val2, val3, val4), (uchar4)1, sum); \
588 ARM_DOT((uchar4)(val5, val6, val7, val8), (uchar4)1, sum); \
589 })
590
591#define DOT_PRODUCT_REDUCTION_WEIGHTS(sum, w0, w1) \
592 ({ \
593 sum = w1; \
594 ARM_DOT(w0.s0123, (uchar4)1, sum); \
595 ARM_DOT(w0.s4567, (uchar4)1, sum); \
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100596 })
597
Georgios Pinitasdaa38552018-08-28 17:43:18 +0100598#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100599
Pablo Tello47104362019-02-27 13:32:51 +0000600#if defined(CONV_STRIDE_X) && defined(CONV_STRIDE_Y) && VEC_SIZE == 4
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100601/** This function computes the depthwise convolution quantized for NHWC data layout when the stride along the width or height is not 1.
602 *
Pablo Tello47104362019-02-27 13:32:51 +0000603 * @note This kernel assumes VEC_SIZE is 4.
giuros016d109962019-01-07 17:47:19 +0000604 * @note The weights tensor is expected to be reshaped using @ref CLDepthwiseConvolutionLayerReshapeWeightsKernel.
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100605 * @note The number of elements read per thread must be passed at compile time using -DVEC_SIZE (e.g. -DVEC_SIZE=2)
606 * @note Dimension two of the input tensor (height for NHWC data layout) must be passed at compile time using -DSRC_DIM2 (e.g. -DSRC_DIM_2=112)
607 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_TOP (e.g. -DCONV_PAD_TOP=1)
608 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_LEFT (e.g. -DCONV_PAD_LEFT=1)
609 * @note The convolution stride along the width must be passed at compile time using -DCONV_STRIDE_X (e.g. -DCONV_STRIDE_Y=X)
610 * @note The convolution stride along the height must be passed at compile time using -DCONV_STRIDE_Y (e.g. -DCONV_STRIDE_Y=1)
611 *
Georgios Pinitas37044642018-10-30 14:53:25 +0000612 * @param[in] src_ptr Pointer to the source tensor. Supported data types: QASYMM8
613 * @param[in] src_stride_x Stride of the source tensor in X dimension (in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100614 * @param[in] src_step_x src_stride_x * number of elements along X processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000615 * @param[in] src_stride_y Stride of the source tensor in Y dimension (in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100616 * @param[in] src_step_y src_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100617 * @param[in] src_stride_z Stride of the source tensor in Z dimension (in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000618 * @param[in] src_step_z src_stride_y * number of elements along Z processed per workitem(in bytes)
619 * @param[in] src_stride_w Stride of the source tensor in W dimension (in bytes)
620 * @param[in] src_step_w src_stride_w * number of elements along W processed per workitem(in bytes)
621 * @param[in] src_offset_first_element_in_bytes The offset of the first element in the source tensor
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100622 * @param[in] dst_ptr Pointer to the destination tensor. Supported data types: same as @p src_ptr
623 * @param[in] dst_stride_x Stride of the destination tensor in X dimension (in bytes)
624 * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes)
625 * @param[in] dst_stride_y Stride of the destination tensor in Y dimension (in bytes)
626 * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes)
627 * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes)
628 * @param[in] dst_step_z dst_stride_z * number of elements along Y processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000629 * @param[in] dst_stride_w Stride of the destination tensor in W dimension (in bytes)
630 * @param[in] dst_step_w dst_stride_w * number of elements along W processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100631 * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination tensor
giuros016d109962019-01-07 17:47:19 +0000632 * @param[in] weights_ptr Pointer to the weights tensor reshaped. Supported data types: same as @p src_ptr
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100633 * @param[in] weights_stride_x Stride of the weights tensor in X dimension (in bytes)
634 * @param[in] weights_step_x weights_stride_x * number of elements along X processed per workitem(in bytes)
635 * @param[in] weights_stride_y Stride of the weights tensor in Y dimension (in bytes)
636 * @param[in] weights_step_y weights_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100637 * @param[in] weights_offset_first_element_in_bytes The offset of the first element in the weights tensor
638 * @param[in] biases_ptr (Optional) Pointer to the biases vector. Supported data types: same as @p src_ptr
639 * @param[in] biases_stride_x (Optional) Stride of the biases vector in X dimension (in bytes)
640 * @param[in] biases_step_x (Optional) biases_stride_x * number of elements along X processed per workitem(in bytes)
641 * @param[in] biases_offset_first_element_in_bytes (Optional) The offset of the first element in the biases vector
642 * @param[in] max_offset Max offset for the input tensor
643 */
Pablo Tello47104362019-02-27 13:32:51 +0000644__kernel void dwc_3x3_reshaped_qasymm8_nhwc(
Georgios Pinitas37044642018-10-30 14:53:25 +0000645 TENSOR4D_DECLARATION(src),
646 TENSOR4D_DECLARATION(dst),
giuros016d109962019-01-07 17:47:19 +0000647 IMAGE_DECLARATION(weights),
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100648#if defined(HAS_BIAS)
649 VECTOR_DECLARATION(biases),
650#endif /* defined(HAS_BIAS) */
651 int max_offset)
652{
653 const int x = get_global_id(0); // channels
654 const int y = get_global_id(1); // spatial coordinate x
Georgios Pinitas37044642018-10-30 14:53:25 +0000655#if defined(DST_DEPTH)
656 int z = get_global_id(2) % (int)DST_DEPTH; // spatial coordinate y
657 int b = get_global_id(2) / (int)DST_DEPTH; // batch
Pablo Tello47104362019-02-27 13:32:51 +0000658#else // defined(DST_DEPTH)
659 int z = get_global_id(2); // spatial coordinate y
660#endif // defined(DST_DEPTH)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100661
giuros016d109962019-01-07 17:47:19 +0000662 __global uchar *weights_addr = weights_ptr + weights_offset_first_element_in_bytes + x * weights_stride_y;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100663
Georgios Pinitas37044642018-10-30 14:53:25 +0000664#if defined(DST_DEPTH)
665 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE + b * src_stride_w;
666#else /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100667 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE;
Georgios Pinitas37044642018-10-30 14:53:25 +0000668#endif /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100669
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100670 int z_coord = 0;
671 int4 offset = 0;
672 int4 y_coord = ((int4)(y * CONV_STRIDE_X) + (int4)(0, 1, 2, 3)) - (int)CONV_PAD_LEFT;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100673
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100674 // Only for y = 0 we can have a negative coordinate. If so, we convert it to SRC_DIM_1
675 y_coord.s0 = min((uint)y_coord.s0, (uint)SRC_DIM_1);
676 y_coord.s1 = min((uint)y_coord.s1, (uint)SRC_DIM_1);
677 y_coord.s2 = min((uint)y_coord.s2, (uint)SRC_DIM_1);
678 y_coord.s3 = min((uint)y_coord.s3, (uint)SRC_DIM_1);
679
680 int4 y_offset = convert_int4(y_coord * (int)src_stride_y);
681
giuros016d109962019-01-07 17:47:19 +0000682 // We compute VEC_SIZEx1x1 [C,W,H] elements
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100683 VEC_INT acc = 0, sum = 0;
684
685 // Load weights
Pablo Tello47104362019-02-27 13:32:51 +0000686 uchar16 w0_tmp = VLOAD(16)(0, weights_addr);
687 uchar16 w1_tmp = VLOAD(16)(0, weights_addr + 16);
688 uchar4 w8 = VLOAD(4)(0, weights_addr + 2 * 16);
689
690 uchar4 w0 = w0_tmp.s0123;
691 uchar4 w1 = w0_tmp.s4567;
692 uchar4 w2 = w0_tmp.s89AB;
693 uchar4 w3 = w0_tmp.sCDEF;
694
695 uchar4 w4 = w1_tmp.s0123;
696 uchar4 w5 = w1_tmp.s4567;
697 uchar4 w6 = w1_tmp.s89AB;
698 uchar4 w7 = w1_tmp.sCDEF;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100699
700#if INPUT_OFFSET != 0
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100701 VEC_INT sum_we = CONVERT(w0, VEC_INT) + CONVERT(w1, VEC_INT) + CONVERT(w2, VEC_INT)
702 + CONVERT(w3, VEC_INT) + CONVERT(w4, VEC_INT) + CONVERT(w5, VEC_INT)
703 + CONVERT(w6, VEC_INT) + CONVERT(w7, VEC_INT) + CONVERT(w8, VEC_INT);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100704#endif /* INPUT_OFFSET != 0 */
705
706 // Load input values
707 // z == 0
708 // Clamp z_coord as for z = 0, it can be negative
709 // z_coord is casted to unsigned int in order to use just a min() operation
710 // A "-1" 32 bit signed variable converted to unsigned gives 4294967295
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100711 z_coord = z * (int)CONV_STRIDE_Y - (int)CONV_PAD_TOP;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100712 z_coord = min((uint)z_coord, (uint)SRC_DIM_2);
713 offset = y_offset + (int4)(z_coord * src_stride_z);
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100714 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100715
716 VEC_UCHAR values0 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
717 VEC_UCHAR values1 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
718 VEC_UCHAR values2 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
719
720 // z == 1
721 // z_coord can be only negative for z = 0 so we do not need to clamp it
722 // Moreover z_coord cannot be out-of-bound for z = 1 so we do not need to clamp the offset
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100723 z_coord = z * (int)CONV_STRIDE_Y - (int)CONV_PAD_TOP + 1;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100724 offset = y_offset + (int4)(z_coord * src_stride_z);
725 VEC_UCHAR values3 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
726 VEC_UCHAR values4 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
727 VEC_UCHAR values5 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
728
729 // z == 2
730 // After z = 1 we can simply add src_stride_z to offset without updating z_coord
731 // However offset can be out-of-bound so we need to check if it is greater than max_offset
732 offset += (int4)src_stride_z;
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100733 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100734 VEC_UCHAR values6 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
735 VEC_UCHAR values7 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
736 VEC_UCHAR values8 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
737
738 MULTIPLY_ADD_ACCUMULATE(values0, w0, acc, sum);
739 MULTIPLY_ADD_ACCUMULATE(values1, w1, acc, sum);
740 MULTIPLY_ADD_ACCUMULATE(values2, w2, acc, sum);
741
742 MULTIPLY_ADD_ACCUMULATE(values3, w3, acc, sum);
743 MULTIPLY_ADD_ACCUMULATE(values4, w4, acc, sum);
744 MULTIPLY_ADD_ACCUMULATE(values5, w5, acc, sum);
745
746 MULTIPLY_ADD_ACCUMULATE(values6, w6, acc, sum);
747 MULTIPLY_ADD_ACCUMULATE(values7, w7, acc, sum);
748 MULTIPLY_ADD_ACCUMULATE(values8, w8, acc, sum);
749
750#if defined(HAS_BIAS)
751 Vector biases = CONVERT_TO_VECTOR_STRUCT(biases);
752 VEC_INT bias_values = VLOAD(VEC_SIZE)(0, (__global int *)biases.ptr);
753 acc += bias_values;
754#endif // defined(HAS_BIAS)
755
756#if WEIGHTS_OFFSET != 0
757 acc += WEIGHTS_OFFSET * sum;
758#endif /* WEIGHTS_OFFSET != 0 */
759
760#if INPUT_OFFSET != 0
761 acc += INPUT_OFFSET * sum_we;
762#endif /* INPUT_OFFSET != 0 */
763
764#if K_OFFSET != 0
765 acc += (VEC_INT)K_OFFSET;
766#endif /* K_OFFSET != 0 */
767
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100768#if defined(REAL_MULTIPLIER)
769
770 acc = CONVERT(round(CONVERT(acc, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
771
772#else // defined(REAL_MULTIPLIER)
773
Pablo Tello47104362019-02-27 13:32:51 +0000774 acc = asymm_mult_by_quant_multiplier_less_than_one(acc, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100775#endif // defined(REAL_MULTIPLIER)
776
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100777 acc += (VEC_INT)OUTPUT_OFFSET;
778
779 VEC_UCHAR res = CONVERT_SAT(acc, VEC_UCHAR);
780 res = CLAMP(res, (VEC_UCHAR)0, (VEC_UCHAR)255);
781
Georgios Pinitas37044642018-10-30 14:53:25 +0000782#if defined(DST_DEPTH)
783 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + z * dst_step_z + b * dst_stride_w;
784#else /* defined(DST_DEPTH) */
785 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + z * dst_step_z;
786#endif /* defined(DST_DEPTH) */
787
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100788 VSTORE(VEC_SIZE)
Georgios Pinitas37044642018-10-30 14:53:25 +0000789 (ACTIVATION_FUNC(res), 0, dst_addr);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100790}
791#endif // defined(CONV_STRIDE_X) && defined(CONV_STRIDE_Y)
792
Pablo Tello47104362019-02-27 13:32:51 +0000793#if defined(NUM_ROWS_PROCESSED) && defined(NUM_PLANES_PROCESSED) && VEC_SIZE == 4
giuros016d109962019-01-07 17:47:19 +0000794/** This function computes the depthwise convolution quantized for NHWC data layout when the stride along the width and height is 1.
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100795 *
Pablo Tello47104362019-02-27 13:32:51 +0000796 * @note This kernel assumes VEC_SIZE is 4.
giuros016d109962019-01-07 17:47:19 +0000797 * @note The weights tensor is expected to be reshaped using @ref CLDepthwiseConvolutionLayerReshapeWeightsKernel.
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100798 * @note The number of elements read per thread must be passed at compile time using -DVEC_SIZE (e.g. -DVEC_SIZE=2)
799 * @note Dimension two of the input tensor (height for NHWC data layout) must be passed at compile time using -DSRC_DIM2 (e.g. -DSRC_DIM_2=112)
800 * @note The number of rows processed per thread must be passed at compile time using -DNUM_ROWS_PROCESSED (i.e. -DNUM_ROWS_PROCESSED=2)
801 * @note The number of planes processed per thread must be passed at compile time using -DNUM_PLANES_PROCESSED (i.e. -DNUM_PLANES_PROCESSED=2)
802 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_TOP (e.g. -DCONV_PAD_TOP=1)
803 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_LEFT (e.g. -DCONV_PAD_LEFT=1).
804 *
Georgios Pinitas37044642018-10-30 14:53:25 +0000805 * @param[in] src_ptr Pointer to the source tensor. Supported data types: QASYMM8
806 * @param[in] src_stride_x Stride of the source tensor in X dimension (in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100807 * @param[in] src_step_x src_stride_x * number of elements along X processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000808 * @param[in] src_stride_y Stride of the source tensor in Y dimension (in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100809 * @param[in] src_step_y src_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100810 * @param[in] src_stride_z Stride of the source tensor in Z dimension (in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000811 * @param[in] src_step_z src_stride_y * number of elements along Z processed per workitem(in bytes)
812 * @param[in] src_stride_w Stride of the source tensor in W dimension (in bytes)
813 * @param[in] src_step_w src_stride_w * number of elements along W processed per workitem(in bytes)
814 * @param[in] src_offset_first_element_in_bytes The offset of the first element in the source tensor
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100815 * @param[in] dst_ptr Pointer to the destination tensor. Supported data types: same as @p src_ptr
816 * @param[in] dst_stride_x Stride of the destination tensor in X dimension (in bytes)
817 * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes)
818 * @param[in] dst_stride_y Stride of the destination tensor in Y dimension (in bytes)
819 * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes)
820 * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes)
821 * @param[in] dst_step_z dst_stride_z * number of elements along Y processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +0000822 * @param[in] dst_stride_w Stride of the destination tensor in W dimension (in bytes)
823 * @param[in] dst_step_w dst_stride_w * number of elements along W processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100824 * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination tensor
825 * @param[in] weights_ptr Pointer to the weights tensor. Supported data types: same as @p src_ptr
826 * @param[in] weights_stride_x Stride of the weights tensor in X dimension (in bytes)
827 * @param[in] weights_step_x weights_stride_x * number of elements along X processed per workitem(in bytes)
828 * @param[in] weights_stride_y Stride of the weights tensor in Y dimension (in bytes)
829 * @param[in] weights_step_y weights_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100830 * @param[in] weights_offset_first_element_in_bytes The offset of the first element in the weights tensor
831 * @param[in] biases_ptr (Optional) Pointer to the biases vector. Supported data types: same as @p src_ptr
832 * @param[in] biases_stride_x (Optional) Stride of the biases vector in X dimension (in bytes)
833 * @param[in] biases_step_x (Optional) biases_stride_x * number of elements along X processed per workitem(in bytes)
834 * @param[in] biases_offset_first_element_in_bytes (Optional) The offset of the first element in the biases vector
835 * @param[in] max_offset Max offset for the input tensor
836 */
837
Pablo Tello47104362019-02-27 13:32:51 +0000838__kernel void dwc_3x3_reshaped_qasymm8_stride1_nhwc(
Georgios Pinitas37044642018-10-30 14:53:25 +0000839 TENSOR4D_DECLARATION(src),
840 TENSOR4D_DECLARATION(dst),
giuros016d109962019-01-07 17:47:19 +0000841 IMAGE_DECLARATION(weights),
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000842#if defined(HAS_BIAS)
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100843 VECTOR_DECLARATION(biases),
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000844#endif /* defined(HAS_BIAS) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100845 int max_offset)
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000846{
Giorgio Arenafa23f112018-06-19 11:27:38 +0100847 int x = get_global_id(0);
848 int y = get_global_id(1);
Georgios Pinitas37044642018-10-30 14:53:25 +0000849#if defined(DST_DEPTH)
850 int z = get_global_id(2) % (int)DST_DEPTH; // spatial coordinate y
851 int b = get_global_id(2) / (int)DST_DEPTH; // batch
Pablo Tello47104362019-02-27 13:32:51 +0000852#else // defined(DST_DEPTH)
853 int z = get_global_id(2); // spatial coordinate y
854#endif // defined(DST_DEPTH)
Giorgio Arenafa23f112018-06-19 11:27:38 +0100855
giuros016d109962019-01-07 17:47:19 +0000856 __global uchar *weights_addr = weights_ptr + weights_offset_first_element_in_bytes + x * weights_stride_y;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100857
Georgios Pinitas37044642018-10-30 14:53:25 +0000858#if defined(DST_DEPTH)
859 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE + b * src_stride_w;
860#else /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100861 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE;
Georgios Pinitas37044642018-10-30 14:53:25 +0000862#endif /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100863
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100864 int z_coord = 0;
865 int4 offset = 0;
866 int4 y_coord = ((int4)(y * NUM_ROWS_PROCESSED) + (int4)(0, 1, 2, 3)) - (int)CONV_PAD_LEFT;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100867
Gian Marco Iodice4b908652018-10-18 10:21:02 +0100868 // Only for y = 0 we can have a negative coordinate. If so, we convert it to SRC_DIM_1
869 y_coord.s0 = min((uint)y_coord.s0, (uint)SRC_DIM_1);
870 y_coord.s1 = min((uint)y_coord.s1, (uint)SRC_DIM_1);
871 y_coord.s2 = min((uint)y_coord.s2, (uint)SRC_DIM_1);
872 y_coord.s3 = min((uint)y_coord.s3, (uint)SRC_DIM_1);
873
874 int4 y_offset = convert_int4(y_coord * (int)src_stride_y);
875
876 // We compute 4x2x2 [C,W,H] elements
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100877 VEC_INT acc0 = 0, sum0 = 0;
878 VEC_INT acc1 = 0, sum1 = 0;
879 VEC_INT acc2 = 0, sum2 = 0;
880 VEC_INT acc3 = 0, sum3 = 0;
881
882 // Load weights
Pablo Tello47104362019-02-27 13:32:51 +0000883 uchar16 w0_tmp = VLOAD(16)(0, weights_addr);
884 uchar16 w1_tmp = VLOAD(16)(0, weights_addr + 16);
885 uchar4 w8 = VLOAD(4)(0, weights_addr + 2 * 16);
886
887 uchar4 w0 = w0_tmp.s0123;
888 uchar4 w1 = w0_tmp.s4567;
889 uchar4 w2 = w0_tmp.s89AB;
890 uchar4 w3 = w0_tmp.sCDEF;
891
892 uchar4 w4 = w1_tmp.s0123;
893 uchar4 w5 = w1_tmp.s4567;
894 uchar4 w6 = w1_tmp.s89AB;
895 uchar4 w7 = w1_tmp.sCDEF;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100896
897#if INPUT_OFFSET != 0
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100898 VEC_INT sum_we = CONVERT(w0, VEC_INT) + CONVERT(w1, VEC_INT) + CONVERT(w2, VEC_INT)
899 + CONVERT(w3, VEC_INT) + CONVERT(w4, VEC_INT) + CONVERT(w5, VEC_INT)
900 + CONVERT(w6, VEC_INT) + CONVERT(w7, VEC_INT) + CONVERT(w8, VEC_INT);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100901#endif /* INPUT_OFFSET != 0 */
902
903 // Load input values
904 // z == 0
905 // Clamp z_coord as for z = 0, it can be negative
906 // z_coord is casted to unsigned int in order to use just a min() operation
907 // A "-1" 32 bit signed variable converted to unsigned gives 4294967295
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100908 z_coord = z * (int)NUM_PLANES_PROCESSED - (int)CONV_PAD_TOP;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100909 z_coord = min((uint)z_coord, (uint)SRC_DIM_2);
910 offset = y_offset + (int4)(z_coord * src_stride_z);
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100911 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100912
913 VEC_UCHAR values0 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
914 VEC_UCHAR values1 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
915 VEC_UCHAR values2 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
916 VEC_UCHAR values3 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
917
918 // z == 1
919 // z_coord can be only negative for z = 0 so we do not need to clamp it
920 // Moreover z_coord cannot be out-of-bound for z = 1 so we do not need to clamp the offset
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100921 z_coord = z * (int)NUM_PLANES_PROCESSED - (int)CONV_PAD_TOP + 1;
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100922 offset = y_offset + (int4)(z_coord * src_stride_z);
923 VEC_UCHAR values4 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
924 VEC_UCHAR values5 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
925 VEC_UCHAR values6 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
926 VEC_UCHAR values7 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
927
928 // z == 2
929 // After z = 1 we can simply add src_stride_z to offset without updating z_coord
930 // However offset can be out-of-bound so we need to check if it is greater than max_offset
931 offset += (int4)src_stride_z;
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100932 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100933 VEC_UCHAR values8 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
934 VEC_UCHAR values9 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
935 VEC_UCHAR values10 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
936 VEC_UCHAR values11 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
937
938 // z == 3
939 // After z = 1 we can simply add src_stride_z to offset without updating z_coord
940 // However offset can be out-of-bound so we need to check if it is greater than max_offset
941 offset += (int4)(src_stride_z);
Georgios Pinitas4e0d3812018-08-01 20:16:34 +0100942 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +0100943 VEC_UCHAR values12 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
944 VEC_UCHAR values13 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
945 VEC_UCHAR values14 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
946 VEC_UCHAR values15 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
947
948 MULTIPLY_ADD_ACCUMULATE(values0, w0, acc0, sum0);
949 MULTIPLY_ADD_ACCUMULATE(values1, w1, acc0, sum0);
950 MULTIPLY_ADD_ACCUMULATE(values2, w2, acc0, sum0);
951 MULTIPLY_ADD_ACCUMULATE(values1, w0, acc1, sum1);
952 MULTIPLY_ADD_ACCUMULATE(values2, w1, acc1, sum1);
953 MULTIPLY_ADD_ACCUMULATE(values3, w2, acc1, sum1);
954
955 MULTIPLY_ADD_ACCUMULATE(values4, w3, acc0, sum0);
956 MULTIPLY_ADD_ACCUMULATE(values5, w4, acc0, sum0);
957 MULTIPLY_ADD_ACCUMULATE(values6, w5, acc0, sum0);
958 MULTIPLY_ADD_ACCUMULATE(values5, w3, acc1, sum1);
959 MULTIPLY_ADD_ACCUMULATE(values6, w4, acc1, sum1);
960 MULTIPLY_ADD_ACCUMULATE(values7, w5, acc1, sum1);
961
962 MULTIPLY_ADD_ACCUMULATE(values8, w6, acc0, sum0);
963 MULTIPLY_ADD_ACCUMULATE(values9, w7, acc0, sum0);
964 MULTIPLY_ADD_ACCUMULATE(values10, w8, acc0, sum0);
965 MULTIPLY_ADD_ACCUMULATE(values9, w6, acc1, sum1);
966 MULTIPLY_ADD_ACCUMULATE(values10, w7, acc1, sum1);
967 MULTIPLY_ADD_ACCUMULATE(values11, w8, acc1, sum1);
968
969 MULTIPLY_ADD_ACCUMULATE(values4, w0, acc2, sum2);
970 MULTIPLY_ADD_ACCUMULATE(values5, w1, acc2, sum2);
971 MULTIPLY_ADD_ACCUMULATE(values6, w2, acc2, sum2);
972 MULTIPLY_ADD_ACCUMULATE(values5, w0, acc3, sum3);
973 MULTIPLY_ADD_ACCUMULATE(values6, w1, acc3, sum3);
974 MULTIPLY_ADD_ACCUMULATE(values7, w2, acc3, sum3);
975
976 MULTIPLY_ADD_ACCUMULATE(values8, w3, acc2, sum2);
977 MULTIPLY_ADD_ACCUMULATE(values9, w4, acc2, sum2);
978 MULTIPLY_ADD_ACCUMULATE(values10, w5, acc2, sum2);
979 MULTIPLY_ADD_ACCUMULATE(values9, w3, acc3, sum3);
980 MULTIPLY_ADD_ACCUMULATE(values10, w4, acc3, sum3);
981 MULTIPLY_ADD_ACCUMULATE(values11, w5, acc3, sum3);
982
983 MULTIPLY_ADD_ACCUMULATE(values12, w6, acc2, sum2);
984 MULTIPLY_ADD_ACCUMULATE(values13, w7, acc2, sum2);
985 MULTIPLY_ADD_ACCUMULATE(values14, w8, acc2, sum2);
986 MULTIPLY_ADD_ACCUMULATE(values13, w6, acc3, sum3);
987 MULTIPLY_ADD_ACCUMULATE(values14, w7, acc3, sum3);
988 MULTIPLY_ADD_ACCUMULATE(values15, w8, acc3, sum3);
989
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000990#if defined(HAS_BIAS)
991 Vector biases = CONVERT_TO_VECTOR_STRUCT(biases);
992
993 VEC_INT bias_values = VLOAD(VEC_SIZE)(0, (__global int *)biases.ptr);
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000994
Giorgio Arenadfca60b2018-01-31 10:30:59 +0000995 acc0 += bias_values;
996 acc1 += bias_values;
997 acc2 += bias_values;
998 acc3 += bias_values;
999#endif /* defined(HAS_BIAS) */
1000
1001#if WEIGHTS_OFFSET != 0
1002 acc0 += WEIGHTS_OFFSET * sum0;
1003 acc1 += WEIGHTS_OFFSET * sum1;
1004 acc2 += WEIGHTS_OFFSET * sum2;
1005 acc3 += WEIGHTS_OFFSET * sum3;
1006#endif /* WEIGHTS_OFFSET != 0 */
1007
1008#if INPUT_OFFSET != 0
1009 VEC_INT offs = INPUT_OFFSET * sum_we;
1010
1011 acc0 += offs;
1012 acc1 += offs;
1013 acc2 += offs;
1014 acc3 += offs;
1015#endif /* INPUT_OFFSET != 0 */
1016
1017#if K_OFFSET != 0
1018 acc0 += (VEC_INT)K_OFFSET;
1019 acc1 += (VEC_INT)K_OFFSET;
1020 acc2 += (VEC_INT)K_OFFSET;
1021 acc3 += (VEC_INT)K_OFFSET;
1022#endif /* K_OFFSET != 0 */
1023
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001024#if defined(REAL_MULTIPLIER)
1025
1026 acc0 = CONVERT(round(CONVERT(acc0, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1027 acc1 = CONVERT(round(CONVERT(acc1, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1028 acc2 = CONVERT(round(CONVERT(acc2, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1029 acc3 = CONVERT(round(CONVERT(acc3, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1030
1031#else // defined(REAL_MULTIPLIER)
1032
Pablo Tello47104362019-02-27 13:32:51 +00001033 acc0 = asymm_mult_by_quant_multiplier_less_than_one(acc0, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
1034 acc1 = asymm_mult_by_quant_multiplier_less_than_one(acc1, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
1035 acc2 = asymm_mult_by_quant_multiplier_less_than_one(acc2, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
1036 acc3 = asymm_mult_by_quant_multiplier_less_than_one(acc3, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001037
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001038#endif // defined(REAL_MULTIPLIER)
1039
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001040 acc0 += (VEC_INT)OUTPUT_OFFSET;
1041 acc1 += (VEC_INT)OUTPUT_OFFSET;
1042 acc2 += (VEC_INT)OUTPUT_OFFSET;
1043 acc3 += (VEC_INT)OUTPUT_OFFSET;
1044
1045 VEC_UCHAR res0 = CONVERT_SAT(acc0, VEC_UCHAR);
1046 VEC_UCHAR res1 = CONVERT_SAT(acc1, VEC_UCHAR);
1047 VEC_UCHAR res2 = CONVERT_SAT(acc2, VEC_UCHAR);
1048 VEC_UCHAR res3 = CONVERT_SAT(acc3, VEC_UCHAR);
1049
1050 res0 = CLAMP(res0, (VEC_UCHAR)0, (VEC_UCHAR)255);
1051 res1 = CLAMP(res1, (VEC_UCHAR)0, (VEC_UCHAR)255);
1052 res2 = CLAMP(res2, (VEC_UCHAR)0, (VEC_UCHAR)255);
1053 res3 = CLAMP(res3, (VEC_UCHAR)0, (VEC_UCHAR)255);
1054
Georgios Pinitas37044642018-10-30 14:53:25 +00001055#if defined(DST_DEPTH)
1056 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + (z * NUM_PLANES_PROCESSED) * dst_step_z + b * dst_stride_w;
1057#else /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001058 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + (z * NUM_PLANES_PROCESSED) * dst_step_z;
Georgios Pinitas37044642018-10-30 14:53:25 +00001059#endif /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001060
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001061 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001062 (ACTIVATION_FUNC(res0), 0, dst_addr + 0 * dst_stride_y);
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001063 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001064 (ACTIVATION_FUNC(res1), 0, dst_addr + 1 * dst_stride_y);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001065
1066#if((DST_DIM_2 % NUM_PLANES_PROCESSED) != 0)
1067 if((z * NUM_PLANES_PROCESSED + 1) < DST_DIM_2)
1068#endif // ((DST_DIM_2 % NUM_PLANES_PROCESSED) != 0)
1069 {
1070 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001071 (ACTIVATION_FUNC(res2), 0, dst_addr + 0 * dst_stride_y + 1 * dst_stride_z);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001072 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001073 (ACTIVATION_FUNC(res3), 0, dst_addr + 1 * dst_stride_y + 1 * dst_stride_z);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001074 }
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001075}
1076
giuros016d109962019-01-07 17:47:19 +00001077#if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) && VEC_SIZE == 4
1078/** This function computes the depthwise convolution quantized for NHWC data layout when the stride along the width and height is 1 using dot product.
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001079 *
giuros016d109962019-01-07 17:47:19 +00001080 * @note This kernel assumes VEC_SIZE is 4.
1081 * @note The weights tensor is expected to be reshaped using @ref CLDepthwiseConvolutionLayerReshapeWeightsKernel.
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001082 * @note The number of elements read per thread must be passed at compile time using -DVEC_SIZE (e.g. -DVEC_SIZE=2)
1083 * @note Dimension two of the input tensor (height for NHWC data layout) must be passed at compile time using -DSRC_DIM2 (e.g. -DSRC_DIM_2=112)
1084 * @note The number of rows processed per thread must be passed at compile time using -DNUM_ROWS_PROCESSED (i.e. -DNUM_ROWS_PROCESSED=2)
1085 * @note The number of planes processed per thread must be passed at compile time using -DNUM_PLANES_PROCESSED (i.e. -DNUM_PLANES_PROCESSED=2)
1086 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_TOP (e.g. -DCONV_PAD_TOP=1)
1087 * @note The convolution pad top must be passed at compile time using -DCONV_PAD_LEFT (e.g. -DCONV_PAD_LEFT=1).
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001088 * @note If REAL_MULTIPLIER is passed at compile time (i.e. -DREAL_MULTIPLIER=1.355f), the final quantization is performed using a floating point multiplication.
1089 * If not, the quantization will be performed using a fixed point multiplication
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001090 *
Georgios Pinitas37044642018-10-30 14:53:25 +00001091 * @param[in] src_ptr Pointer to the source tensor. Supported data types: QASYMM8
1092 * @param[in] src_stride_x Stride of the source tensor in X dimension (in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001093 * @param[in] src_step_x src_stride_x * number of elements along X processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +00001094 * @param[in] src_stride_y Stride of the source tensor in Y dimension (in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001095 * @param[in] src_step_y src_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001096 * @param[in] src_stride_z Stride of the source tensor in Z dimension (in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +00001097 * @param[in] src_step_z src_stride_y * number of elements along Z processed per workitem(in bytes)
1098 * @param[in] src_stride_w Stride of the source tensor in W dimension (in bytes)
1099 * @param[in] src_step_w src_stride_w * number of elements along W processed per workitem(in bytes)
1100 * @param[in] src_offset_first_element_in_bytes The offset of the first element in the source tensor
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001101 * @param[in] dst_ptr Pointer to the destination tensor. Supported data types: QASYMM8
1102 * @param[in] dst_stride_x Stride of the destination tensor in X dimension (in bytes)
1103 * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes)
1104 * @param[in] dst_stride_y Stride of the destination tensor in Y dimension (in bytes)
1105 * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes)
1106 * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes)
1107 * @param[in] dst_step_z dst_stride_z * number of elements along Y processed per workitem(in bytes)
Georgios Pinitas37044642018-10-30 14:53:25 +00001108 * @param[in] dst_stride_w Stride of the destination tensor in W dimension (in bytes)
1109 * @param[in] dst_step_w dst_stride_w * number of elements along W processed per workitem(in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001110 * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination tensor
1111 * @param[in] weights_ptr Pointer to the weights tensor. Supported data types: QASYMM8
1112 * @param[in] weights_stride_x Stride of the weights tensor in X dimension (in bytes)
1113 * @param[in] weights_step_x weights_stride_x * number of elements along X processed per workitem(in bytes)
1114 * @param[in] weights_stride_y Stride of the weights tensor in Y dimension (in bytes)
1115 * @param[in] weights_step_y weights_stride_y * number of elements along Y processed per workitem(in bytes)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001116 * @param[in] weights_offset_first_element_in_bytes The offset of the first element in the weights tensor
1117 * @param[in] biases_ptr (Optional) Pointer to the biases vector. Supported data types: QASYMM8
1118 * @param[in] biases_stride_x (Optional) Stride of the biases vector in X dimension (in bytes)
1119 * @param[in] biases_step_x (Optional) biases_stride_x * number of elements along X processed per workitem(in bytes)
1120 * @param[in] biases_offset_first_element_in_bytes (Optional) The offset of the first element in the biases vector
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001121 * @param[in] max_offset The maximum allowed offset for the input tensor
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001122 */
Pablo Tello47104362019-02-27 13:32:51 +00001123__kernel void dwc_3x3_reshaped_qasymm8_dot8_stride1_nhwc(
Georgios Pinitas37044642018-10-30 14:53:25 +00001124 TENSOR4D_DECLARATION(src),
1125 TENSOR4D_DECLARATION(dst),
giuros016d109962019-01-07 17:47:19 +00001126 IMAGE_DECLARATION(weights),
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001127#if defined(HAS_BIAS)
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001128 VECTOR_DECLARATION(biases),
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001129#endif // defined(HAS_BIAS)
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001130 int max_offset)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001131{
Giorgio Arenafa23f112018-06-19 11:27:38 +01001132 int x = get_global_id(0);
1133 int y = get_global_id(1);
Georgios Pinitas37044642018-10-30 14:53:25 +00001134#if defined(DST_DEPTH)
1135 int z = get_global_id(2) % (int)DST_DEPTH; // spatial coordinate y
1136 int b = get_global_id(2) / (int)DST_DEPTH; // batch
Pablo Tello47104362019-02-27 13:32:51 +00001137#else // defined(DST_DEPTH)
1138 int z = get_global_id(2); // spatial coordinate y
1139#endif // defined(DST_DEPTH)
Giorgio Arenafa23f112018-06-19 11:27:38 +01001140
giuros016d109962019-01-07 17:47:19 +00001141 __global uchar *weights_addr = weights_ptr + weights_offset_first_element_in_bytes + x * weights_stride_y;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001142
Georgios Pinitas37044642018-10-30 14:53:25 +00001143#if defined(DST_DEPTH)
1144 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE + b * src_stride_w;
1145#else /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001146 __global uchar *src_addr = src_ptr + src_offset_first_element_in_bytes + x * VEC_SIZE;
Georgios Pinitas37044642018-10-30 14:53:25 +00001147#endif /* defined(DST_DEPTH) */
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001148
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001149 int z_coord = 0;
1150 int4 offset = 0;
1151 int4 y_coord = ((int4)(y * NUM_ROWS_PROCESSED) + (int4)(0, 1, 2, 3)) - (int)CONV_PAD_LEFT;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001152
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001153 // Only for y = 0 we can have a negative coordinate. If so, we convert it to SRC_DIM_1
1154 y_coord.s0 = min((uint)y_coord.s0, (uint)SRC_DIM_1);
1155 y_coord.s1 = min((uint)y_coord.s1, (uint)SRC_DIM_1);
1156 y_coord.s2 = min((uint)y_coord.s2, (uint)SRC_DIM_1);
1157 y_coord.s3 = min((uint)y_coord.s3, (uint)SRC_DIM_1);
1158
1159 int4 y_offset = convert_int4(y_coord * (int)src_stride_y);
1160
1161 // We compute 4x2x1 [C,W,H] elements
1162 VEC_INT acc0 = 0;
1163 VEC_INT acc1 = 0;
1164 VEC_INT sum0 = 0;
1165 VEC_INT sum1 = 0;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001166
1167 // Load weights
giuros016d109962019-01-07 17:47:19 +00001168 uchar16 w0 = VLOAD(16)(0, weights_addr);
1169 uchar16 w1 = VLOAD(16)(0, weights_addr + 16);
1170 uchar4 w2 = VLOAD(4)(0, weights_addr + 32);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001171
1172#if INPUT_OFFSET != 0
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001173 // Initilize the final result with the weights reduction multiplied by INPUT_OFFSET
giuros016d109962019-01-07 17:47:19 +00001174 DOT_PRODUCT_REDUCTION_WEIGHTS(acc0.s0, w0.s01234567, w0.s8);
1175 DOT_PRODUCT_REDUCTION_WEIGHTS(acc0.s1, (uchar8)((w0.s9ABC), (w0.sDEF), w1.s0), w1.s1);
1176 DOT_PRODUCT_REDUCTION_WEIGHTS(acc0.s2, w1.s23456789, w1.sA);
1177 DOT_PRODUCT_REDUCTION_WEIGHTS(acc0.s3, (uchar8)((w1.sBCD), (w1.sEF), (w2.s012)), w2.s3);
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001178
1179 // Multiply the weights reduction with INPUT_OFFSET
1180 acc0 = INPUT_OFFSET * acc0;
1181
1182 acc1 = acc0;
1183#endif // INPUT_OFFSET != 0
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001184
1185 // Load input values
1186 // z == 0
1187 // Clamp z_coord as for z = 0, it can be negative
1188 // z_coord is casted to unsigned int in order to use just a min() operation
1189 // A "-1" 32 bit signed variable converted to unsigned gives 4294967295
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001190 z_coord = z - (int)CONV_PAD_TOP;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001191 z_coord = min((uint)z_coord, (uint)SRC_DIM_2);
1192 offset = y_offset + (int4)(z_coord * src_stride_z);
Georgios Pinitas4e0d3812018-08-01 20:16:34 +01001193 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001194
1195 VEC_UCHAR values0 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
1196 VEC_UCHAR values1 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
1197 VEC_UCHAR values2 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
1198 VEC_UCHAR values3 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
1199
1200 // z == 1
1201 // z_coord can be only negative for z = 0 so we do not need to clamp it
1202 // Moreover z_coord cannot be out-of-bound for z = 1 so we do not need to clamp the offset
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001203 z_coord = z - (int)CONV_PAD_TOP + 1;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001204 offset = y_offset + (int4)(z_coord * src_stride_z);
1205 VEC_UCHAR values4 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
1206 VEC_UCHAR values5 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
1207 VEC_UCHAR values6 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
1208 VEC_UCHAR values7 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
1209
1210 // z == 2
1211 // After z = 1 we can simply add src_stride_z to offset without updating z_coord
1212 // However offset can be out-of-bound so we need to check if it is greater than max_offset
1213 offset += (int4)src_stride_z;
Georgios Pinitas4e0d3812018-08-01 20:16:34 +01001214 offset = min(offset, (int4)max_offset);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001215 VEC_UCHAR values8 = VLOAD(VEC_SIZE)(0, src_addr + offset.s0);
1216 VEC_UCHAR values9 = VLOAD(VEC_SIZE)(0, src_addr + offset.s1);
1217 VEC_UCHAR values10 = VLOAD(VEC_SIZE)(0, src_addr + offset.s2);
1218 VEC_UCHAR values11 = VLOAD(VEC_SIZE)(0, src_addr + offset.s3);
1219
giuros016d109962019-01-07 17:47:19 +00001220 DOT_PRODUCT_REDUCTION(sum0.s0, values0.s0, values1.s0, values2.s0, values4.s0, values5.s0, values6.s0, values8.s0, values9.s0, values10.s0);
1221 DOT_PRODUCT_REDUCTION(sum1.s0, values1.s0, values2.s0, values3.s0, values5.s0, values6.s0, values7.s0, values9.s0, values10.s0, values11.s0);
1222 DOT_PRODUCT(acc0.s0, values0.s0, values1.s0, values2.s0, values4.s0, values5.s0, values6.s0, values8.s0, values9.s0, values10.s0, w0.s01234567, w0.s8);
1223 DOT_PRODUCT(acc1.s0, values1.s0, values2.s0, values3.s0, values5.s0, values6.s0, values7.s0, values9.s0, values10.s0, values11.s0, w0.s01234567, w0.s8);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001224
giuros016d109962019-01-07 17:47:19 +00001225 DOT_PRODUCT_REDUCTION(sum0.s1, values0.s1, values1.s1, values2.s1, values4.s1, values5.s1, values6.s1, values8.s1, values9.s1, values10.s1);
1226 DOT_PRODUCT_REDUCTION(sum1.s1, values1.s1, values2.s1, values3.s1, values5.s1, values6.s1, values7.s1, values9.s1, values10.s1, values11.s1);
1227 DOT_PRODUCT(acc0.s1, values0.s1, values1.s1, values2.s1, values4.s1, values5.s1, values6.s1, values8.s1, values9.s1, values10.s1, (uchar8)((w0.s9ABC), (w0.sDEF), w1.s0), w1.s1);
1228 DOT_PRODUCT(acc1.s1, values1.s1, values2.s1, values3.s1, values5.s1, values6.s1, values7.s1, values9.s1, values10.s1, values11.s1, (uchar8)((w0.s9ABC), (w0.sDEF), w1.s0), w1.s1);
1229
1230 DOT_PRODUCT_REDUCTION(sum0.s2, values0.s2, values1.s2, values2.s2, values4.s2, values5.s2, values6.s2, values8.s2, values9.s2, values10.s2);
1231 DOT_PRODUCT_REDUCTION(sum1.s2, values1.s2, values2.s2, values3.s2, values5.s2, values6.s2, values7.s2, values9.s2, values10.s2, values11.s2);
1232 DOT_PRODUCT(acc0.s2, values0.s2, values1.s2, values2.s2, values4.s2, values5.s2, values6.s2, values8.s2, values9.s2, values10.s2, w1.s23456789, w1.sA);
1233 DOT_PRODUCT(acc1.s2, values1.s2, values2.s2, values3.s2, values5.s2, values6.s2, values7.s2, values9.s2, values10.s2, values11.s2, w1.s23456789, w1.sA);
1234
1235 DOT_PRODUCT_REDUCTION(sum0.s3, values0.s3, values1.s3, values2.s3, values4.s3, values5.s3, values6.s3, values8.s3, values9.s3, values10.s3);
1236 DOT_PRODUCT_REDUCTION(sum1.s3, values1.s3, values2.s3, values3.s3, values5.s3, values6.s3, values7.s3, values9.s3, values10.s3, values11.s3);
1237 DOT_PRODUCT(acc0.s3, values0.s3, values1.s3, values2.s3, values4.s3, values5.s3, values6.s3, values8.s3, values9.s3, values10.s3, (uchar8)((w1.sBCD), (w1.sEF), (w2.s012)), w2.s3);
1238 DOT_PRODUCT(acc1.s3, values1.s3, values2.s3, values3.s3, values5.s3, values6.s3, values7.s3, values9.s3, values10.s3, values11.s3, (uchar8)((w1.sBCD), (w1.sEF), (w2.s012)), w2.s3);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001239
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001240#if defined(HAS_BIAS)
1241 Vector biases = CONVERT_TO_VECTOR_STRUCT(biases);
1242
1243 VEC_INT bias_values = VLOAD(VEC_SIZE)(0, (__global int *)biases.ptr);
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001244
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001245 acc0 += bias_values;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001246 acc1 += bias_values;
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001247
1248#endif // defined(HAS_BIAS)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001249
1250#if WEIGHTS_OFFSET != 0
1251 acc0 += WEIGHTS_OFFSET * sum0;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001252 acc1 += WEIGHTS_OFFSET * sum1;
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001253#endif // WEIGHTS_OFFSET != 0
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001254
1255#if K_OFFSET != 0
1256 acc0 += (VEC_INT)K_OFFSET;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001257 acc1 += (VEC_INT)K_OFFSET;
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001258
1259#endif // K_OFFSET != 0
1260
1261#if defined(REAL_MULTIPLIER)
1262
1263 acc0 = CONVERT(round(CONVERT(acc0, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1264 acc1 = CONVERT(round(CONVERT(acc1, VEC_FLOAT) * (VEC_FLOAT)REAL_MULTIPLIER), VEC_INT);
1265
1266#else // defined(REAL_MULTIPLIER)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001267
Pablo Tello47104362019-02-27 13:32:51 +00001268 acc0 = asymm_mult_by_quant_multiplier_less_than_one(acc0, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
1269 acc1 = asymm_mult_by_quant_multiplier_less_than_one(acc1, OUTPUT_MULTIPLIER, OUTPUT_SHIFT);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001270
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001271#endif // defined(REAL_MULTIPLIER)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001272 acc0 += (VEC_INT)OUTPUT_OFFSET;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001273 acc1 += (VEC_INT)OUTPUT_OFFSET;
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001274
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001275 VEC_UCHAR res0 = CONVERT_SAT(acc0, VEC_UCHAR);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001276 VEC_UCHAR res1 = CONVERT_SAT(acc1, VEC_UCHAR);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001277
1278 res0 = CLAMP(res0, (VEC_UCHAR)0, (VEC_UCHAR)255);
1279 res1 = CLAMP(res1, (VEC_UCHAR)0, (VEC_UCHAR)255);
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001280
Georgios Pinitas37044642018-10-30 14:53:25 +00001281#if defined(DST_DEPTH)
1282 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + z * dst_step_z + b * dst_stride_w;
1283#else /* defined(DST_DEPTH) */
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001284 __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + x * dst_step_x + y * dst_step_y + z * dst_step_z;
Georgios Pinitas37044642018-10-30 14:53:25 +00001285#endif /* defined(DST_DEPTH) */
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001286
1287 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001288 (ACTIVATION_FUNC(res0), 0, dst_addr + 0 * dst_stride_y);
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001289 VSTORE(VEC_SIZE)
Georgios Pinitas60e98252018-10-22 16:17:20 +01001290 (ACTIVATION_FUNC(res1), 0, dst_addr + 1 * dst_stride_y);
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001291}
giuros016d109962019-01-07 17:47:19 +00001292#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) && VEC_SIZE==4
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001293
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001294#endif // defined(NUM_ROWS_PROCESSED) && defined(NUM_PLANES_PROCESSED)
Giorgio Arenadfca60b2018-01-31 10:30:59 +00001295
Giorgio Arenaeff8d952018-07-02 15:29:57 +01001296#endif // defined(VEC_SIZE) && defined(SRC_DIM_1) && defined(SRC_DIM_2) && defined(CONV_PAD_TOP) && defined(CONV_PAD_LEFT)
1297
Gian Marco Iodice4b908652018-10-18 10:21:02 +01001298#endif // defined(WEIGHTS_OFFSET) && defined(INPUT_OFFSET) && defined(K_OFFSET) && ((defined(OUTPUT_OFFSET) && defined(OUTPUT_MULTIPLIER) && defined(OUTPUT_SHIFT)) || defined(REAL_MULTIPLIER))