Skip to content

Add composable C++ graph pattern rewriter - #33631

Draft
G. Ramalingam (gramalingam) wants to merge 45 commits into
microsoft:mainfrom
gramalingam:gramalingam-rewriter3
Draft

G. Ramalingam (gramalingam) wants to merge 45 commits into
microsoft:mainfrom
gramalingam:gramalingam-rewriter3

Conversation

@gramalingam

Copy link
Copy Markdown
Contributor

Description

Introduce an experimental, explicitly opt-in C++ graph pattern rewriter in onnxruntime::rewrite, with documentation and component/integration tests. This is infrastructure for authoring rewrites, not a production fusion rollout; it is not registered among ORT's default optimizers.

  • Provide independently usable Pattern::MatchAt / VisitMatches and Replacement::Instantiate APIs, candidate-local Match bindings, C++ check-and-bind callbacks, rewrite rules, and a GraphTransformer-compatible engine.
  • Add a text DSL for connected DAG patterns and replacements, positional multi-output bindings, constants and attributes, optional tensor slots, type/shape constraints, immutable tensor constraints, and typed constant readers.
  • Support reusable named pattern libraries, hygienic nested calls, interfaces, aliases and bounded source-order alternatives. Scoped binding views expose the selected alternative; handwritten C++ views illustrate usage without adding a code generator.
  • Support optional attribute captures such as perm : ints | None, distinguishing captured absence from unknown names, with nullable lookup and replacement omission followed by schema default/required-attribute validation.
  • Preserve graph-output/external-user deletion boundaries, provider/layer and explicit opset eligibility, candidate binding isolation, deterministic rule priority, and staging/rollback before committing a replacement. Replacement errors propagate rather than silently retrying. Whole engine runs and subsequent Graph::Resolve are not transactional.
  • Document component contracts, borrowed graph lifetimes, Resolve boundaries, bounded matching, diagnostics, opt-in integration, and natural Transpose-composition plus small numerical fusion examples. No MoE/QMoE lowering or sibling fusion changes are included.

Validation already performed on Windows CPU Release at implementation commit e5927feb856711fc0f4c06145a553c1c2675cdf9; the subsequent head 319fa38a679b3730ca7487c2269fa388dca58f2b changes documentation order/cross-links only:

.\.venv\Scripts\python.exe tools\ci_build\build.py --config Release --build_dir C:\ortbuild\rewriter3 --build --parallel 12 --target onnxruntime_test_all --skip_submodule_sync --compile_no_warning_as_error --no_telemetry

Result: passed incremental Release build.

From C:\ortbuild\rewriter3\Release\Release:

.\onnxruntime_test_all.exe --gtest_filter=PatternComponentTests.*:ReplacementComponentTests.*:MatchComponentTests.*:CheckAndBindComponentTests.*:PatternRewriterTests.*

Result: 155 tests passed across five suites, including component contracts, numerical CPU equivalence, composition/alternative retry, optional attributes, graph boundaries and replacement rollback.

Independent focused review additionally rebuilt the engine and artifact-only harnesses, passed 100 grouped optional-attribute checks plus existing component/dimension/positional checks, and compiled both test translation units in normal and DISABLE_CONTRIB_OPS configurations (four successful compiles). The reviewer also reran the owner-built 155-test executable. These are local results, not upstream CI results. No clean full runtime rebuild, disabled-contrib runtime execution, or GPU validation was performed. Documentation checks preserve all code examples and verify local anchors, fenced blocks, heading order and git diff --check.

Motivation and Context

Graph rewrites combine recurring structural matching and graph-edit safety logic with rule-specific semantic and kernel-support checks. This separates those responsibilities: patterns describe structure, C++ check-and-bind functions establish applicability and prepare values, replacements describe construction, and the engine handles ordered application.

Standalone matching and fragment construction make each component independently testable. Reusable pattern composition avoids duplicating common graph forms while preserving local bindings and explicit alternative selection. Semantic equivalence, arithmetic/dependent constraints and kernel compatibility remain the rule author's responsibility in C++; there are no higher-order pattern parameters, general recursive backtracking, automatic C++ generation, or default optimizer behavior changes.

This draft is submitted from the existing gramalingam:gramalingam-rewriter3 branch for upstream discussion. It targets upstream main (the current default; master is no longer an actual upstream branch) without rebasing the source branch.

