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
61 changes: 61 additions & 0 deletions backends/native/runtime/Method.cpp
Original file line number Diff line number Diff line change
@@ -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 <executorch/backends/native/runtime/Method.h>

#include <string>

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 "?";
}

} // namespace

std::string Method::to_string() const {
std::string s = "method " + name + "\n";
s += graph.to_string();

s += "data_bindings: [";
for (size_t i = 0; i < data_bindings.size(); ++i) {
if (i) {
s += ", ";
}
const DataBinding& b = data_bindings[i];
s += "%" + std::to_string(b.value_ref) + "=" + 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 < output_specs.size(); ++i) {
if (i) {
s += ", ";
}
const OutputSpec& o = output_specs[i];
s += output_kind_name(o.kind);
if (valid(o.target_ref)) {
s += "(%" + std::to_string(o.target_ref) + ")";
}
}
s += "]\n";

return s;
}

} // namespace ptn
77 changes: 77 additions & 0 deletions backends/native/runtime/Method.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 <string>
#include <vector>

#include <executorch/backends/native/runtime/graph/Graph.h>
#include <executorch/backends/native/runtime/graph/Ids.h>
#include <executorch/backends/native/runtime/graph/Value.h>

namespace ptn {

// Classifies a graph-level output value (schema OutputKind). USER_OUTPUT is a
// real result; the mutation kinds mark a value that writes back into a
// persistent buffer or a user input. Distinct from the node-level
// OutputValueKind (tensor / list / scalar).
enum class OutputKind : int8_t {
UserOutput = 0,
BufferMutation = 1,
UserInputMutation = 2,
};

// Binds a method-graph placeholder value to its external storage. Unifies the
// schema's NamedTensorRef (constant-backed: parameter / frozen constant /
// persistent buffer — data shipped under `key`) and MutableBufferSpec
// (non-persistent buffer — no data, zero-initialized at load, e.g. a KV cache).
// `key` is the namespace-3 fully-qualified name: an external-constant-file key
// when `has_data`, else the buffer's cross-method identity. `has_data` selects
// the load path (fetch bytes vs zero-init). `mutated` (schema
// NamedTensorRef.mutated) marks a Buffer written in place, whose state persists
// across executions — parameters / frozen constants / read-only buffers are
// false. `role` mirrors the bound Value's role (kept here too for direct access
// when iterating bindings). Tensor metadata is not duplicated — it lives on the
// bound Value.
struct DataBinding {
ValueRef value_ref = kInvalid;
ValueRole role = ValueRole::Parameter; // Parameter / Buffer / ConstantTensor
// Namespace-3 fqn: the constant-file key if has_data, else the buffer's
// cross-method identity.
std::string key;
bool has_data = true; // bytes shipped under key, else zero-init at load
bool mutated = false; // written in place; state persists across executions
};

// Per graph-output classification (schema OutputSpec), aligned to
// graph.output_refs by index. `target_ref` references the mutated placeholder
// value in this method graph — the user input (UserInputMutation) or the buffer
// (BufferMutation), both of which are lifted placeholders here; kInvalid for
// UserOutput. The wire strings are recovered from that Value: its `name` (SSA,
// namespace 2) and, for a buffer, its DataBinding's `key` (fqn, namespace 3).
struct OutputSpec {
OutputKind kind = OutputKind::UserOutput;
ValueRef target_ref = kInvalid;
};

// A named method: one top-level pure Graph plus its stateful signature
// bindings. `data_bindings` is the authoritative placeholder→storage table
// (merging the schema's constants + mutable_buffers); the deserializer also
// stamps each bound placeholder Value's role / data_key for O(1) per-value
// queries. HOP subgraphs (inside graph.subgraphs) carry no bindings — their
// params are lifted here and passed as operands.
struct Method {
std::string name;
Graph graph;
std::vector<DataBinding> data_bindings;
std::vector<OutputSpec> output_specs; // aligned to graph.output_refs by index

// Multi-line debug dump: name, the graph, and the binding tables.
std::string to_string() const;
};

} // namespace ptn
37 changes: 37 additions & 0 deletions backends/native/runtime/graph/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,37 @@ enum class ValueKind : int8_t {
List = 3,
};

// How a Value's storage is owned / sourced (a method-binding fact stamped by
// the deserializer). Intermediate: produced by a node (a pooled activation).
// The rest classify a placeholder: UserInput (caller-wired each call);
// Parameter / ConstantTensor (frozen external data, located by
// Value::data_key); Buffer (persistent state when data_key is set, else
// zero-initialized at load). Mirrors the schema InputKind, plus Intermediate
// for non-placeholder values.
enum class ValueRole : int8_t {
Intermediate = 0,
UserInput = 1,
Parameter = 2,
Buffer = 3,
ConstantTensor = 4,
};

inline 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 "?";
}

// A single SSA value (dataflow edge) in a Graph: its contents plus def-use
// wiring, a storage-alias fact, and an open attrs map.
//
Expand Down Expand Up @@ -55,6 +86,12 @@ class Value {
std::vector<NodeRef> consumer_refs;
// Shares storage with this value (a view); fresh if invalid.
ValueRef alias_ref = kInvalid;
// Storage ownership / source class, stamped by the deserializer from the
// Method bindings.
ValueRole role = ValueRole::Intermediate;
// External-constant key for Parameter / ConstantTensor / persistent Buffer;
// empty otherwise.
std::string data_key;
// Scratch + planner annotations.
std::unordered_map<std::string, std::any> attrs;

Expand Down
17 changes: 17 additions & 0 deletions backends/native/runtime/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,20 @@ 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",
srcs = ["Method.cpp"],
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/..."],
)
Loading