From ec4b3270aae0a15b8830dfc7c379271f111556f7 Mon Sep 17 00:00:00 2001 From: RJ Ascani Date: Wed, 19 Aug 2026 21:50:52 -0700 Subject: [PATCH] Update [ghstack-poisoned] --- backends/cortex_m/ops/cortex_m_ops_common.h | 48 ++++++--- .../cortex_m/ops/op_quantized_avg_pool2d.cpp | 35 ++++++- backends/cortex_m/ops/op_quantized_conv2d.cpp | 96 +++++++++++++----- .../ops/op_quantized_depthwise_conv2d.cpp | 99 +++++++++++++++---- .../cortex_m/ops/op_quantized_max_pool2d.cpp | 38 ++++++- .../ops/op_quantized_transpose_conv2d.cpp | 94 ++++++++++++++---- .../cortex_m/test/models/test_mobilenet_v3.py | 4 - 7 files changed, 334 insertions(+), 80 deletions(-) diff --git a/backends/cortex_m/ops/cortex_m_ops_common.h b/backends/cortex_m/ops/cortex_m_ops_common.h index 2e3f49dd861..bcaed0a1bc7 100644 --- a/backends/cortex_m/ops/cortex_m_ops_common.h +++ b/backends/cortex_m/ops/cortex_m_ops_common.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -36,6 +37,11 @@ using KernelRuntimeContext = torch::executor::KernelRuntimeContext; // 16-byte alignment for MVE vector operations. constexpr size_t kCortexMMveAlignment = 16; +enum class ActivationLayout { + NCHWLogical, + NHWCLogical, +}; + // Basic tensor type / layout validation and dimension order checking inline void validate_cmsis_nn_tensor_requirements( const Tensor& input1, @@ -203,7 +209,7 @@ inline bool prepare_cmsis_pool2d_config( int64_t activation_min, int64_t activation_max, CmsisPool2DConfig& config, - bool require_channels_last = true, + ActivationLayout layout, bool allow_ceil_mode = false) { if (input.dim() != 4 || output.dim() != 4) { ET_LOG(Error, "%s: tensors must be 4-D", op_name); @@ -218,7 +224,9 @@ inline bool prepare_cmsis_pool2d_config( return false; } - if (input.size(0) != output.size(0) || input.size(1) != output.size(1)) { + const int64_t channel_dim = layout == ActivationLayout::NHWCLogical ? 3 : 1; + if (input.size(0) != output.size(0) || + input.size(channel_dim) != output.size(channel_dim)) { ET_LOG( Error, "%s: batch and channel dimensions must match between input and output", @@ -227,13 +235,21 @@ inline bool prepare_cmsis_pool2d_config( return false; } - if (require_channels_last) { - if (!is_channels_last_tensor(input) || !is_channels_last_tensor(output)) { - ET_LOG( - Error, "%s: tensors must use channels_last dimension order", op_name); + if (layout == ActivationLayout::NHWCLogical) { + if (!executorch::runtime::is_contiguous_dim_order( + input.dim_order().data(), input.dim_order().size()) || + !executorch::runtime::is_contiguous_dim_order( + output.dim_order().data(), output.dim_order().size())) { + ET_LOG(Error, "%s: tensors must use contiguous dimension order", op_name); context.fail(Error::InvalidArgument); return false; } + } else if ( + !is_channels_last_tensor(input) || !is_channels_last_tensor(output)) { + ET_LOG( + Error, "%s: tensors must use channels_last dimension order", op_name); + context.fail(Error::InvalidArgument); + return false; } auto check_tuple_len = [&](const Int64ArrayRef& arr, @@ -312,19 +328,29 @@ inline bool prepare_cmsis_pool2d_config( return false; } + const int64_t height_dim = layout == ActivationLayout::NHWCLogical ? 1 : 2; + const int64_t width_dim = layout == ActivationLayout::NHWCLogical ? 2 : 3; int32_t batch, channels, input_h, input_w, output_h, output_w; if (!check_int32_within_range( context, op_name, input.size(0), "input batch", batch) || !check_int32_within_range( - context, op_name, input.size(1), "input channels", channels) || + context, + op_name, + input.size(channel_dim), + "input channels", + channels) || !check_int32_within_range( - context, op_name, input.size(2), "input height", input_h) || + context, op_name, input.size(height_dim), "input height", input_h) || !check_int32_within_range( - context, op_name, input.size(3), "input width", input_w) || + context, op_name, input.size(width_dim), "input width", input_w) || !check_int32_within_range( - context, op_name, output.size(2), "output height", output_h) || + context, + op_name, + output.size(height_dim), + "output height", + output_h) || !check_int32_within_range( - context, op_name, output.size(3), "output width", output_w)) { + context, op_name, output.size(width_dim), "output width", output_w)) { return false; } diff --git a/backends/cortex_m/ops/op_quantized_avg_pool2d.cpp b/backends/cortex_m/ops/op_quantized_avg_pool2d.cpp index 39b6432c45a..66940f18997 100644 --- a/backends/cortex_m/ops/op_quantized_avg_pool2d.cpp +++ b/backends/cortex_m/ops/op_quantized_avg_pool2d.cpp @@ -1,4 +1,6 @@ /* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. * Copyright 2025-2026 Arm Limited and/or its affiliates. * * This source code is licensed under the BSD-style license found in the @@ -66,7 +68,7 @@ bool validate_avg_pool2d_output_size( } // namespace // cppcheck-suppress unusedFunction -Tensor& quantized_avg_pool2d_out( +static Tensor& quantized_avg_pool2d_out_impl( KernelRuntimeContext& context, const Tensor& input, const Int64ArrayRef kernel_size, @@ -77,6 +79,7 @@ Tensor& quantized_avg_pool2d_out( const int64_t multiplier, const int64_t shift, const Tensor& scratch, + ActivationLayout layout, Tensor& out) { constexpr int32_t activation_min = std::numeric_limits::min(); constexpr int32_t activation_max = std::numeric_limits::max(); @@ -97,7 +100,7 @@ Tensor& quantized_avg_pool2d_out( activation_min, activation_max, pool_config, - true, + layout, true)) { return out; } @@ -153,5 +156,33 @@ Tensor& quantized_avg_pool2d_out( return out; } +// cppcheck-suppress unusedFunction +Tensor& quantized_avg_pool2d_out( + KernelRuntimeContext& context, + const Tensor& input, + const Int64ArrayRef kernel_size, + const Int64ArrayRef stride, + const Int64ArrayRef padding, + const bool ceil_mode, + const int64_t zero_point, + const int64_t multiplier, + const int64_t shift, + const Tensor& scratch, + Tensor& out) { + return quantized_avg_pool2d_out_impl( + context, + input, + kernel_size, + stride, + padding, + ceil_mode, + zero_point, + multiplier, + shift, + scratch, + ActivationLayout::NCHWLogical, + out); +} + } // namespace native } // namespace cortex_m diff --git a/backends/cortex_m/ops/op_quantized_conv2d.cpp b/backends/cortex_m/ops/op_quantized_conv2d.cpp index 204a2b8369b..7865b50e486 100644 --- a/backends/cortex_m/ops/op_quantized_conv2d.cpp +++ b/backends/cortex_m/ops/op_quantized_conv2d.cpp @@ -1,10 +1,14 @@ /* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. * Copyright 2025-2026 Arm Limited and/or its affiliates. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +#include + #include "cortex_m_ops_common.h" namespace cortex_m { @@ -25,7 +29,8 @@ bool validate_conv2d_arguments( const Int64ArrayRef& padding, const Int64ArrayRef& dilation, const Tensor& requantize_multipliers, - const Tensor& requantize_shifts) { + const Tensor& requantize_shifts, + ActivationLayout layout) { if (input.dim() != kConvDim || weight.dim() != kConvDim || output.dim() != kConvDim) { ET_LOG(Error, "quantized_conv2d_out: tensors must be 4-D"); @@ -33,20 +38,22 @@ bool validate_conv2d_arguments( return false; } - // Check for channels_last dim_order (NHWC: 0, 2, 3, 1) - // Skip check if channels == 1, as dim_order is ambiguous in that case - if (input.size(1) > 1 && !is_channels_last_tensor(input)) { - ET_LOG( - Error, - "quantized_conv2d_out: input must have channels_last dim_order (NHWC)"); - context.fail(Error::InvalidArgument); - return false; - } - - if (output.size(1) > 1 && !is_channels_last_tensor(output)) { + if (layout == ActivationLayout::NHWCLogical) { + if (!executorch::runtime::is_contiguous_dim_order( + input.dim_order().data(), input.dim_order().size()) || + !executorch::runtime::is_contiguous_dim_order( + output.dim_order().data(), output.dim_order().size())) { + ET_LOG( + Error, + "quantized_conv2d_nhwc_out: input and output must have contiguous dim_order"); + context.fail(Error::InvalidArgument); + return false; + } + } else if ( + !is_channels_last_tensor(input) || !is_channels_last_tensor(output)) { ET_LOG( Error, - "quantized_conv2d_out: output must have channels_last dim_order (NHWC)"); + "quantized_conv2d_out: input and output must have channels_last dim_order"); context.fail(Error::InvalidArgument); return false; } @@ -78,7 +85,8 @@ bool validate_conv2d_arguments( return false; } - const int64_t out_channels = output.size(1); + const int64_t out_channels = + output.size(layout == ActivationLayout::NHWCLogical ? 3 : 1); if (requantize_multipliers.size(0) != out_channels || requantize_shifts.size(0) != out_channels) { ET_LOG( @@ -94,7 +102,7 @@ bool validate_conv2d_arguments( } // namespace // cppcheck-suppress unusedFunction -Tensor& quantized_conv2d_out( +static Tensor& quantized_conv2d_out_impl( KernelRuntimeContext& context, const Tensor& input, const Tensor& weight, @@ -109,6 +117,7 @@ Tensor& quantized_conv2d_out( const int64_t activation_min, const int64_t activation_max, const Tensor& scratch, + ActivationLayout layout, Tensor& out) { if (!validate_conv2d_arguments( context, @@ -120,23 +129,30 @@ Tensor& quantized_conv2d_out( padding, dilation, requantize_multipliers, - requantize_shifts)) { + requantize_shifts, + layout)) { return out; } const int32_t batch = static_cast(input.size(0)); - const int32_t input_channels = static_cast(input.size(1)); - const int32_t input_height = static_cast(input.size(2)); - const int32_t input_width = static_cast(input.size(3)); + const int32_t input_channels = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t input_height = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t input_width = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); const int32_t kernel_output_channels = static_cast(weight.size(0)); const int32_t kernel_height = static_cast(weight.size(1)); const int32_t kernel_width = static_cast(weight.size(2)); const int32_t kernel_input_channels = static_cast(weight.size(3)); - const int32_t output_channels = static_cast(out.size(1)); - const int32_t output_height = static_cast(out.size(2)); - const int32_t output_width = static_cast(out.size(3)); + const int32_t output_channels = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t output_height = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t output_width = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); const int32_t input_offset_val = static_cast(input_offset); const int32_t output_offset_val = static_cast(output_offset); @@ -228,5 +244,41 @@ Tensor& quantized_conv2d_out( return out; } +// cppcheck-suppress unusedFunction +Tensor& quantized_conv2d_out( + KernelRuntimeContext& context, + const Tensor& input, + const Tensor& weight, + const std::optional& bias, + const Int64ArrayRef stride, + const Int64ArrayRef padding, + const Int64ArrayRef dilation, + const int64_t input_offset, + const int64_t output_offset, + const Tensor& requantize_multipliers, + const Tensor& requantize_shifts, + const int64_t activation_min, + const int64_t activation_max, + const Tensor& scratch, + Tensor& out) { + return quantized_conv2d_out_impl( + context, + input, + weight, + bias, + stride, + padding, + dilation, + input_offset, + output_offset, + requantize_multipliers, + requantize_shifts, + activation_min, + activation_max, + scratch, + ActivationLayout::NCHWLogical, + out); +} + } // namespace native } // namespace cortex_m diff --git a/backends/cortex_m/ops/op_quantized_depthwise_conv2d.cpp b/backends/cortex_m/ops/op_quantized_depthwise_conv2d.cpp index 0793606de44..4aa58bb33dd 100644 --- a/backends/cortex_m/ops/op_quantized_depthwise_conv2d.cpp +++ b/backends/cortex_m/ops/op_quantized_depthwise_conv2d.cpp @@ -1,10 +1,14 @@ /* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. * Copyright 2025-2026 Arm Limited and/or its affiliates. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +#include + #include "cortex_m_ops_common.h" namespace cortex_m { @@ -26,7 +30,8 @@ bool validate_depthwise_conv2d_arguments( const Int64ArrayRef& dilation, const int64_t depth_multiplier, const Tensor& requantize_multipliers, - const Tensor& requantize_shifts) { + const Tensor& requantize_shifts, + ActivationLayout layout) { if (input.dim() != kConvDim || weight.dim() != kConvDim || output.dim() != kConvDim) { ET_LOG(Error, "quantized_depthwise_conv2d_out: tensors must be 4-D"); @@ -55,7 +60,8 @@ bool validate_depthwise_conv2d_arguments( } const int64_t weight_output_channels = weight.size(3); - const int64_t output_channels = output.size(1); + const int64_t output_channels = + output.size(layout == ActivationLayout::NHWCLogical ? 3 : 1); if (weight_output_channels != output_channels) { ET_LOG( Error, @@ -66,16 +72,22 @@ bool validate_depthwise_conv2d_arguments( return false; } - if (!is_channels_last_tensor(input)) { - ET_LOG( - Error, "quantized_depthwise_conv2d_out: input must be channels_last"); - context.fail(Error::InvalidArgument); - return false; - } - - if (!is_channels_last_tensor(output)) { + if (layout == ActivationLayout::NHWCLogical) { + if (!executorch::runtime::is_contiguous_dim_order( + input.dim_order().data(), input.dim_order().size()) || + !executorch::runtime::is_contiguous_dim_order( + output.dim_order().data(), output.dim_order().size())) { + ET_LOG( + Error, + "quantized_depthwise_conv2d_nhwc_out: input and output must have contiguous dim_order"); + context.fail(Error::InvalidArgument); + return false; + } + } else if ( + !is_channels_last_tensor(input) || !is_channels_last_tensor(output)) { ET_LOG( - Error, "quantized_depthwise_conv2d_out: output must be channels_last"); + Error, + "quantized_depthwise_conv2d_out: input and output must be channels_last"); context.fail(Error::InvalidArgument); return false; } @@ -108,7 +120,8 @@ bool validate_depthwise_conv2d_arguments( return false; } - const int64_t input_channels = input.size(1); + const int64_t input_channels = + input.size(layout == ActivationLayout::NHWCLogical ? 3 : 1); // output_channels already extracted above for weight validation if (output_channels != input_channels * depth_multiplier) { ET_LOG( @@ -136,7 +149,7 @@ bool validate_depthwise_conv2d_arguments( } // namespace // cppcheck-suppress unusedFunction -Tensor& quantized_depthwise_conv2d_out( +static Tensor& quantized_depthwise_conv2d_out_impl( KernelRuntimeContext& context, const Tensor& input, const Tensor& weight, @@ -152,6 +165,7 @@ Tensor& quantized_depthwise_conv2d_out( const int64_t activation_min, const int64_t activation_max, const Tensor& scratch, + ActivationLayout layout, Tensor& out) { if (!validate_depthwise_conv2d_arguments( context, @@ -164,23 +178,30 @@ Tensor& quantized_depthwise_conv2d_out( dilation, depth_multiplier, requantize_multipliers, - requantize_shifts)) { + requantize_shifts, + layout)) { return out; } const int32_t batch = static_cast(input.size(0)); - const int32_t input_channels = static_cast(input.size(1)); - const int32_t input_height = static_cast(input.size(2)); - const int32_t input_width = static_cast(input.size(3)); + const int32_t input_channels = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t input_height = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t input_width = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); // Weight is in IHWO layout after permutation in the pass: [1, H, W, C_OUT] // For depthwise conv, this matches CMSIS-NN's expected format const int32_t kernel_height = static_cast(weight.size(1)); const int32_t kernel_width = static_cast(weight.size(2)); - const int32_t output_channels = static_cast(out.size(1)); - const int32_t output_height = static_cast(out.size(2)); - const int32_t output_width = static_cast(out.size(3)); + const int32_t output_channels = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t output_height = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t output_width = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); const int32_t depth_multiplier_val = static_cast(depth_multiplier); @@ -272,5 +293,43 @@ Tensor& quantized_depthwise_conv2d_out( return out; } +// cppcheck-suppress unusedFunction +Tensor& quantized_depthwise_conv2d_out( + KernelRuntimeContext& context, + const Tensor& input, + const Tensor& weight, + const std::optional& bias, + const Int64ArrayRef stride, + const Int64ArrayRef padding, + const Int64ArrayRef dilation, + const int64_t depth_multiplier, + const int64_t input_offset, + const int64_t output_offset, + const Tensor& requantize_multipliers, + const Tensor& requantize_shifts, + const int64_t activation_min, + const int64_t activation_max, + const Tensor& scratch, + Tensor& out) { + return quantized_depthwise_conv2d_out_impl( + context, + input, + weight, + bias, + stride, + padding, + dilation, + depth_multiplier, + input_offset, + output_offset, + requantize_multipliers, + requantize_shifts, + activation_min, + activation_max, + scratch, + ActivationLayout::NCHWLogical, + out); +} + } // namespace native } // namespace cortex_m diff --git a/backends/cortex_m/ops/op_quantized_max_pool2d.cpp b/backends/cortex_m/ops/op_quantized_max_pool2d.cpp index ca1b00ff340..68caa764ad5 100644 --- a/backends/cortex_m/ops/op_quantized_max_pool2d.cpp +++ b/backends/cortex_m/ops/op_quantized_max_pool2d.cpp @@ -1,4 +1,6 @@ /* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. * Copyright 2026 Arm Limited and/or its affiliates. * * This source code is licensed under the BSD-style license found in the @@ -11,7 +13,7 @@ namespace cortex_m { namespace native { // cppcheck-suppress unusedFunction -Tensor& quantized_max_pool2d_out( +static Tensor& quantized_max_pool2d_out_impl( KernelRuntimeContext& context, const Tensor& input, const Int64ArrayRef kernel_size, @@ -23,6 +25,7 @@ Tensor& quantized_max_pool2d_out( const int64_t output_zero_point, const int64_t activation_min, const int64_t activation_max, + ActivationLayout layout, Tensor& out) { CmsisPool2DConfig pool_config; if (!prepare_cmsis_pool2d_config( @@ -37,7 +40,8 @@ Tensor& quantized_max_pool2d_out( ceil_mode, activation_min, activation_max, - pool_config)) { + pool_config, + layout)) { return out; } @@ -95,5 +99,35 @@ Tensor& quantized_max_pool2d_out( return out; } +// cppcheck-suppress unusedFunction +Tensor& quantized_max_pool2d_out( + KernelRuntimeContext& context, + const Tensor& input, + const Int64ArrayRef kernel_size, + const Int64ArrayRef stride, + const Int64ArrayRef padding, + const Int64ArrayRef dilation, + const bool ceil_mode, + const int64_t input_zero_point, + const int64_t output_zero_point, + const int64_t activation_min, + const int64_t activation_max, + Tensor& out) { + return quantized_max_pool2d_out_impl( + context, + input, + kernel_size, + stride, + padding, + dilation, + ceil_mode, + input_zero_point, + output_zero_point, + activation_min, + activation_max, + ActivationLayout::NCHWLogical, + out); +} + } // namespace native } // namespace cortex_m diff --git a/backends/cortex_m/ops/op_quantized_transpose_conv2d.cpp b/backends/cortex_m/ops/op_quantized_transpose_conv2d.cpp index 04d57d4c693..fcfe78ce48d 100644 --- a/backends/cortex_m/ops/op_quantized_transpose_conv2d.cpp +++ b/backends/cortex_m/ops/op_quantized_transpose_conv2d.cpp @@ -24,7 +24,8 @@ bool validate_transpose_conv2d_arguments( const std::optional& bias, const Tensor& output, const Tensor& requantize_multipliers, - const Tensor& requantize_shifts) { + const Tensor& requantize_shifts, + ActivationLayout layout) { if (input.dim() != kConvTransposeDim || weight.dim() != kConvTransposeDim || output.dim() != kConvTransposeDim) { ET_LOG(Error, "quantized_transpose_conv2d_out: tensors must be 4-D"); @@ -32,16 +33,22 @@ bool validate_transpose_conv2d_arguments( return false; } - if (!is_channels_last_tensor(input)) { - ET_LOG( - Error, "quantized_transpose_conv2d_out: input must be channels_last"); - context.fail(Error::InvalidArgument); - return false; - } - - if (!is_channels_last_tensor(output)) { + if (layout == ActivationLayout::NHWCLogical) { + if (!executorch::runtime::is_contiguous_dim_order( + input.dim_order().data(), input.dim_order().size()) || + !executorch::runtime::is_contiguous_dim_order( + output.dim_order().data(), output.dim_order().size())) { + ET_LOG( + Error, + "quantized_transpose_conv2d_nhwc_out: input and output must have contiguous dim_order"); + context.fail(Error::InvalidArgument); + return false; + } + } else if ( + !is_channels_last_tensor(input) || !is_channels_last_tensor(output)) { ET_LOG( - Error, "quantized_transpose_conv2d_out: output must be channels_last"); + Error, + "quantized_transpose_conv2d_out: input and output must be channels_last"); context.fail(Error::InvalidArgument); return false; } @@ -68,7 +75,8 @@ bool validate_transpose_conv2d_arguments( return false; } - const int64_t out_channels = output.size(1); + const int64_t out_channels = + output.size(layout == ActivationLayout::NHWCLogical ? 3 : 1); if (requantize_multipliers.size(0) != out_channels || requantize_shifts.size(0) != out_channels) { ET_LOG( @@ -84,7 +92,7 @@ bool validate_transpose_conv2d_arguments( } // namespace // cppcheck-suppress unusedFunction -Tensor& quantized_transpose_conv2d_out( +static Tensor& quantized_transpose_conv2d_out_impl( KernelRuntimeContext& context, const Tensor& input, const Tensor& weight, @@ -101,6 +109,7 @@ Tensor& quantized_transpose_conv2d_out( const int64_t activation_max, const Tensor& scratch, const Tensor& output_scratch, + ActivationLayout layout, Tensor& out) { if (!validate_transpose_conv2d_arguments( context, @@ -109,23 +118,30 @@ Tensor& quantized_transpose_conv2d_out( bias, out, requantize_multipliers, - requantize_shifts)) { + requantize_shifts, + layout)) { return out; } const int32_t batch = static_cast(input.size(0)); - const int32_t input_channels = static_cast(input.size(1)); - const int32_t input_height = static_cast(input.size(2)); - const int32_t input_width = static_cast(input.size(3)); + const int32_t input_channels = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t input_height = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t input_width = static_cast( + input.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); const int32_t kernel_output_channels = static_cast(weight.size(0)); const int32_t kernel_height = static_cast(weight.size(1)); const int32_t kernel_width = static_cast(weight.size(2)); const int32_t kernel_input_channels = static_cast(weight.size(3)); - const int32_t output_channels = static_cast(out.size(1)); - const int32_t output_height = static_cast(out.size(2)); - const int32_t output_width = static_cast(out.size(3)); + const int32_t output_channels = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 3 : 1)); + const int32_t output_height = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 1 : 2)); + const int32_t output_width = static_cast( + out.size(layout == ActivationLayout::NHWCLogical ? 2 : 3)); if (kernel_output_channels != output_channels) { ET_LOG( @@ -246,5 +262,45 @@ Tensor& quantized_transpose_conv2d_out( return out; } +// cppcheck-suppress unusedFunction +Tensor& quantized_transpose_conv2d_out( + KernelRuntimeContext& context, + const Tensor& input, + const Tensor& weight, + const std::optional& bias, + const Int64ArrayRef stride, + const Int64ArrayRef padding, + const Int64ArrayRef output_padding, + const Int64ArrayRef dilation, + const int64_t input_offset, + const int64_t output_offset, + const Tensor& requantize_multipliers, + const Tensor& requantize_shifts, + const int64_t activation_min, + const int64_t activation_max, + const Tensor& scratch, + const Tensor& output_scratch, + Tensor& out) { + return quantized_transpose_conv2d_out_impl( + context, + input, + weight, + bias, + stride, + padding, + output_padding, + dilation, + input_offset, + output_offset, + requantize_multipliers, + requantize_shifts, + activation_min, + activation_max, + scratch, + output_scratch, + ActivationLayout::NCHWLogical, + out); +} + } // namespace native } // namespace cortex_m diff --git a/backends/cortex_m/test/models/test_mobilenet_v3.py b/backends/cortex_m/test/models/test_mobilenet_v3.py index 08633d54dd6..2fccc89c131 100644 --- a/backends/cortex_m/test/models/test_mobilenet_v3.py +++ b/backends/cortex_m/test/models/test_mobilenet_v3.py @@ -59,10 +59,6 @@ @parametrize( "test_case", test_cases, - xfails={ - "mobilenet_v3_small": "MLETORCH-1821 - Investigate mobilenet_v3_small flakyness" - }, - strict=False, ) def test_dialect_mv3(test_case): inputs = test_case.get_example_inputs()