| 189 | |
| 190 | template <typename T> |
| 191 | void mul_saturate_quantized_8(const ITensor *src1, const ITensor *src2, ITensor *out, const Window &window, float scale) |
| 192 | { |
| 193 | // Create input windows |
| 194 | Window win = window; |
| 195 | Window input1_win = window.broadcast_if_dimension_le_one(src1->info()->tensor_shape()); |
| 196 | Window input2_win = window.broadcast_if_dimension_le_one(src2->info()->tensor_shape()); |
| 197 | |
| 198 | // Clear X Dimension on execution window as we handle manually |
| 199 | win.set(Window::DimX, Window::Dimension(0, 1, 1)); |
| 200 | |
| 201 | const int window_step_x = 16 / sizeof(T); |
| 202 | const auto window_start_x = static_cast<int>(window.x().start()); |
| 203 | const auto window_end_x = static_cast<int>(window.x().end()); |
| 204 | const bool is_broadcast_across_x = src1->info()->tensor_shape().x() != src2->info()->tensor_shape().x(); |
| 205 | |
| 206 | const UniformQuantizationInfo output_qua_info = out->info()->quantization_info().uniform(); |
| 207 | const UniformQuantizationInfo tmp_qua_info = {output_qua_info.scale / scale, output_qua_info.offset}; |
| 208 | |
| 209 | if (is_broadcast_across_x) |
| 210 | { |
| 211 | const bool is_broadcast_input_2 = input2_win.x().step() == 0; |
| 212 | Window broadcast_win = is_broadcast_input_2 ? input2_win : input1_win; |
| 213 | Window non_broadcast_win = !is_broadcast_input_2 ? input2_win : input1_win; |
| 214 | const ITensor *broadcast_tensor = is_broadcast_input_2 ? src2 : src1; |
| 215 | const ITensor *non_broadcast_tensor = !is_broadcast_input_2 ? src2 : src1; |
| 216 | const UniformQuantizationInfo broadcast_qinfo = broadcast_tensor->info()->quantization_info().uniform(); |
| 217 | const UniformQuantizationInfo non_broadcast_qinfo = non_broadcast_tensor->info()->quantization_info().uniform(); |
| 218 | |
| 219 | // Clear X Dimension on execution window as we handle manually |
| 220 | non_broadcast_win.set(Window::DimX, Window::Dimension(0, 1, 1)); |
| 221 | |
| 222 | Iterator broadcast_input(broadcast_tensor, broadcast_win); |
| 223 | Iterator non_broadcast_input(non_broadcast_tensor, non_broadcast_win); |
| 224 | Iterator dst(out, win); |
| 225 | |
| 226 | using ExactTagType = typename wrapper::traits::neon_vector<T, window_step_x>::tag_type; |
| 227 | |
| 228 | execute_window_loop( |
| 229 | win, |
| 230 | [&](const Coordinates &) |
| 231 | { |
| 232 | const auto non_broadcast_input_ptr = reinterpret_cast<const T *>(non_broadcast_input.ptr()); |
| 233 | const auto output_ptr = reinterpret_cast<T *>(dst.ptr()); |
| 234 | |
| 235 | const auto broadcast_value = *reinterpret_cast<const T *>(broadcast_input.ptr()); |
| 236 | const auto broadcast_value_vec = wrapper::vdup_n(broadcast_value, ExactTagType{}); |
| 237 | |
| 238 | // Compute window_step_x elements per iteration |
| 239 | int x = window_start_x; |
| 240 | for (; x <= (window_end_x - window_step_x); x += window_step_x) |
| 241 | { |
| 242 | const auto non_broadcast_v = wrapper::vloadq(non_broadcast_input_ptr + x); |
| 243 | |
| 244 | // Dequantize inputs |
| 245 | const float32x4x4_t in1_f32x4x4 = vdequantize(non_broadcast_v, non_broadcast_qinfo); |
| 246 | const float32x4x4_t in2_f32x4x4 = vdequantize(broadcast_value_vec, broadcast_qinfo); |
| 247 | |
| 248 | const float32x4x4_t out_f32x4x4 = { |
nothing calls this directly
no test coverage detected