From a57bebd5ec54add1ee160eb63bd9c09b4bc3515a Mon Sep 17 00:00:00 2001 From: Stephen Jia Date: Fri, 28 Aug 2026 13:46:18 -0700 Subject: [PATCH 1/2] Update [ghstack-poisoned] --- backends/native/runtime/graph/ScalarType.h | 89 +++++++++++++++++++ backends/native/runtime/graph/TensorMeta.cpp | 55 ++++++++++++ backends/native/runtime/graph/TensorMeta.h | 71 +++++++++++++++ backends/native/runtime/graph/targets.bzl | 35 ++++++++ backends/native/runtime/graph/utils/Print.cpp | 34 +++++++ backends/native/runtime/graph/utils/Print.h | 24 +++++ 6 files changed, 308 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 create mode 100644 backends/native/runtime/graph/utils/Print.cpp create mode 100644 backends/native/runtime/graph/utils/Print.h diff --git a/backends/native/runtime/graph/ScalarType.h b/backends/native/runtime/graph/ScalarType.h new file mode 100644 index 00000000000..303d2cb5a00 --- /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) \ + inline 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. +constexpr 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. +constexpr 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..56f43657b96 --- /dev/null +++ b/backends/native/runtime/graph/TensorMeta.cpp @@ -0,0 +1,55 @@ +// 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 +#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; +} + +} // namespace ptn diff --git a/backends/native/runtime/graph/TensorMeta.h b/backends/native/runtime/graph/TensorMeta.h new file mode 100644 index 00000000000..259d310cbb0 --- /dev/null +++ b/backends/native/runtime/graph/TensorMeta.h @@ -0,0 +1,71 @@ +// 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 + +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; + + // 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 d468560227d..f145936dbbb 100644 --- a/backends/native/runtime/graph/targets.bzl +++ b/backends/native/runtime/graph/targets.bzl @@ -8,3 +8,38 @@ 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/..."], + ) + + # utils/ has no BUCK of its own, so the IR printer's target lives here. Kept + # separate from the IR libraries so only a consumer that dumps the IR links + # the formatting code. + runtime.cxx_library( + name = "print", + srcs = ["utils/Print.cpp"], + exported_headers = [ + "utils/Print.h", + ], + exported_deps = [ + ":tensor_meta", + ], + visibility = ["//executorch/backends/native/..."], + ) diff --git a/backends/native/runtime/graph/utils/Print.cpp b/backends/native/runtime/graph/utils/Print.cpp new file mode 100644 index 00000000000..9bc7ce8c486 --- /dev/null +++ b/backends/native/runtime/graph/utils/Print.cpp @@ -0,0 +1,34 @@ +// 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 + +namespace ptn { + +std::string to_string(const TensorMeta& meta) { + std::string s = scalar_type_name(meta.dtype); + s += "["; + for (size_t i = 0; i < meta.sizes.size(); ++i) { + if (i != 0) { + s += ","; + } + const Dim& d = meta.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/utils/Print.h b/backends/native/runtime/graph/utils/Print.h new file mode 100644 index 00000000000..e9740618acd --- /dev/null +++ b/backends/native/runtime/graph/utils/Print.h @@ -0,0 +1,24 @@ +// 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 + +namespace ptn { + +// Debug renderings of the in-memory IR. Free functions in their own target so +// nothing on an execution path links the formatting code; members would tie +// and these format choices to every consumer of the IR headers. The +// output is for humans -- nothing parses it back, and it is not versioned. + +// e.g. "Float[16,16]", "Float[1..8,16]" (bounded dynamic), "Float[0..?,16]" +// (unbounded). +std::string to_string(const TensorMeta& meta); + +} // namespace ptn From 978a757324aecac76b7fb18e6a1e35339612ba11 Mon Sep 17 00:00:00 2001 From: Stephen Jia Date: Mon, 31 Aug 2026 13:23:55 -0700 Subject: [PATCH 2/2] Update [ghstack-poisoned] --- backends/native/runtime/graph/TensorMeta.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/native/runtime/graph/TensorMeta.h b/backends/native/runtime/graph/TensorMeta.h index 0c8466aaba3..bf5b3288701 100644 --- a/backends/native/runtime/graph/TensorMeta.h +++ b/backends/native/runtime/graph/TensorMeta.h @@ -16,10 +16,10 @@ namespace ptn { // Logical tensor metadata: element type and shape. No storage, no quant scheme. // -// sizes holds concrete extents. The wire format carries a per-dim range instead, -// but a runtime that plans and executes at fixed shapes cannot honor a dynamic -// dim, so deserialization rejects one rather than silently collapsing it to its -// upper bound. +// sizes holds concrete extents. The wire format carries a per-dim range +// instead, but a runtime that plans and executes at fixed shapes cannot honor a +// dynamic dim, so deserialization rejects one rather than silently collapsing +// it to its upper bound. // // 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