From d234ebe786f74713c54ed1db7fcc9da458599f7b Mon Sep 17 00:00:00 2001 From: Stephen Jia Date: Thu, 27 Aug 2026 08:01:59 -0700 Subject: [PATCH 1/2] Update [ghstack-poisoned] --- backends/native/runtime/graph/ScalarType.h | 89 ++++++++++++++++++++ backends/native/runtime/graph/TensorMeta.cpp | 74 ++++++++++++++++ backends/native/runtime/graph/TensorMeta.h | 76 +++++++++++++++++ backends/native/runtime/graph/targets.bzl | 20 +++++ 4 files changed, 259 insertions(+) create mode 100644 backends/native/runtime/graph/ScalarType.h create mode 100644 backends/native/runtime/graph/TensorMeta.cpp create mode 100644 backends/native/runtime/graph/TensorMeta.h diff --git a/backends/native/runtime/graph/ScalarType.h b/backends/native/runtime/graph/ScalarType.h new file mode 100644 index 00000000000..276619db27e --- /dev/null +++ b/backends/native/runtime/graph/ScalarType.h @@ -0,0 +1,89 @@ +// 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. + +#pragma once + +#include +#include +#include + +namespace ptn { + +// X-macro table of scalar types: (CPP_TYPE, NAME, ID). +// +// The ids are the serialized ScalarType values, so a deserializer maps a +// serialized byte straight to this enum. They are not sequential: the gaps are +// ids reserved for element types this header does not carry. +// +// Half and BFloat16 have no 16-bit float type in this dependency-free header; +// they map to uint16_t as a raw storage stand-in, correct for size and layout. +#define PTN_FORALL_SCALAR_TYPES(_) \ + _(uint8_t, Byte, 0) \ + _(int8_t, Char, 1) \ + _(int16_t, Short, 2) \ + _(int32_t, Int, 3) \ + _(int64_t, Long, 4) \ + _(uint16_t, Half, 5) \ + _(float, Float, 6) \ + _(double, Double, 7) \ + _(bool, Bool, 11) \ + _(uint16_t, BFloat16, 15) \ + _(uint16_t, UInt16, 16) \ + _(uint32_t, UInt32, 17) \ + _(uint64_t, UInt64, 18) + +enum class ScalarType : int8_t { +#define PTN_DEFINE_ENUM(cpp_type, name, id) name = id, + PTN_FORALL_SCALAR_TYPES(PTN_DEFINE_ENUM) +#undef PTN_DEFINE_ENUM +}; + +#define PTN_DEFINE_CONSTANT(cpp_type, name, id) \ + constexpr ScalarType k##name = ScalarType::name; +PTN_FORALL_SCALAR_TYPES(PTN_DEFINE_CONSTANT) +#undef PTN_DEFINE_CONSTANT + +// Forward mapping only: a reverse C++-type -> ScalarType trait is omitted, +// since uint16_t would collide across Half / BFloat16 / UInt16. +template +struct ScalarTypeToCppType; +#define PTN_SPECIALIZE_S2C(cpp_type, name, id) \ + template <> \ + struct ScalarTypeToCppType { \ + using type = cpp_type; \ + }; +PTN_FORALL_SCALAR_TYPES(PTN_SPECIALIZE_S2C) +#undef PTN_SPECIALIZE_S2C + +template +using cpp_type_t = typename ScalarTypeToCppType::type; + +// Throws std::runtime_error on a value outside the table, e.g. a bad cast from +// an out-of-range serialized byte. +inline size_t element_size(ScalarType t) { + switch (t) { +#define PTN_CASE_ELEMSIZE(cpp_type, name, id) \ + case ScalarType::name: \ + return sizeof(cpp_type); + PTN_FORALL_SCALAR_TYPES(PTN_CASE_ELEMSIZE) +#undef PTN_CASE_ELEMSIZE + } + throw std::runtime_error("element_size: unrecognized ScalarType"); +} + +// Enumerator name, e.g. "Float". Throws on a value outside the table. +inline const char* scalar_type_name(ScalarType t) { + switch (t) { +#define PTN_CASE_NAME(cpp_type, name, id) \ + case ScalarType::name: \ + return #name; + PTN_FORALL_SCALAR_TYPES(PTN_CASE_NAME) +#undef PTN_CASE_NAME + } + throw std::runtime_error("scalar_type_name: unrecognized ScalarType"); +} + +} // namespace ptn diff --git a/backends/native/runtime/graph/TensorMeta.cpp b/backends/native/runtime/graph/TensorMeta.cpp new file mode 100644 index 00000000000..6c0f60ee23d --- /dev/null +++ b/backends/native/runtime/graph/TensorMeta.cpp @@ -0,0 +1,74 @@ +// 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 +#include +#include +#include + +namespace ptn { + +Dim::Dim(int64_t min_v, int64_t max_v) : min(min_v), max(max_v) { + if (min_v < 0 || (max_v >= 0 && max_v < min_v)) { + throw std::runtime_error( + "Dim: no shape has the range " + std::to_string(min_v) + ".." + + std::to_string(max_v)); + } +} + +bool TensorMeta::is_static() const { + return std::ranges::all_of(sizes, &Dim::is_static); +} + +bool TensorMeta::is_contiguous() const { + if (dim_order_hint.empty()) { + return true; + } + // A length mismatch already makes this unequal. + return std::ranges::equal( + dim_order_hint, + std::views::iota(int32_t{0}, static_cast(sizes.size()))); +} + +int64_t TensorMeta::numel() const { + int64_t n = 1; + for (const Dim& d : sizes) { + const int64_t extent = d.is_static() ? d.min : d.max; + if (extent < 0) { + throw std::runtime_error("TensorMeta::numel: unbounded dynamic dim"); + } + // Signed overflow is UB, so the product must be checked before it happens. + if (extent != 0 && n > std::numeric_limits::max() / extent) { + throw std::runtime_error("TensorMeta::numel: element count overflows"); + } + n *= extent; + } + return n; +} + +std::string TensorMeta::to_string() const { + std::string s = scalar_type_name(dtype); + s += "["; + for (size_t i = 0; i < sizes.size(); ++i) { + if (i != 0) { + s += ","; + } + const Dim& d = sizes[i]; + if (d.is_static()) { + s += std::to_string(d.min); + } else if (d.max < 0) { + s += std::to_string(d.min) + "..?"; + } else { + s += std::to_string(d.min) + ".." + std::to_string(d.max); + } + } + s += "]"; + return s; +} + +} // namespace ptn diff --git a/backends/native/runtime/graph/TensorMeta.h b/backends/native/runtime/graph/TensorMeta.h new file mode 100644 index 00000000000..9a36b38594d --- /dev/null +++ b/backends/native/runtime/graph/TensorMeta.h @@ -0,0 +1,76 @@ +// 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. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace ptn { + +// One tensor dimension as an inclusive range. Static: min == max. Dynamic: +// min < max, or max < 0 for unbounded. +struct Dim { + int64_t min = 0; + int64_t max = -1; + + Dim() = default; + // Implicit so a shape can be written as a plain int list, e.g. {16, 8}. + // cppcheck-suppress noExplicitConstructor + /* implicit */ Dim(int64_t extent) : Dim(extent, extent) {} + // Throws std::runtime_error on a range no shape can have: a negative lower + // bound, or a bounded upper bound below it. A funnel, not an invariant -- + // min / max stay public and assignable. + Dim(int64_t min_v, int64_t max_v); + + bool is_static() const { + return min == max; + } + + bool operator==(const Dim&) const = default; +}; + +// Logical tensor metadata: element type and per-dim size ranges. No storage, no +// quant scheme. +// +// dim_order_hint is a permutation of dim indices, outermost first; empty means +// contiguous ([0, 1, ..., n-1]). It is a hint only for a tensor with no stored +// content — an activation — where an engine is free to pick its own physical +// layout. For a tensor whose bytes are serialized it instead describes the +// layout those bytes are actually in, and an engine that ignores it reads the +// weight wrong. +struct TensorMeta { + ScalarType dtype = ScalarType::Float; + std::vector sizes; + std::vector dim_order_hint; + + size_t ndim() const { + return sizes.size(); + } + + bool is_static() const; + + // True if dim_order_hint is empty or the identity permutation. + bool is_contiguous() const; + + // Element count from each dim's upper bound. Throws std::runtime_error on an + // unbounded dynamic dim (max < 0), which has no finite count. + int64_t numel() const; + + // e.g. "Float[16,16]", "Float[1..8,16]" (bounded dynamic), "Float[0..?,16]" + // (unbounded). + std::string to_string() const; + + // Exact on dim_order_hint: an empty hint and a spelled-out identity + // permutation compare unequal though they mean the same layout. + bool operator==(const TensorMeta&) const = default; +}; + +} // namespace ptn diff --git a/backends/native/runtime/graph/targets.bzl b/backends/native/runtime/graph/targets.bzl index b73a09de315..1a273e947a4 100644 --- a/backends/native/runtime/graph/targets.bzl +++ b/backends/native/runtime/graph/targets.bzl @@ -8,3 +8,23 @@ def define_common_targets(): ], visibility = ["//executorch/backends/native/..."], ) + + runtime.cxx_library( + name = "scalar_type", + exported_headers = [ + "ScalarType.h", + ], + visibility = ["//executorch/backends/native/..."], + ) + + runtime.cxx_library( + name = "tensor_meta", + srcs = ["TensorMeta.cpp"], + exported_headers = [ + "TensorMeta.h", + ], + exported_deps = [ + ":scalar_type", + ], + visibility = ["//executorch/backends/native/..."], + ) From 038de22bbd5dfd975c59bec535bd3f75c15fe2cb Mon Sep 17 00:00:00 2001 From: Stephen Jia Date: Thu, 27 Aug 2026 12:44:15 -0700 Subject: [PATCH 2/2] Update [ghstack-poisoned] --- backends/native/runtime/graph/ScalarType.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/native/runtime/graph/ScalarType.h b/backends/native/runtime/graph/ScalarType.h index 276619db27e..303d2cb5a00 100644 --- a/backends/native/runtime/graph/ScalarType.h +++ b/backends/native/runtime/graph/ScalarType.h @@ -42,7 +42,7 @@ enum class ScalarType : int8_t { }; #define PTN_DEFINE_CONSTANT(cpp_type, name, id) \ - constexpr ScalarType k##name = ScalarType::name; + inline constexpr ScalarType k##name = ScalarType::name; PTN_FORALL_SCALAR_TYPES(PTN_DEFINE_CONSTANT) #undef PTN_DEFINE_CONSTANT @@ -63,7 +63,7 @@ using cpp_type_t = typename ScalarTypeToCppType::type; // Throws std::runtime_error on a value outside the table, e.g. a bad cast from // an out-of-range serialized byte. -inline size_t element_size(ScalarType t) { +constexpr size_t element_size(ScalarType t) { switch (t) { #define PTN_CASE_ELEMSIZE(cpp_type, name, id) \ case ScalarType::name: \ @@ -75,7 +75,7 @@ inline size_t element_size(ScalarType t) { } // Enumerator name, e.g. "Float". Throws on a value outside the table. -inline const char* scalar_type_name(ScalarType t) { +constexpr const char* scalar_type_name(ScalarType t) { switch (t) { #define PTN_CASE_NAME(cpp_type, name, id) \ case ScalarType::name: \