Expand description
Model-local function inlining (ONNX function expansion) at load time.
An ONNX ModelProto may declare reusable subgraphs as FunctionProtos in
ModelProto.functions. A node whose (domain, op_type, overload) matches a
declared function’s (domain, name, overload) is a function call: it is
semantically equivalent to the function body with the call’s actual inputs,
outputs, and attributes substituted in.
Our executor only has kernels for primitive ops, so this module rewrites the
ModelProto at the proto level — before [crate::graph_builder] runs —
so the rest of the pipeline never sees a function call. Because the rewrite
is proto-level, the existing NodeProto → IR conversion (attributes,
control-flow subgraphs) is reused unchanged.
§Algorithm (standard ONNX function expansion)
For each function-call node, we splice in a fresh copy of the matched function body:
-
Value remapping. Formal
input[i]/output[j]names are mapped to the call’s actual argument names (positionally). Every other value name in the body (an intermediate result) is renamed to a globally-fresh unique name (__fn{K}_{orig}, bumped until unused) so instantiations never collide with each other or with pre-existing model names. The empty name""(ONNX “absent optional”) is never remapped. A pass-through output whose formal name aliases an input is wired via a boundaryIdentity. -
Attribute binding. A body-node attribute with a non-empty
ref_attr_name = Ais a reference to the function’s formal attributeA. It is resolved from the call site (the call node’s attributeA), else the function’s declared default (attribute_protoentry namedA), else — ifAis a required attribute (FunctionProto.attribute) — an error; else the attribute is dropped. Literal (non-ref) attributes are kept as-is. -
Recursion + fixpoint. A function body may call other functions; those calls are expanded recursively to a fixpoint. True recursion (a function that transitively calls itself) is rejected rather than looped forever.
-
Control-flow subgraphs. Function calls may appear inside If/Loop/Scan subgraph bodies, and function bodies may themselves contain control flow; both are handled by recursing into every node’s
Graph/Graphsattributes. Attribute binding and value remapping are scope-aware: nestedref_attr_namereferences are bound at every depth, and a subgraph’s own locals (inputs, initializers, node outputs) shadow outer captures.
§Opset policy
FunctionProto.opset_import domains/versions are merged into the model’s
opset_import, taking the highest version per domain. Per the ONNX spec the
operator schemas for a shared domain must be compatible across the two opset
lists, so a version difference is not treated as a conflict; a domain the
model does not yet declare is added.
§Overload policy
Matching is exact on the full (domain, name, overload) triple, so an
overload set is disambiguated by the node’s overload field.
Functions§
- inline_
functions - Expand every call to a model-local function in
modelinto the function’s body, so the returnedModelProto’s graph (and all nested subgraphs) contain only calls to ops the runtime has kernels for.