This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-tirx.git
The following commit(s) were added to refs/heads/main by this push:
new 37ac4abdf [DOCS] Add an IR walkthrough with runnable examples (#24)
37ac4abdf is described below
commit 37ac4abdfa4f3f37fb85883e8bfe498947017397
Author: Shushi Hong <[email protected]>
AuthorDate: Sat Oct 24 18:29:59 2026 -0400
[DOCS] Add an IR walkthrough with runnable examples (#24)
This pr adds an IR Walkthrough covering the shared IR object hierarchy,
types and expressions, statements, operators and calls, functions and
modules, and TVMScript printing and parsing.
Include minimal examples explaining how the IR objects relate and how to
use them, with downloadable Python scripts and links to the API
reference.
---------
Signed-off-by: Yuchuan <[email protected]>
Signed-off-by: tlopex <[email protected]>
Co-authored-by: Yuchuan <[email protected]>
---
docs/index.rst | 12 ++
docs/ir/examples/operators_and_calls.py | 56 ++++++
docs/ir/examples/printing_and_parsing.py | 132 +++++++++++++
docs/ir/examples/statements_and_functions.py | 78 ++++++++
docs/ir/examples/types_and_expressions.py | 100 ++++++++++
docs/ir/functions_and_modules.rst | 134 +++++++++++++
docs/ir/operators_and_calls.rst | 195 +++++++++++++++++++
docs/ir/overview.rst | 166 ++++++++++++++++
docs/ir/printing_and_parsing.rst | 139 ++++++++++++++
docs/ir/statements.rst | 190 +++++++++++++++++++
docs/ir/types_and_expressions.rst | 274 +++++++++++++++++++++++++++
python/tvm_tirx/ir/expr.py | 26 ++-
python/tvm_tirx/ir/module.py | 19 +-
python/tvm_tirx/ir/stmt.py | 11 +-
python/tvm_tirx/tirx/function.py | 7 +-
15 files changed, 1528 insertions(+), 11 deletions(-)
diff --git a/docs/index.rst b/docs/index.rst
index cf288fe58..5f5d9967d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,6 +21,18 @@ tvm-tirx
Table of Contents
-----------------
+.. toctree::
+ :maxdepth: 1
+ :caption: IR Walkthrough
+ :titlesonly:
+
+ ir/overview
+ ir/types_and_expressions
+ ir/statements
+ ir/operators_and_calls
+ ir/functions_and_modules
+ ir/printing_and_parsing
+
.. toctree::
:maxdepth: 1
:caption: Reference
diff --git a/docs/ir/examples/operators_and_calls.py
b/docs/ir/examples/operators_and_calls.py
new file mode 100644
index 000000000..385c4ce53
--- /dev/null
+++ b/docs/ir/examples/operators_and_calls.py
@@ -0,0 +1,56 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Inspect an operator, construct calls, and check result types."""
+
+from tvm_tirx import ir
+
+# begin operator lookup
+op = ir.Op.get("prim.exp")
+argument_names = [entry.name for entry in op.args_info]
+effect = op.get_attr("TCallEffectKind")
+# end operator lookup
+
+# begin call contract
+value = ir.const(1.0, "float32")
+call = ir.Call(op, [value])
+# end call contract
+
+# begin function call type
+callee = ir.Var("f", ir.FuncType([value.ty], value.ty))
+function_call = ir.Call(callee, [value])
+# end function call type
+
+# begin validation failure
+invalid = ir.Call(op, [], ty="float32")
+# end validation failure
+try:
+ invalid.validate()
+except TypeError as error:
+ assert "expected 1 argument, got 0" in str(error)
+else:
+ raise AssertionError("Expected an argument-count error")
+
+# begin provisional call
+provisional = ir.Call(op, [value], ty=ir.Type.missing())
+inferred = ir.reinfer_type(provisional)
+# end provisional call
+
+assert argument_names == ["x"] and effect == ir.CallEffectKind.Pure
+assert call.ty == function_call.ty == inferred == value.ty
+assert call.validate() is None
+assert isinstance(provisional.ty, ir.MissingType)
+print("Operator and call examples passed.")
diff --git a/docs/ir/examples/printing_and_parsing.py
b/docs/ir/examples/printing_and_parsing.py
new file mode 100644
index 000000000..1c5ed9738
--- /dev/null
+++ b/docs/ir/examples/printing_and_parsing.py
@@ -0,0 +1,132 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Generate tensor programs, inspect their IR, and check source round trips."""
+
+# TVMScript resolves dependent parameter annotations when constructing IR.
+
+# begin tvmscript example
+from __future__ import annotations
+
+from tvm_tirx import ir, tirx
+from tvm_tirx.script import from_source
+from tvm_tirx.script import ir as I
+from tvm_tirx.script import tirx as T
+
+
[email protected]
+def f(n: T.int64, A: T.Tensor((2 * n, 128), "float32")) -> T.void:
+ for i, j in T.grid(2 * n, T.int64(128)):
+ A[i, j] = A[i, j] + T.float32(1)
+
+
+# end tvmscript example
+
+
+# begin script helpers
[email protected]
+def increment(value):
+ return value + ir.const(1, value.ty)
+
+
[email protected]
+def increment_at(A, i, j):
+ A[i, j] = increment(A[i, j])
+
+
+# end script helpers
+
+
+# begin program factory
+def make_increment(width, dtype="float32"):
+ if not isinstance(width, int) or width <= 0:
+ raise ValueError("width must be a positive Python integer")
+
+ @T.function
+ def generated(n: T.int64, A: T.Tensor((2 * n, width), dtype)) -> T.void:
+ for i, j in T.grid(2 * n, T.int64(width)):
+ increment_at(A, i, j)
+
+ return generated
+
+
+wide = make_increment(128)
+narrow = make_increment(64, "float16")
+# end program factory
+
+# begin print generated function
+source = wide.script()
+print(source)
+# end print generated function
+
+# begin diagnostic display
+wide.show(
+ print_line_numbers=True,
+ obj_to_underline=[wide.body[0]],
+)
+# end diagnostic display
+
+# begin printing and parsing
+restored = from_source(source, extra_vars={"T": T})
+ir.assert_structural_equal(wide, restored)
+# end printing and parsing
+
+
+# begin module round trip
[email protected]_module
+class Kernels:
+ wide = wide.with_attr("global_symbol", "wide")
+ narrow = narrow.with_attr("global_symbol", "narrow")
+
+
+module_source = Kernels.script()
+restored_module = from_source(module_source, extra_vars={"I": I, "T": T})
+ir.assert_structural_equal(Kernels, restored_module)
+# end module round trip
+
+# begin standalone source
+standalone_source = Kernels.script(extra_config={"ir.comment_imports": False})
+standalone_module = from_source(standalone_source)
+ir.assert_structural_equal(Kernels, standalone_module)
+# end standalone source
+
+assert isinstance(f, tirx.Function)
+ir.assert_structural_equal(f.with_attr("global_symbol", ""),
wide.with_attr("global_symbol", ""))
+for program, width, dtype in ((wide, 128, "float32"), (narrow, 64, "float16")):
+ n, tensor = program.params
+ assert tensor.ty.shape[0].b.same_as(n)
+ assert program.body[0].end.b.same_as(n)
+ assert int(tensor.ty.shape[1]) == width
+ assert tensor.ty.dtype == ir.PrimType(dtype)
+ assert tirx.analysis.check_well_formed(program)
+assert tirx.analysis.check_well_formed(Kernels)
+assert "increment_at(" not in source and "increment(" not in source
+assert wide.params[1].ty.script().startswith("T.Tensor(")
+assert "for " in wide.body[0].script()
+assert source == wide.script()
+try:
+ from_source(source)
+except SyntaxError:
+ pass
+else:
+ raise AssertionError("Source text must supply its namespace through
imports or extra_vars")
+try:
+ make_increment(0)
+except ValueError:
+ pass
+else:
+ raise AssertionError("The factory should reject a zero width")
+print("Printing and parsing example passed.")
diff --git a/docs/ir/examples/statements_and_functions.py
b/docs/ir/examples/statements_and_functions.py
new file mode 100644
index 000000000..1126b04aa
--- /dev/null
+++ b/docs/ir/examples/statements_and_functions.py
@@ -0,0 +1,78 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Assemble an integer increment function from expressions and statements."""
+
+from tvm_tirx import ir, tirx
+
+# begin statements
+x = ir.Var("x", "int32")
+added = x + 1
+result = ir.Var("result", added.ty)
+body = ir.SeqStmt(
+ [
+ ir.Bind(result, added),
+ ir.Return(result),
+ ]
+)
+# end statements
+
+# begin branch
+branch = ir.If(x > 0, ir.Return(added), ir.Return(x))
+# end branch
+
+# begin loop
+i = ir.Var("i", "int32")
+loop = ir.For(i, 0, 4, 1, ir.ForKind.DEFAULT, ir.Evaluate(i))
+# end loop
+
+# begin function construction
+value = ir.Var("value", "int32")
+function = tirx.Function([value], ir.Return(value + 1), ret_type=value.ty)
+# end function construction
+
+# begin module construction
+function = function.with_attr("global_symbol", "main")
+entry = ir.GlobalVar("main")
+module = ir.IRModule({entry: function})
+# end module construction
+
+# begin global call
+global_call = ir.Call(entry, [ir.const(3, "int32")], ty=function.ret_type)
+# end global call
+
+# begin module serialization
+restored = ir.load_json(ir.save_json(module))
+ir.assert_structural_equal(module, restored)
+# end module serialization
+
+assert global_call.ty == x.ty
+assert module[entry].same_as(module["main"])
+assert tirx.analysis.check_well_formed(module)
+assert tirx.analysis.check_well_formed(tirx.Function([x], body, ret_type=x.ty))
+assert tirx.analysis.check_well_formed(tirx.Function([x], branch,
ret_type=x.ty))
+# begin module update
+variant = module.clone()
+variant["main"] = function.with_attr("example.note", "clone")
+assert "example.note" not in module["main"].attrs
+# end module update
+
+escaped = tirx.Function([], [loop, ir.Evaluate(i)])
+assert not tirx.analysis.check_well_formed(escaped, assert_mode=False)
+executable = tirx.compile(module, target="llvm")
+for value in (-2, 0, 3):
+ assert executable(value) == value + 1
+print("Statement, function, and execution examples passed.")
diff --git a/docs/ir/examples/types_and_expressions.py
b/docs/ir/examples/types_and_expressions.py
new file mode 100644
index 000000000..4f8d7b766
--- /dev/null
+++ b/docs/ir/examples/types_and_expressions.py
@@ -0,0 +1,100 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Build and inspect typed scalar and tensor expressions.
+
+Run ``python types_and_expressions.py`` with TIRx installed.
+
+The four steps show scalar expressions, symbolic tensor shapes, operator
+calls, and a custom type. Each step prints the constructed IR and its type.
+For example, the tensor step prints::
+
+ 2. Tensor expressions
+ Fixed tensor type: T.Tensor((8, 128), T.float32)
+ Symbolic tensor type: T.Tensor((T.int64(2) * n, 128), T.float32)
+ A[0, 0] : T.float32
+
+The printed forms use TVMScript notation, such as T.int32 and T.Tensor(...).
+"""
+
+from tvm_ffi.dataclasses import py_class
+
+from tvm_tirx import ir, tirx
+
+# 1. Describe an integer computation, then construct its input and addition.
+# begin composite type
+integer_type = ir.PrimType("int32")
+signature = ir.FuncType([integer_type], integer_type)
+# end composite type
+
+# begin scalar expressions
+x = ir.Var("x", integer_type)
+added = x + ir.const(1, integer_type)
+# end scalar expressions
+
+print("1. Scalar expressions")
+print(f" {added.script()} : {added.ty.script()}")
+print(f" Function signature: {signature.script()}")
+
+# 2. A tensor type can use fixed dimensions or reuse integer expressions.
+# begin constant tensor type
+tile_type = tirx.TensorType((8, 128), "float32")
+# end constant tensor type
+
+# begin tensor expressions
+n = ir.Var("n", "int64")
+tensor_type = tirx.TensorType((2 * n, 128), "float32")
+tensor = ir.Var("A", tensor_type)
+element = tensor[0, 0]
+# end tensor expressions
+
+# The shape retains the same n object; no numeric value for n is needed here.
+assert tensor.ty.shape[0].b.same_as(n)
+print("\n2. Tensor expressions")
+print(f" Fixed tensor type: {tile_type.script()}")
+print(f" Symbolic tensor type: {tensor_type.script()}")
+print(f" {element.script()} : {element.ty.script()}")
+
+# 3. Retrieve one operator and use it in calls with different input
expressions.
+# begin operator expressions
+exp_op = ir.Op.get("prim.exp")
+value = ir.Var("value", "float32")
+exp_call = ir.Call(exp_op, [value])
+# end operator expressions
+
+tensor_exp = ir.Call(exp_op, [element])
+assert tensor_exp.op.same_as(exp_call.op)
+print("\n3. Operator calls")
+print(f" Registered operator: {exp_op.name}")
+print(f" {exp_call.script()} : {exp_call.ty.script()}")
+print(f" {tensor_exp.script()} : {tensor_exp.ty.script()}")
+
+
+# 4. Define a type for a pair of values using existing Type objects as fields.
+# begin custom type
+@py_class("my_ext.PairType")
+class PairType(ir.Type):
+ first: ir.Type
+ second: ir.Type
+
+
+pair = ir.Var("p", PairType(ir.PrimType("int32"), ir.PrimType("float32")))
+# end custom type
+
+print("\n4. A custom type")
+print(f" {pair.name} : {type(pair.ty).__name__}")
+print(f" Component types: {pair.ty.first.script()},
{pair.ty.second.script()}")
+print(f" Inherited source location: {type(pair.ty.loc).__name__}")
diff --git a/docs/ir/functions_and_modules.rst
b/docs/ir/functions_and_modules.rst
new file mode 100644
index 000000000..36d6d5dc1
--- /dev/null
+++ b/docs/ir/functions_and_modules.rst
@@ -0,0 +1,134 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Functions and Modules
+=====================
+
+Functions define computations through parameters, result types, and statement
+bodies, while modules collect named function definitions. Extensions can build
+programs with custom types and operators using these same structures, and
+passes can inspect or replace definitions through the existing interfaces.
+
+Functions
+---------
+
+``ir.BaseFunc`` is the shared base class for function definitions and is itself
+an expression. It inherits ``ty`` and ``loc`` from ``ir.Expr`` and adds a field
+shared by function definitions:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 15 25 60
+
+ * - Field
+ - Value type
+ - Meaning
+ * - ``attrs``
+ - ``ir.DictAttrs``
+ - Named function properties.
+
+``tirx.Function`` adds these fields:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 75
+
+ * - Field
+ - Meaning
+ * - ``params``
+ - Parameter variables in calling order.
+ * - ``ret_type``
+ - The declared result type; an omitted type becomes an empty
``TupleType``.
+ * - ``body``
+ - A ``SeqStmt``, or None for a declaration without an implementation.
+
+For example, this function takes an int32 input and returns that value plus
+one:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin function construction
+ :end-before: # end function construction
+ :prepend: from tvm_tirx import ir, tirx
+
+``value`` is defined as a parameter and used by the expression in the return
+statement. The function's ``ty`` is ``ir.FuncType([value.ty], value.ty)``:
+construction derives this signature from the parameter types and ``ret_type``.
+The return type is declared explicitly; it is not inferred from the body.
+Passing None for ``body`` creates a declaration with a signature but no
+implementation.
+
+Modules
+-------
+
+``ir.IRModule`` maps ``ir.GlobalVar`` references to ``ir.BaseFunc``
definitions.
+Calls can use these references to identify a function, allowing a pass to
+replace its definition while retaining the reference. Place the increment
+function in a module under the name ``main``:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin module construction
+ :end-before: # end module construction
+
+``entry`` is the reference; ``function`` contains the parameters and body.
+``module[entry]`` and ``module["main"]`` retrieve that same definition.
+``global_symbol`` sets the exported function name used by code generation.
+
+Use the stored reference to construct a call representing ``main(3)``:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin global call
+ :end-before: # end global call
+
+This creates a call expression with an int32 result type. The signature lives
+on ``function.ty``; insertion does not copy it to ``entry.ty``, so the call
+supplies its result type explicitly. Reuse ``entry`` or retrieve it with
+``module.get_global_var("main")`` when referring to this definition.
+
+To update a definition while keeping the original module, clone the module
+and replace the definition in the clone. This example adds an attribute to
+the cloned module's ``main`` function:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin module update
+ :end-before: # end module update
+
+``with_attr`` returns a new function, and assignment updates ``variant``'s
+function map. The original ``module["main"]`` keeps its attributes. Use
+:meth:`~tvm_tirx.tirx.Function.with_body` to replace a function body, and see
+:class:`~tvm_tirx.ir.IRModule` for lookup, copying, and update APIs.
+
+A module also carries metadata: ``attrs`` stores named attributes, and
+``global_infos`` stores named arrays of ``ir.GlobalInfo``. Save a module as
JSON
+to reconstruct its IR later:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin module serialization
+ :end-before: # end module serialization
+
+Structural comparison checks the representation, including corresponding bound
+variables. Loading requires the graph's object types to be registered in the
+receiving process.
+
+See :class:`~tvm_tirx.tirx.Function` and :class:`~tvm_tirx.ir.IRModule` for the
+complete APIs. The :download:`statements_and_functions.py
<examples/statements_and_functions.py>`
+example checks scopes and updates, compiles with LLVM, and verifies that an
+input of three returns four.
diff --git a/docs/ir/operators_and_calls.rst b/docs/ir/operators_and_calls.rst
new file mode 100644
index 000000000..db33168b8
--- /dev/null
+++ b/docs/ir/operators_and_calls.rst
@@ -0,0 +1,195 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Operators and Calls
+===================
+
+Operators describe computations, while calls apply them to particular inputs.
+Custom operators can be registered alongside built-in ones, with their own
+argument requirements, type rules, and effects. Their calls use the existing
+IR representation, so extensions can build analyses, transformations, and
+lowering passes around the same objects and interfaces.
+
+Operators
+---------
+
+``ir.Op`` is an expression that identifies a registered operator. The registry
+stores shared operator definitions, which can be retrieved by name with
+``ir.Op.get(name)``. An operator's ``args_info`` describes its required value
+arguments. A validator is a registered function that checks whether a call's
+arguments meet the operator's requirements.
+
+Operator attributes describe properties shared by all calls to that operator.
+The ``TCallEffectKind`` attribute records whether a call only computes a value
+or can read or change program state.
+
+``prim.exp`` computes the exponential of one input. It declares an argument
+named ``x`` and has the effect kind ``Pure``: it computes a value without
+reading or changing external state. Inspect these properties on the registered
+object:
+
+.. literalinclude:: examples/operators_and_calls.py
+ :language: python
+ :start-after: # begin operator lookup
+ :end-before: # end operator lookup
+ :prepend: from tvm_tirx import ir
+
+Here, ``argument_names`` is ``["x"]``, and ``effect`` equals
+``ir.CallEffectKind.Pure``. See :class:`~tvm_tirx.ir.Op` for the registration
+and attribute APIs.
+
+Calls
+-----
+
+``ir.Call`` represents an invocation of an operator or function. The expression
+being called is its *callee*. A call inherits ``ty`` and ``loc`` from
+``ir.Expr`` and adds these fields:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 75
+
+ * - Field
+ - Meaning
+ * - ``op``
+ - The callee: a registered operator or another function-valued expression.
+ * - ``args``
+ - Value arguments in calling order.
+ * - ``ty_args``
+ - Explicit type arguments, empty by default.
+ * - ``attrs``
+ - Attributes for this invocation, or None. A dictionary becomes
+ ``ir.DictAttrs``.
+
+``ty_args`` supplies types as inputs to the operation; ``ty`` describes its
+result. Call attributes belong to one invocation, while the operator's
+registration supplies the rules used to interpret them.
+
+For example, this call applies the registered exponential operator to a float32
+constant:
+
+.. literalinclude:: examples/operators_and_calls.py
+ :language: python
+ :start-after: # begin call contract
+ :end-before: # end call contract
+
+This constructs the IR for ``exp(1.0)``; its result type is
+``ir.PrimType("float32")``. The computation is performed when a program
+containing the call is compiled and executed.
+
+A function-valued variable can also be the callee. Here, ``f`` has a signature
+that takes one float32 value and returns a float32 value:
+
+.. literalinclude:: examples/operators_and_calls.py
+ :language: python
+ :start-after: # begin function call type
+ :end-before: # end function call type
+
+``function_call`` represents ``f(1.0)``. The variable supplies a signature;
+the example does not provide an implementation of ``f``.
+
+.. _types-and-validation:
+
+Type Inference and Validation
+-----------------------------
+
+To avoid repeating type information at each call, ``ir.Call`` can infer its
+result type from the operator's registered rule or the callee's function
+signature. Omitting ``ty`` asks ``ir.Call`` to infer it; passing an explicit
+type stores that type instead. ``MissingType`` marks a result whose type has
+not yet been determined.
+
+Validation checks whether the arguments meet the operator's requirements.
+It is separate from construction, so tools can assemble a call before
+checking its arguments with ``call.validate()``. Type inference can also
+reject inputs while computing the result type.
+
+For ``prim.exp``, an explicit result type lets the constructor record a call
+with no arguments. Validation then rejects the argument count:
+
+.. literalinclude:: examples/operators_and_calls.py
+ :language: python
+ :start-after: # begin validation failure
+ :end-before: # end validation failure
+ :append: invalid.validate() # raises TypeError: expected one argument
+
+When constructing or rewriting IR, a call can temporarily carry a missing
+result type. ``ir.reinfer_type`` derives the type from its current inputs:
+
+.. literalinclude:: examples/operators_and_calls.py
+ :language: python
+ :start-after: # begin provisional call
+ :end-before: # end provisional call
+
+Here, ``inferred`` is ``ir.PrimType("float32")``. The function returns a type;
+it leaves ``provisional.ty`` unchanged. See :class:`~tvm_tirx.ir.Call` and
+:func:`~tvm_tirx.ir.reinfer_type` for the inference rules, and
+:meth:`~tvm_tirx.ir.Call.validate` for the validation contract.
+
+Register New Operators
+----------------------
+
+Registering an operator gives a custom computation an argument and result-type
+contract that calls and passes can share. New operators can be added from a
+Rust extension without modifying the C++ core.
+
+Use a ``register_ops!`` block with one ``OpDef`` chain per operator. Each chain
+supplies the operator's name, argument requirements, type-inference rule, and
+attributes.
+The operator joins the same registry as the built-in operators, so Python,
+C++, and Rust code can retrieve it.
+
+For example, this block registers the interface of ``my_ext.clamp``, an
+operation for clamping an integer index to ``[0, bound)``. A call has the type
+of its index, and ``validate()`` rejects arguments that are not integer
+expressions:
+
+.. code-block:: rust
+
+ use tvm_tirx::ir::op_attr::CALL_EFFECT_KIND;
+ use tvm_tirx::ir::sig::{self, IntExpr};
+ use tvm_tirx::ir::{CallEffectKind, CallObj, OpDef, Type};
+ use tvm_tirx::tvm_ffi::Result;
+
+ fn index_type(call: &CallObj) -> Result<Type> {
+ Ok(call.args.get(0)?.ty.clone())
+ }
+
+ tvm_tirx::register_ops! {
+ OpDef::new("my_ext.clamp", "Clamp an index to the range `[0, bound)`.")
+ .signature((
+ sig::arg::<IntExpr>("index", "The index."),
+ sig::arg::<IntExpr>("bound", "The exclusive upper bound."),
+ ))
+ .set_infer_type(index_type)
+ .set_attr(CALL_EFFECT_KIND, CallEffectKind::Pure);
+ }
+
+Here, ``.signature`` generates a validator from the ``IntExpr`` argument
+constraints. ``.set_infer_type`` registers ``index_type``, which returns the
+first argument's type. ``.set_attr`` records that the operation is pure.
+
+Once the extension is loaded and the operator is registered, Python can
+retrieve it with ``ir.Op.get("my_ext.clamp")`` and construct calls in the same
+way as for a built-in operator.
+
+To make these calls executable, supply a lowering that replaces them with
+existing IR or implement the operation in a backend.
+
+See :doc:`/reference/python/ir` for the complete operator and call APIs. The
+:download:`operators_and_calls.py <examples/operators_and_calls.py>` example
includes runnable
+calls, re-inference, and validation failures.
diff --git a/docs/ir/overview.rst b/docs/ir/overview.rst
new file mode 100644
index 000000000..db392288b
--- /dev/null
+++ b/docs/ir/overview.rst
@@ -0,0 +1,166 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+IR Overview
+===========
+
+An intermediate representation (IR) describes a program in a form that compiler
+components can inspect and transform. It makes values, computations, control
+flow, and types explicit, allowing analyses and transformations to work with
the
+program's structure independently of the syntax used to construct it.
+
+TIRx's C++, Python, and Rust interfaces share a common IR object system through
+TVM FFI, allowing compiler components written in different languages to work
+together. The IR can be extended with custom operators and types, and its
+existing interfaces can be used to build custom analyses, transformation
passes,
+and backends.
+
+The following tree shows the inheritance relationships between IR object types.
+Registered names can differ from Python API names. For example, ``prim.Add``
+is available as ``ir.prim.Add`` in Python.
+
+.. code-block:: text
+
+ ffi.Object TVM FFI base object
+ ├── ir.Expr Expressions
+ │ ├── ir.Constant Literal constants
+ │ │ ├── ir.IntImm Integer constants
+ │ │ ├── ir.FloatImm Floating-point constants
+ │ │ ├── ir.StringImm String constants
+ │ │ ├── ir.DataTypeImm Dtype constants
+ │ │ └── ir.GenericConst Constants with
arbitrary payloads
+ │ ├── ir.Var Local variables
+ │ ├── ir.GlobalVar Global function
references
+ │ ├── ir.Tuple Tuple construction
+ │ ├── ir.TupleGetItem Tuple field access
+ │ ├── ir.Call Calls
+ │ ├── ir.Op Registered operators
+ │ ├── ir.TensorLoad Tensor element reads
+ │ ├── ir.TensorRegion Tensor regions
+ │ ├── ir.BaseFunc Function definitions
+ │ │ └── tirx.Function Tensor-program functions
+ │ ├── ir.StagingExpr Expressions removed
during compilation
+ │ │ └── ir.LambdaExpr Construction-time
lambdas
+ │ ├── ir.OpaqueExpr Opaque
construction-time expressions
+ │ ├── prim.Cast Dtype conversion
+ │ ├── prim.Add Addition
+ │ ├── prim.Sub Subtraction
+ │ ├── prim.Mul Multiplication
+ │ ├── prim.Div Division
+ │ ├── prim.Mod Remainder
+ │ ├── prim.FloorDiv Floor division
+ │ ├── prim.FloorMod Floor remainder
+ │ ├── prim.Min Minimum
+ │ ├── prim.Max Maximum
+ │ ├── prim.LShift Left bit shift
+ │ ├── prim.RShift Right bit shift
+ │ ├── prim.BitwiseAnd Bitwise AND
+ │ ├── prim.BitwiseOr Bitwise OR
+ │ ├── prim.BitwiseXor Bitwise XOR
+ │ ├── prim.BitwiseNot Bitwise NOT
+ │ ├── prim.EQ Equal
+ │ ├── prim.NE Not equal
+ │ ├── prim.LT Less than
+ │ ├── prim.LE Less than or equal
+ │ ├── prim.GT Greater than
+ │ ├── prim.GE Greater than or equal
+ │ ├── prim.And Logical AND
+ │ ├── prim.Or Logical OR
+ │ ├── prim.Not Logical NOT
+ │ ├── prim.Select Conditional value
selection
+ │ ├── prim.Let Expression-local binding
+ │ ├── prim.Ramp Vector arithmetic
progression
+ │ ├── prim.Broadcast Scalar broadcast to
vector lanes
+ │ ├── prim.Shuffle Vector lane selection
+ │ ├── sym.IterMapExpr Iterator mapping
expressions
+ │ │ ├── sym.IterSplitExpr Iterator splitting
+ │ │ └── sym.IterSumExpr Sums of iterator
expressions
+ │ └── sym.CanonicalExpr Internal simplifier
expressions
+ │ ├── sym.SplitExpr Internal split
expressions
+ │ └── sym.SumExpr Internal sum expressions
+ ├── ir.Type Types
+ │ ├── ir.MissingType Unresolved type
information
+ │ ├── ir.NoneType The type of None
+ │ ├── ir.AnyType Any value
+ │ ├── ir.OpaqueType Opaque
construction-time types
+ │ ├── ir.PrimType Scalar and vector dtypes
+ │ ├── ir.StringType String values
+ │ ├── ir.PtrType Pointer types
+ │ ├── ir.TupleType Tuple field types
+ │ ├── ir.FuncType Function signatures
+ │ ├── ir.TensorRegionType Tensor region values
+ │ ├── tirx.TensorType Tensor types
+ │ └── tirx.TensorMapType Tensor map types
+ ├── ir.Stmt Statements
+ │ ├── ir.SeqStmt Statement sequences
+ │ ├── ir.Bind Variable definitions
+ │ ├── ir.RegionStmt Operator-defined
lexical regions
+ │ ├── ir.AssertStmt Program assertions
+ │ ├── ir.TensorStore Tensor element writes
+ │ ├── ir.Evaluate Evaluation with an
unused result
+ │ ├── ir.If Conditional branches
+ │ ├── ir.For Counted loops
+ │ ├── ir.While Conditional loops
+ │ ├── ir.Return Function returns
+ │ ├── ir.Break Loop exits
+ │ └── ir.Continue Next loop iterations
+ ├── ir.IRModule Global function
definitions
+ ├── ir.Attrs Structured attributes
+ │ ├── ir.DictAttrs Named attribute maps
+ │ ├── tirx.CallFFIKernelAttr FFI kernel call
attributes
+ │ ├── tirx.tensor.ScopeAttrs Tensor instruction
scopes
+ │ ├── tirx.tensor.TrnAttrs Tensor instruction
attributes
+ │ ├── tirx.cuda.TensorMapEncodeTiledAttr CUDA tensor map
encoding attributes
+ │ ├── tirx.cuda.TCGen05InstrDescriptorAttrs CUDA instruction
descriptors
+ │ └── tirx.cuda.TCGen05InstrDescriptorBlockScaledAttrs Block-scaled CUDA
descriptors
+ ├── ir.Location Source locations
+ │ ├── ir.UnknownLoc Unavailable source
information
+ │ ├── ir.SourceLoc Source ranges
+ │ └── ir.CallSiteLoc Callee and caller
locations
+ ├── ir.SourceName Source identifiers
+ ├── ir.Range Begin and end bounds
+ ├── ir.ArgumentInfo Operator argument
descriptions
+ ├── ir.GlobalInfo Module-level metadata
+ ├── ir.EnvFunc Named global function
references
+ ├── ir.UniqueNameSupply Unique-name generation
+ ├── tirx.Layout Index-to-axis mappings
+ │ ├── tirx.TileLayout Tile layouts
+ │ └── tirx.ComposeLayout Composed layouts
+ ├── tirx.Iter Layout iterators
+ ├── tirx.IndexMap Index transformations
+ ├── sym.IterMark Iterator source and
extent
+ └── ffi.Enum Registered enumeration
objects
+ ├── tirx.Axis Layout axis identities
+ └── tirx.ThreadScope Execution scope
identities
+
+.. _choose-a-chapter:
+
+What to Read Next
+-----------------
+
+The following chapters explain how to work with each part of the IR:
+
+* To understand how values, computations, and tensor shapes are represented,
+ see :doc:`types_and_expressions`.
+* To build statement bodies with bindings, tensor writes, branches, and loops,
+ see :doc:`statements`.
+* To work with registered operators and function calls, see
+ :doc:`operators_and_calls`.
+* To define functions and organize them into modules, see
+ :doc:`functions_and_modules`.
+* To write or generate TVMScript and print or parse IR, see
+ :doc:`printing_and_parsing`.
diff --git a/docs/ir/printing_and_parsing.rst b/docs/ir/printing_and_parsing.rst
new file mode 100644
index 000000000..df2cb8566
--- /dev/null
+++ b/docs/ir/printing_and_parsing.rst
@@ -0,0 +1,139 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _ir-construction-and-printing:
+
+Printing and Parsing
+====================
+
+Printing renders IR as readable TVMScript, and parsing constructs IR from
+Python source. Compiler extensions can reuse these interfaces to generate
+programs, inspect the resulting IR, and reconstruct programs from source.
+Extensions can customize how source builds IR through builder namespaces
+and how IR is rendered through printer hooks.
+
+For example, this function increments every element of a float32 tensor with
+shape ``(2*n, 128)``. Save the code in a Python file so the decorator can
inspect
+its source:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin tvmscript example
+ :end-before: # end tvmscript example
+
+``@T.function`` parses the definition into a ``tirx.Function``. The future
+import postpones annotation evaluation until the parser reads the definition,
+so ``n`` in the tensor annotation can resolve to the first IR parameter. The
+tensor's shape and the loop bound refer to that same variable, keeping ``2*n``
+symbolic in both places. The loops and indexing construct IR; the tensor
+computation runs when the compiled function is called.
+
+Script helpers let generated functions reuse parts of a computation.
+An ``@T.inline`` helper can return an expression, while ``@T.macro`` inserts
+statements into the function being built. These helpers separate the element
+addition from the tensor write:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin script helpers
+ :end-before: # end script helpers
+
+``increment`` constructs the addition, and ``increment_at`` writes its result
+back to the tensor. ``ir.const(1, value.ty)`` gives the constant the element's
+type, so the helpers also work with other element types.
+
+An ordinary Python function can choose a shape and dtype, construct a
+``@T.function`` definition, and return the resulting ``tirx.Function``.
+Here, ``make_increment`` builds a function with the requested column count
+and element type:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin program factory
+ :end-before: # end program factory
+
+``width`` and ``dtype`` are Python values read while the factory runs. ``wide``
+has a fixed second dimension of 128 and float32 elements; ``narrow`` has a
second
+dimension of 64 and float16 elements. Their first dimension remains ``2*n``,
+where ``n`` is an IR parameter supplied when the compiled function is called.
+The width check and function construction run while ``make_increment``
+executes; the generated loops describe the later tensor computation.
+Generating IR with Python logic in this way is metaprogramming.
+
+Printing lets you inspect what this Python code generated:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin print generated function
+ :end-before: # end print generated function
+
+``script()`` returns source text. The printed ``wide`` function contains these
+loops, using the selected width and element type:
+
+.. code-block:: text
+
+ for i in range(T.int64(0), T.int64(2) * n):
+ for j in range(T.int64(0), T.int64(128)):
+ A[i, j] = A[i, j] + T.float32(1.0)
+
+The helper calls have expanded into an addition and a tensor write. The source
+also declares ``n`` with ``T.dynamic``, which creates a symbolic ``ir.Var`` for
+reuse in the signature and tensor shape. You can also print the result of a
+pass or smaller parts of a program:
+``wide.params[1].ty.script()`` renders its tensor type, and
+``wide.body[0].script()`` renders its outer loop.
+
+For interactive inspection, ``wide.show()`` displays highlighted source.
+See :meth:`~tvm_tirx.tirx.Function.show` for options such as line numbers and
+underlining IR objects.
+
+``from_source`` parses a source string into a new IR object. Names used in
+the text must be supplied by its imports or the ``extra_vars`` dictionary.
+The printed source refers to ``T``, but its import is a comment by default,
+so this example supplies ``T`` through ``extra_vars``:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin printing and parsing
+ :end-before: # end printing and parsing
+
+The structural comparison checks that the reconstructed program has the same
+operations, types, and variable bindings. Its printed syntax can differ from
+the original: for example, ``T.grid`` becomes nested loops.
+
+The same process works for modules. ``@I.ir_module`` turns a class containing
+function definitions into an ``ir.IRModule``. Here it collects both generated
+variants, which can then be printed and parsed together:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin module round trip
+ :end-before: # end module round trip
+
+To include import statements in the printed text, set ``ir.comment_imports``
+to False. The parser can then resolve ``I`` and ``T`` from those imports,
+without ``extra_vars``. This is useful when saving the source for another
+process:
+
+.. literalinclude:: examples/printing_and_parsing.py
+ :language: python
+ :start-after: # begin standalone source
+ :end-before: # end standalone source
+
+Download the :download:`complete example <examples/printing_and_parsing.py>`
+for runnable code. See the :doc:`parser </reference/python/script.parser>` and
+:doc:`printer </reference/python/script.printer>` API references for details.
diff --git a/docs/ir/statements.rst b/docs/ir/statements.rst
new file mode 100644
index 000000000..091c6c1d9
--- /dev/null
+++ b/docs/ir/statements.rst
@@ -0,0 +1,190 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _statements-and-control-flow:
+
+Statements
+==========
+
+Statements describe variable bindings, tensor writes, and control flow.
+Extensions can construct and rewrite program bodies with the existing
+statement nodes. Custom operators can use ``ir.RegionStmt`` to define how
+a group of statements executes.
+
+.. _statements-and-sequences:
+
+Statement Nodes
+---------------
+
+``ir.Stmt`` is the base class for all statement nodes, including sequences,
+branches, and loops. It defines the following field:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 15 25 60
+
+ * - Field
+ - Value type
+ - Meaning
+ * - ``loc``
+ - ``ir.Location``
+ - Source information for diagnostics. Defaults to ``UnknownLoc`` and does
+ not affect structural comparison.
+
+Subclasses inherit ``loc`` and add fields for operands or nested statements.
+The shared statement classes are:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 35 40
+
+ * - Class
+ - Represents
+ - Additional fields
+ * - ``ir.Bind``
+ - A variable definition
+ - ``var``, ``value``
+ * - ``ir.TensorStore``
+ - A tensor element write; the destination type must support stores
+ - ``dest``, ``indices``, ``value``
+ * - ``ir.Evaluate``
+ - Expression evaluation with its result discarded
+ - ``value``
+ * - ``ir.AssertStmt``
+ - A program assertion
+ - ``condition``, ``error_kind``, ``message_parts``
+ * - ``ir.Return``
+ - A function return
+ - ``value``
+ * - ``ir.Break``
+ - Exit the current loop
+ - None
+ * - ``ir.Continue``
+ - Start the next loop iteration
+ - None
+ * - ``ir.SeqStmt``
+ - A sequence of statements in execution order
+ - ``seq``
+ * - ``ir.If``
+ - Select a branch
+ - ``condition``, ``then_case``, ``else_case``
+ * - ``ir.For``
+ - Repeat a body over an integer range
+ - ``var``, ``begin``, ``end``, ``step``, ``kind``, ``body``,
``annotations``
+ * - ``ir.While``
+ - Repeat while a condition is true
+ - ``condition``, ``body``
+ * - ``ir.RegionStmt``
+ - An operator-defined statement with a nested body
+ - ``op``, ``args``, ``attrs``, ``body_params``, ``body``, ``result_vars``
+
+Statement Sequences
+-------------------
+
+A return that uses a bound variable must follow its definition.
+``ir.SeqStmt`` records the execution order of such statements.
+For example, consider a function that adds one to its input:
+
+.. code-block:: python
+
+ def increment(x):
+ result = x + 1
+ return result
+
+The following code constructs that function body as IR objects:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin statements
+ :end-before: # end statements
+ :prepend: from tvm_tirx import ir, tirx
+
+The Python variable ``added`` holds the expression for ``x + 1``. ``ir.Var``
+creates the IR variable ``result``, and ``ir.Bind(result, added)`` defines it
+with that expression as its value. ``added.ty`` gives ``result`` the addition's
+``int32`` type. ``ir.Return(result)`` refers to the same variable, and
+``SeqStmt`` places the definition before the return.
+
+Nested sequences are flattened, and a sequence introduces no scope of its
+own. Variables defined by ``Bind`` are available to subsequent statements in
+the enclosing scope.
+
+Control Flow and Scope
+----------------------
+
+Control-flow statement nodes include ``ir.If`` for branching, ``ir.For`` and
+``ir.While`` for loops, ``ir.Break`` and ``ir.Continue`` for loop control, and
+``ir.Return`` for returning from a function.
+
+The statements inside a branch or loop form its body. Branch bodies are
+stored in ``If.then_case`` and ``If.else_case``, while loop bodies are stored
+in ``For.body`` and ``While.body``. Each body is a ``SeqStmt`` and can contain
+further branches and loops.
+
+For example, this branch returns the incremented value only for positive
+inputs. Both bodies are normalized to ``SeqStmt``:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin branch
+ :end-before: # end branch
+
+``If`` and ``While`` use boolean conditions. ``If`` requires an explicit
+``else_case`` argument; pass None when there is no else branch.
+
+A loop instead repeats a body. This loop evaluates each index from zero through
+three:
+
+.. literalinclude:: examples/statements_and_functions.py
+ :language: python
+ :start-after: # begin loop
+ :end-before: # end loop
+
+``For`` takes an exclusive ``end`` and explicit ``step`` and ``kind``
arguments.
+Its variable, bounds, and step must have matching scalar integer types after
+conversion.
+
+The ``kind`` argument controls how loop iterations are executed.
+``ForKind.DEFAULT`` executes them in order, one after another.
+See :class:`~tvm_tirx.ir.ForKind` for the available execution kinds.
+
+``Break`` exits a loop, and ``Continue`` starts its next iteration.
+
+Variable scope determines where a definition can be used. The loop defines
``i``
+inside its body; it cannot be used after the loop. Branch-local definitions
+similarly stay inside their branch. Each variable object has one definition,
+and its uses refer to that object.
+
+``ir.RegionStmt`` lets an operator define how a nested body is executed without
+introducing a new statement class. Its ``op`` field refers to the registered
+``ir.Op`` that defines this behavior. Inputs in ``args`` and ``attrs``
+are evaluated in the enclosing scope. ``body_params`` defines variables
+visible within ``body``;
+``result_vars`` defines variables available to subsequent statements in the
+enclosing sequence. See :class:`~tvm_tirx.ir.RegionStmt` for the operator
+contract.
+
+To check these definition and scope rules, the body needs the context of a
+complete function or module. :doc:`functions_and_modules` supplies that
context;
+``tirx.analysis.check_well_formed`` then checks variable and tensor uses and
loop
+control. Invalid uses raise ``RuntimeError`` by default; ``assert_mode=False``
+returns False instead.
+
+The :download:`statements_and_functions.py
<examples/statements_and_functions.py>`
+example completes and runs the increment function. See
+:doc:`/reference/python/ir` for statement constructors and
+:doc:`/reference/python/tirx.analysis` for verification APIs.
diff --git a/docs/ir/types_and_expressions.rst
b/docs/ir/types_and_expressions.rst
new file mode 100644
index 000000000..4ab478ac0
--- /dev/null
+++ b/docs/ir/types_and_expressions.rst
@@ -0,0 +1,274 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _expressions-and-types:
+
+Types and Expressions
+=====================
+
+Expressions represent program values and computations, while types describe
+properties such as dtypes, tensor shapes, and function signatures. This shared
+representation can be extended with custom types and operators. Programs using
+these constructs can be built, inspected, and transformed through the existing
+IR interfaces.
+
+.. _user-ir-type:
+.. _user-ir-prim-type:
+
+Types
+-----
+
+``ir.Type`` is the base class for type objects, giving analyses and
+transformations a common interface across different kinds of values.
+It defines the following field:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 15 25 60
+
+ * - Field
+ - Value type
+ - Meaning
+ * - ``loc``
+ - ``ir.Location``
+ - Source information for diagnostics. Defaults to ``UnknownLoc`` and does
+ not affect structural comparison.
+
+Concrete subclasses inherit ``loc`` and add fields describing different kinds
+of values. Common examples include:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 30 35 35
+
+ * - Class
+ - Describes
+ - Additional fields
+ * - ``ir.PrimType``
+ - A scalar or vector dtype
+ - ``dtype``
+ * - ``ir.PtrType``
+ - A pointer's element type and memory scope
+ - ``element_type``, ``scope``
+ * - ``ir.TupleType``
+ - Ordered tuple field types
+ - ``fields``
+ * - ``ir.FuncType``
+ - Function argument and return types
+ - ``arg_types``, ``ret_type``
+ * - ``tirx.TensorType``
+ - A tensor's shape, element type, and storage properties
+ - ``shape``, ``dtype``, ``scope``, ``data_alignment``, ``layout``
+
+Some of these fields refer to other type objects. For example, ``FuncType``
+stores the types of a function's arguments and result:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin composite type
+ :end-before: # end composite type
+ :prepend: from tvm_tirx import ir, tirx
+
+``signature`` describes a function that takes and returns int32 values.
+``integer_type.dtype`` holds the int32 dtype descriptor;
+``signature.arg_types[0]`` and ``signature.ret_type`` both refer to the
+``integer_type`` object. Tuple fields and pointer element types similarly
+refer to other type objects.
+
+.. _user-tensor-type:
+
+``tirx.TensorType`` combines an element type with shape and storage
information:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin constant tensor type
+ :end-before: # end constant tensor type
+
+``tile_type`` describes an ``(8, 128)`` tensor. Its ``shape`` contains integer
+expressions for ``8`` and ``128``, and its ``dtype`` is
+``ir.PrimType("float32")``. Dimensions can also use variables and arithmetic.
+
+Use ``==`` to compare type structure and ``same_as`` to compare object
identity.
+
+.. _user-ir-var:
+
+Expressions
+-----------
+
+Expression nodes make the inputs to a computation explicit, so analyses can
+inspect its operands and transformations can replace parts of the computation.
+``ir.Expr`` is the base class for these nodes. It defines these fields:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 15 25 60
+
+ * - Field
+ - Value type
+ - Meaning
+ * - ``ty``
+ - ``ir.Type``
+ - Result type. ``ir.MissingType`` marks type information that has not yet
+ been supplied or computed.
+ * - ``loc``
+ - ``ir.Location``
+ - Source information for diagnostics. Defaults to ``UnknownLoc`` and does
+ not affect structural comparison.
+
+Subclasses inherit ``ty`` and ``loc`` and add fields for their stored values or
+operands. The following examples show common expression classes and selected
+fields:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 30 35 35
+
+ * - Classes (examples)
+ - Represents
+ - Additional fields
+ * - ``ir.Var``
+ - A variable
+ - ``name``
+ * - ``ir.IntImm``, ``ir.FloatImm``, ``ir.StringImm``
+ - Literal constants
+ - ``value``
+ * - ``ir.prim.Add``, ``ir.prim.Sub``, ``ir.prim.Mul``
+ - Binary arithmetic
+ - ``a``, ``b``
+ * - ``ir.prim.EQ``, ``ir.prim.LT``
+ - Comparisons
+ - ``a``, ``b``
+ * - ``ir.Op``
+ - A registered operator
+ - ``name``, ``doc``, ``args_info``
+ * - ``ir.Call``
+ - A call to an operator or function
+ - ``op``, ``args``, ``attrs``, ``ty_args``
+ * - ``ir.TensorLoad``
+ - A tensor element read
+ - ``source``, ``indices``
+
+Arithmetic and comparison nodes for primitive values are available under
+``ir.prim``. The binary nodes above store their left and right operand
+expressions in ``a`` and ``b``.
+
+Registered operators are themselves expressions of class ``ir.Op``. Each has
+a name, such as ``prim.exp``, and metadata describing its arguments.
+``ir.Op.get`` retrieves the operator object by name; ``ir.Call`` uses it as
+the callee of an invocation:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin operator expressions
+ :end-before: # end operator expressions
+
+``exp_call.op`` refers to ``exp_op``, and ``exp_call.args`` contains the input
+``value``. The same operator object can be used by many calls, each with its
+own arguments. ``ir.prim.exp(value)`` is a helper that constructs this call
+form. :doc:`operators_and_calls` explains operator registration, attributes,
+type inference, and validation.
+
+Operands and call arguments are themselves expressions, allowing these forms
+to be combined into larger computations.
+
+.. _user-ir-const:
+
+To describe ``x + 1``, create a variable with the integer type above and a
+constant of that type. Adding them constructs an ``Add`` expression:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin scalar expressions
+ :end-before: # end scalar expressions
+
+``x`` is a ``Var`` and ``added`` is an ``Add``. Both carry ``integer_type`` in
+their ``ty`` field. Reuse the same variable object when a symbol appears in
+several places: another ``ir.Var("x", integer_type)`` is a distinct symbol,
+even with the same name.
+
+A tensor variable follows the same pattern, with a ``TensorType`` in its
+``ty`` field. Its shape can reuse the integer expressions described above:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin tensor expressions
+ :end-before: # end tensor expressions
+
+The first dimension stores the expression ``2*n``. The same ``n`` can appear
+in loop bounds and indices. The element load ``tensor[0, 0]`` is another
+expression, with type ``ir.PrimType("float32")``.
+
+The symbol ``n`` has a known int64 type even though its value is not yet known.
+Constructing these types and expressions does not allocate tensor storage.
+
+.. _user-ir-register-type:
+
+Register New Types
+------------------
+
+Custom types let extensions describe their own kinds of values and use them
+in existing IR variables and function signatures. New types can be added from
+Python or Rust without modifying the C++ core.
+Define a subclass of ``ir.Type`` and register it under its own key. Its
+reflected fields let structural comparison and printing handle it in the same
+way as built-in types.
+
+In Python, use ``tvm_ffi.dataclasses.py_class``; in Rust, use
+``tvm_ffi::rs_class`` and ``tvm_ffi::def_object_ref!``.
+
+For example, this type describes a pair of values by the types of its two
+components. In Python, the fields are annotated class attributes, and the
+constructor takes them in order:
+
+.. literalinclude:: examples/types_and_expressions.py
+ :language: python
+ :start-after: # begin custom type
+ :end-before: # end custom type
+ :prepend: from tvm_ffi.dataclasses import py_class
+ from tvm_tirx import ir
+
+In Rust, the object struct starts with the ``TypeObj`` it extends, and the
+generated ``new`` for ``PairTypeObj`` takes that parent object and then the
+fields:
+
+.. code-block:: rust
+
+ use tvm_tirx::ir::{Location, Type, TypeObj};
+ use tvm_tirx::tvm_ffi::{self, def_object_ref, rs_class};
+
+ #[rs_class("my_ext.PairType", final)]
+ pub struct PairTypeObj {
+ base: TypeObj,
+ pub first: Type,
+ pub second: Type,
+ }
+
+ def_object_ref!(pub PairType, Type, PairTypeObj);
+
+ impl PairType {
+ pub fn new(first: impl Into<Type>, second: impl Into<Type>) -> Self {
+ PairTypeObj::new(TypeObj::new(Location::unknown()), first,
second).into()
+ }
+ }
+
+Once registered, the type can be assigned to variables and used inside other
+types, just like a built-in type.
+
+See :class:`~tvm_tirx.tirx.TensorType` for the complete constructor and storage
+options, and :doc:`/reference/python/ir` for expression and type APIs.
+Download :download:`types_and_expressions.py
<examples/types_and_expressions.py>`
+for runnable examples from this chapter.
diff --git a/python/tvm_tirx/ir/expr.py b/python/tvm_tirx/ir/expr.py
index b7467b029..5dbb5ed74 100644
--- a/python/tvm_tirx/ir/expr.py
+++ b/python/tvm_tirx/ir/expr.py
@@ -150,6 +150,10 @@ class GlobalVar(Expr):
GlobalVar is used to refer to the global functions
stored in the IRModule.
+ Each construction creates a distinct reference, even when names match.
+ Use ``module.get_global_var(name)`` to retrieve the stored reference when
+ constructing calls or replacing definitions.
+
Parameters
----------
name_hint: str
@@ -703,6 +707,11 @@ class Call(_CallableExprWithOp):
contract explicitly after inputs are ready. Constructing a ``Call`` never
emits a builder statement, including calls to allocation operators.
+ For an Op callee, a registered fixed return type takes precedence over its
+ type-inference callback. For another callee with a FuncType, inference uses
+ the signature's return type. Without an applicable rule, the result is
+ MissingType. An explicit ``ty``, including MissingType, bypasses inference.
+
Parameters
----------
op : Expr or str
@@ -759,15 +768,26 @@ class Call(_CallableExprWithOp):
return ty, op, args, attrs, ty_args, loc
def validate(self) -> None:
- """Check the registered operator contract without changing this
Call."""
+ """Run the registered operator's validator without changing this Call.
+
+ An Op callee with a validator invokes that validator; other callees and
+ operators without validators perform no checks. Success returns None,
+ and failed checks raise a diagnostic exception. The checks depend on
the
+ registered validator and do not generally establish agreement between
+ the stored result type and the type implied by the inputs.
+ """
_ffi_api.CallValidate(self)
def reinfer_type(call: Call) -> "tvm_tirx.ir.Type":
"""Derive a Call's result type from its current inputs without changing
the Call.
- The operator must register a fixed return type or a context-free inference
- rule. This does not invoke the operator's validator.
+ For an Op callee, a registered ``TFixedReturnType`` takes precedence over
+ ``FInferType``. For another callee whose type is a FuncType, the result is
+ that function type's return type. Without an applicable rule, this returns
+ MissingType. Inference errors propagate.
+
+ This does not invoke the operator's validator or overwrite ``call.ty``.
"""
return _ffi_api.CallReinferType(call)
diff --git a/python/tvm_tirx/ir/module.py b/python/tvm_tirx/ir/module.py
index 1a4405d1e..5e221b6e4 100644
--- a/python/tvm_tirx/ir/module.py
+++ b/python/tvm_tirx/ir/module.py
@@ -69,6 +69,13 @@ class IRModule(Node, Scriptable):
self.__pyfuncs__ = {}
def clone(self) -> "IRModule":
+ """Create a module object with the same functions and metadata.
+
+ The clone initially shares its function objects and other contents with
+ the original. Adding, replacing, or removing function-map entries on
the
+ clone preserves the original module's entries. Contents are not
+ recursively copied.
+ """
return _ffi_api.IRModuleClone(self)
def functions_items(self):
@@ -130,12 +137,18 @@ class IRModule(Node, Scriptable):
return _ffi_api.IRModuleContains(self, var)
def update(self, other):
- """Insert functions in another Module to current one.
+ """Insert function definitions from another module or dictionary in
place.
+
+ Definitions are merged using their GlobalVar keys. A dictionary's
+ string keys create fresh GlobalVar objects; a fresh key with the same
+ name as an existing key raises RuntimeError. To replace an existing
+ definition, reuse its stored GlobalVar or use assignment by name.
Parameters
----------
- other: IRModule
- The module to merge into the current Module.
+ other : IRModule or dict[GlobalVar or str, BaseFunc]
+ Function definitions to merge into this module. Module attributes
+ and global_infos are not merged.
"""
if isinstance(other, dict):
other = IRModule(other)
diff --git a/python/tvm_tirx/ir/stmt.py b/python/tvm_tirx/ir/stmt.py
index cf94199c3..13c57b706 100644
--- a/python/tvm_tirx/ir/stmt.py
+++ b/python/tvm_tirx/ir/stmt.py
@@ -114,9 +114,14 @@ class AssertStmt(Stmt):
class ForKind(IntEnum):
"""Execution semantics of a :class:`For` loop.
- ``DEFAULT`` executes iterations sequentially; ``PARALLEL`` permits parallel
- execution. ``VECTORIZED`` requests a vectorized body and ``UNROLLED``
requests
- unrolling. Transformations must preserve the selected execution semantics.
+ * ``DEFAULT`` executes iterations in order, one after another.
+ * ``PARALLEL`` allows different iterations to run concurrently.
+ * ``VECTORIZED`` asks the compiler to combine iterations into vector
+ operations that process multiple elements at once.
+ * ``UNROLLED`` asks the compiler to expand the loop into repeated copies
+ of its body.
+
+ Transformations must preserve the selected execution semantics.
"""
DEFAULT = 0 # Ordinary loop with sequential iteration semantics.
diff --git a/python/tvm_tirx/tirx/function.py b/python/tvm_tirx/tirx/function.py
index b1da7e8ca..d3c6fae0c 100644
--- a/python/tvm_tirx/tirx/function.py
+++ b/python/tvm_tirx/tirx/function.py
@@ -43,7 +43,9 @@ class Function(BaseFunc, Scriptable):
body : Stmt or Sequence[Stmt] or None
The function body, or None for a declaration without an implementation.
ret_type : tvm_tirx.ir.Type, optional
- The return type; defaults to MissingType until supplied or inferred.
+ The declared return type. Omitting it or supplying MissingType produces
+ an empty TupleType, the type returned by the C++ VoidType() helper.
+ Construction does not infer a return type from the body.
attrs : tvm_tirx.ir.DictAttrs, optional
Function attributes, empty by default.
loc : Location, optional
@@ -90,7 +92,8 @@ class Function(BaseFunc, Scriptable):
The new body, or None for a declaration.
loc : Location, optional
- The source location of the function.
+ The source location of the new function. Defaults to UNKNOWN_LOC;
+ pass ``loc=self.loc`` to preserve the original location.
Returns
-------