Skip to main content

Module function_inline

Module function_inline 

Source
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:

  1. 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 boundary Identity.

  2. Attribute binding. A body-node attribute with a non-empty ref_attr_name = A is a reference to the function’s formal attribute A. It is resolved from the call site (the call node’s attribute A), else the function’s declared default (attribute_proto entry named A), else — if A is a required attribute (FunctionProto.attribute) — an error; else the attribute is dropped. Literal (non-ref) attributes are kept as-is.

  3. 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.

  4. 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/Graphs attributes. Attribute binding and value remapping are scope-aware: nested ref_attr_name references 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 model into the function’s body, so the returned ModelProto’s graph (and all nested subgraphs) contain only calls to ops the runtime has kernels for.