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
202 changes: 202 additions & 0 deletions backends/native/runtime/graph/Node.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// 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/Node.h>

#include <string>

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

namespace ptn {

namespace {

std::string ref_str(ValueRef ref) {
return valid(ref) ? "%" + std::to_string(ref) : "None";
}

// Compact one-line rendering of an argument for Node::to_string. Symbolic
// scalars (a valid ref) render as the ref; literals render as their value.
std::string arg_str(const Argument& arg) {
switch (arg.kind()) {
case ArgKind::None:
return "None";
case ArgKind::Tensor:
return ref_str(arg.as_tensor().ref);
case ArgKind::Int: {
const IntArg& a = arg.as_int();
return valid(a.ref) ? ref_str(a.ref) : std::to_string(a.value);
}
case ArgKind::Float: {
const FloatArg& a = arg.as_float();
return valid(a.ref) ? ref_str(a.ref) : format_double(a.value);
}
case ArgKind::Bool: {
const BoolArg& a = arg.as_bool();
return valid(a.ref) ? ref_str(a.ref) : (a.value ? "true" : "false");
}
case ArgKind::String:
return "\"" + arg.as_string().value + "\"";
case ArgKind::ScalarType:
return scalar_type_name(arg.as_scalar_type().value);
case ArgKind::IntList: {
const IntListArg& a = arg.as_int_list();
std::string s = "[";
for (size_t i = 0; i < a.values.size(); ++i) {
if (i) {
s += ", ";
}
const bool sym = i < a.refs.size() && valid(a.refs[i]);
s += sym ? ref_str(a.refs[i]) : std::to_string(a.values[i]);
}
return s + "]";
}
case ArgKind::FloatList: {
const FloatListArg& a = arg.as_float_list();
std::string s = "[";
for (size_t i = 0; i < a.values.size(); ++i) {
if (i) {
s += ", ";
}
s += format_double(a.values[i]);
}
return s + "]";
}
case ArgKind::BoolList: {
const BoolListArg& a = arg.as_bool_list();
std::string s = "[";
for (size_t i = 0; i < a.values.size(); ++i) {
if (i) {
s += ", ";
}
s += a.values[i] ? "true" : "false";
}
return s + "]";
}
case ArgKind::TensorList:
case ArgKind::OptionalTensorList: {
const std::vector<ValueRef>& refs = arg.kind() == ArgKind::TensorList
? arg.as_tensor_list().refs
: arg.as_optional_tensor_list().refs;
std::string s = "[";
for (size_t i = 0; i < refs.size(); ++i) {
if (i) {
s += ", ";
}
s += ref_str(refs[i]);
}
return s + "]";
}
case ArgKind::Graph:
return "graph(" + arg.as_graph().name + ")";
}
return "?";
}

std::string output_str(const Output& out) {
if (out.kind == OutputValueKind::TensorList) {
std::string s = "[";
for (size_t i = 0; i < out.elem_refs.size(); ++i) {
if (i) {
s += ", ";
}
s += ref_str(out.elem_refs[i]);
}
return s + "]";
}
return ref_str(out.value_ref);
}

// Append `ref` to `refs` unless it is kInvalid (an unwired / literal operand).
void push_ref(std::vector<ValueRef>& refs, ValueRef ref) {
if (valid(ref)) {
refs.push_back(ref);
}
}

} // namespace

std::vector<ValueRef> Node::input_value_refs() const {
std::vector<ValueRef> refs;
for (const NamedArgument& named : inputs) {
const Argument& arg = named.arg;
switch (arg.kind()) {
case ArgKind::Tensor:
push_ref(refs, arg.as_tensor().ref);
break;
case ArgKind::Int:
push_ref(refs, arg.as_int().ref);
break;
case ArgKind::Float:
push_ref(refs, arg.as_float().ref);
break;
case ArgKind::Bool:
push_ref(refs, arg.as_bool().ref);
break;
case ArgKind::IntList:
for (ValueRef r : arg.as_int_list().refs) {
push_ref(refs, r);
}
break;
case ArgKind::TensorList:
for (ValueRef r : arg.as_tensor_list().refs) {
push_ref(refs, r);
}
break;
case ArgKind::OptionalTensorList:
for (ValueRef r : arg.as_optional_tensor_list().refs) {
push_ref(refs, r);
}
break;
default:
break; // None / String / ScalarType / Float|BoolList / Graph: no refs
}
}
return refs;
}

std::string Node::to_string() const {
std::string s = name.empty() ? "_" : name;
s += " = ";
switch (op_kind) {
case OpKind::CallFunction:
s += target;
break;
case OpKind::Placeholder:
s += "<placeholder>";
break;
case OpKind::Output:
s += "<output>";
break;
}
s += "(";
for (size_t i = 0; i < inputs.size(); ++i) {
if (i) {
s += ", ";
}
const NamedArgument& named = inputs[i];
if (!named.name.empty()) {
s += named.name + "=";
}
s += arg_str(named.arg);
if (named.mutated) {
s += "!";
}
}
s += ")";
if (!outputs.empty()) {
s += " -> ";
for (size_t i = 0; i < outputs.size(); ++i) {
if (i) {
s += ", ";
}
s += output_str(outputs[i]);
}
}
return s;
}

} // namespace ptn
82 changes: 82 additions & 0 deletions backends/native/runtime/graph/Node.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 <any>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>

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

