From 0a0488377a29c3f881d2e9be0abbdeb064595394 Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Fri, 24 Jan 2025 10:57:04 -0800 Subject: [PATCH 1/4] Update [ghstack-poisoned] --- runtime/core/array_ref.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/core/array_ref.h b/runtime/core/array_ref.h index 463284d9c68..d02aac955ce 100644 --- a/runtime/core/array_ref.h +++ b/runtime/core/array_ref.h @@ -19,7 +19,6 @@ // removed some implicit const -> non-const conversions that rely on // complicated std::enable_if meta-programming // removed a bunch of slice variants for simplicity... -// remove constructors for std::array // remove constructors and operators for std::vector // removed some prevention of accidental assignments from temporary that // required std::enable_if meta-programming @@ -27,6 +26,7 @@ #pragma once +#include #include #include @@ -87,6 +87,11 @@ class ArrayRef final { /// Construct a ArrayRef from a range. ArrayRef(const T* begin, const T* end) : Data(begin), Length(end - begin) {} + /// Construct an ArrayRef from a std::array + template + /* implicit */ constexpr ArrayRef(const std::array& Arr) + : Data(Arr.data()), Length(N) {} + /// Construct a ArrayRef from a C array. template /* implicit */ constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {} @@ -202,6 +207,12 @@ ArrayRef makeArrayRef(const T* begin, const T* end) { return ArrayRef(begin, end); } +/// Construct an ArrayRef from a std::array. +template +ArrayRef makeArrayRef(const std::array& Arr) { + return Arr; +} + /// Construct an ArrayRef from an ArrayRef (no-op) (const) template ArrayRef makeArrayRef(const ArrayRef& Vec) { From 7ebb45fe39b7c71d21ece7ed376113b19bed1c0d Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Fri, 24 Jan 2025 10:57:09 -0800 Subject: [PATCH 2/4] Update [ghstack-poisoned] --- runtime/core/exec_aten/util/targets.bzl | 2 +- runtime/core/exec_aten/util/tensor_util.cpp | 43 +++++++++++++++++++ runtime/core/exec_aten/util/tensor_util.h | 23 ++++++++++ .../exec_aten/util/test/tensor_util_test.cpp | 43 +++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 runtime/core/exec_aten/util/tensor_util.cpp diff --git a/runtime/core/exec_aten/util/targets.bzl b/runtime/core/exec_aten/util/targets.bzl index e66ffa3a028..9cdead37883 100644 --- a/runtime/core/exec_aten/util/targets.bzl +++ b/runtime/core/exec_aten/util/targets.bzl @@ -50,7 +50,7 @@ def define_common_targets(): runtime.cxx_library( name = "tensor_util" + aten_suffix, - srcs = ["tensor_util_aten.cpp"] if aten_mode else ["tensor_util_portable.cpp"], + srcs = ["tensor_util.cpp"] + (["tensor_util_aten.cpp"] if aten_mode else ["tensor_util_portable.cpp"]), exported_headers = [ "tensor_util.h", ], diff --git a/runtime/core/exec_aten/util/tensor_util.cpp b/runtime/core/exec_aten/util/tensor_util.cpp new file mode 100644 index 00000000000..e99a7d080ef --- /dev/null +++ b/runtime/core/exec_aten/util/tensor_util.cpp @@ -0,0 +1,43 @@ +#include "tensor_util.h" + +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * 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 + +namespace executorch::runtime { +/** + * Shared implementation for tensor_util.h, may only contain code that + * works whether or not ATen mode is active. + */ +void tensor_shape_to_c_string( + char out[kTensorShapeStringSizeLimit], + executorch::aten::ArrayRef shape) { + char* p = out; + *p++ = '('; + for (const auto elem : shape) { + if (elem < 0 || elem > kMaximumPrintableTensorShapeElement) { + strcpy(p, "ERR, "); + p += strlen("ERR, "); + } else { + // snprintf returns characters *except* the NUL terminator, which is what + // we want. + p += snprintf( + p, + kTensorShapeStringSizeLimit - (p - out), + "%" PRIu32 ", ", + static_cast(elem)); + } + } + *(p - 2) = ')'; + *(p - 1) = '\0'; +} + +} // namespace executorch::runtime diff --git a/runtime/core/exec_aten/util/tensor_util.h b/runtime/core/exec_aten/util/tensor_util.h index 6fdc1bc2936..0469efcfbac 100644 --- a/runtime/core/exec_aten/util/tensor_util.h +++ b/runtime/core/exec_aten/util/tensor_util.h @@ -1120,6 +1120,29 @@ bool extract_scalar_tensor(executorch::aten::Tensor tensor, BOOL_T* out_val) { return true; } +/** + * Maximum size of a string returned by tensor_shape_to_c_string, for + * stack allocation. + */ +constexpr size_t kTensorShapeStringSizeLimit = 1 + /* opening parenthesis */ + 10 * kTensorDimensionLimit + /* maximum digits we will print; update + * kMaximumPrintableTensorShapeElement + * if changing */ + 2 * kTensorDimensionLimit + /* comma and space after each item, + * overwritten with closing paren and + * NUL terminator for last element */ + 1; /* padding for temporary NUL terminator for simplicity of implementation + */ + +constexpr size_t kMaximumPrintableTensorShapeElement = + std::is_same_v + ? std::numeric_limits::max() + : std::numeric_limits::max(); + +void tensor_shape_to_c_string( + char out[kTensorShapeStringSizeLimit], + executorch::aten::ArrayRef shape); + /// These APIs should not be used outside of Executor.cpp. namespace internal { /** diff --git a/runtime/core/exec_aten/util/test/tensor_util_test.cpp b/runtime/core/exec_aten/util/test/tensor_util_test.cpp index 88588dade68..1a0db784147 100644 --- a/runtime/core/exec_aten/util/test/tensor_util_test.cpp +++ b/runtime/core/exec_aten/util/test/tensor_util_test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using namespace ::testing; @@ -605,3 +606,45 @@ TEST_F(TensorUtilTest, SameShapesDifferentDimOrder) { EXPECT_FALSE(tensors_have_same_dim_order(a, c, b)); EXPECT_FALSE(tensors_have_same_dim_order(c, b, a)); } + +TEST_F(TensorUtilTest, TensorShapeToCStringBasic) { + char str[executorch::runtime::kTensorShapeStringSizeLimit]; + std::array sizes = {123, 456, 789}; + executorch::runtime::tensor_shape_to_c_string(str, sizes); + EXPECT_STREQ(str, "(123, 456, 789)"); + + std::array one_size = {1234567890}; + executorch::runtime::tensor_shape_to_c_string(str, one_size); + EXPECT_STREQ(str, "(1234567890)"); +} + +TEST_F(TensorUtilTest, TensorShapeToCStringMaximumLength) { + using executorch::runtime::kMaximumPrintableTensorShapeElement; + using executorch::runtime::kTensorDimensionLimit; + using executorch::runtime::kTensorShapeStringSizeLimit; + using executorch::runtime::tensor_shape_to_c_string; + char str[executorch::runtime::kTensorShapeStringSizeLimit + 1]; + std::memset(str, '@', sizeof(str)); + + std::array< + executorch::aten::SizesType, + executorch::runtime::kTensorDimensionLimit> + sizes; + std::fill( + sizes.begin(), + sizes.end(), + executorch::runtime::kMaximumPrintableTensorShapeElement); + + executorch::runtime::tensor_shape_to_c_string(str, sizes); + + std::ostringstream expected; + expected << '(' << kMaximumPrintableTensorShapeElement; + for (int ii = 0; ii < kTensorDimensionLimit - 1; ++ii) { + expected << ", " << kMaximumPrintableTensorShapeElement; + } + expected << ')'; + auto expected_str = expected.str(); + + EXPECT_EQ(str[executorch::runtime::kTensorShapeStringSizeLimit], '@'); + EXPECT_EQ(expected_str, str); +} From c27d91dff6cd5a5abeb33cc6e4f7bbc93d0e046e Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Fri, 24 Jan 2025 10:57:13 -0800 Subject: [PATCH 3/4] Update [ghstack-poisoned] --- kernels/portable/cpu/util/broadcast_util.cpp | 16 ++++++++++++---- .../portable/cpu/util/test/broadcast_test.cpp | 9 +++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/kernels/portable/cpu/util/broadcast_util.cpp b/kernels/portable/cpu/util/broadcast_util.cpp index 943219490b0..9d6599e9fce 100644 --- a/kernels/portable/cpu/util/broadcast_util.cpp +++ b/kernels/portable/cpu/util/broadcast_util.cpp @@ -213,10 +213,18 @@ ET_NODISCARD Error get_broadcast_target_size( Tensor::SizesType* out_sizes, const size_t out_sizes_len, size_t* out_dim) { - ET_CHECK_OR_RETURN_ERROR( - tensors_are_broadcastable_between(a_size, b_size), - InvalidArgument, - "Two input tensors should be broadcastable.\n"); + if (!tensors_are_broadcastable_between(a_size, b_size)) { + char a_shape_str[executorch::runtime::kTensorShapeStringSizeLimit]; + char b_shape_str[executorch::runtime::kTensorShapeStringSizeLimit]; + tensor_shape_to_c_string(a_shape_str, a_size); + tensor_shape_to_c_string(b_shape_str, b_size); + ET_LOG( + Error, + "Two input tensors should be broadcastable but got shapes %s and %s.", + a_shape_str, + b_shape_str); + return executorch::runtime::Error::InvalidArgument; + } auto a_dim = a_size.size(); auto b_dim = b_size.size(); diff --git a/kernels/portable/cpu/util/test/broadcast_test.cpp b/kernels/portable/cpu/util/test/broadcast_test.cpp index d87e8ecec85..954525b9490 100644 --- a/kernels/portable/cpu/util/test/broadcast_test.cpp +++ b/kernels/portable/cpu/util/test/broadcast_test.cpp @@ -129,6 +129,15 @@ TEST(BroadcastUtilTest, GetBroadcastTargetSize) { EXPECT_TRUE( ArrayRef(expected_output_size, expected_output_dim) .equals(ArrayRef({5, 2, 2}))); + + Tensor c = tf.zeros({4, 5}); + err = get_broadcast_target_size( + a, + c, + expected_output_size, + torch::executor::kTensorDimensionLimit, + &expected_output_dim); + EXPECT_EQ(err, torch::executor::Error::InvalidArgument); } size_t linearize_indexes(size_t* indexes, size_t indexes_len, const Tensor& t) { From f2538df1723366e14bfbe94b88c0e786ca54895a Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Fri, 7 Feb 2025 10:18:15 -0800 Subject: [PATCH 4/4] Update [ghstack-poisoned] --- kernels/portable/cpu/util/broadcast_util.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernels/portable/cpu/util/broadcast_util.cpp b/kernels/portable/cpu/util/broadcast_util.cpp index 38f41289378..aca56ce7e97 100644 --- a/kernels/portable/cpu/util/broadcast_util.cpp +++ b/kernels/portable/cpu/util/broadcast_util.cpp @@ -213,13 +213,15 @@ ET_NODISCARD Error get_broadcast_target_size( Tensor::SizesType* out_sizes, const size_t out_sizes_len, size_t* out_dim) { - if (!tensors_are_broadcastable_between(a_size, b_size)) { + if ET_UNLIKELY (!tensors_are_broadcastable_between(a_size, b_size)) { +#ifdef ET_LOG_ENABLED const auto a_shape_str = tensor_shape_to_c_string( executorch::runtime::Span( a_size.data(), a_size.size())); const auto b_shape_str = tensor_shape_to_c_string( executorch::runtime::Span( b_size.data(), b_size.size())); +#endif ET_LOG( Error, "Two input tensors should be broadcastable but got shapes %s and %s.",