Skip to content

[Arith] Make Analyzer a tvm-ffi Object - #19675

Merged
tqchen merged 2 commits into
apache:mainfrom
tlopex:analyzer-ffi-object
Jun 8, 2026
Merged

[Arith] Make Analyzer a tvm-ffi Object#19675
tqchen merged 2 commits into
apache:mainfrom
tlopex:analyzer-ffi-object

Conversation

@tlopex

@tlopex tlopex commented Jun 5, 2026

Copy link
Copy Markdown
Member

This PR makes arith::Analyzer a first-class tvm-ffi object.

The implementation splits the previous concrete Analyzer class into:

  • AnalyzerObj, the mutable object node that owns analyzer state, sub-analyzers, caches, and bindings
  • Analyzer, a reference-counted ObjectRef handle that can be passed across the tvm-ffi boundary

This allows Python and C++ to share the same analyzer instance, so bindings, constraints, and cached facts can persist across FFI calls.

Public APIs that accept an analyzer now use const arith::Analyzer&, while internal helper APIs that only borrow the object continue to use AnalyzerObj*.

@tlopex
tlopex force-pushed the analyzer-ffi-object branch from e155636 to 55f38ab Compare June 5, 2026 08:52

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the arithmetic Analyzer in TVM to be a first-class FFI object. The stateful Analyzer class is renamed to AnalyzerObj (inheriting from ffi::Object), and Analyzer is redefined as a lightweight, reference-counted handle (ffi::ObjectRef) wrapping it. This allows Analyzer instances to be passed across the TVM FFI boundary (e.g., between Python and C++) and shared, preserving accumulated bindings and constraints. Numerous function signatures and call sites across the codebase have been updated to accept AnalyzerObj* instead of Analyzer*, and member accesses on Analyzer now use the arrow operator (->). Additionally, Python bindings and index mapping utilities have been updated to support passing an external Analyzer instance, and corresponding tests have been added. No reviewer comments were provided, so there is no additional feedback to address.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

tqchen
tqchen previously requested changes Jun 5, 2026
Comment thread include/tvm/arith/int_set.h Outdated
@tlopex
tlopex force-pushed the analyzer-ffi-object branch 5 times, most recently from 9697f79 to ef6cabb Compare June 5, 2026 20:15
@tqchen
tqchen dismissed their stale review June 5, 2026 22:19

stale

@tlopex
tlopex force-pushed the analyzer-ffi-object branch 3 times, most recently from 986240b to 2eed01e Compare June 7, 2026 03:10
@tlopex
tlopex force-pushed the analyzer-ffi-object branch from 2eed01e to 66191ea Compare June 7, 2026 03:24
@Ubospica
Ubospica force-pushed the analyzer-ffi-object branch from 8f84480 to f07de85 Compare June 8, 2026 02:22
@tlopex
tlopex force-pushed the analyzer-ffi-object branch from f07de85 to 3478fd9 Compare June 8, 2026 03:57
…d tests

Now that arith::Analyzer is a tvm-ffi object, expose more of its stateful surface to Python and let callers share a single analyzer across FFI calls.

- Add Python bindings for additional Analyzer methods.
- Accept optional analyzers in iter-map, int-set, and IndexMap helper APIs.
- Keep IndexMap inverse temporary bindings out of caller-provided analyzers.
- Add targeted tests for optional analyzer reuse and state isolation.
@tlopex
tlopex force-pushed the analyzer-ffi-object branch from 3478fd9 to 23af9ce Compare June 8, 2026 04:01
@Ubospica
Ubospica force-pushed the analyzer-ffi-object branch 2 times, most recently from b58d406 to 55a6199 Compare June 8, 2026 04:58
@tlopex
tlopex requested a review from tqchen June 8, 2026 05:16
@Ubospica
Ubospica force-pushed the analyzer-ffi-object branch from 55a6199 to 23af9ce Compare June 8, 2026 05:19

@Ubospica Ubospica left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! cc @tqchen

@guan404ming guan404ming left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, thanks @tlopex

@tqchen
tqchen merged commit b172d5e into apache:main Jun 8, 2026
14 of 15 checks passed
tlopex added a commit to tlopex/tvm that referenced this pull request Jun 18, 2026
…t Analyzer&

After apache#19675 made arith::Analyzer an FFI object, IRMutatorWithAnalyzer and
its subclasses took AnalyzerObj*, forcing every instantiation site to pass
analyzer.get(). Migrate the IRMutatorWithAnalyzer family to accept the
Analyzer handle directly.

Changes:
- IRMutatorWithAnalyzer gains a `const Analyzer&` constructor overload. The
  existing `AnalyzerObj*` overload is kept on purpose: RewriteSimplifier::Impl
  is constructed from the AnalyzerObj's own `this` mid-construction, where no
  Analyzer handle exists yet and GetRef on a half-built, self-owned object
  would be unsafe / form an ownership cycle.
- The directly-instantiated subclass constructors switch AnalyzerObj* ->
  const Analyzer&, dropping analyzer.get() at their call sites.
- For the factory-style subclasses (NoOpRemover, StmtSimplifier) the
  construction happens inside their static Apply()/free-function entry points,
  so those internal (src/, non-FFI, non-public) signatures move to
  const Analyzer& as well -- StmtSimplify(), RemoveNoOp() and
  BlockBufferAccessSimplifier::Simplify() -- removing analyzer.get() at the
  remaining call sites.

