| 241 | CpuGemmConv2d::~CpuGemmConv2d() = default; |
| 242 | |
| 243 | void CpuGemmConv2d::configure_mm(const ITensorInfo *src, |
| 244 | const ITensorInfo *weights, |
| 245 | const ITensorInfo *biases, |
| 246 | ITensorInfo *dst, |
| 247 | const ActivationLayerInfo &act_info, |
| 248 | bool enable_fast_math, |
| 249 | int gemm_3d_depth, |
| 250 | bool fixed_format, |
| 251 | arm_compute::WeightFormat weight_format) |
| 252 | { |
| 253 | ARM_COMPUTE_ERROR_ON_NULLPTR(src, weights); |
| 254 | ARM_COMPUTE_ERROR_THROW_ON(validate_mm(src, weights, biases, dst, act_info, enable_fast_math, gemm_3d_depth, |
| 255 | _skip_im2col, fixed_format, weight_format)); |
| 256 | |
| 257 | // Supported activations in GEMM |
| 258 | const std::set<ActivationLayerInfo::ActivationFunction> supported_acts = { |
| 259 | ActivationLayerInfo::ActivationFunction::RELU, ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, |
| 260 | ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU}; |
| 261 | |
| 262 | if (_is_quantized) |
| 263 | { |
| 264 | TensorInfo tmp_src{*src}; |
| 265 | TensorInfo tmp_weights{*weights}; |
| 266 | // Since we need negative offsets for computing convolution, we need to change QuantizationInfo() |
| 267 | // Extract and negate input and weights offset |
| 268 | const QuantizationInfo iqinfo = src->quantization_info(); |
| 269 | const QuantizationInfo wqinfo = weights->quantization_info(); |
| 270 | const QuantizationInfo oqinfo = (dst->total_size() == 0) ? iqinfo : dst->quantization_info(); |
| 271 | const UniformQuantizationInfo uiqinfo = iqinfo.uniform(); |
| 272 | const UniformQuantizationInfo uoqinfo = oqinfo.uniform(); |
| 273 | const DataType data_type = src->data_type(); |
| 274 | |
| 275 | tmp_src.set_quantization_info(QuantizationInfo(uiqinfo.scale, -uiqinfo.offset)); |
| 276 | if (!is_data_type_quantized_per_channel(tmp_weights.data_type())) |
| 277 | { |
| 278 | const UniformQuantizationInfo uwqinfo = wqinfo.uniform(); |
| 279 | tmp_weights.set_quantization_info(QuantizationInfo(uwqinfo.scale, -uwqinfo.offset)); |
| 280 | } |
| 281 | |
| 282 | // Merge activation with output stage |
| 283 | PixelValue type_min{}; |
| 284 | PixelValue type_max{}; |
| 285 | std::tie(type_min, type_max) = get_min_max(data_type); |
| 286 | int32_t min_activation = type_min.get<int32_t>(); |
| 287 | int32_t max_activation = type_max.get<int32_t>(); |
| 288 | |
| 289 | _act_info = act_info; |
| 290 | if (supported_acts.count(act_info.activation()) != 0) |
| 291 | { |
| 292 | std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, uoqinfo); |
| 293 | } |
| 294 | |
| 295 | GEMMLowpOutputStageInfo output_info; |
| 296 | |
| 297 | // F32 dequant path? (input quantized, output float) |
| 298 | if (int8_dequantize_f32_path(data_type, dst->data_type())) |
| 299 | { |
| 300 | // No requant stage; offsets are handled via offset-contribution on int32 |
nothing calls this directly
no test coverage detected