run_nodes.h#

Tiny dispatcher that runs the matching backend test kernel for one NodeProto or for a list of nodes (an iterator), mirroring the per-operator :cpp:func:onnx_optim::shapes::ShapesContext::ComputeShapeNode / :cpp:func:onnx_optim::shapes::ShapesContext::ComputeShapes pair used by onnx_optim for shape inference.

Inputs and outputs are exchanged through a name-keyed :cpp:type:TensorMap (owned by :cpp:class:RuntimeContext) so a chain of nodes can be evaluated in topological order (as required by the ONNX specification for GraphProto::node()). A node is dispatched by its (domain, op_type) pair through :cpp:func:KernelDispatchTable; new operators are added by registering a single new entry in that table without changing :cpp:func:RunNode / :cpp:func:RunNodes.

Only a small, working baseline of operators is registered today (the simple element-wise ai.onnx ops Abs, Add, Div, Mul, Neg, Sub). The dispatcher is deliberately extensible: as more kernels become wirable through a uniform NodeProto-driven call site, additional entries can be added to KernelDispatchTable and they become callable from :cpp:func:RunNode / :cpp:func:RunNodes automatically.

In addition to the static :cpp:func:KernelDispatchTable, :cpp:func:RunNode also consults :cpp:func:RuntimeContext::functions for model-local functions (ModelProto::functions). When a node’s (domain, op_type, overload) triple matches a registered :cpp:type:FunctionProto, the call is dispatched to :cpp:func:RunFunction with a fresh child :cpp:class:RuntimeContext bound to the function’s formal inputs; the function’s formal outputs are then propagated back to the caller under the names declared by node.output. :cpp:func:RunModel populates that registry from model.functions() before delegating to :cpp:func:RunGraph, so nodes referring to local functions are resolved transparently.

namespace onnx_light

Alias that makes onnx-light headers compatible with code that references ONNX_LIGHT_NAMESPACE (the macro used in the standard onnx package).

Set to ONNX_LIGHT_NAMESPACE so both names resolve to the same namespace.

Symbol-visibility attribute for the public onnx-light C++ API.

Defined as empty because onnx-light does not require explicit __declspec(dllexport) or __attribute__((visibility("default"))) annotations — visibility is controlled at the shared-library level. The macro is provided so that vendored ONNX headers that decorate their declarations with ONNX_API compile without modification.

Namespace alias so that ONNX C++ code (and consumers such as onnxruntime) that refers to the literal onnx namespace — rather than the ONNX_NAMESPACE macro — resolves to the onnx-light namespace. The standard onnx package lives in namespace onnx; onnx-light uses onnx_light (via ONNX_LIGHT_NAMESPACE), so this alias keeps onnx-light a true drop-in. It is only introduced when the onnx-light namespace differs from onnx.

namespace onnx_kernels

Functions

void RunNode(const NodeProto &node, RuntimeContext &rt)#

Signature of every per-operator trampoline registered in :cpp:func:KernelDispatchTable. Implementations read their inputs from rt.tensors() by name, call the matching kernel (constructed with rt.kernel_ctx()), and insert the produced outputs back into rt.tensors() under the names declared by node.output(i).

The alias and the table itself are now declared in onnx_kernels/kernel_dispatch_table.h (transitively included above); this file is left as a comment so the public API surface of run_nodes.h remains documented in one place. Runs the kernel registered for node and stores its outputs in rt.tensors().

The node’s input descriptors are read from rt.tensors() by name (so every non-empty input must already be present), and the output descriptors are inserted into rt.tensors() under the names declared by node.output(i).

In addition to table-dispatched kernels and model-local functions, this dispatcher also evaluates control-flow nodes (If, Loop, Scan) by recursively executing their embedded subgraphs.

Parameters:
  • node – The node to execute.

  • rt – In/out runtime context. rt.tensors() must already contain entries for every input referenced by node; on return it also contains entries for every output declared by node. rt.kernel_ctx() is used to construct the per-operator kernel.

Throws:

std::invalid_argument – if node.op_type() is not registered in :cpp:func:KernelDispatchTable, if a required input is missing from rt.tensors(), or if the per-operator trampoline rejects the node.

void RunNodes(const utils::RepeatedProtoField<NodeProto> &nodes, RuntimeContext &rt)#

Runs :cpp:func:RunNode on every node of nodes in order.

The sequence must be topologically sorted with respect to data dependencies (as required by the ONNX specification for GraphProto::node) so that every input of a node has already been produced — either as a pre-existing graph input/initializer carried in rt.tensors() or as the output of an earlier node in nodes — by the time the node is processed.

Parameters:
  • nodes – The list of nodes to execute, in topological order.

  • rt – In/out runtime context seeded with the graph inputs and initializers in rt.tensors(); on return it additionally contains every node output.

Throws:

std::invalid_argument – if any node cannot be dispatched.

void RunNodes(const utils::RepeatedProtoField<NodeProto> &nodes, RuntimeContext &rt, const ExecutionPlan &plan)#