Deliberately left as AnalyzerObj*:
- The per-sub-analyzer `AnalyzerObj* parent` constructors (ConstIntBound,
  ModularSet, IntSet, Canonical, RewriteSimplifier): AnalyzerObj owns them by
  value, so a strong back-reference would be a cycle.
- VTInjector: it is built from the inherited raw analyzer_ member and never
  wrote analyzer.get() to begin with.

The field stays `AnalyzerObj* analyzer_` (borrowed), so there is no extra
refcount and no ownership cycle. Because AnalyzerObj is _type_mutable, a
const Analyzer& still exposes a non-const operator->, so subclass bodies
(analyzer_->Bind(...)) are unchanged. No Python / FFI / public-header surface
changes; runtime behavior is identical.
tlopex added a commit to tlopex/tvm that referenced this pull request Jun 18, 2026
After apache#19675 made arith::Analyzer an FFI object, IRMutatorWithAnalyzer and
its subclasses took AnalyzerObj*, forcing every instantiation site to pass
analyzer.get(). Migrate the IRMutatorWithAnalyzer family to accept the
Analyzer handle directly.

Changes:
- IRMutatorWithAnalyzer gains a `const Analyzer&` constructor overload. The
  existing `AnalyzerObj*` overload is kept on purpose: RewriteSimplifier::Impl
  is constructed from the AnalyzerObj's own `this` mid-construction, where no
  Analyzer handle exists yet and GetRef on a half-built, self-owned object
  would be unsafe / form an ownership cycle.
- The directly-instantiated subclass constructors switch AnalyzerObj* ->
  const Analyzer&, dropping analyzer.get() at their call sites.
- For the factory-style subclasses (NoOpRemover, StmtSimplifier) the
  construction happens inside their static Apply()/free-function entry points,
  so those internal (src/, non-FFI, non-public) signatures move to
  const Analyzer& as well -- StmtSimplify(), RemoveNoOp() and
  BlockBufferAccessSimplifier::Simplify() -- removing analyzer.get() at the
  remaining call sites.

Deliberately left as AnalyzerObj*:
- The per-sub-analyzer `AnalyzerObj* parent` constructors (ConstIntBound,
  ModularSet, IntSet, Canonical, RewriteSimplifier): AnalyzerObj owns them by
  value, so a strong back-reference would be a cycle.
- VTInjector: it is built from the inherited raw analyzer_ member and never
  wrote analyzer.get() to begin with.

The field stays `AnalyzerObj* analyzer_` (borrowed), so there is no extra
refcount and no ownership cycle. Because AnalyzerObj is _type_mutable, a
const Analyzer& still exposes a non-const operator->, so subclass bodies
(analyzer_->Bind(...)) are unchanged. No Python / FFI / public-header surface
changes; runtime behavior is identical.
tlopex added a commit to tlopex/tvm that referenced this pull request Jun 18, 2026
After apache#19675 made arith::Analyzer an FFI object, IRMutatorWithAnalyzer and
its subclasses took AnalyzerObj*, forcing every instantiation site to pass
analyzer.get(). Migrate the IRMutatorWithAnalyzer family to accept the
Analyzer handle directly.

Changes:
- IRMutatorWithAnalyzer gains a `const Analyzer&` constructor overload. The
  existing `AnalyzerObj*` overload is kept on purpose: RewriteSimplifier::Impl
  is constructed from the AnalyzerObj's own `this` mid-construction, where no
  Analyzer handle exists yet and GetRef on a half-built, self-owned object
  would be unsafe / form an ownership cycle.
- The directly-instantiated subclass constructors switch AnalyzerObj* ->
  const Analyzer&, dropping analyzer.get() at their call sites.
- For the factory-style subclasses (NoOpRemover, StmtSimplifier) the
  construction happens inside their static Apply()/free-function entry points,
  so those internal (src/, non-FFI, non-public) signatures move to
  const Analyzer& as well -- StmtSimplify(), RemoveNoOp() and
  BlockBufferAccessSimplifier::Simplify() -- removing analyzer.get() at the
  remaining call sites.

Deliberately left as AnalyzerObj*:
- The per-sub-analyzer `AnalyzerObj* parent` constructors (ConstIntBound,
  ModularSet, IntSet, Canonical, RewriteSimplifier): AnalyzerObj owns them by
  value, so a strong back-reference would be a cycle.
- VTInjector: it is built from the inherited raw analyzer_ member and never
  wrote analyzer.get() to begin with.

The field stays `AnalyzerObj* analyzer_` (borrowed), so there is no extra
refcount and no ownership cycle. Because AnalyzerObj is _type_mutable, a
const Analyzer& still exposes a non-const operator->, so subclass bodies
(analyzer_->Bind(...)) are unchanged. No Python / FFI / public-header surface
changes; runtime behavior is identical.
tlopex added a commit that referenced this pull request Jun 18, 2026
This pr is the follow-up pr to #19675. IRMutatorWithAnalyzer and its
subclasses took AnalyzerObj*, so callers had to pass analyzer.get().
This adds a const Analyzer& constructor and migrates the family (and
their Apply() / free-function entry points) to take the handle directly,
dropping .get() at the call sites.

The original AnalyzerObj* overload is kept because
RewriteSimplifier::Impl is constructed from the AnalyzerObj's own this
during the analyzer's construction, where no Analyzer handle exists yet.
And the same dual-overload pattern already used by
arith::ConstraintContext.
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.

4 participants