Skip to content

[REFACTOR][IR] Add structural hooks for Expr and Stmt - #20275

Merged
tqchen merged 1 commit into
apache:mainfrom
tqchen:redo-expr-stmt-structural-hooks-current
Sep 6, 2026
Merged

[REFACTOR][IR] Add structural hooks for Expr and Stmt#20275
tqchen merged 1 commit into
apache:mainfrom
tqchen:redo-expr-stmt-structural-hooks-current

Conversation

@tqchen

@tqchen tqchen commented Sep 5, 2026

Copy link
Copy Markdown
Member

Add compiled structural visit, mutate, and maybe-in-place-mutate
hooks for every Expr and Stmt node family using the current tvm-ffi
structural-hook ABI.

StmtNode and TypeNode declare
_type_s_eq_hash_subclass_kind_fixed = true. The current hierarchy census
finds 18 final registered children under Stmt and 14 under Type, with no FreeVar
or DAG kind override in either hierarchy. This lets typed structural-map
callbacks over Stmt and Type fold out the identity-remap branch. Expr,
PrimExpr, and BaseFunc retain the default because their descendants
include FreeVar or DAG kinds.

Definition-region annotations are aligned with reflected fallback behavior:
direct definitions use non-recursive regions, container-owned definitions are
established at their Var sites, and dynamic Var type descent remains a use. The
hooks preserve the established visitor/mutator field set and same-object short
circuits on unchanged mutations.

@tqchen
tqchen force-pushed the redo-expr-stmt-structural-hooks-current branch 7 times, most recently from 73402f2 to e401670 Compare September 6, 2026 16:51
Add compiled structural visit, mutate, and maybe-in-place-mutate
hooks for every Expr and Stmt node family using the current tvm-ffi
structural-hook ABI.

The branch pins tvm-ffi at `9d784c4da74ff7360d76c79a89fe60a63516f880`,
including the callback-owned `StructuralMutate` API from tvm-ffi apache#751 and the
visit-hook success-tail macro from apache#753. Checked Map-valued hook fields use the
current Expected-returning type-error path.

`StmtNode` and `TypeNode` declare
`_type_s_eq_hash_subclass_kind_fixed = true`. The current hierarchy census
finds 18 final registered children under Stmt and 14 under Type, with no FreeVar
or DAG kind override in either hierarchy. This lets typed structural-map
callbacks over Stmt and Type fold out the identity-remap branch. `Expr`,
`PrimExpr`, and `BaseFunc` retain the default because their descendants
include FreeVar or DAG kinds.

Definition-region annotations are aligned with reflected fallback behavior:
direct definitions use non-recursive regions, container-owned definitions are
established at their Var sites, and dynamic Var type descent remains a use. The
hooks preserve the established visitor/mutator field set and same-object short
circuits on unchanged mutations.

Call hooks keep ordinary descent for args and nonempty type arguments, while
skipping primitive result types, interned Op operators, and empty type-argument
containers. Dynamic result types, function-valued operators, and populated type
arguments retain ordinary descent.

`PostOrderVisit` and `Substitute` remain unchanged. Direct
`StructuralMap` roots in this PR are confined to focused tests: the
intentionally shared lvalue exercises copy-on-write, while owned roots move
into the by-value API to preserve eligible in-place reuse.

All eight hook source files use one anonymous namespace and non-static
file-local hook functions. Rebuilds box moved typed copies directly, unchanged
paths box typed `self`, and constant leaves explicitly extract typed `self`.
Every visit hook uses direct early-return macro calls per field and terminates
with `TVM_FFI_S_VISIT_RETURN_NONE()`.

`MaybeInplaceMutateSeqStmtRaw` separately verifies unique ownership of its
`seq` field and borrows each element from raw array storage so the element
remains eligible for in-place mutation. Mapped `Evaluate(0)` entries are
omitted just as they are by `SeqStmt::Flatten`; zero or one remaining statement
normalizes to `Evaluate(0)` or the sole statement without constructing a
prohibited SeqStmt. This does not change the SeqStmt constructor or contract.

Both SeqStmt mutation hooks scan their unchanged prefix with only one
same-object test per element, then tail-return to changed-path helpers at the
first divergence. Ordinary mutation allocates and initializes its unchanged
prefix only there. In-place mutation begins its `total` cursor there: drops and
splices compact into the existing array while writes remain behind unread
input; a splice that would cross that boundary transfers once to an
`InplaceSplice` output and continues the remaining callbacks exactly once.
Differential tests cover first and last positions, multiple splices, growth,
size preservation, `Evaluate(0)` shrink, zero/one-result normalization, array
reuse, overflow replacement, and callback non-replay without constructing raw
empty or size-one SeqStmt nodes.

Owner measurements in the apache#373 harness on a 256-element SeqStmt record the
accepted timing guardrail. Two replacements change from 23,511 to 24,444 ns
(+4.0%), 16 from 24,430 to 24,768 ns (+1.4%), 64 from 25,464 to 25,132 ns
(-1.3%), and 128 from 27,367 to 25,245 ns (-7.8%). Rebuilds fall from 3-5 to
zero at every density, with crossover near one quarter. A follow-up L=8
allocation check finds that repaired shrink reuses the node array instead of
allocating once, while growth remains one allocation with or without spare
capacity. This repair lands for correct element-level in-place semantics and
reduced splice allocation, not as a uniform speedup.

Earlier general performance measurements for this work in apache#364/apache#365 were
collected at the previous `556514c76225cb35a55e0649098b07be3fba2991` engine
pin and predate this bump; they should not be interpreted as measurements of
the newer engine. The SeqStmt-specific figures above are the later apache#373
measurement of the accepted repair tradeoff.
@tqchen
tqchen force-pushed the redo-expr-stmt-structural-hooks-current branch from e401670 to a1031a2 Compare September 6, 2026 18:09
@tqchen
tqchen merged commit 7e06fc6 into apache:main Sep 6, 2026
8 checks passed
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.

2 participants