Integrate ONNX 1.22.0 (opset 27) — issue #28752
Integrate ONNX 1.22.0rc1 (opset 27)
Resolves #28752.
Pin: onnx/onnx@bc3be77bec2f628788796dff60819186bacf49df (VERSION_NUMBER 1.22.0rc1).
ONNX 1.21.0 → 1.22.0rc1. Max ai.onnx opset 26 → 27. IR version unchanged (13 / 0x0D).
This is the RC validation phase of an incremental integration (same strategy as the ONNX 1.21 bump, #27601). The formal v1.22.0 GitHub release is still a draft (no git tag yet), so re-pinning to the released tag is deferred to Phase 2 (see Follow-ups). Landing the RC now validates ONNX 1.22 against ORT before ONNX publishes the formal release.
Update — ONNX 1.22.0 FINAL re-pin + rebase onto upstream/main + closes #28969
ONNX published the formal v1.22.0 GitHub release, so this PR is re-pinned rc2 → FINAL (onnx/onnx@v1.22.0) — the Phase-2 step deferred in the rc1 description below. The branch was also rebased onto upstream/main to pick up the intervening optimizer/opset-26 work. The released tag tarball is a different asset hash than the RCs, so the vcpkg MS-internal asset mirror was re-seeded for the final tag (otherwise --use_vcpkg legs 404).
Also closes #28969 (WebGPU binary-elementwise broadcast SIZE_MAX underflow). ONNX 1.22's expanded-Attention reference tests exposed a latent WebGPU bug where a broadcast shape computed dim - 1 on a zero/unit dimension and underflowed to SIZE_MAX; the fix is included here and the previously-skipped reference tests are re-enabled.
Opset-27 *CurrentOpset test handling. ONNX 1.22.0 FINAL ships DomainToVersionRange map-max 27 while the last released opset is 26, so opset 27 stays under development for the whole 1.22 cycle. Strict legs (the default, or ALLOW_RELEASED_ONNX_OPSET_ONLY=1) therefore throw "Opset 27 under development" at model load on every *CurrentOpset fusion test that builds at the max opset. These tests now load with per-model ModelOptions{/*allow_released_opsets_only*/ false, /*strict_shape_type_inference*/ false}, extending the existing 38f17243b / GatherToSlice precedent to the rest of the *CurrentOpset suite. This is leg-agnostic (exercises opset 27 on every CI leg, not just the relaxed ones) and preserves opset coverage (vs. GTEST_SKIP). Each call site is annotated with a one-line WHY + tracking issue (#28966) so the relaxation can be removed once opset 27 is released.
Resolves #28752 (unchanged). Closes #28969.
Update — ONNX 1.22.0rc2 re-pin + ConvTranspose conforms to ONNX output_shape spec
Since the original rc1 description below, this PR was re-pinned rc1 → rc2 (onnx/onnx@b124e0188a, VERSION_NUMBER 1.22.0rc2) to pick up the upstream Xcode/iOS CMake fix (onnx#8056). rc2 also carries onnx#8051, which tightened convTransposeShapeInference to reject an output_shape/output_padding whose size does not match the number of spatial dimensions (per the ONNX spec clarification onnx#5400). ONNX Runtime now conforms to that spec instead of patching ONNX to preserve a non-standard form.
⚠️ Breaking change — ConvTranspose output_shape now follows the ONNX spec (spatial dimensions only). ORT previously also accepted a non-standard rank + 2 form that included batch and channel, i.e. (N, C, H, W). As of ONNX 1.22, a rank + 2 output_shape on a ConvTranspose whose input has a statically-known rank is rejected at Graph::Resolve with "Attribute output_shape has incorrect size". Migration: specify output_shape with spatial dimensions only — e.g. {1, 1, 1, 14} → {1, 14} (batch and channel are always inferred from the input and weight, so results are identical; the kernel ignores N, C). Models whose ConvTranspose input has a dynamic/unknown rank are unaffected — ONNX skips the size check and ORT computes the same result (covered by the new ConvTranspose_RankPlus2_OutputShape_DynamicRankInput_Runtime test).
Patch inventory — supersedes "2 files, 3 hunks" below. cmake/patches/onnx/onnx.patch (and its byte-identical binskim.patch mirror) carries only the ONNX_MINIMAL_BUILD option hunk and the GroupNormalization-18 .Deprecate() removal — no ConvTranspose hunks. rc2's strict shape-inference check is kept as-is; ORT's own test models were conformed to the spec. The upstream archive hash, deps.txt, portfile.cmake, vcpkg.json, and the submodule pin are unchanged.
Additional rc2 test conform. rc2 also tightened convPoolShapeInference to reject Conv inputs with rank < 3 ("Input tensor must have at least 3 dimensions"). The hand-authored model in onnxruntime/test/python/quantization/test_op_split.py declared a spec-invalid rank-2 Conv input/weight; it was conformed to a valid NCHW shape ([6, 3] → [1, 1, 6, 3], weight → [2, 1, 1, 1]), keeping the quantized-Split graph and expected outputs identical. No ORT source change.
This note should also seed the GitHub Release notes for the ONNX 1.22 / opset 27 milestone and the squash-commit message.
What changed (29 files)
Version plumbing
cmake/deps.txt— onnx archive URL → rc1 commit zip + SHA1421e5a9afb6c41a54696e424e5b9a3796aab6821.cmake/external/onnx— submodule →bc3be77b.cmake/vcpkg-ports/onnx/portfile.cmake—REFcommit form + tar.gz SHA512e0c526f5…3ce467.cmake/vcpkg-ports/onnx/vcpkg.json—version-semver1.22.0,port-version0.cmake/patches/onnx/onnx.patch+cmake/vcpkg-ports/onnx/binskim.patch— byte-identical rebase onto 1.22 (2 files, 3 hunks): kept theONNX_MINIMAL_BUILDoption (restructured for 1.22's newonnx_coreOBJECT-lib /add_subdirectory(onnx)layout) and the GroupNormalization-18.Deprecate()removal; dropped theUtils.cmakeprotobuf-warnings hunk (already merged upstream in 1.22).
Opset-27 op enablement (Range)
onnxruntime/core/providers/cpu/generator/range.cc— split into versioned[11, 26]+ a new unversioned27registration. The opset-27 kernel natively supports the existing common numeric types (float/double/int16/int32/int64). fp16 Range is covered via ONNX's Range-27 function body, which ORT expands into primitive ops at partition time. bf16 Range is deferred to that same function expansion — there is no native bf16 kernel, and its bf16 reference node test (test_range_bfloat16_type_positive_delta, base +_expanded) is not exercised by the Python/numpy ONNX backend series, whose harness cannot materialize bf16 (Numpy_type 256); a native fp16/bf16 kernel +stash_typehandling is a follow-up (efficiency, not correctness).onnxruntime/core/providers/cpu/cpu_execution_provider.cc— versioned the Range forward-declare +BuildKernelCreateInfoentries and added the opset-27 registration.- CUDA Range — same versioned
[11, 26]+ opset-27 split as CPU (onnxruntime/core/providers/cuda/generator/range.cc+cuda_execution_provider.cc); GPU-verified locally:onnx_test_runner -e cuda8/8 opset-27 Range node tests pass, native Range-27 placed on CUDAExecutionProvider (fp16/bf16 via function expansion).
Optimizer / EP opset ceilings
…/transpose_optimization/optimizer_api.h—kMaxSupportedOpset26 → 27.coreml/nnapi/vsinpu/webnnbase_op_builder.h—GetMaxSupportedOpSet()25 → 27 (upper guard only; per-op support checks still gate — these EPs gain no new kernels here).
Fusion updates
onnxruntime/core/optimizer/gather_fusion.cc— GatherToSlice Range version list{1,11}→{1,11,27}.onnxruntime/core/optimizer/embed_layer_norm_fusion.cc— add27to the two Range path-matchers (parent_path_3/4) so embedding fusion still matches opset-27 models.onnxruntime/test/optimizer/graph_transform_test.cc— new opset-27 GatherToSliceFusion test.
Requirements (7 bumped)
- All 7 CI
requirements.txt→onnx==1.22.0rc1(rc1 wheel is on PyPI). The 3 transformers pins remain frozen at1.18.0(unrelated to this bump; intentionally untouched).
Generated docs / test data
js/web/docs/webgl-operators.md— regenerated.docs/OperatorKernels.md— surgical edit: CPU EP and CUDA EP Range rows (27++[11, 26]continuation each); see caveats.onnxruntime/test/testdata/onnx_backend_test_series_filters.jsonc— comment-only: documents why no opset-27 CPU exclusions are needed (all opset-27 node tests pass via function expansion).
Docs
.agents/skills/onnx-opset-bump-checklist/SKILL.md— new reusable checklist skill distilled from this integration. Now also documents the "bump all execution providers together" tradition (CPU + CUDA + JS/DML assessment in one pass) so future opset bumps don't ship a partial EP set.
Validation (CPU EP + CUDA EP, Linux x64)
- Full build ✅
--minimal_build extendedbuild ✅ (validates the rebasedONNX_MINIMAL_BUILDpatch hunk independently of the vcpkg mirror path)onnxruntime_test_all✅ — 1595 passed / 0 failedonnx_test_runner -e cpuon the ONNX 1.22 opset-27 node tests ✅ — 62/62 pass via ONNX function-body expansion (run withALLOW_RELEASED_ONNX_OPSET_ONLY=0), including CausalConvWithState, LinearAttention, and fp16/bf16 Range — despite no native kernels for them.- CUDA EP (H100): built
--use_cudaclean in both Debug and RelWithDebInfo ✅;onnx_test_runner -e cudaon the opset-27 Range node tests ✅ — 8/8 pass, with native Range-27 placed on CUDAExecutionProvider (no CPU fallback) and fp16/bf16 covered via function-body expansion.
Standing caveats (please read before reviewing)
- CUDA EP now locally verified for Range; other GPU EPs/ops still CI-only. The CUDA EP was built and the opset-27 Range node tests run locally on an H100 (8/8 pass). DML and the remaining GPU EPs/ops were not exercised here. Function-body expansion is EP-agnostic, so other opset-27 models are expected to run on those EPs too, but broader GPU coverage remains a CI/follow-up item.
OperatorKernels.mdupdated surgically (CPU Range row only). A CPU-only full regen would destructively wipe the CUDA/DML/other-EP sections (the generator only emits rows for the EPs in the built module). A correct multi-EP regen needs a build per EP and is a follow-up.- Opset 27 is "under development" in ONNX's released-versions map. ORT's load-time validation rejects opset-27 models unless
ALLOW_RELEASED_ONNX_OPSET_ONLY=0(ORT CI already sets this). The opset-27 schemas are always compiled in from the submodule regardless — this gate only affects model load-time acceptance, not schema availability. - EP
GetMaxSupportedOpSetjumped 25 → 27 (skips 26). This is an upper guard only; raising it merely lets opset-26/27 nodes reach the per-op support checks that still gate correctness. No regression — it also retroactively un-caps opset-26 for these EPs. - iOS/macOS Xcode framework build is currently broken by an upstream ONNX CMake regression (the
onnx_coreOBJECT-library split in onnx/onnx#7733 reintroduced the Xcode breakage originally fixed by onnx/onnx#7515 for onnx/onnx#7514). This is NOT caused by this opset bump. Tracked upstream at onnx/onnx#8053. Non-Xcode builds (Linux/Windows/Android/WASM) and all CPU/CUDA validation are unaffected. This resolves at the Phase 2 formalv1.22.0re-pin once ONNX ships the fix.
Follow-ups (explicitly NOT in this PR)
- GPU/multi-EP coverage: run opset-27 CUDA/DML node tests; regenerate
OperatorKernels.mdacross all EPs. - JS EP Range
[11, 26]+27split (currently registered open-ended at11; mirror the CPU/CUDA versioned split). - DML Range opset-27 assessment (DML uses its own
REG_INFOregistration system — assess whether an opset-27 entry is needed). - WebGPU EP Range opset-27 split —
range.ccregistersRange.SinceVersion(11)open-ended, so it already claims opset-27 Range; only the new bf16 type is unsupported and falls back via theTtype-constraint (function expansion). Mirror the CPU/CUDA versioned[11, 26]+27split. - Native kernels: implement CPU (and EP)
CausalConvWithStateandLinearAttentionkernels, and a native fp16/bf16 +stash_typeRange-27 kernel (replace today's function-expansion path with efficient kernels). - Phase 2 — formal
v1.22.0re-pin: re-pindeps.txt/submodule/portfile/requirements to the released tag once ONNX publishes it (currently blocked on ONNX tagging the release); upload the tag tarball to the vcpkg mirror. This also restores the iOS/macOS Xcode framework build once the upstream onnx OBJECT-library Xcode regression (caveat 5) is resolved and re-pinned. - Tooling: fix the pre-existing crash in
find_optimizer_opset_version_updates_required.py(placeholderverparsed as int) so it can be relied on for future bumps.