namespace ptn {

// fx node kind (node.op). Pinned to the schema OpKind ids.
enum class OpKind : int8_t {
CallFunction = 0,
Placeholder = 1,
Output = 2,
};

// What a single node Output produces. Pinned to the schema OutputValueKind ids;
// named to match the schema (distinct from the graph-level OutputKind — user
// output vs buffer mutation — that lands with Method).
enum class OutputValueKind : int8_t {
Tensor = 0,
TensorList = 1,
Int = 2,
Bool = 3,
Float = 4,
};

// One value produced by a node. Tensor / Int / Bool / Float use `value_ref`;
// TensorList (e.g. split) uses `elem_refs`. The return-ABI grouping is
// preserved so engine translation can tell a single result from a tuple / list
// (topk emits two Tensor outputs; split emits one TensorList output). The
// storage-alias fact lives on the produced Value, not here.
struct Output {
OutputValueKind kind = OutputValueKind::Tensor;
ValueRef value_ref = kInvalid;
std::vector<ValueRef> elem_refs;
};

// One fx node: an op invocation (CallFunction) or a graph-boundary marker
// (Placeholder / Output). For an Output node, `inputs` is the ordered return
// list (tensors and literals alike) and `outputs` is empty; for a Placeholder,
// `target` is empty and it produces a single output. `attrs` is a transient
// scratch map (the fx node.meta analog); it is not serialized.
struct Node {
std::string name;
OpKind op_kind = OpKind::CallFunction;
// fqn, e.g. "torch.ops.aten.addmm.default"; empty for placeholder / output.
std::string target;
std::vector<NamedArgument> inputs;
std::vector<Output> outputs;
std::unordered_map<std::string, std::any> attrs;

bool is_call() const {
return op_kind == OpKind::CallFunction;
}
bool is_placeholder() const {
return op_kind == OpKind::Placeholder;
}
bool is_output() const {
return op_kind == OpKind::Output;
}

// Every ValueRef this node consumes: tensor args, tensor-list / optional-list
// elements, and symbolic scalar refs (kInvalid entries skipped). Used to
// (re)build def-use wiring.
std::vector<ValueRef> input_value_refs() const;

// Single-line debug rendering, e.g. "a = aten.add.Tensor(x, y, alpha=1)".
std::string to_string() const;
};

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

# One fx node: an op invocation or graph-boundary marker, with typed Outputs
# preserving the op return ABI (single / tuple / tensor-list / scalar).
runtime.cxx_library(
name = "node",
srcs = ["Node.cpp"],
exported_headers = [
"Node.h",
],
exported_deps = [
":argument",
":ids",
],
deps = [":format"],
visibility = ["//executorch/backends/native/..."],
)
Loading