Release-aware overload of :cpp:func:RunNodes. Runs every node of nodes in order and, after each node, frees from rt every intermediate whose last reference has been reached according to plan. Names that the caller seeded into rt on top of plan.keep() (graph inputs / initializers / outputs already covered by the plan) are preserved automatically — they are detected at run start and excluded from the release loop.

The plan is not built here: callers (typically :cpp:func:RunGraph / :cpp:func:RunFunction) obtain it via :cpp:func:RuntimeContext::GetExecutionPlan so the analysis is paid only once per model and reused across every subsequent run.

Parameters:
  • nodes – The list of nodes to execute, in topological order.

  • rt – In/out runtime context.

  • plan – Precomputed release schedule covering nodes.

template<class InputIt>
void RunNodes(InputIt first, InputIt last, RuntimeContext &rt)#

Generic iterator overload of :cpp:func:RunNodes. Accepts any input-iterator range whose value_type (after dereferencing) is convertible to const NodeProto &, so callers can drive the dispatcher from std::vector<NodeProto>, std::list<NodeProto> or any other container — not only RepeatedProtoField.

void RunGraph(const GraphProto &graph, RuntimeContext &rt)#

Runs all nodes in a GraphProto using the provided runtime context.

Before executing the node sequence the function seeds rt.tensors() with every TensorProto in graph.initializer(), so that downstream nodes can look up constant values by name. Graph inputs that the caller has already placed in rt.tensors() are left as-is.

Parameters:
  • graph – The graph to evaluate. Its node list must already be in topological order (as required by the ONNX spec).

  • rt – In/out runtime context seeded with the graph inputs; on return rt.tensors() additionally contains every node output and every initializer.

Throws:

std::invalid_argument – if any node cannot be dispatched.

void RunFunction(const FunctionProto &func, RuntimeContext &rt)#

Runs all nodes in a FunctionProto using the provided runtime context.

The caller is responsible for inserting the function’s input tensors into rt.tensors() before calling this function. On return rt.tensors() additionally contains every node output.

Parameters:
  • func – The function to evaluate. Its node list must already be in topological order.

  • rt – In/out runtime context seeded with the function’s inputs; on return it additionally contains every node output.

Throws:

std::invalid_argument – if any node cannot be dispatched.

void RunModel(const ModelProto &model, RuntimeContext &rt)#

Runs the graph embedded in a ModelProto using the provided runtime context.

Before delegating to :cpp:func:RunGraph, every FunctionProto in model.functions() is registered in :cpp:func:RuntimeContext::functions so that nodes referring to a model-local function by (domain, op_type, overload) are dispatched through :cpp:func:RunFunction rather than the static :cpp:func:KernelDispatchTable. The caller is responsible for inserting the model’s input tensors into rt.tensors() beforehand.

Parameters:
  • model – The model whose graph field will be evaluated.

  • rt – In/out runtime context seeded with the model inputs; on return it additionally contains every node output and every graph initializer.

Throws:

std::invalid_argument – if the model has no graph or any node cannot be dispatched.

std::vector<Tensor> RunSubgraph(const GraphProto &graph, const std::vector<std::pair<std::string, Tensor>> &bindings, RuntimeContext &rt, const std::string &attr_name = "")#

Evaluates a subgraph in a fresh child :cpp:class:RuntimeContext that inherits the caller’s tensor map and function registry, additionally seeded with bindings (typically the formal-input ↔ actual-input tensor pairs for the subgraph). Returns the subgraph’s outputs in the order declared by graph.output().

When the caller’s context has event logging enabled (:cpp:func:RuntimeContext::events_enabled), child events are appended to the caller’s event log after the subgraph finishes. Each propagated event carries :cpp:var:RuntimeEvent::subgraph_node_index set to rt.current_node_index() (the index of the control-flow node in the parent graph) and :cpp:var:RuntimeEvent::subgraph_attr_name set to attr_name, so consumers can distinguish subgraph events from top-level events.

Exposed publicly so control-flow kernels (e.g. :cpp:class:kernel::Scan) can run their body subgraph without going through :cpp:func:RunNode themselves.

Parameters:

attr_name – Attribute name identifying the subgraph within its owning control-flow node (e.g. "body", "then_branch", "else_branch"). Stored in :cpp:var:RuntimeEvent::subgraph_attr_name of every event produced during the subgraph run.

Throws:

std::invalid_argument – if a subgraph output has an empty name or is not produced by the body.

int64_t ResolveAxis(int64_t axis, std::size_t rank, const std::string &op_name)#

Resolves a possibly-negative axis against a tensor of rank rank and returns the non-negative axis in [0, rank). Throws std::invalid_argument with a message that mentions op_name when the axis is out of range.

Tensor SliceTensorAlongAxis(const Tensor &t, int64_t axis, int64_t index, const std::string &op_name)#

Returns the tensor obtained by selecting the index-th slice of t along axis axis (the resulting tensor’s rank is t.shape.size() - 1). The slice is copied into a fresh buffer; the source tensor is not modified. op_name only appears in error messages.

Throws:

std::invalid_argument – if t is a rank-0 tensor, the index is out of range, or the slice would exceed addressable buffer size.