diff --git a/backends/native/runtime/graph/Scalar.cpp b/backends/native/runtime/graph/Scalar.cpp new file mode 100644 index 00000000000..4547a0f6c3c --- /dev/null +++ b/backends/native/runtime/graph/Scalar.cpp @@ -0,0 +1,61 @@ +// 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 { + +int64_t Scalar::to_int() const { + const int64_t* v = std::get_if(&value_); + if (v == nullptr) { + throw std::runtime_error("Scalar::to_int: scalar is not an Int"); + } + return *v; +} + +double Scalar::to_double() const { + const double* v = std::get_if(&value_); + if (v == nullptr) { + throw std::runtime_error("Scalar::to_double: scalar is not a Double"); + } + return *v; +} + +bool Scalar::to_bool() const { + const bool* v = std::get_if(&value_); + if (v == nullptr) { + throw std::runtime_error("Scalar::to_bool: scalar is not a Bool"); + } + return *v; +} + +const char* Scalar::tag_name() const { + switch (tag()) { + case Tag::Int: + return "Int"; + case Tag::Double: + return "Double"; + case Tag::Bool: + return "Bool"; + } + return "?"; +} + +std::string Scalar::to_string() const { + if (const bool* b = std::get_if(&value_)) { + return *b ? "true" : "false"; + } + if (is_int()) { + return std::to_string(to_int()); + } + return format_double(to_double()); +} + +} // namespace ptn diff --git a/backends/native/runtime/graph/Scalar.h b/backends/native/runtime/graph/Scalar.h new file mode 100644 index 00000000000..cd309081ac9 --- /dev/null +++ b/backends/native/runtime/graph/Scalar.h @@ -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. + +#pragma once + +#include +#include +#include + +namespace ptn { + +// A concrete scalar value: an int / double / bool. Analogous to c10::Scalar. +// Trivially copyable. There is no "none" state — the graph's Value owns that. +class Scalar { + public: + // Which alternative is live. Pinned to the order of the variant below, so a + // Tag is exactly an index into it. + enum class Tag : int8_t { Int = 0, Double = 1, Bool = 2 }; + + private: + std::variant value_ = int64_t{0}; + + public: + constexpr Scalar() = default; + // Implicit by design (ergonomic: `Scalar s = 5;`). The int overload + // disambiguates `Scalar(5)` — without it int -> {int64_t,double,bool} is an + // ambiguous conversion. The suppression is scoped to this block so a + // genuinely accidental implicit constructor added later is still reported. + // cppcheck-suppress-begin noExplicitConstructor + /* implicit */ constexpr Scalar(int v) : value_(static_cast(v)) {} + /* implicit */ constexpr Scalar(int64_t v) : value_(v) {} + /* implicit */ constexpr Scalar(double v) : value_(v) {} + /* implicit */ constexpr Scalar(bool v) : value_(v) {} + // cppcheck-suppress-end noExplicitConstructor + // Every pointer converts to bool, so without this `Scalar s = some_ptr;` + // would quietly yield a Bool. Deleted rather than made explicit so the + // implicit numeric constructors above keep their ergonomics. + template + Scalar(T*) = delete; + + constexpr Tag tag() const { + return static_cast(value_.index()); + } + constexpr bool is_int() const { + return std::holds_alternative(value_); + } + constexpr bool is_double() const { + return std::holds_alternative(value_); + } + constexpr bool is_bool() const { + return std::holds_alternative(value_); + } + + // Strict accessors: return the live alternative, throw std::runtime_error on + // a tag mismatch. + int64_t to_int() const; + double to_double() const; + bool to_bool() const; + + // Promoting read: static_cast the live alternative to T (like + // c10::Scalar::to()); works whichever tag is live. + template + constexpr T to() const { + return std::visit([](auto v) { return static_cast(v); }, value_); + } + + const char* tag_name() const; + std::string to_string() const; +}; + +} // namespace ptn diff --git a/backends/native/runtime/graph/targets.bzl b/backends/native/runtime/graph/targets.bzl index 33a14571450..88f73072fe4 100644 --- a/backends/native/runtime/graph/targets.bzl +++ b/backends/native/runtime/graph/targets.bzl @@ -19,6 +19,17 @@ def define_common_targets(): visibility = ["//executorch/backends/native/..."], ) + # A concrete scalar value (int / double / bool), tagged. c10::Scalar analog. + runtime.cxx_library( + name = "scalar", + srcs = ["Scalar.cpp"], + exported_headers = [ + "Scalar.h", + ], + deps = [":format"], + visibility = ["//executorch/backends/native/..."], + ) + # Concrete in-memory IR value types (pure std; no ExecuTorch, no flatbuffers). runtime.cxx_library( name = "tensor_meta",