Skip to content

[REFACTOR][IR] Unify PrimExpr with Expr typed view#19910

Open
tqchen wants to merge 5 commits into
apache:mainfrom
tqchen:tvm-pr1-collapse-primexpr-typed-view-and-call
Open

[REFACTOR][IR] Unify PrimExpr with Expr typed view#19910
tqchen wants to merge 5 commits into
apache:mainfrom
tqchen:tvm-pr1-collapse-primexpr-typed-view-and-call

Conversation

@tqchen

@tqchen tqchen commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

  • Make PrimExpr a typed C++ view over Expr values whose ExprNode::ty is PrimType, instead of using a separate runtime node class as the proof of primitive-ness.
  • Use the shared ir::Call node for Relax, TIRX, and primitive-valued calls, while keeping primitive-only APIs explicit at their semantic boundaries.
  • Keep Python on the general Expr surface for primitive-typed values so isinstance behavior does not imply a nominal primitive-expression subclass.

Design Rationale

The main advantage of this change is that common expression nodes such as Call can be unified without specializing each one to PrimType. A single ir::Call can represent a Relax tensor call, a Relax scalar call, or a primitive-valued intrinsic call; the result type stored in ExprNode::ty determines whether that particular value can be viewed as PrimExpr.

This keeps the IR node hierarchy focused on expression structure rather than result-type categories. Nodes that are intrinsically primitive, such as integer and floating-point literals or TIRX primitive operators, still have strongly typed C++ APIs and data structures. General nodes whose result type may vary, such as Call, remain general Expr nodes and are narrowed to PrimExpr only where primitive-only semantics are required.

The PR also keeps the compatibility surface practical: C++ primitive-only APIs continue to accept PrimExpr, Python exposes a compatibility predicate for checking the primitive typed category, and visitors/printers use one natural Call path rather than duplicating Relax and primitive call handling. Missing expression types are represented explicitly with Type::Missing() so constructors can leave type inference to later analysis without relying on nullable Type values.

@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 TVM IR and Relax type systems by unifying primitive expressions under the base Expr class with a PrimType annotation, replacing the separate PrimExpr class. It also introduces Type::Missing() as a sentinel for unpopulated type information. The review feedback highlights a potential compiler crash in src/relax/op/op.cc when DeriveCallRetType returns Type::Missing(), and suggests several code simplifications, such as using ffi::downcast in C++ and utilizing the is_prim_expr helper in Python instead of manual type checks.

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.

Comment thread src/relax/op/op.cc
Comment thread include/tvm/ir/base_expr.h
Comment thread python/tvm/ir/expr.py Outdated
Comment thread python/tvm/relax/expr.py Outdated
Comment thread python/tvm/relax/expr.py Outdated
@tqchen tqchen force-pushed the tvm-pr1-collapse-primexpr-typed-view-and-call branch from 5167e05 to c90fc7b Compare June 30, 2026 01:39
@tqchen

tqchen commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

Follow-up for the latest review batch is pushed in 27aef69ffc31cfbbf2c7efb6072a1f7dfc9c1a44.

  • Added the missing Type::Missing() guard before TIRVarsInType in call_tir output inference, with a regression test for an uninferrable output shape.
  • Replaced the local unsafe object construction in TypedExpr::ty() with the public checked path: Type::as<ExpectedType::ContainerType>() plus ffi::GetRef<ExpectedType>(). This checkout does not provide a free-function ffi::downcast.
  • Kept the explicit Python Expr plus PrimType checks in the reviewed construction/dispatch paths so those local boundaries continue to spell out the typed-category condition; tvm.ir.is_prim_expr remains the compatibility predicate elsewhere.

@tqchen tqchen force-pushed the tvm-pr1-collapse-primexpr-typed-view-and-call branch 5 times, most recently from 4c01b4e to fd154de Compare June 30, 2026 12:19
PrimExpr is now a typed view over Expr values with PrimType results, and Call is shared across IR dialects.

Migration guide:

- In C++, narrow a generic Expr with as_or_throw<PrimExpr>(); use direct GetRef<PrimExpr> only where the concrete node invariant guarantees a PrimType result.

- Construct shared ir::Call with an explicit result Type. Primitive-valued calls carry PrimType and can then be viewed as PrimExpr.

- Represent unavailable type information with Type::Missing(), and check IsMissing() before typed traversal or narrowing.

- In Python, use tvm.ir.is_prim_expr(value), or an explicit Expr plus PrimType check at a deliberate API boundary, instead of nominal PrimExpr isinstance checks.

- Store shared expression collections as Expr and perform checked element narrowing only at primitive-only consumers; likewise replace visitor and overload assumptions tied to a nominal PrimExpr node class.
@tqchen tqchen force-pushed the tvm-pr1-collapse-primexpr-typed-view-and-call branch from fd154de to a03f59f Compare June 30, 2026 13:17
Use is_prim_expr consistently at Python primitive-expression boundaries and remove annotations made redundant by the shared Expr hierarchy.

Use checked as_or_throw narrowing for generic C++ expressions and checked array conversion when every collection element must be primitive.
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