Extract the engine, six example rules, API/design documentation, and correctness tests from the completed rewriter implementation without performance-evaluation artifacts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace match-wide float32 scans with explicit data-binding checks. Keep scalar initializer validation and add focused guard tests plus numerical RMS coverage with an independently typed int64 Pow exponent.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Expose textual Pattern and Replacement construction with read-only introspection. Keep parser builders private through a named internal parser, support explicit named final outputs, migrate correctness helpers and malformed-recipe coverage to parsed definitions, and replace the design investigation with a user-facing rule-writing guide.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the last mutable single-node recipe use with a parsed plan containing an invalid schema attribute, preserving runtime failure and unchanged-graph assertions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep the winning rule's pattern rather than the shared structural group's pattern when lowering parsed callback replacements. This preserves rule-local names for shared plans and the existing named-lookup safety regression.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Graph::Resolve deletes unused untyped graph NodeArgs. Keep the negative guard-test NodeArg on the stack so copied matches never reference freed graph-owned storage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Accept optional braced bodies and returns of all final operator outputs in order. Reject combined assignment-return syntax and migrate multi-output definitions and examples while preserving direct calls and unbraced compatibility.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use explicit void-cast brace expressions in Google Test exception macros to avoid declaration parsing and default-constructor lookup.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Parse dotted domain names in patterns as well as replacements. Match and synthesize numeric rank-one tensor literals in positional inputs and exact numeric list attributes in keyword arguments, with parsing, structural, rollback and numerical coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The mismatching [1,3] graph correctly matched the additional [1,3] rule. Use [4,5] for the always-rejected structural-plan regression and trace mode/scenario explicitly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…iagnostics

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…tions

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Make every concrete named pattern callable, defer rooted-entry checks to GetPattern, and preserve nested alternative selection views. Update composition tests, manual C++ examples, and syntax documentation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Preserve tensor interface ports, reject aliased duplicate entry outputs, and bound cumulative expanded calls before scoped metadata copying. Use the portable classic-locale float reader and cover all four review findings with regressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Make structural signatures locale-independent, require mapped interface outputs, preserve global checker order with cached structural matches, and guard contrib-only helpers. Add focused regressions and document the contracts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Keep annotation occurrences ordered through argument remapping, preserve tensor interface requirements across callers, materialize replacement schema defaults, and reject dependency nesting beyond 64 definitions before native recursion. Add focused regressions and document the guarantees and bound.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Retain tensor classification for annotations imported from ordinary callees, including identity aliases, while preserving already-classified typed attribute formals. Diagnose conflicts during library construction and cover nested calls and classification order in both matcher modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Treat singleton named dimensions as permissive captures and repeated names as equality constraints requiring known matching metadata in every selected occurrence. Count only complete annotation choices, preserve scoped composition and retry isolation, and provide named diagnostics. Add regressions and document semantics; explicitly discard nodiscard Status in the throwing test assertion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Relocate the unchanged six-rule example factory and helpers into the test anonymous namespace, remove the production source and public declaration, and clarify documentation usage. Production callers author their own rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Move construction and ownership details after the opening example and use the explicit float[...] any-rank annotation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Keep type-alternative discussion local to annotations and move call constraint ordering and graph/type retry distinctions into the composition section.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Support typed positional signatures, explicit multi-output calls, tensor aliases, wildcard actuals, and branch-optional captures while retaining legacy instance-only multi-output calls. Bound nested expansion and document scoped introspection separately from tensor lookup.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Allow unconstrained inferred result branches without adding annotation occurrences, reject output labels that shadow tensor aliases, and preserve ordinary typed attribute inputs with explicit output signatures. Add direct and nested regression coverage in both matcher modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Document component contracts, check-and-bind, and engine resolution boundaries with a two-Transpose example. Add standalone component tests and numerical rule integration coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Signed-off-by: Ganesan Ramalingam <grama@microsoft.com>
Address PR16 feedback with MatchAt first-candidate matching, explicit lookup types, optional attribute constraints, and later rule construction documentation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Keep the initial component API example focused on MatchAt, then introduce multiple candidates and checker retry with a concrete disjunction example.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Add typed attribute-or-None captures with explicit presence state, nullable scoped lookup, repeated-binding checks, and omission through shared replacement staging. Preserve standalone missing-name errors with explicit absent-attribute bindings.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
Keep the composed checker/replacement example as a later standalone section and update navigation for the new reading order.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: daf231a0-ce61-4a2b-9649-0f31e2296e4f
@gramalingam G. Ramalingam (gramalingam) changed the title Add experimental composable C++ graph pattern rewriter Add composable C++ graph pattern rewriter Oct 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant