blob: b3710555dfaf425d376e69fdcfc648962abc18a2 [file] [log] [blame]
Michalis Spyrou110b9202018-12-28 16:32:49 +00001/*
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +01002 * Copyright (c) 2018-2021, 2023 Arm Limited.
Michalis Spyrou110b9202018-12-28 16:32:49 +00003 *
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 */
Michalis Spyrouebcebf12020-10-21 00:04:14 +010024#include "src/core/NEON/kernels/NEReverseKernel.h"
Michalis Spyrou110b9202018-12-28 16:32:49 +000025
Michalis Spyrou110b9202018-12-28 16:32:49 +000026#include "arm_compute/core/TensorInfo.h"
Michalis Spyrou110b9202018-12-28 16:32:49 +000027#include "arm_compute/core/Validate.h"
28#include "arm_compute/core/Window.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010029
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +010030#include "src/core/helpers/AutoConfiguration.h"
31#include "src/core/helpers/WindowHelpers.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010032#include "src/core/NEON/wrapper/wrapper.h"
Michalis Spyrou110b9202018-12-28 16:32:49 +000033
Michalis Spyrou110b9202018-12-28 16:32:49 +000034namespace arm_compute
35{
36namespace
37{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010038Status
39validate_arguments(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, bool use_inverted_axis)
Michalis Spyrou110b9202018-12-28 16:32:49 +000040{
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010041 ARM_COMPUTE_UNUSED(use_inverted_axis);
Georgios Pinitas8f5802f2019-02-22 11:08:32 +000042 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output, axis);
Michele Di Giorgio33f41fa2021-03-09 14:09:08 +000043 //Note: ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input) is not needed here as this kernel doesn't use CPU FP16 instructions.
Georgios Pinitas33843562019-12-10 13:33:18 +000044 ARM_COMPUTE_RETURN_ERROR_ON(input->data_type() == DataType::UNKNOWN);
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010045 ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(axis, 1, DataType::U32, DataType::S32);
Michalis Spyrou110b9202018-12-28 16:32:49 +000046 ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis->num_dimensions() > 1, "Axis must be a 1D tensor");
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010047 ARM_COMPUTE_RETURN_ERROR_ON_MSG(input->num_dimensions() > 4,
48 "Current implementation only supports up to 4 dimensions.");
Michalis Spyrou110b9202018-12-28 16:32:49 +000049 ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis->dimension(0) > 4, "Only up to 4 dimensions can be reversed");
50
51 // Checks performed when output is configured
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010052 if (output->total_size() != 0)
Michalis Spyrou110b9202018-12-28 16:32:49 +000053 {
54 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(input, output);
55 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
Isabella Gottardi0a1090a2019-02-14 18:07:36 +000056 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_QUANTIZATION_INFO(input, output);
Michalis Spyrou110b9202018-12-28 16:32:49 +000057 }
58
59 return Status{};
60}
61} // namespace
62
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010063NEReverseKernel::NEReverseKernel() : _input(nullptr), _output(nullptr), _axis(nullptr), _use_inverted_axis(false)
Michalis Spyrou110b9202018-12-28 16:32:49 +000064{
65}
66
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010067void NEReverseKernel::configure(const ITensor *input, ITensor *output, const ITensor *axis, bool use_inverted_axis)
Michalis Spyrou110b9202018-12-28 16:32:49 +000068{
69 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output, axis);
70
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010071 _input = input;
72 _output = output;
73 _axis = axis;
74 _use_inverted_axis = use_inverted_axis;
Michalis Spyrou110b9202018-12-28 16:32:49 +000075
76 // Output tensor auto initialization if not yet initialized
77 auto_init_if_empty(*output->info(), *input->info()->clone());
78
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010079 ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info(), axis->info(), use_inverted_axis));
Michalis Spyrou110b9202018-12-28 16:32:49 +000080
81 // Configure kernel window
82 INEKernel::configure(calculate_max_window(*output->info()));
83}
84
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010085Status NEReverseKernel::validate(const ITensorInfo *input,
86 const ITensorInfo *output,
87 const ITensorInfo *axis,
88 bool use_inverted_axis)
Michalis Spyrou110b9202018-12-28 16:32:49 +000089{
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010090 ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output, axis, use_inverted_axis));
Michalis Spyrou110b9202018-12-28 16:32:49 +000091
92 return Status{};
93}
94
95template <typename T>
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010096void run_reverse(
97 const Window &window, const ITensor *input, const ITensor *axis, ITensor *output, bool use_inverted_axis)
Michalis Spyrou110b9202018-12-28 16:32:49 +000098{
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +010099 unsigned int axis_bit = 0;
100 const int rank = input->info()->num_dimensions();
101
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100102 for (unsigned int i = 0; i < axis->info()->dimension(0); ++i)
Michalis Spyrou110b9202018-12-28 16:32:49 +0000103 {
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100104 int axis_i = *(reinterpret_cast<const int *>(axis->buffer()) + i);
105
106 // The values of axis tensor must be between [-rank, rank-1].
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100107 if ((axis_i < -rank) || (axis_i >= rank))
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100108 {
Adnan AlSinan0a99c792023-10-03 15:15:41 +0100109 ARM_COMPUTE_ERROR("the values of the axis tensor must be within [-rank, rank-1].");
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100110 }
111
112 // In case of negative axis value i.e targeted axis(i) = rank + axis(i)
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100113 if (axis_i < 0)
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100114 {
115 axis_i = rank + axis_i;
116 }
117
118 // Reverse ACL axis indices convention i.e. (inverted)axis = (tensor_rank - 1) - axis
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100119 if (use_inverted_axis)
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100120 {
121 axis_i = (rank - 1) - axis_i;
122 }
123
Michalis Spyrou110b9202018-12-28 16:32:49 +0000124 axis_bit |= 1 << axis_i;
125 }
126
127 // Check if we need a left-over loop for the y dimension
Luca Foschianifedefc32020-02-17 17:02:49 +0000128 const int window_step_x = 16 / input->info()->element_size();
129 const int window_start_x = window.x().start();
130 const int window_end_x = window.x().end();
Michalis Spyrou110b9202018-12-28 16:32:49 +0000131
Luca Foschianifedefc32020-02-17 17:02:49 +0000132 Window win(window);
133 win.set(Window::DimX, Window::Dimension(0, 1, 1));
Michalis Spyrou110b9202018-12-28 16:32:49 +0000134
Luca Foschianifedefc32020-02-17 17:02:49 +0000135 Iterator input_it(input, win);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100136 execute_window_loop(
137 win,
138 [&](const Coordinates &id)
Michalis Spyrou110b9202018-12-28 16:32:49 +0000139 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100140 int x = window_start_x;
141 for (; x <= (window_end_x - window_step_x); x += window_step_x)
Michalis Spyrou110b9202018-12-28 16:32:49 +0000142 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100143 auto in = wrapper::vloadq(reinterpret_cast<T *>(input_it.ptr()) + x);
144
145 // Reverse 0 axis
146 if (axis_bit & 0x1)
147 {
148 in = wrapper::vrev64(in);
149 in = wrapper::vcombine(wrapper::vgethigh(in), wrapper::vgetlow(in));
150 }
151
152 const int offset_x = (axis_bit & 0x1) ? output->info()->dimension(0) - x - window_step_x : x;
153 const int offset_y = (axis_bit & 0x2) ? output->info()->dimension(1) - id.y() - 1 : id.y();
154 const int offset_z = (axis_bit & 0x4) ? output->info()->dimension(2) - id.z() - 1 : id.z();
155 const int offset_w = (axis_bit & 0x8) ? output->info()->dimension(3) - id[3] - 1 : id[3];
156
157 auto out_ptr =
158 reinterpret_cast<T *>(output->ptr_to_element(Coordinates(offset_x, offset_y, offset_z, offset_w)));
159 wrapper::vstore(out_ptr, in);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000160 }
161
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100162 // Compute left-over elements
163 for (; x < window_end_x; ++x)
164 {
165 const auto in = *(reinterpret_cast<T *>(input_it.ptr()) + x);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000166
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100167 const int offset_x = (axis_bit & 0x1) ? output->info()->dimension(0) - x - 1 : x;
168 const int offset_y = (axis_bit & 0x2) ? output->info()->dimension(1) - id.y() - 1 : id.y();
169 const int offset_z = (axis_bit & 0x4) ? output->info()->dimension(2) - id.z() - 1 : id.z();
170 const int offset_w = (axis_bit & 0x8) ? output->info()->dimension(3) - id[3] - 1 : id[3];
Michalis Spyrou110b9202018-12-28 16:32:49 +0000171
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100172 *reinterpret_cast<T *>(output->ptr_to_element(Coordinates(offset_x, offset_y, offset_z, offset_w))) =
173 in;
174 }
175 },
176 input_it);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000177}
178
179void NEReverseKernel::run(const Window &window, const ThreadInfo &info)
180{
181 ARM_COMPUTE_UNUSED(info);
182 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
183 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
184
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100185 switch (_input->info()->element_size())
Michalis Spyrou110b9202018-12-28 16:32:49 +0000186 {
Michele Di Giorgiof9b595a2020-07-03 13:34:52 +0100187 case 4:
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100188 run_reverse<uint32_t>(window, _input, _axis, _output, _use_inverted_axis);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000189 break;
Michele Di Giorgiof9b595a2020-07-03 13:34:52 +0100190 case 2:
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100191 run_reverse<uint16_t>(window, _input, _axis, _output, _use_inverted_axis);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000192 break;
Michele Di Giorgiof9b595a2020-07-03 13:34:52 +0100193 case 1:
Adnan AlSinanbdcb4c12023-09-18 14:49:45 +0100194 run_reverse<uint8_t>(window, _input, _axis, _output, _use_inverted_axis);
Michalis Spyrou110b9202018-12-28 16:32:49 +0000195 break;
196 default:
Michele Di Giorgiof9b595a2020-07-03 13:34:52 +0100197 ARM_COMPUTE_ERROR("Element size not supported");
Michalis Spyrou110b9202018-12-28 16:32:49 +0000198 }
199}
Sheri Zhangac6499a2021-02-10 15:32:38 +0000200} // namespace arm_compute