Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions backends/native/runtime/graph/ScalarType.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// 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 <cstddef>
#include <cstdint>
#include <stdexcept>

namespace ptn {

// X-macro table of scalar types: (CPP_TYPE, NAME, ID). One row per supported
// element type; the row drives the enum, the k<Name> constants, the
// ScalarType -> C++ type trait, element_size(), and scalar_type_name().
//
// IDs are pinned to ExecuTorch's ScalarType (runtime/core/portable_type/
// scalar_type.h) and the native_graph.fbs ScalarType enum, so a deserializer
// maps the serialized byte straight to this enum. The ids are therefore NOT
// sequential (complex / quantized-int ids are reserved and omitted).
//
// Half and BFloat16 have no standalone C++ 16-bit-float type in this
// dependency-free header; they map to uint16_t as a raw storage stand-in, which
// is correct for size / layout purposes.
#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
};

// Shorthand constants: kFloat, kLong, ...
#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

// ScalarType -> C++ type. Use as `ptn::cpp_type_t<ptn::kFloat>` (== float).
// Forward mapping only: a reverse C++-type -> ScalarType trait is
// intentionally omitted, since uint16_t would collide across Half / BFloat16 /
// UInt16.
template <ScalarType N>
struct ScalarTypeToCppType;
#define PTN_SPECIALIZE_S2C(cpp_type, name, id) \
template <> \
struct ScalarTypeToCppType<ScalarType::name> { \
using type = cpp_type; \
};
PTN_FORALL_SCALAR_TYPES(PTN_SPECIALIZE_S2C)
#undef PTN_SPECIALIZE_S2C

template <ScalarType N>
using cpp_type_t = typename ScalarTypeToCppType<N>::type;

// Size in bytes of one element. Throws std::runtime_error on an unrecognized
// value (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");
}

// Human-readable enumerator name (e.g. "Float"). Throws on unrecognized value.
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
75 changes: 75 additions & 0 deletions backends/native/runtime/graph/TensorMeta.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 <executorch/backends/native/runtime/graph/TensorMeta.h>

#include <algorithm>
#include <limits>
#include <ranges>
#include <stdexcept>

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 makes this unequal, so it needs no separate check.
return std::ranges::equal(
dim_order_hint,
std::views::iota(int32_t{0}, static_cast<int32_t>(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 has to be checked before it
// happens: a malformed shape must not silently plan a smaller buffer.
if (extent != 0 && n > std::numeric_limits<int64_t>::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
77 changes: 77 additions & 0 deletions backends/native/runtime/graph/TensorMeta.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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 <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include <executorch/backends/native/runtime/graph/ScalarType.h>

namespace ptn {

// One tensor dimension as an inclusive range. Static: min == max. Dynamic:
// min < max, or max < 0 for unbounded. Memory is planned from the upper bound.
struct Dim {
int64_t min = 0;
int64_t max = -1;

Dim() = default;
// A static dimension: min == max == extent. Implicit so a shape can be
// written as a plain int list, e.g. sizes = {16, 8}.
// cppcheck-suppress noExplicitConstructor
/* implicit */ Dim(int64_t extent) : Dim(extent, extent) {}
// A range [min_v, max_v] (dynamic when min_v < max_v; max_v < 0 unbounded).
// Throws std::runtime_error on a range no shape can have: a negative lower
// bound, or a bounded upper bound below it. This catches a malformed
// serialized shape at the point it enters the IR rather than letting it
// surface as a wrong numel() later. It is 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 and
// no quant scheme (deferred). dim_order_hint is a *suggested* memory layout — a
// permutation of dim indices, outermost first; empty means contiguous
// ([0, 1, ..., n-1]). It is advisory only: engines choose their own physical
// layout and may ignore it. TensorMeta stays non-prescriptive about layout.
struct TensorMeta {
ScalarType dtype = ScalarType::Float;
std::vector<Dim> sizes;
std::vector<int32_t> dim_order_hint;

size_t ndim() const {
return sizes.size();
}

// True if every dimension is static (min == max).
bool is_static() const;

// True if dim_order_hint is empty or the identity permutation
// [0, 1, ..., n-1] (i.e. the hint suggests a contiguous layout).
bool is_contiguous() const;

// Element count using each dim's upper bound (its size when static). This is
// the memory-planning extent. Throws std::runtime_error on an unbounded
// dynamic dim (max < 0), which has no finite element count.
int64_t numel() const;

// e.g. "Float[16,16]" (static), "Float[1..8,16]" (bounded dynamic), or
// "Float[0..?,16]" (unbounded). Debug aid.
std::string to_string() const;

bool operator==(const TensorMeta&) const = default;
};

} // namespace ptn
22 changes: 22 additions & 0 deletions backends/native/runtime/graph/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,25 @@ def define_common_targets():
],
visibility = ["//executorch/backends/native/..."],
)

# Scalar element type + C++-type mapping (header-only; macro-driven, standalone).
runtime.cxx_library(
name = "scalar_type",
exported_headers = [
"ScalarType.h",
],
visibility = ["//executorch/backends/native/..."],
)

# Concrete in-memory IR value types (pure std; no ExecuTorch, no flatbuffers).
runtime.cxx_library(
name = "tensor_meta",
srcs = ["TensorMeta.cpp"],
exported_headers = [
"TensorMeta.h",
],
exported_deps = [
":scalar_type",
],
visibility = ["//executorch/backends/native/..."],
)
Loading