| name | add-op |
| description | Add a new ONNX operator or update an existing operator to a new opset version. Use when asked to define an operator schema, register an op, add inputs/outputs/attributes to an op, move an op to old.cc, or bump an op's opset version. |
Follow the full procedure in docs/AddNewOp.md.
Files to Modify
| Component | File |
|---|
| Schema definition | onnx/defs/<domain>/defs.cc |
| Operator set registration | onnx/defs/operator_sets.h |
| Type/shape inference | Inline in schema via .TypeAndShapeInferenceFunction(...) |
| Function body (if applicable) | Inline in schema via .FunctionBody(...) |
| Reference implementation | onnx/reference/ops/op_<lowercase_name>.py |
| Node tests | onnx/backend/test/case/node/<lowercase_name>.py |
| Shape inference tests | onnx/test/shape_inference_test.py |
| Version converter adapter (if behavior changed) | onnx/version_converter/adapters/<name>_<from>_<to>.h |
| Upgrade/downgrade tests | onnx/test/version_converter/automatic_upgrade_test.py and automatic_downgrade_test.py |
Domain subdirectories under onnx/defs/: math/, nn/, tensor/, logical/, reduction/, rnn/, sequence/, image/, text/, quantization/, controlflow/, optional/, traditionalml/, training/
Schema Registration Pattern
ONNX_OPERATOR_SET_SCHEMA(
OperatorName,
OPSET_VERSION,
OpSchema()
.SetDoc(OperatorName_verN_doc)
.Input(0, "X", "Description", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
.Output(0, "Y", "Description", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
.Attr("attr_name", "Description", AttributeProto::FLOAT, default_value)
.TypeConstraint("T", {"tensor(float)", "tensor(double)", ...}, "Description")
.TypeAndShapeInferenceFunction(InferShapeForOperatorName)
.FunctionBody(R"ONNX(
{
...
}
)ONNX", FUNCTION_OPSET_VERSION));
Updating an Existing Operator
- Move current schema from
defs.cc to old.cc in the same domain directory
- Create new version in
defs.cc with the new opset version number
- Update
onnx/defs/operator_sets.h
- Add a version converter adapter if behavior changed
- Add upgrade/downgrade tests
Avoiding duplication between defs.cc and old.cc
When moving a schema to old.cc, avoid significant code/documentation duplication:
- Extract common logic (doc strings, type constraint lists, shape inference helpers) into shared functions in the domain's header or utils file.
- Use parameterized helpers when versions differ only slightly (e.g., expanded type list, additional optional input).
- Some duplication is acceptable when sharing would create overly complicated logic. Prefer clarity over DRY when the alternative makes either version harder to understand independently.
Code Style: Prefer Named Functions
Define shape inference functions and function body builders as separate named functions rather than inline lambdas within ONNX_OPERATOR_SET_SCHEMA. The macro expansion makes setting breakpoints on inline lambdas unreliable in debuggers.
static void InferShapeForMyOp(InferenceContext& ctx) {
propagateElemTypeFromInputToOutput(ctx, 0, 0);
}
ONNX_OPERATOR_SET_SCHEMA(
MyOp, 21,
OpSchema()
.TypeAndShapeInferenceFunction(InferShapeForMyOp));
Writing Tests
For test fixtures, prefer the ONNX text format via onnx.parser / C++ OnnxParser. See the onnxtxt skill for the per-file recommendations table, body-subgraph idioms, the argument-order convention, and the unk__* materialization gotcha. (Empirical: PR #7962 cut ~58–70% of test LOC by switching.)
After Making Changes
python onnx/defs/gen_doc.py
python onnx/backend/test/stat_coverage.py
python onnx/gen_proto.py
lintrunner -a --output oneline
References