| 351 | } |
| 352 | |
| 353 | Status CpuGemmConv2d::validate_mm(const ITensorInfo *src, |
| 354 | const ITensorInfo *weights, |
| 355 | const ITensorInfo *biases, |
| 356 | const ITensorInfo *dst, |
| 357 | const ActivationLayerInfo &act_info, |
| 358 | bool enable_fast_math, |
| 359 | int gemm_3d_depth, |
| 360 | bool skip_im2col, |
| 361 | bool fixed_format, |
| 362 | arm_compute::WeightFormat weight_format) |
| 363 | { |
| 364 | const DataType data_type = src->data_type(); |
| 365 | const bool is_quantized = is_data_type_quantized_asymmetric(data_type); |
| 366 | const bool is_activation_enabled = act_info.enabled(); |
| 367 | |
| 368 | if (is_quantized) |
| 369 | { |
| 370 | // Since we need negative offsets for computing convolution, we need to change QuantizationInfo() |
| 371 | // Extract and negate input and weights offset |
| 372 | const QuantizationInfo &iqinfo = src->quantization_info(); |
| 373 | const QuantizationInfo &wqinfo = weights->quantization_info(); |
| 374 | const QuantizationInfo &oqinfo = (dst->total_size() == 0) ? iqinfo : dst->quantization_info(); |
| 375 | const UniformQuantizationInfo uoqinfo = oqinfo.uniform(); |
| 376 | |
| 377 | // Merge activation with output stage |
| 378 | PixelValue type_min{}; |
| 379 | PixelValue type_max{}; |
| 380 | std::tie(type_min, type_max) = get_min_max(data_type); |
| 381 | int32_t min_activation = type_min.get<int32_t>(); |
| 382 | int32_t max_activation = type_max.get<int32_t>(); |
| 383 | |
| 384 | const std::set<ActivationLayerInfo::ActivationFunction> supported_acts = { |
| 385 | ActivationLayerInfo::ActivationFunction::RELU, ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, |
| 386 | ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU}; |
| 387 | if (is_activation_enabled && supported_acts.count(act_info.activation()) != 0) |
| 388 | { |
| 389 | std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, uoqinfo); |
| 390 | } |
| 391 | // F32 dequant path? (input quantized, output float) |
| 392 | GEMMLowpOutputStageInfo output_info; |
| 393 | if (int8_dequantize_f32_path(data_type, dst->data_type())) |
| 394 | { |
| 395 | // No requant stage; offsets are handled via offset-contribution on int32 |
| 396 | output_info.type = GEMMLowpOutputStageType::NONE; |
| 397 | output_info.gemmlowp_offset = 0; |
| 398 | output_info.gemmlowp_min_bound = 0; |
| 399 | output_info.gemmlowp_max_bound = 0; |
| 400 | output_info.is_quantized_per_channel = false; // irrelevant when NONE |
| 401 | } |
| 402 | else |
| 403 | { |
| 404 | // Existing Q->Q path |
| 405 | output_info.type = GEMMLowpOutputStageType::QUANTIZE_DOWN_FIXEDPOINT; |
| 406 | output_info.gemmlowp_offset = uoqinfo.offset; |
| 407 | output_info.gemmlowp_min_bound = min_activation; |
| 408 | output_info.gemmlowp_max_bound = max_activation; |
| 409 | output_info.is_quantized_per_channel = (weights->data_type() == DataType::QSYMM8_PER_CHANNEL); |
| 410 | ARM_COMPUTE_RETURN_ON_ERROR( |
nothing calls this directly
no test coverage detected