diff --git a/backends/native/runtime/Method.h b/backends/native/runtime/Method.h new file mode 100644 index 00000000000..bacdca5c454 --- /dev/null +++ b/backends/native/runtime/Method.h @@ -0,0 +1,62 @@ +// 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 { + +// Classifies a graph-level output position; pinned to the schema OutputKind +// ids. Orthogonal to ValueRole, which classifies a value's storage: the value +// produced at a mutation output is an ordinary Intermediate, and the Buffer or +// UserInput role sits on its target. +enum class OutputKind : int8_t { + UserOutput = 0, + BufferMutation = 1, + UserInputMutation = 2, +}; + +// Binds a method-graph placeholder to its external storage, unifying the +// schema's NamedTensorRef (data shipped under `key`) and MutableBufferSpec (no +// data, zero-initialized at load, e.g. a KV cache). Tensor metadata is not +// duplicated — it lives on the bound Value, which also mirrors `role`. +struct DataBinding { + ValueId value_id = kInvalid; + ValueRole role = ValueRole::Parameter; // Parameter / Buffer / ConstantTensor + // Fully-qualified name: the constant-file key if has_data, else the buffer's + // cross-method identity. + std::string key; + bool has_data = true; // false only for a non-persistent Buffer + bool mutated = false; // written in place; state persists across executions +}; + +// Classifies one graph output. `target_id` is the placeholder value the output +// writes back into — a user input or a buffer, both lifted to placeholders +// here; kInvalid for UserOutput. It replaces the schema's dual-namespace target +// string, whose contents are recoverable from that Value. +struct OutputSpec { + OutputKind kind = OutputKind::UserOutput; + ValueId target_id = kInvalid; +}; + +// A named method: one top-level pure Graph plus the stateful signature bindings +// that Graph deliberately lacks. HOP subgraphs (inside graph.subgraphs) carry +// no bindings of their own — their params are lifted here and passed as +// operands, which is why Method wraps Graph rather than folding into it. +struct Method { + std::string name; + Graph graph; + std::vector data_bindings; + std::vector output_specs; // aligned to graph.output_ids by index +}; + +} // namespace ptn diff --git a/backends/native/runtime/graph/Value.h b/backends/native/runtime/graph/Value.h index 215f3feab05..a18495a7569 100644 --- a/backends/native/runtime/graph/Value.h +++ b/backends/native/runtime/graph/Value.h @@ -27,6 +27,18 @@ enum class ValueKind : int8_t { List = 3, }; +// How a Value's storage is owned / sourced. The placeholder roles are pinned to +// the schema InputKind ids; Intermediate extends them for a value a node +// produces. Parameter / ConstantTensor are frozen external data; a Buffer is +// persistent state when its binding ships data, else zero-initialized at load. +enum class ValueRole : int8_t { + UserInput = 0, + Parameter = 1, + Buffer = 2, + ConstantTensor = 3, + Intermediate = 4, +}; + // A single SSA value (dataflow edge) in a Graph: its contents plus def-use // wiring, a storage alias and an open annotation map. The id fields are plain // handles; whether one is in range is a property of the owning arena, so @@ -53,6 +65,9 @@ class Value { std::vector consumer_ids; // Shares storage with this value (a view); fresh if invalid. ValueId alias_id = kInvalid; + // Stamped from the Method's data bindings, so a value can be classified + // without searching them; the storage itself is located through the binding. + ValueRole role = ValueRole::Intermediate; // Open annotations for graph passes and engines, like node.meta in FX. std::unordered_map attrs; diff --git a/backends/native/runtime/targets.bzl b/backends/native/runtime/targets.bzl index 7ec52895218..911924c818a 100644 --- a/backends/native/runtime/targets.bzl +++ b/backends/native/runtime/targets.bzl @@ -73,3 +73,36 @@ def define_common_targets(): ], visibility = ["PUBLIC"], ) + + # A named method: one top-level Graph plus its stateful signature bindings + # (data bindings + output specs). Sits at the Program level (peer to the reader), + # above the graph/ arena package. + runtime.cxx_library( + name = "method", + exported_headers = [ + "Method.h", + ], + exported_deps = [ + "//executorch/backends/native/runtime/graph:graph", + "//executorch/backends/native/runtime/graph:ids", + "//executorch/backends/native/runtime/graph:value", + ], + visibility = ["//executorch/backends/native/..."], + ) + + # The graph/ printer stops at Graph, since Method sits above that package; + # this adds the method layer on top of it. + runtime.cxx_library( + name = "print", + srcs = [ + "utils/Print.cpp", + ], + exported_headers = [ + "utils/Print.h", + ], + exported_deps = [ + ":method", + "//executorch/backends/native/runtime/graph:print", + ], + visibility = ["//executorch/backends/native/..."], + ) diff --git a/backends/native/runtime/utils/Print.cpp b/backends/native/runtime/utils/Print.cpp new file mode 100644 index 00000000000..a19d736bed8 --- /dev/null +++ b/backends/native/runtime/utils/Print.cpp @@ -0,0 +1,78 @@ +// 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 { + +namespace { + +const char* output_kind_name(OutputKind kind) { + switch (kind) { + case OutputKind::UserOutput: + return "UserOutput"; + case OutputKind::BufferMutation: + return "BufferMutation"; + case OutputKind::UserInputMutation: + return "UserInputMutation"; + } + return "?"; +} + +const char* value_role_name(ValueRole role) { + switch (role) { + case ValueRole::Intermediate: + return "Intermediate"; + case ValueRole::UserInput: + return "UserInput"; + case ValueRole::Parameter: + return "Parameter"; + case ValueRole::Buffer: + return "Buffer"; + case ValueRole::ConstantTensor: + return "ConstantTensor"; + } + return "?"; +} + +} // namespace + +std::string to_string(const Method& method) { + std::string s = "method " + method.name + "\n"; + s += to_string(method.graph); + + s += "data_bindings: ["; + for (size_t i = 0; i < method.data_bindings.size(); ++i) { + if (i) { + s += ", "; + } + const DataBinding& b = method.data_bindings[i]; + s += "%" + std::to_string(b.value_id) + "=" + b.key + "(" + + value_role_name(b.role) + (b.has_data ? "" : ",zero_init") + + (b.mutated ? ",mutated" : "") + ")"; + } + s += "]\n"; + + s += "output_specs: ["; + for (size_t i = 0; i < method.output_specs.size(); ++i) { + if (i) { + s += ", "; + } + const OutputSpec& o = method.output_specs[i]; + s += output_kind_name(o.kind); + if (valid(o.target_id)) { + s += "(%" + std::to_string(o.target_id) + ")"; + } + } + s += "]\n"; + + return s; +} + +} // namespace ptn diff --git a/backends/native/runtime/utils/Print.h b/backends/native/runtime/utils/Print.h new file mode 100644 index 00000000000..a799ed4ad8c --- /dev/null +++ b/backends/native/runtime/utils/Print.h @@ -0,0 +1,22 @@ +// 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 { + +// The method layer of the IR printer, split out only because Method sits above +// the graph/ package. Same contract: debug-only, for humans, not parsed back. + +// Multi-line: the method name, its graph, then the binding tables. +std::string to_string(const Method& method); + +} // namespace ptn