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 cbca9548f [Rust] Native docs for tirx, sym, backend and the docs pages 
(#221)
cbca9548f is described below

commit cbca9548fb8ec1ef4a7219dcaa6a5ecaa0b1535c
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Nov 6 22:31:22 2026 -0500

    [Rust] Native docs for tirx, sym, backend and the docs pages (#221)
    
    Continue the native rustdoc pass from #220 over the rest of the crate
    and the Rust docs pages. Docs only; no code change.
    
    - `tirx`, `sym`, `backend`, `runtime`, `target`, `codegen`, and `script`
    modules get short docs on what each is about and how its central types
    relate; examples move from module docs to the key types and functions.
    - The object types (43) get type docs on the reference types, written
    above the generated blocks so regeneration keeps them.
    - Rustdoc no longer mentions C++; method docs describe behavior in Rust
    terms.
    - The crate doc gets a rewritten module list and drops the claims that
    constructors follow the C++ ones and modules follow the Python package.
    - `defining_ir_extensions.rst` and `working_with_ir_in_rust.rst` drop
    C++ comparisons, and the `#[dispatch]` quick reference matches the
    current signature form.
---
 docs/advanced/defining_ir_extensions.rst           | 33 +++++-----
 docs/ir_guides/working_with_ir_in_rust.rst         | 10 ++-
 rust/src/backend.rs                                |  3 +-
 rust/src/backend/cuda.rs                           |  2 +-
 rust/src/backend/cuda/op.rs                        | 18 ++++--
 rust/src/codegen.rs                                |  5 +-
 rust/src/codegen/llvm.rs                           | 39 ++++++------
 rust/src/lib.rs                                    | 30 +++++----
 rust/src/macro/def_expr_op_overloads.rs            |  6 +-
 rust/src/runtime.rs                                | 41 +++++++------
 rust/src/script.rs                                 | 45 +++++++-------
 rust/src/sym.rs                                    |  7 +--
 rust/src/sym/analyzer.rs                           | 71 ++++++++++++++--------
 rust/src/sym/constraint.rs                         | 63 ++++++++++---------
 rust/src/sym/int_set.rs                            | 49 ++++++++-------
 rust/src/sym/iter_affine_map.rs                    | 68 ++++++++++++---------
 rust/src/target.rs                                 | 44 ++++++++------
 rust/src/tirx.rs                                   |  7 +--
 rust/src/tirx/analysis.rs                          |  4 +-
 rust/src/tirx/analysis/analysis.rs                 | 32 +++++-----
 rust/src/tirx/function.rs                          | 54 ++++++++--------
 rust/src/tirx/index_map.rs                         | 53 ++++++++--------
 rust/src/tirx/layout.rs                            | 67 ++++++++++++--------
 rust/src/tirx/op.rs                                | 52 ++++++++--------
 rust/src/tirx/op_attr.rs                           |  8 +--
 rust/src/tirx/structural.rs                        | 15 +++--
 .../tirx/structural_map_with_constraint_context.rs |  4 +-
 .../structural_mutate_with_constraint_context.rs   |  4 +-
 .../structural_visit_with_constraint_context.rs    |  4 +-
 .../structural_walk_with_constraint_context.rs     |  3 +-
 rust/src/tirx/tensor.rs                            | 69 +++++++++++----------
 rust/src/tirx/thread_scope.rs                      | 35 ++++++-----
 rust/src/tirx/tile.rs                              |  4 +-
 rust/src/tirx/tile/dispatch.rs                     | 63 ++++++++++---------
 rust/src/tirx/tile/instruction.rs                  | 11 ++--
 rust/src/tirx/transform.rs                         |  3 +-
 rust/src/tirx/transform/function_pass.rs           | 57 ++++++++---------
 rust/src/tirx/transform/transform.rs               | 57 +++++++++--------
 rust/src/tirx/type.rs                              | 22 ++-----
 39 files changed, 628 insertions(+), 534 deletions(-)

diff --git a/docs/advanced/defining_ir_extensions.rst 
b/docs/advanced/defining_ir_extensions.rst
index f69099318..9a872488c 100644
--- a/docs/advanced/defining_ir_extensions.rst
+++ b/docs/advanced/defining_ir_extensions.rst
@@ -77,9 +77,8 @@ real and imaginary parts, as in ``TensorType``:
 
    ``#[derive(ObjectRef)]`` makes ``ComplexType``, whose one field is an
    ``ObjectArc<ComplexTypeObj>``, the reference type of the object type
-   ``ComplexTypeObj``, as the C++ macro
-   ``TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE`` does. A reference holds a
-   reference-counted pointer to its object, and the derive gives it:
+   ``ComplexTypeObj``. A reference holds a reference-counted pointer to its
+   object, and the derive gives it:
 
    - ``Deref`` to the object, so its fields read directly: ``ty.dtype``;
    - a private ``from_object``, which wraps a new ``ComplexTypeObj`` for the
@@ -89,12 +88,12 @@ real and imaginary parts, as in ``TensorType``:
      ``into()`` wherever one is expected, such as the result of type
      inference.
 
-In Rust, as in C++, the type is defined in two parts: ``ComplexTypeObj`` is the
-object that holds the fields, and ``ComplexType`` is a reference-counted
-reference to it, which IR nodes store and functions pass around.
-``base_: TypeObj`` makes the object an IR type, and ``rs_object`` registers its
-fields for reflection, through which Python and IR JSON loading construct the
-type as they construct a C++ one. ``#[derive(ObjectRef)]`` defines the 
reference.
+In Rust, the type is defined in two parts: ``ComplexTypeObj`` is the object 
that
+holds the fields, and ``ComplexType`` is a reference-counted reference to it,
+which IR nodes store and functions pass around. ``base_: TypeObj`` makes the
+object an IR type, and ``rs_object`` registers its fields for reflection,
+through which Python and IR JSON loading construct the type as they construct
+a built-in one. ``#[derive(ObjectRef)]`` defines the reference.
 
 The ``static_init_block!`` registers the type when the library loads.
 
@@ -103,21 +102,21 @@ The ``static_init_block!`` registers the type when the 
library loads.
    **What does static_init_block! do?**
 
    ``static_init_block! { ... }`` runs its body when the program or library 
that
-   holds it loads, as a C++ ``TVM_FFI_STATIC_INIT_BLOCK`` does. The body
-   registers what the library defines, such as types, operators, and global
-   functions, and Python sees these registrations once it loads the library.
+   holds it loads. The body registers what the library defines, such as types,
+   operators, and global functions, and Python sees these registrations once it
+   loads the library.
    The body is the body of a function that returns ``Result<()>``, so ``?``
    stops it at its first error.
 
    Here, ``register_type::<ComplexType>()`` registers ``ComplexType`` as an IR
    type, so that Python constructs it from its fields and IR JSON holds it, as
-   for a C++ type.
+   for a built-in type.
 
 In Python, one class is enough. ``py_class`` from ``tvm_ffi.dataclasses``
 registers it as an IR object: ``my_ext.ComplexType`` is its type key,
 ``ir.Type`` its parent, and the annotated ``dtype`` its field. The class gets a
 constructor, ``ComplexType(dtype)``, and its field takes part in structural
-equality, printing, and IR JSON, as the fields of a C++ type do. The global
+equality, printing, and IR JSON, as the fields of a built-in type do. The 
global
 function ``ir.expose_type`` then publishes the type, as ``register_type`` does
 in Rust.
 
@@ -267,9 +266,9 @@ that pair. This lets the rules apply to the step-by-step 
function from
    Im[i] = product_im
 
 Only scalar arithmetic remains. To apply the rules, the pass tests whether a
-call's ``op`` is one of the four operators. The Rust pass looks them up by 
name,
-as C++ does with ``Op::Get``. The operators are registered when the library
-loads, and ``Op::get`` returns the registry's single object for a name.
+call's ``op`` is one of the four operators. The Rust pass looks them up by 
name.
+The operators are registered when the library loads, and ``Op::get`` returns
+the registry's single object for a name.
 ``call.op.same_as(&Op::get("my_ext.cmul")?)`` therefore tests whether a call's
 ``op`` is ``my_ext.cmul`` by comparing pointers. The Python pass compares with
 the operators that ``Op.register`` returned, as in ``call.op.same_as(CMUL)``.
diff --git a/docs/ir_guides/working_with_ir_in_rust.rst 
b/docs/ir_guides/working_with_ir_in_rust.rst
index 39f48ee37..32dc4ed3c 100644
--- a/docs/ir_guides/working_with_ir_in_rust.rst
+++ b/docs/ir_guides/working_with_ir_in_rust.rst
@@ -143,7 +143,7 @@ The output is:
 directly from ``x``, and the literals ``2`` and ``1`` become constants of
 its type.
 
-As in C++, the operators take a ``PrimExpr``, an expression whose type is a
+The operators take a ``PrimExpr``, an expression whose type is a
 ``PrimType``. ``x`` is a ``PrimVar``, a variable of a primitive type, because a
 plain ``Var`` may have any type: it takes part in arithmetic only after
 ``downcast_ref::<PrimVar>()`` or ``downcast::<PrimVar>()`` checks it, and 
``&var + 1``
@@ -215,8 +215,8 @@ This is the newtype pattern. Simplified from the crate, the 
pair is:
 Rust has no inheritance; instead, ``#[repr(C)]`` puts the parent's object,
 ``ExprObj``, first in ``AddObj``, so every addition is also an expression.
 ``Add`` implements ``Deref`` to ``AddObj``, which is why ``add.a`` works.
-``AddObj`` has the same memory layout as its C++ counterpart, so
-``add.a`` is a direct field read, as efficient as in C++.
+``AddObj`` has the same memory layout as the native object, so
+``add.a`` is a direct field read.
 
 **Nodes are immutable.** As with data behind an ``Arc``, a managed
 reference gives read access only; to change a node, we build a new one.
@@ -791,7 +791,7 @@ Quick Reference
      - Receives the definition region kind
    * - |structural_equal|_\ ``(&a, &b)``
      - Compares by structure
-   * - |dispatch|_\ ``(prefix, (Carrier, ...), ResultType)``
+   * - |dispatch|_\ ``(prefix, (First, Args...), Ret)``
      - Generates ``dispatch``, a ``match_any!`` over the ``prefix_*``
        methods of a struct
    * - |structural_visit|_, |structural_mutate|_
@@ -848,8 +848,6 @@ Quick Reference
 .. _structural_walk_with_constraint_context: 
../reference/rust/generated/tvm_tirx/tirx/structural_walk_with_constraint_context/fn.structural_walk_with_constraint_context.html
 .. |structural_mutate_with_constraint_context| replace:: 
``structural_mutate_with_constraint_context``
 .. _structural_mutate_with_constraint_context: 
../reference/rust/generated/tvm_tirx/tirx/structural_mutate_with_constraint_context/fn.structural_mutate_with_constraint_context.html
-.. |StructuralVisitorWithConstraintContextView| replace:: 
``StructuralVisitorWithConstraintContextView``
-.. _StructuralVisitorWithConstraintContextView: 
../reference/rust/generated/tvm_tirx/tirx/structural_visit_with_constraint_context/type.StructuralVisitorWithConstraintContextView.html
 .. |match_any!| replace:: ``match_any!``
 .. _match_any!: ../reference/rust/generated/tvm_ffi/macro.match_any.html
 .. |PrimVar::new| replace:: ``PrimVar::new``
diff --git a/rust/src/backend.rs b/rust/src/backend.rs
index 05299c460..0a2159aea 100644
--- a/rust/src/backend.rs
+++ b/rust/src/backend.rs
@@ -17,6 +17,7 @@
  * under the License.
  */
 
-//! Backend-specific objects, such as the attributes of CUDA instructions in 
[`cuda`].
+//! Backends. Each submodule holds the objects of one backend, such as the 
attributes of the CUDA
+//! instructions in [`cuda`].
 
 pub mod cuda;
diff --git a/rust/src/backend/cuda.rs b/rust/src/backend/cuda.rs
index 48263277b..ee3ef18e0 100644
--- a/rust/src/backend/cuda.rs
+++ b/rust/src/backend/cuda.rs
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-//! The CUDA backend: the objects of the `tirx.cuda` namespace.
+//! The CUDA backend. [`op`] holds the static attributes its instructions 
carry.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // An object registered under `tirx.cuda` that no file has a block for yet is 
generated here, to be
diff --git a/rust/src/backend/cuda/op.rs b/rust/src/backend/cuda/op.rs
index 92d010a37..7c329abb8 100644
--- a/rust/src/backend/cuda/op.rs
+++ b/rust/src/backend/cuda/op.rs
@@ -21,10 +21,9 @@
 // The generated constructors take one argument per reflected field.
 #![allow(clippy::too_many_arguments)]
 
-//! The attributes of CUDA instructions.
-//!
-//! [`TCGen05InstrDescriptorAttrs`] and 
[`TCGen05InstrDescriptorBlockScaledAttrs`] describe a
-//! `tcgen05` instruction descriptor, and [`TensorMapEncodeTiledAttr`] a tiled 
tensor-map encoding.
+//! Attributes of CUDA instructions. [`TCGen05InstrDescriptorAttrs`] and
+//! [`TCGen05InstrDescriptorBlockScaledAttrs`] are the static options of a 
`tcgen05` instruction
+//! descriptor, and [`TensorMapEncodeTiledAttr`] those of a tiled tensor-map 
encoding.
 
 // tvm-ffi-stubgen(missing-docs): allow
 
@@ -37,6 +36,10 @@ use tvm_ffi::ObjectArc;
 use tvm_ffi::String;
 // tvm-ffi-stubgen(end)
 
+/// The static options of a dense `tcgen05` instruction descriptor, which
+/// `tirx.cuda.tcgen05_encode_instr_descriptor` encodes: the data types of the 
result `d_dtype` and
+/// the operands `a_dtype` and `b_dtype`, the shape `M` x `N` x `K`, the 
operand transposes, the
+/// number of CTA groups, operand negation, result saturation `sat_d` and 
sparsity.
 // tvm-ffi-stubgen(begin): object/tirx.cuda.TCGen05InstrDescriptorAttrs
 #[allow(missing_docs)]
 #[repr(C)]
@@ -164,6 +167,10 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
TCGen05InstrDescriptorAttrsObj
 tvm_ffi::unsafe_impl_object_ref_upcast!(TCGen05InstrDescriptorAttrs => Attrs);
 // tvm-ffi-stubgen(end)
 
+/// The static options of a block-scaled `tcgen05` instruction descriptor, 
which
+/// `tirx.cuda.tcgen05_encode_instr_descriptor_block_scaled` encodes: those of
+/// [`TCGen05InstrDescriptorAttrs`] but saturation, and the data types of the 
scale factors
+/// `sfa_dtype` and `sfb_dtype`.
 // tvm-ffi-stubgen(begin): 
object/tirx.cuda.TCGen05InstrDescriptorBlockScaledAttrs
 #[allow(missing_docs)]
 #[repr(C)]
@@ -296,6 +303,9 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
TCGen05InstrDescriptorBlockScal
 tvm_ffi::unsafe_impl_object_ref_upcast!(TCGen05InstrDescriptorBlockScaledAttrs 
=> Attrs);
 // tvm-ffi-stubgen(end)
 
+/// The fixed options of a `tirx.cuda.tensormap_encode_tiled` call, which 
encodes a tiled tensor map
+/// at run time: the `descriptor_dtype` and `rank` of the tensor, and its 
interleave, swizzle, L2
+/// promotion, out-of-bounds fill and data-type options.
 // tvm-ffi-stubgen(begin): object/tirx.cuda.TensorMapEncodeTiledAttr
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/codegen.rs b/rust/src/codegen.rs
index 102166cc3..ac57ae3f0 100644
--- a/rust/src/codegen.rs
+++ b/rust/src/codegen.rs
@@ -17,9 +17,8 @@
  * under the License.
  */
 
-//! Code generation: IR modules compiled into runtime modules.
-//!
-//! The queries of the LLVM backend are in [`llvm`].
+//! Code generation. [`build_module`] compiles an [`IRModule`] into a runtime 
[`Module`] with the
+//! code generator of the target's kind, and [`llvm`] queries the LLVM backend.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
diff --git a/rust/src/codegen/llvm.rs b/rust/src/codegen/llvm.rs
index 226552290..6b41fc5a9 100644
--- a/rust/src/codegen/llvm.rs
+++ b/rust/src/codegen/llvm.rs
@@ -17,26 +17,9 @@
  * under the License.
  */
 
-//! The LLVM backend: intrinsics, the host CPU and the CPUs and features of 
LLVM targets.
-//!
-//! The queries are registered by `libtvm_tirx_llvm_backend` when it is 
loaded, as `import tvm_tirx`
-//! loads it. A query given no target uses the current one.
-//!
-//! # Examples
-//!
-//! ```
-//! use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
-//! use tvm_tirx::codegen::llvm;
-//! use tvm_tirx::target::Target;
-//! use tvm_tirx::tvm_ffi::Module;
-//!
-//! let backend = format!("{DLL_PREFIX}tvm_tirx_llvm_backend{DLL_SUFFIX}");
-//! let _backend = Module::load_from_file(backend)?;
-//! let cpu = llvm::get_system_cpu()?;
-//! let host = Target::new(&format!(r#"{{"kind": "llvm", "mcpu": "{}"}}"#, 
cpu.as_str()))?;
-//! assert!(llvm::get_vector_width(Some(&host))? > 0);
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! The LLVM backend. Queries of LLVM intrinsics, the host CPU and the CPUs 
and features of LLVM
+//! targets, which `libtvm_tirx_llvm_backend` registers when it is loaded; a 
query given no target
+//! uses the current one.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -124,6 +107,22 @@ pub fn cpu_has_feature(feature: &str, target: 
Option<&Target>) -> Result<bool> {
 
 /// Return the vector width of the CPU of `target` in bits, or -1 for a target 
that is not `llvm`
 /// (`codegen.llvm_get_vector_width`).
+///
+/// # Examples
+///
+/// ```
+/// use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
+/// use tvm_tirx::codegen::llvm;
+/// use tvm_tirx::target::Target;
+/// use tvm_tirx::tvm_ffi::Module;
+///
+/// let backend = format!("{DLL_PREFIX}tvm_tirx_llvm_backend{DLL_SUFFIX}");
+/// let _backend = Module::load_from_file(backend)?;
+/// let cpu = llvm::get_system_cpu()?;
+/// let host = Target::new(&format!(r#"{{"kind": "llvm", "mcpu": "{}"}}"#, 
cpu.as_str()))?;
+/// assert!(llvm::get_vector_width(Some(&host))? > 0);
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 pub fn get_vector_width(target: Option<&Target>) -> Result<i32> {
     tvm_ffi::Function::get_global("codegen.llvm_get_vector_width")?
         .call_tuple((target.cloned(),))?
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 27ac9cf21..4fc2cf726 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -20,17 +20,17 @@
 //! The TIRx compiler in Rust: its IR objects over TVM FFI, and the definition 
of new types,
 //! operators and passes that extend it.
 //!
-//! The IR bindings are generated by `tvm-ffi-stubgen` from the reflection 
metadata of
-//! `libtvm_tirx_compiler`, and the modules follow the Python package 
`tvm_tirx`:
+//! The modules:
 //!
-//! - [`ir`]: the shared IR, with the primitive expressions in [`ir::prim`] 
and the passes in
-//!   [`ir::transform`].
-//! - [`tirx`]: tensor functions and types, layouts, traversals, tile 
instructions and passes.
-//! - [`sym`]: symbolic analysis.
+//! - [`ir`]: the IR shared by every dialect, with the primitive expressions 
in [`ir::prim`] and
+//!   the pass infrastructure in [`ir::transform`].
+//! - [`tirx`]: tensor programs: functions, tensor types, layouts, tile 
instructions, passes and
+//!   analyses.
+//! - [`sym`]: symbolic integer analysis.
 //! - [`target`]: compilation targets.
-//! - [`backend`]: backend-specific objects, such as the attributes of CUDA 
instructions.
-//! - [`runtime`]: runtime objects, tensor allocation and runtime queries.
-//! - [`codegen`]: compilation of IR modules into runtime modules.
+//! - [`backend`]: backend objects, such as the attributes of CUDA 
instructions.
+//! - [`runtime`]: runtime objects and queries, and tensor allocation.
+//! - [`codegen`]: compiling IR modules into runtime modules.
 //! - [`script`]: printing IR objects as TVMScript.
 //!
 //! # Using the bindings
@@ -55,9 +55,8 @@
 //! ```
 //!
 //! A handle such as [`ir::Expr`] is a reference-counted pointer to its 
object, [`ir::ExprObj`], so
-//! cloning it does not copy the IR. The constructors (`new`) follow the C++ 
ones, with their checks
-//! and defaults, and return a `Result` when the C++ one can throw; 
`from_fields` stores the fields
-//! as given.
+//! cloning it does not copy the IR. A constructor `new` checks its arguments 
and fills in defaults,
+//! and returns a `Result` when a check can fail; `from_fields` stores the 
fields as given.
 //!
 //! # Defining operators and types
 //!
@@ -99,9 +98,8 @@
 //! # Ok::<(), tvm_ffi::Error>(())
 //! ```
 //!
-//! A type extends [`ir::TypeObj`]; its fields are reflected, so C++, Python 
and the TVMScript
-//! printer see them. The generated code names `tvm_ffi`, which `use 
tvm_tirx::tvm_ffi` brings into
-//! scope.
+//! A type extends [`ir::TypeObj`]; its fields are reflected, so Python and 
the TVMScript printer
+//! see them. The generated code names `tvm_ffi`, which `use 
tvm_tirx::tvm_ffi` brings into scope.
 //!
 //! ```
 //! use tvm_tirx::ir::{PrimType, Type, TypeObj, UNKNOWN_LOC};
@@ -138,7 +136,7 @@
 //! # The library Python loads
 //!
 //! The crate is also built as `libtvm_tirx_rust`, which `import tvm_tirx` 
loads, so Python uses the
-//! operators and types the crate defines as it uses the C++ ones. A `cdylib` 
of operators of its
+//! operators and types the crate defines as it uses the native ones. A 
`cdylib` of operators of its
 //! own, such as `examples/define_ir.rs`, registers them when Python loads it; 
a crate it depends on
 //! but never names is not linked in, so name it, as `use my_ops as _;` does.
 //!
diff --git a/rust/src/macro/def_expr_op_overloads.rs 
b/rust/src/macro/def_expr_op_overloads.rs
index c8637709b..d3ea12b9c 100644
--- a/rust/src/macro/def_expr_op_overloads.rs
+++ b/rust/src/macro/def_expr_op_overloads.rs
@@ -23,9 +23,9 @@
 /// The type, a module-scope path to a reference wrapper implementing `Clone` 
and
 /// `Into<PrimExpr>`, gets the operators `+`, `-`, `*`, `&`, `|`, `^`, `<<`, 
`>>`, unary `-` and `!`
 /// and [`PrimOps`](crate::ir::prim::PrimOps), all returning an
-/// [`ExprResult`](crate::ir::prim::ExprResult). As the C++ operators take a 
`PrimExpr`, the type
-/// is one: an upcast to [`PrimExpr`](crate::ir::PrimExpr) declares that every 
value of it is typed
-/// by a `PrimType`.
+/// [`ExprResult`](crate::ir::prim::ExprResult). The operators take
+/// [`PrimExpr`](crate::ir::PrimExpr) operands, so the type must convert into 
one: its upcast to
+/// `PrimExpr` declares that every value of it is typed by a `PrimType`.
 ///
 /// # Examples
 ///
diff --git a/rust/src/runtime.rs b/rust/src/runtime.rs
index 64990ffb1..68f3b78e5 100644
--- a/rust/src/runtime.rs
+++ b/rust/src/runtime.rs
@@ -17,25 +17,8 @@
  * under the License.
  */
 
-//! The runtime objects the IR refers to, tensor allocation and runtime 
queries.
-//!
-//! A [`MemoryScope`] is the storage scope of a 
[`PtrType`](crate::ir::PtrType).
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::dtype;
-//! use tvm_tirx::runtime;
-//! use tvm_tirx::tvm_ffi::{DLDevice, DLDeviceType};
-//!
-//! let cpu = DLDevice::new(DLDeviceType::kDLCPU, 0);
-//! let buffer = runtime::empty(&[2, 8], dtype::float(32), cpu, None)?;
-//!
-//! let bytes = runtime::create_view(&buffer, &[64], dtype::int(8), 0)?;
-//! assert_eq!(bytes.shape(), &[64]);
-//!
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Runtime objects and queries. A [`MemoryScope`] names the memory a pointer 
or tensor lives in;
+//! the functions allocate tensors and query devices and the runtime.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // No `prefix`: only the objects with a block below are bound, not all of 
`runtime`.
@@ -71,6 +54,10 @@ struct FfiEnumObj {
 // tvm-ffi-stubgen(end)
 
 // `ir.PtrType.storage_scope` refers to this type.
+/// A registered memory space, such as `global` or `shared`, that a pointer or 
tensor lives in.
+///
+/// Registration order has no meaning, and a backend must support a scope 
before it can allocate
+/// storage in it; [`MemoryScope::register`] adds one.
 // tvm-ffi-stubgen(begin): object/runtime.MemoryScope
 #[allow(missing_docs)]
 #[repr(C)]
@@ -114,6 +101,22 @@ impl MemoryScope {
 ///
 /// Returns an error if the allocator of `device` does not support 
`mem_scope`; the CPU one supports
 /// only the global scope.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::dtype;
+/// use tvm_tirx::runtime;
+/// use tvm_tirx::tvm_ffi::{DLDevice, DLDeviceType};
+///
+/// let cpu = DLDevice::new(DLDeviceType::kDLCPU, 0);
+/// let buffer = runtime::empty(&[2, 8], dtype::float(32), cpu, None)?;
+///
+/// let bytes = runtime::create_view(&buffer, &[64], dtype::int(8), 0)?;
+/// assert_eq!(bytes.shape(), &[64]);
+///
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 pub fn empty(
     shape: &[i64],
     dtype: DLDataType,
diff --git a/rust/src/script.rs b/rust/src/script.rs
index 7a9e6046f..241703f28 100644
--- a/rust/src/script.rs
+++ b/rust/src/script.rs
@@ -17,28 +17,8 @@
  * under the License.
  */
 
-//! TVMScript: IR objects printed as the Python script that builds them.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{
-//!     DictAttrs, Expr, PrimExpr, PrimType, PrimVar, Return, SeqStmt, Var, 
UNKNOWN_LOC,
-//! };
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::Function;
-//! use tvm_tirx::tvm_ffi::Map;
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! assert_eq!(script(&PrimExpr::try_from(&x * 2 + 1)?)?, "x * 2 + 1");
-//!
-//! let body = SeqStmt::new([Return::new(Expr::try_from(&x + 1)?, 
UNKNOWN_LOC)], UNKNOWN_LOC);
-//! let attrs = DictAttrs::new(Map::new());
-//! let func = Function::new([&x], &body, PrimType::int(32), attrs, 
UNKNOWN_LOC);
-//! let text = script(&func)?;
-//! assert!(text.contains("def main(x: T.int32) -> T.int32:\n    return x + 
1"));
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! TVMScript printing. [`script`] prints an IR object as TVMScript, the 
Python-syntax text form
+//! of the IR.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -59,6 +39,27 @@ use tvm_ffi::String;
 
 /// Print the IR object `obj` as TVMScript, with the default printer config
 /// (`script.printer.Script`).
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{
+///     DictAttrs, Expr, PrimExpr, PrimType, PrimVar, Return, SeqStmt, Var, 
UNKNOWN_LOC,
+/// };
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::tirx::Function;
+/// use tvm_tirx::tvm_ffi::Map;
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// assert_eq!(script(&PrimExpr::try_from(&x * 2 + 1)?)?, "x * 2 + 1");
+///
+/// let body = SeqStmt::new([Return::new(Expr::try_from(&x + 1)?, 
UNKNOWN_LOC)], UNKNOWN_LOC);
+/// let attrs = DictAttrs::new(Map::new());
+/// let func = Function::new([&x], &body, PrimType::int(32), attrs, 
UNKNOWN_LOC);
+/// let text = script(&func)?;
+/// assert!(text.contains("def main(x: T.int32) -> T.int32:\n    return x + 
1"));
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 pub fn script<T: ObjectRefCore + AnyCompatible>(obj: &T) -> Result<String> {
     tvm_ffi::cached_global_func!("script.printer.Script")
         .call_packed(&[AnyView::from(obj), AnyView::new()])?
diff --git a/rust/src/sym.rs b/rust/src/sym.rs
index 64a7a29f2..0931f604f 100644
--- a/rust/src/sym.rs
+++ b/rust/src/sym.rs
@@ -17,10 +17,9 @@
  * under the License.
  */
 
-//! Symbolic analysis of integer expressions.
-//!
-//! An [`Analyzer`] proves and simplifies under the facts of a 
[`ConstraintContext`]; [`int_set`]
-//! bounds expressions, and [`iter_affine_map`] detects iterator maps.
+//! Symbolic integer analysis. An [`Analyzer`] proves, bounds and simplifies 
integer expressions
+//! under the facts of a [`ConstraintContext`]; [`int_set`] and 
[`iter_affine_map`] describe the
+//! values and the iterator structure of index expressions.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // An object registered under `sym` that no file has a block for yet is 
generated here, to be moved
diff --git a/rust/src/sym/analyzer.rs b/rust/src/sym/analyzer.rs
index 3a9df9b73..e36154674 100644
--- a/rust/src/sym/analyzer.rs
+++ b/rust/src/sym/analyzer.rs
@@ -17,31 +17,9 @@
  * under the License.
  */
 
-//! The analyzer and its results.
-//!
-//! An [`Analyzer`] proves and simplifies expressions under the facts it 
knows. [`ConstIntBound`]
-//! and [`ModularSet`] are its bound and modular results.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::prim::op::floordiv;
-//! use tvm_tirx::ir::{PrimType, PrimVar, Range, Var, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::sym::{Analyzer, ConstraintContext, RangeConstraint};
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! let context = ConstraintContext::new();
-//! let domain = Range::from_min_extent(0, 16, UNKNOWN_LOC)?;
-//! context.append(&RangeConstraint::new(x.clone(), domain).into())?;
-//! let analyzer = Analyzer::new();
-//! analyzer.sync_constraint_context(&context)?;
-//!
-//! assert_eq!(script(&analyzer.simplify(floordiv(&x, 16)?, 2)?)?, "0");
-//!
-//! assert_eq!(script(&Analyzer::new().simplify(floordiv(&x, 16)?, 2)?)?, "x 
// 16");
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! The analyzer. An [`Analyzer`] proves, bounds and simplifies integer 
expressions under the facts
+//! of a [`ConstraintContext`]; its bounds are [`ConstIntBound`]s and 
[`ModularSet`]s, and
+//! [`SplitExpr`] and [`SumExpr`] are the forms its canonical simplifier works 
in.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Python has no class for `CanonicalExpr`, `SumExpr` and `SplitExpr`, the 
forms of the canonical
@@ -94,6 +72,34 @@ use tvm_ffi::VALUE_ERROR;
 // 
---------------------------------------------------------------------------------------------
 // The analyzer
 
+/// An analyzer of integer expressions: it proves, bounds and simplifies them 
under the facts it
+/// knows, which 
[`sync_constraint_context`](Analyzer::sync_constraint_context) sets from a
+/// [`ConstraintContext`].
+///
+/// A clone shares the analyzer and its facts; 
[`deep_clone`](Analyzer::deep_clone) copies them.
+/// When the build includes the Z3 prover, default-strength proofs fall back 
to it unless
+/// [`set_z3_default_fallback`](Analyzer::set_z3_default_fallback) turns that 
off.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::prim::op::floordiv;
+/// use tvm_tirx::ir::{PrimType, PrimVar, Range, Var, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::sym::{Analyzer, ConstraintContext, RangeConstraint};
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// let context = ConstraintContext::new();
+/// let domain = Range::from_min_extent(0, 16, UNKNOWN_LOC)?;
+/// context.append(&RangeConstraint::new(x.clone(), domain).into())?;
+/// let analyzer = Analyzer::new();
+/// analyzer.sync_constraint_context(&context)?;
+///
+/// assert_eq!(script(&analyzer.simplify(floordiv(&x, 16)?, 2)?)?, "0");
+///
+/// assert_eq!(script(&Analyzer::new().simplify(floordiv(&x, 16)?, 2)?)?, "x 
// 16");
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/sym.Analyzer
 #[allow(missing_docs)]
 #[repr(C)]
@@ -313,7 +319,7 @@ impl Analyzer {
     }
 }
 
-/// How hard [`Analyzer::can_prove`] tries, the C++ `sym::ProofStrength`.
+/// How hard [`Analyzer::can_prove`] tries.
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
 #[repr(i32)]
 pub enum ProofStrength {
@@ -324,7 +330,7 @@ pub enum ProofStrength {
     SymbolicBound = 1,
 }
 
-/// The result of [`Analyzer::try_compare`], the C++ `sym::CompareResult`.
+/// The result of [`Analyzer::try_compare`].
 ///
 /// The relations other than `Inconsistent` and `Unknown` say what is known of 
`lhs` against `rhs`.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -395,6 +401,8 @@ pub fn allow_uint_as_index() -> 
Result<AllowUintAsIndexScope> {
 // 
---------------------------------------------------------------------------------------------
 // Results of the analyses
 
+/// The inclusive constant bounds `[min_value, max_value]` of an integer 
expression, where
+/// `i64::MAX` and `-i64::MAX` stand for unbounded ends.
 // tvm-ffi-stubgen(begin): object/sym.ConstIntBound
 #[allow(missing_docs)]
 #[repr(C)]
@@ -442,6 +450,8 @@ impl ConstIntBound {
 unsafe impl tvm_ffi::object::ImmutableObject for ConstIntBoundObj {}
 // tvm-ffi-stubgen(end)
 
+/// The congruence class `{coeff * x + base}` over the integers `x` that an 
integer expression lies
+/// in: a `coeff` of 0 is the single value `base`, and a `coeff` of 1 any 
integer.
 // tvm-ffi-stubgen(begin): object/sym.ModularSet
 #[allow(missing_docs)]
 #[repr(C)]
@@ -489,6 +499,9 @@ impl ModularSet {
 unsafe impl tvm_ffi::object::ImmutableObject for ModularSetObj {}
 // tvm-ffi-stubgen(end)
 
+/// The counters of an analyzer's rewrite simplifier: the nodes it visited, 
the constraints it
+/// entered, the rewrites it attempted and performed, its deepest recursion 
and its recursive
+/// rewrites.
 // tvm-ffi-stubgen(begin): object/sym.RewriteSimplifierStats
 #[allow(missing_docs)]
 #[repr(C)]
@@ -572,6 +585,8 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
RewriteSimplifierStatsObj {}
 // 
---------------------------------------------------------------------------------------------
 // The forms of the canonical simplifier
 
+/// The base of the intermediate forms the canonical simplifier introduces, 
[`SplitExpr`] and
+/// [`SumExpr`].
 // tvm-ffi-stubgen(begin): object/sym.CanonicalExpr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -601,6 +616,7 @@ impl Deref for CanonicalExprObj {
 tvm_ffi::unsafe_impl_object_ref_upcast!(CanonicalExpr => Expr);
 // tvm-ffi-stubgen(end)
 
+/// The canonical form of a split index: `((index % upper_factor) / 
lower_factor) * scale`.
 // tvm-ffi-stubgen(begin): object/sym.SplitExpr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -630,6 +646,7 @@ impl Deref for SplitExprObj {
 tvm_ffi::unsafe_impl_object_ref_upcast!(SplitExpr => Expr, SplitExpr => 
CanonicalExpr, SplitExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
+/// The canonical form of a sum: a sum of [`SplitExpr`]s plus a constant base.
 // tvm-ffi-stubgen(begin): object/sym.SumExpr
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/sym/constraint.rs b/rust/src/sym/constraint.rs
index a41d2d408..4bcff25dd 100644
--- a/rust/src/sym/constraint.rs
+++ b/rust/src/sym/constraint.rs
@@ -19,32 +19,9 @@
 // The generated `new` of a field-less base needs no `Default`.
 #![allow(clippy::new_without_default)]
 
-//! Constraints: the facts in scope at a point of a traversal.
-//!
-//! A [`ConstraintContext`] holds [`BindingConstraint`]s, [`RangeConstraint`]s 
and
-//! [`PredicateConstraint`]s, ancestors' first. Its handles are neither `Send` 
nor `Sync`; do not
-//! share a context across threads, also not through an erased `ObjectRef`.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::prim::op::{ge, lt};
-//! use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
-//! use tvm_tirx::sym::{Analyzer, ConstraintContext, PredicateConstraint, 
ProofStrength};
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! let outer = ConstraintContext::new();
-//! outer.append(&PredicateConstraint::new(ge(&x, 0)?, false).into())?;
-//! let inner = outer.snapshot()?;
-//! inner.append(&PredicateConstraint::new(lt(&x, 16)?, false).into())?;
-//!
-//! let analyzer = Analyzer::new();
-//! analyzer.sync_constraint_context(&inner)?;
-//! assert!(analyzer.can_prove(lt(&x, 16)?, ProofStrength::Default)?);
-//! analyzer.sync_constraint_context(&outer)?;
-//! assert!(!analyzer.can_prove(lt(&x, 16)?, ProofStrength::Default)?);
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Constraints. A [`Constraint`] is one fact in scope, a 
[`BindingConstraint`],
+//! [`RangeConstraint`] or [`PredicateConstraint`]; a [`ConstraintContext`] 
holds the facts in
+//! scope at a point of a traversal, which an [`Analyzer`](super::Analyzer) 
proves under.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // tvm-ffi-stubgen(ty-map): sym.ConstraintContext -> 
crate::sym::constraint::ConstraintContext
@@ -68,6 +45,7 @@ use tvm_ffi::ShallowCopy;
 use tvm_ffi::UnsafeInit;
 // tvm-ffi-stubgen(end)
 
+/// The base of every fact a [`ConstraintContext`] holds; it owns no analyzer 
state.
 // tvm-ffi-stubgen(begin): object/sym.Constraint
 #[allow(missing_docs)]
 #[repr(C)]
@@ -126,6 +104,8 @@ unsafe impl UnsafeInit for ConstraintObj {
 unsafe impl tvm_ffi::object::ImmutableObject for ConstraintObj {}
 // tvm-ffi-stubgen(end)
 
+/// The binding of `var` to `value`. The value is the bound expression as 
written, memory reads
+/// included: it records what `var` was bound to, not an equality that holds 
afterwards.
 // tvm-ffi-stubgen(begin): object/sym.BindingConstraint
 #[allow(missing_docs)]
 #[repr(C)]
@@ -182,6 +162,8 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
BindingConstraintObj {}
 tvm_ffi::unsafe_impl_object_ref_upcast!(BindingConstraint => Constraint);
 // tvm-ffi-stubgen(end)
 
+/// The fact that the boolean `condition` holds. With `assume_always`, the 
caller guarantees it
+/// holds throughout the scope, even after the values it reads change.
 // tvm-ffi-stubgen(begin): object/sym.PredicateConstraint
 #[allow(missing_docs)]
 #[repr(C)]
@@ -238,6 +220,8 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
PredicateConstraintObj {}
 tvm_ffi::unsafe_impl_object_ref_upcast!(PredicateConstraint => Constraint);
 // tvm-ffi-stubgen(end)
 
+/// The fact that `var` lies in the half-open `range`, which may be wider than 
the values `var`
+/// takes.
 // tvm-ffi-stubgen(begin): object/sym.RangeConstraint
 #[allow(missing_docs)]
 #[repr(C)]
@@ -323,7 +307,29 @@ const _: () = {
 /// A context of constraints: the premises in effect at a point of a 
traversal, ancestors' first.
 ///
 /// Clones share the context's identity and later appends; use
-/// [`snapshot`](ConstraintContextObj::snapshot) to capture the current prefix.
+/// [`snapshot`](ConstraintContextObj::snapshot) to capture the current 
prefix. A context is neither
+/// `Send` nor `Sync`: do not share one across threads, also not through an 
erased `ObjectRef`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::prim::op::{ge, lt};
+/// use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
+/// use tvm_tirx::sym::{Analyzer, ConstraintContext, PredicateConstraint, 
ProofStrength};
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// let outer = ConstraintContext::new();
+/// outer.append(&PredicateConstraint::new(ge(&x, 0)?, false).into())?;
+/// let inner = outer.snapshot()?;
+/// inner.append(&PredicateConstraint::new(lt(&x, 16)?, false).into())?;
+///
+/// let analyzer = Analyzer::new();
+/// analyzer.sync_constraint_context(&inner)?;
+/// assert!(analyzer.can_prove(lt(&x, 16)?, ProofStrength::Default)?);
+/// analyzer.sync_constraint_context(&outer)?;
+/// assert!(!analyzer.can_prove(lt(&x, 16)?, ProofStrength::Default)?);
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 #[repr(C)]
 #[derive(tvm_ffi::derive::ObjectRef, Clone)]
 pub struct ConstraintContext {
@@ -393,8 +399,7 @@ impl ConstraintContextObj {
 }
 
 impl ConstraintContext {
-    /// Create an empty root context, with the complete native-compatible 
layout (C++
-    /// `ConstraintContext()`).
+    /// Create an empty root context, with the complete native-compatible 
layout.
     pub fn new() -> Self {
         Self::allocate(None, 0)
     }
diff --git a/rust/src/sym/int_set.rs b/rust/src/sym/int_set.rs
index 52dc5a9a5..014296323 100644
--- a/rust/src/sym/int_set.rs
+++ b/rust/src/sym/int_set.rs
@@ -17,28 +17,9 @@
  * under the License.
  */
 
-//! Sets of integers: [`IntSet`] and the interval [`IntervalSet`], with bound 
deduction and region
-//! bound estimation.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::prim::op::PrimOps;
-//! use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::sym::{self, Analyzer, IntSet, IntervalSet};
-//! use tvm_tirx::tvm_ffi::Map;
-//!
-//! let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
-//! let dom_map = Map::from_iter([(Var::from(&i), 
IntSet::from(IntervalSet::new(0, 15)))]);
-//! let set = Analyzer::new().int_set(&i * 2 + 1, Some(&dom_map))?;
-//! assert_eq!(script(&set)?, "sym.IntervalSet(min_value=1, max_value=31)");
-//!
-//! let none = Map::<Var, IntSet>::new();
-//! let bound = sym::deduce_bound(&i, (&i * 4 + 3).less(64), &none, &none)?;
-//! assert_eq!(script(&bound)?, "sym.IntervalSet(min_value=neg_inf, 
max_value=15)");
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Integer sets. An [`IntSet`], such as the interval [`IntervalSet`], bounds 
the values an
+//! expression takes; [`deduce_bound`] solves a condition for a variable, and 
the
+//! `estimate_region_*` functions bound the region a set of accesses covers.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -73,6 +54,28 @@ use tvm_ffi::ObjectCore;
 use tvm_ffi::Result;
 // tvm-ffi-stubgen(end)
 
+/// The base of every set of integers, one dimension each, that bounds the 
values an expression
+/// takes; [`Analyzer::int_set`] computes one.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::prim::op::PrimOps;
+/// use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::sym::{self, Analyzer, IntSet, IntervalSet};
+/// use tvm_tirx::tvm_ffi::Map;
+///
+/// let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
+/// let dom_map = Map::from_iter([(Var::from(&i), 
IntSet::from(IntervalSet::new(0, 15)))]);
+/// let set = Analyzer::new().int_set(&i * 2 + 1, Some(&dom_map))?;
+/// assert_eq!(script(&set)?, "sym.IntervalSet(min_value=1, max_value=31)");
+///
+/// let none = Map::<Var, IntSet>::new();
+/// let bound = sym::deduce_bound(&i, (&i * 4 + 3).less(64), &none, &none)?;
+/// assert_eq!(script(&bound)?, "sym.IntervalSet(min_value=neg_inf, 
max_value=15)");
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/sym.IntSet
 #[allow(missing_docs)]
 #[repr(C)]
@@ -120,6 +123,8 @@ impl IntSet {
     }
 }
 
+/// The interval `[min_value, max_value]` of integers, whose bounds are 
symbolic expressions and
+/// may be [`neg_inf`] or [`pos_inf`].
 // tvm-ffi-stubgen(begin): object/sym.IntervalSet
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/sym/iter_affine_map.rs b/rust/src/sym/iter_affine_map.rs
index 891af39b1..278140833 100644
--- a/rust/src/sym/iter_affine_map.rs
+++ b/rust/src/sym/iter_affine_map.rs
@@ -17,34 +17,9 @@
  * under the License.
  */
 
-//! Iterator maps: index expressions as sums of splits of iterators.
-//!
-//! An [`IterSumExpr`] is a sum of [`IterSplitExpr`]s `(source // 
lower_factor) % extent * scale`
-//! plus `base_`. [`detect_iter_map`] detects them, and its [`IterMapResult`] 
reports `errors`.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{PrimType, PrimVar, Range, Var, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::sym::{self, IterMapLevel};
-//! use tvm_tirx::tvm_ffi::Map;
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! let y = PrimVar::new("y", PrimType::int(32), UNKNOWN_LOC);
-//! let four = Range::from_min_extent(0, 4, UNKNOWN_LOC)?;
-//! let iters = Map::from_iter([(x.clone(), four.clone()), (y.clone(), four)]);
-//!
-//! let bijective = IterMapLevel::Bijective;
-//! let map = sym::detect_iter_map([&x * 4 + &y], &iters, true, bijective, 
None, true)?;
-//! assert!(map.errors.is_empty());
-//!
-//! let o = PrimVar::new("o", PrimType::int(32), UNKNOWN_LOC);
-//! let inverse = sym::inverse_affine_iter_map(map.indices.iter(), [&o])?;
-//! assert_eq!(script(&inverse.get(&Var::from(&x))?.unwrap())?, "o // 4");
-//! assert_eq!(script(&inverse.get(&Var::from(&y))?.unwrap())?, "o % 4");
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Iterator maps. An iterator map writes index expressions as 
[`IterSumExpr`]s, sums of
+//! [`IterSplitExpr`]s of [`IterMark`]ed iterators; [`detect_iter_map`] finds 
one, and the other
+//! functions simplify, divide and invert such maps.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -95,6 +70,8 @@ use tvm_ffi::Result;
 use tvm_ffi::String;
 // tvm-ffi-stubgen(end)
 
+/// The base of the iterator map forms, [`IterSplitExpr`] and [`IterSumExpr`]. 
They hold the result
+/// of detection and do not appear in a well-formed function.
 // tvm-ffi-stubgen(begin): object/sym.IterMapExpr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -124,6 +101,36 @@ impl Deref for IterMapExprObj {
 tvm_ffi::unsafe_impl_object_ref_upcast!(IterMapExpr => Expr, IterMapExpr => 
PrimExpr);
 // tvm-ffi-stubgen(end)
 
+/// The result of [`detect_iter_map`]: the detected `indices`, or empty 
`indices` and the `errors`
+/// that say why detection failed.
+///
+/// `padding_predicate`, over the input iterators, holds for the indices that 
the returned
+/// iterators cover beyond the bounds of the input ones; it is `None` if 
detection failed before
+/// computing it.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{PrimType, PrimVar, Range, Var, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::sym::{self, IterMapLevel};
+/// use tvm_tirx::tvm_ffi::Map;
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// let y = PrimVar::new("y", PrimType::int(32), UNKNOWN_LOC);
+/// let four = Range::from_min_extent(0, 4, UNKNOWN_LOC)?;
+/// let iters = Map::from_iter([(x.clone(), four.clone()), (y.clone(), four)]);
+///
+/// let bijective = IterMapLevel::Bijective;
+/// let map = sym::detect_iter_map([&x * 4 + &y], &iters, true, bijective, 
None, true)?;
+/// assert!(map.errors.is_empty());
+///
+/// let o = PrimVar::new("o", PrimType::int(32), UNKNOWN_LOC);
+/// let inverse = sym::inverse_affine_iter_map(map.indices.iter(), [&o])?;
+/// assert_eq!(script(&inverse.get(&Var::from(&x))?.unwrap())?, "o // 4");
+/// assert_eq!(script(&inverse.get(&Var::from(&y))?.unwrap())?, "o % 4");
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/sym.IterMapResult
 #[allow(missing_docs)]
 #[repr(C)]
@@ -178,6 +185,7 @@ impl IterMapResult {
 }
 // tvm-ffi-stubgen(end)
 
+/// The mark of `source` as an iterator over `[0, extent)`.
 // tvm-ffi-stubgen(begin): object/sym.IterMark
 #[allow(missing_docs)]
 #[repr(C)]
@@ -225,6 +233,7 @@ impl IterMark {
 unsafe impl tvm_ffi::object::ImmutableObject for IterMarkObj {}
 // tvm-ffi-stubgen(end)
 
+/// The split `(source // lower_factor) % extent * scale` of the marked 
iterator `source`.
 // tvm-ffi-stubgen(begin): object/sym.IterSplitExpr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -276,6 +285,7 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
IterSplitExprObj {}
 tvm_ffi::unsafe_impl_object_ref_upcast!(IterSplitExpr => Expr, IterSplitExpr 
=> IterMapExpr, IterSplitExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
+/// The sum of the splits `args` plus `base`.
 // tvm-ffi-stubgen(begin): object/sym.IterSumExpr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -355,7 +365,7 @@ impl IterSumExpr {
     }
 }
 
-/// How strictly an iterator map is checked, the C++ `sym::IterMapLevel`.
+/// How strictly an iterator map is checked.
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
 #[repr(i32)]
 pub enum IterMapLevel {
diff --git a/rust/src/target.rs b/rust/src/target.rs
index 6b76b9b86..5ccd51896 100644
--- a/rust/src/target.rs
+++ b/rust/src/target.rs
@@ -17,26 +17,9 @@
  * under the License.
  */
 
-//! Compilation targets.
-//!
-//! A [`Target`] has a [`TargetKind`], keys, attributes and an optional host. 
It is created from a
-//! tag, a kind name or a config, and made the current target with 
[`Target::scope`].
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::target::Target;
-//! use tvm_tirx::tvm_ffi::ObjectRefCore;
-//!
-//! let cpu = Target::new(r#"{"kind": "llvm", "mcpu": "skylake"}"#)?;
-//! assert!(cpu.as_json()?.as_str().contains(r#""keys":["cpu"]"#));
-//!
-//! let scope = cpu.scope()?;
-//! assert!(Target::current(false)?.unwrap().same_as(&cpu));
-//! drop(scope);
-//! assert!(Target::current(true)?.is_none());
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Compilation targets. A [`Target`] describes the device and options code is 
compiled for, as an
+//! instance of a registered [`TargetKind`]; it is created from a tag, a kind 
name or a config, and
+//! [`Target::scope`] makes it the current one.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Every object registered under `target` has its block in this file.
@@ -61,6 +44,25 @@ use tvm_ffi::String;
 use tvm_ffi::object::ObjectRef;
 // tvm-ffi-stubgen(end)
 
+/// A compilation target: its `kind`, the `keys` that classify it, such as 
`cpu`, the `attrs` of
+/// its options, and an optional `host` target for the code that runs on the 
host. A target may come
+/// from a registered `tag`, such as `nvidia/nvidia-a100`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::target::Target;
+/// use tvm_tirx::tvm_ffi::ObjectRefCore;
+///
+/// let cpu = Target::new(r#"{"kind": "llvm", "mcpu": "skylake"}"#)?;
+/// assert!(cpu.as_json()?.as_str().contains(r#""keys":["cpu"]"#));
+///
+/// let scope = cpu.scope()?;
+/// assert!(Target::current(false)?.unwrap().same_as(&cpu));
+/// drop(scope);
+/// assert!(Target::current(true)?.is_none());
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/target.Target
 #[allow(missing_docs)]
 #[repr(C)]
@@ -201,6 +203,8 @@ impl Drop for TargetScope {
     }
 }
 
+/// A kind of target, such as `llvm` or `cuda`: its `name`, the options it 
takes, and the device
+/// type and keys of its targets by default.
 // tvm-ffi-stubgen(begin): object/target.TargetKind
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx.rs b/rust/src/tirx.rs
index 884fe4c15..d0607d711 100644
--- a/rust/src/tirx.rs
+++ b/rust/src/tirx.rs
@@ -17,10 +17,9 @@
  * under the License.
  */
 
-//! Tensor programs: functions, tensor types, index maps, layouts and thread 
scopes.
-//!
-//! [`structural`] holds the traversals that keep the facts in scope, [`tile`] 
the tile
-//! instructions, [`transform`] the passes and [`analysis`] the analyses.
+//! Tensor programs. A [`Function`] computes on tensors, variables typed by a 
[`TensorType`] whose
+//! [`Layout`] maps their indices to memory and threads; [`tile`] instructions 
are dispatched per
+//! [`ThreadScope`], and [`transform`] and [`analysis`] hold the passes and 
analyses.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // An object registered under `tirx` that no file has a block for yet is 
generated here, to be moved
diff --git a/rust/src/tirx/analysis.rs b/rust/src/tirx/analysis.rs
index 41916e22d..9ea3bd6c3 100644
--- a/rust/src/tirx/analysis.rs
+++ b/rust/src/tirx/analysis.rs
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-//! Analyses of tensor-level IR: memory accesses, undefined variables, linear 
patterns and
-//! well-formedness.
+//! Analyses. Read-only queries over functions, statements and expressions: 
well-formedness and
+//! host memory access checks, undefined variables, and linear and clip-bound 
index patterns.
 
 #[allow(clippy::module_inception)]
 pub mod analysis;
diff --git a/rust/src/tirx/analysis/analysis.rs 
b/rust/src/tirx/analysis/analysis.rs
index 3c6900bce..445c7c990 100644
--- a/rust/src/tirx/analysis/analysis.rs
+++ b/rust/src/tirx/analysis/analysis.rs
@@ -17,22 +17,7 @@
  * under the License.
  */
 
-//! The analyses of tensor-level IR (`tirx.analysis.*`).
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{Expr, PrimType, PrimVar, Var, UNKNOWN_LOC};
-//! use tvm_tirx::tirx::analysis;
-//! use tvm_tirx::tvm_ffi::ObjectRefCore;
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! let y = PrimVar::new("y", PrimType::int(32), UNKNOWN_LOC);
-//! let sum: Expr = (&x + &y).try_into()?;
-//! let undefined = analysis::undefined_vars(sum, [&x])?;
-//! assert!(undefined.len() == 1 && undefined.get(0)?.same_as(&y));
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! The analyses, which [`analysis`](super) re-exports.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -76,6 +61,21 @@ pub fn verify_memory(func: impl IntoObjectRef<Function>) -> 
Result<bool> {
 
 /// Return the variables `node`, a statement or an expression, uses but 
neither defines nor finds in
 /// `defs` (`tirx.analysis.UndefinedVars`).
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{Expr, PrimType, PrimVar, Var, UNKNOWN_LOC};
+/// use tvm_tirx::tirx::analysis;
+/// use tvm_tirx::tvm_ffi::ObjectRefCore;
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// let y = PrimVar::new("y", PrimType::int(32), UNKNOWN_LOC);
+/// let sum: Expr = (&x + &y).try_into()?;
+/// let undefined = analysis::undefined_vars(sum, [&x])?;
+/// assert!(undefined.len() == 1 && undefined.get(0)?.same_as(&y));
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 pub fn undefined_vars(
     node: impl Into<Any>,
     defs: impl IntoIterator<Item = impl Into<Var>>,
diff --git a/rust/src/tirx/function.rs b/rust/src/tirx/function.rs
index b550375cc..f8a31105a 100644
--- a/rust/src/tirx/function.rs
+++ b/rust/src/tirx/function.rs
@@ -17,32 +17,13 @@
  * under the License.
  */
 
-//! Tensor functions.
-//!
-//! A [`Function`] is a [`BaseFunc`] with parameters, a return type and a 
sequence body.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{DictAttrs, PrimType, PrimVar, Return, SeqStmt, Var, 
UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::{renew_def, Function};
-//! use tvm_tirx::tvm_ffi::{Map, ObjectRefCore};
-//!
-//! let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
-//! let body = SeqStmt::new([Return::new(&x, UNKNOWN_LOC)], UNKNOWN_LOC);
-//! let attrs = DictAttrs::new(Map::new());
-//! let identity = Function::new([&x], &body, PrimType::int(32), attrs, 
UNKNOWN_LOC);
-//! assert!(script(&identity)?.as_str().contains("def main(x: T.int32) -> 
T.int32:"));
-//!
-//! let copy = renew_def(&identity)?;
-//! assert!(!copy.params.get(0)?.same_as(&x));
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Functions of the dialect. A [`Function`] is a [`BaseFunc`] whose type is 
derived from its
+//! parameters and return type, with a [`SeqStmt`] body; [`renew_def`] copies 
one with fresh
+//! variables.
 
 // tvm-ffi-stubgen(missing-docs): allow
-// `custom-new` reserves `new` for a constructor with the C++ one's semantics 
(`ty` derived from the
-// signature); the generated `from_fields` allocates the fields as given.
+// `custom-new` reserves `new` for a hand-written constructor (`ty` derived 
from the signature); the
+// generated `from_fields` allocates the fields as given.
 // tvm-ffi-stubgen(custom-new): tirx.Function
 
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -74,6 +55,27 @@ use tvm_ffi::ObjectRefCore;
 use tvm_ffi::Result;
 // tvm-ffi-stubgen(end)
 
+/// A function: `params`, a `body` and a `ret_type`, typed by the [`FuncType`] 
of its parameter
+/// and return types. A declaration has an empty body and the `declaration` 
attribute set.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{DictAttrs, PrimType, PrimVar, Return, SeqStmt, Var, 
UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::tirx::{renew_def, Function};
+/// use tvm_tirx::tvm_ffi::{Map, ObjectRefCore};
+///
+/// let x = PrimVar::new("x", PrimType::int(32), UNKNOWN_LOC);
+/// let body = SeqStmt::new([Return::new(&x, UNKNOWN_LOC)], UNKNOWN_LOC);
+/// let attrs = DictAttrs::new(Map::new());
+/// let identity = Function::new([&x], &body, PrimType::int(32), attrs, 
UNKNOWN_LOC);
+/// assert!(script(&identity)?.as_str().contains("def main(x: T.int32) -> 
T.int32:"));
+///
+/// let copy = renew_def(&identity)?;
+/// assert!(!copy.params.get(0)?.same_as(&x));
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.Function
 #[allow(missing_docs)]
 #[repr(C)]
@@ -153,8 +155,8 @@ tvm_ffi::unsafe_impl_object_ref_upcast!(Function => Expr, 
Function => BaseFunc);
 // tvm-ffi-stubgen(end)
 
 impl Function {
-    /// Create the function of `params`, `body`, `ret_type` and `attrs`; a 
missing return type
-    /// becomes void (C++ `Function(params, body, ret_type, attrs, loc)`).
+    /// Create the function of `params`, `body`, `ret_type` and `attrs`, typed 
by the function
+    /// type of its signature; a missing return type becomes void, the empty 
tuple type.
     pub fn new(
         params: impl IntoIterator<Item = impl Into<Var>>,
         body: &SeqStmt,
diff --git a/rust/src/tirx/index_map.rs b/rust/src/tirx/index_map.rs
index 8354a78d2..714967cbe 100644
--- a/rust/src/tirx/index_map.rs
+++ b/rust/src/tirx/index_map.rs
@@ -17,29 +17,9 @@
  * under the License.
  */
 
-//! Index maps.
-//!
-//! An [`IndexMap`] maps its `initial_indices` to its `final_indices`, as `(i, 
j) -> (j, i)` does.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{PrimExpr, PrimType, PrimVar, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::IndexMap;
-//! use tvm_tirx::tvm_ffi::{Array, Result};
-//!
-//! let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
-//! let j = PrimVar::new("j", PrimType::int(32), UNKNOWN_LOC);
-//! let transpose = IndexMap::new([&i, &j], [&j, &i], None)?;
-//! assert_eq!(script(&transpose)?, "T.index_map(lambda i, j: (j, i))");
-//!
-//! let text =
-//!     |exprs: Array<PrimExpr>| 
exprs.iter().map(script).collect::<Result<Vec<_>>>();
-//! assert_eq!(text(transpose.map_indices([1, 2], None)?)?, ["2", "1"]);
-//!
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Index maps. An [`IndexMap`] maps the indices of one representation of a 
tensor to those of
+//! another, as a layout transformation does; it maps indices, shapes and 
tensor data, and inverts
+//! when it is bijective.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -51,7 +31,7 @@
 // tvm-ffi-stubgen(import-object): tvm_ffi::Tensor
 // tvm-ffi-stubgen(import-object): crate::ir::expr::IntoExpr
 // tvm-ffi-stubgen(import-object): crate::ir::expr::PrimExpr
-// `custom-new` reserves `new` for the C++ constructor, which takes the 
inverse as an `IndexMap`; the
+// `custom-new` reserves `new` for a constructor that takes the inverse as an 
`IndexMap`; the
 // generated `from_fields` stores it as the `ObjectRef` the field holds.
 // tvm-ffi-stubgen(custom-new): tirx.IndexMap
 // The indices a map computes are `PrimExpr`s, as C++ `IndexMapObj` holds them.
@@ -75,6 +55,28 @@ use tvm_ffi::Tensor;
 use tvm_ffi::object::ObjectRef;
 // tvm-ffi-stubgen(end)
 
+/// The map from the index variables `initial_indices` to the expressions 
`final_indices` over
+/// them, such as `(i, j) -> (j, i)`, with its inverse `inverse_index_map` 
when it is known.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{PrimExpr, PrimType, PrimVar, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::tirx::IndexMap;
+/// use tvm_tirx::tvm_ffi::{Array, Result};
+///
+/// let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
+/// let j = PrimVar::new("j", PrimType::int(32), UNKNOWN_LOC);
+/// let transpose = IndexMap::new([&i, &j], [&j, &i], None)?;
+/// assert_eq!(script(&transpose)?, "T.index_map(lambda i, j: (j, i))");
+///
+/// let text =
+///     |exprs: Array<PrimExpr>| 
exprs.iter().map(script).collect::<Result<Vec<_>>>();
+/// assert_eq!(text(transpose.map_indices([1, 2], None)?)?, ["2", "1"]);
+///
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.IndexMap
 #[allow(missing_docs)]
 #[repr(C)]
@@ -137,8 +139,7 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
IndexMapObj {}
 
 impl IndexMap {
     /// Create the map from `initial_indices` to `final_indices`, with the 
inverse
-    /// `inverse_index_map` if it is known (C++ `IndexMap(initial_indices, 
final_indices,
-    /// inverse_index_map)`).
+    /// `inverse_index_map` if it is known.
     pub fn new(
         initial_indices: impl IntoIterator<Item = impl Into<PrimVar>>,
         final_indices: impl IntoIterator<Item = impl IntoExpr<PrimExpr>>,
diff --git a/rust/src/tirx/layout.rs b/rust/src/tirx/layout.rs
index da61e62b6..ba8362e09 100644
--- a/rust/src/tirx/layout.rs
+++ b/rust/src/tirx/layout.rs
@@ -17,32 +17,9 @@
  * under the License.
  */
 
-//! Layouts: how the indices of a tensor map to memory and to threads.
-//!
-//! A [`TileLayout`] maps indices through the [`Iter`]s of its `shard` 
(distinct elements) and
-//! `replica` (copies), each an extent and a stride over an [`Axis`]; a 
[`ComposeLayout`] swizzles
-//! its memory coordinate. Both are [`Layout`]s.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::sym::Analyzer;
-//! use tvm_tirx::tirx::{Axis, Iter, Layout, TileLayout};
-//! use tvm_tirx::tvm_ffi::Map;
-//!
-//! let m = Axis::get("m");
-//! let shard = [Iter::new(8, 8, m.clone()), Iter::new(8, 1, m)];
-//! let layout = Layout::from(&TileLayout::new(shard, Vec::<Iter>::new(), 
&Map::new()));
-//! assert_eq!(script(&layout.size(None)?)?, "64");
-//!
-//! let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
-//! let j = PrimVar::new("j", PrimType::int(32), UNKNOWN_LOC);
-//! let offset = layout.apply([&i, &j], 
&Analyzer::new())?.get(&"m".into())?.unwrap();
-//! assert_eq!(script(&offset)?, "i * 8 + j");
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Layouts. A [`Layout`] maps the logical indices of a tensor to coordinates 
on named [`Axis`]es,
+//! a memory offset and the threads that hold each element; a [`TileLayout`] 
builds the map from
+//! strided [`Iter`] factors, and a [`ComposeLayout`] swizzles the memory 
coordinate of one.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -89,6 +66,33 @@ struct FfiEnumObj {
 }
 // tvm-ffi-stubgen(end)
 
+/// The base of every layout: a map from the logical indices of a tensor to a 
coordinate on each
+/// of its [`Axis`]es, such as the memory axis `m` or a thread axis.
+///
+/// [`apply`](Layout::apply) maps one index, [`size`](Layout::size) is the 
number of elements the
+/// layout maps and [`span`](Layout::span) the extent of the coordinates it 
reaches. Each kind of
+/// layout, such as [`TileLayout`] or [`ComposeLayout`], is a subtype.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::sym::Analyzer;
+/// use tvm_tirx::tirx::{Axis, Iter, Layout, TileLayout};
+/// use tvm_tirx::tvm_ffi::Map;
+///
+/// let m = Axis::get("m");
+/// let shard = [Iter::new(8, 8, m.clone()), Iter::new(8, 1, m)];
+/// let layout = Layout::from(&TileLayout::new(shard, Vec::<Iter>::new(), 
&Map::new()));
+/// assert_eq!(script(&layout.size(None)?)?, "64");
+///
+/// let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
+/// let j = PrimVar::new("j", PrimType::int(32), UNKNOWN_LOC);
+/// let offset = layout.apply([&i, &j], 
&Analyzer::new())?.get(&"m".into())?.unwrap();
+/// assert_eq!(script(&offset)?, "i * 8 + j");
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.Layout
 #[allow(missing_docs)]
 #[repr(C)]
@@ -222,6 +226,9 @@ impl Layout {
     }
 }
 
+/// The layout of strided factors: each [`Iter`] of `shard` spans distinct 
elements and each of
+/// `replica` their copies, adding its stride per position to the coordinate 
on its axis, from the
+/// origin `offset` gives on each axis.
 // tvm-ffi-stubgen(begin): object/tirx.TileLayout
 #[allow(missing_docs)]
 #[repr(C)]
@@ -334,6 +341,8 @@ impl TileLayout {
     }
 }
 
+/// One factor of a [`TileLayout`]: `extent` positions, each adding `stride` 
to the coordinate on
+/// `axis`.
 // tvm-ffi-stubgen(begin): object/tirx.Iter
 #[allow(missing_docs)]
 #[repr(C)]
@@ -388,6 +397,9 @@ unsafe impl tvm_ffi::object::ImmutableObject for IterObj {}
 
 // The native `tirx.Iter` stores its fields as given, as the generated 
`Iter::new` does.
 
+/// A named coordinate a layout maps to: the memory axis `m`, or a thread 
axis, such as `laneid`,
+/// that indexes the threads of a scope. Each name is one registered axis, 
which [`Axis::get`]
+/// looks up.
 // tvm-ffi-stubgen(begin): object/tirx.Axis
 #[allow(missing_docs)]
 #[repr(C)]
@@ -449,6 +461,9 @@ impl Axis {
     }
 }
 
+/// The layout `tile_layout` with its memory coordinate XOR-swizzled: above 
the low `per_element`
+/// bits, the group of `swizzle_len` bits `atom_len` bits up is XORed into the 
group below if
+/// `swizzle_inner`, and the lower group into the upper one otherwise.
 // tvm-ffi-stubgen(begin): object/tirx.ComposeLayout
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/op.rs b/rust/src/tirx/op.rs
index 76448d949..b5262acd5 100644
--- a/rust/src/tirx/op.rs
+++ b/rust/src/tirx/op.rs
@@ -17,24 +17,9 @@
  * under the License.
  */
 
-//! Attributes of TIRx operator calls.
-//!
-//! The functions build TIRx operator calls; a [`CallFFIKernelAttr`] holds the 
launch parameters of
-//! a `call_ffi_kernel`.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{PrimType, PrimVar, Var, UNKNOWN_LOC};
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::{logaddexp, reinterpret};
-//!
-//! let x = PrimVar::new("x", PrimType::float(32), UNKNOWN_LOC);
-//! let y = PrimVar::new("y", PrimType::float(32), UNKNOWN_LOC);
-//! assert_eq!(script(&logaddexp(&x, &y)?)?, "T.log(T.exp(x) + T.exp(y))");
-//! assert_eq!(script(&reinterpret(PrimType::int(32), &x)?)?, 
"T.reinterpret(x, ty=T.int32)");
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Operators of the dialect. The functions build calls of its operators, such 
as [`logaddexp`]
+//! and [`reinterpret`]; a [`CallFFIKernelAttr`] holds the launch metadata of 
a `call_ffi_kernel`
+//! call.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -96,6 +81,13 @@ use tvm_ffi::String;
 use tvm_ffi::TYPE_ERROR;
 // tvm-ffi-stubgen(end)
 
+/// The launch metadata of a `call_ffi_kernel` call, whose arguments are the 
kernel symbol, the
+/// kernel operands and then the launch values.
+///
+/// `launch_params` tags the launch values in order (a flag-only tag takes no 
value, and the dynamic
+/// shared-memory size, when present, comes last), `launch_fields` names the 
CUDA launch
+/// configuration field each launch value sets, `num_kernel_args` counts the 
kernel operands, and
+/// `kernel_attrs` holds the compile-time requirements of the kernel.
 // tvm-ffi-stubgen(begin): object/tirx.CallFFIKernelAttr
 #[allow(missing_docs)]
 #[repr(C)]
@@ -172,8 +164,7 @@ tvm_ffi::unsafe_impl_object_ref_upcast!(CallFFIKernelAttr 
=> Attrs);
 // The native `tirx.CallFFIKernelAttr` stores its fields as given, as the 
generated
 // `CallFFIKernelAttr::new` does.
 
-/// Build `log(exp(a) + exp(b))` of the floating-point `a` and `b`, matched to 
one type
-/// (C++ `tirx::logaddexp`, `tirx._OpLogAddExp`).
+/// Build `log(exp(a) + exp(b))` of the floating-point `a` and `b`, matched to 
one type.
 ///
 /// # Errors
 ///
@@ -200,12 +191,23 @@ pub fn logaddexp_at(
     log(add(exp(a)?, exp(b)?)?)
 }
 
-/// Reinterpret `value` bit for bit as the primitive or pointer type `ty`
-/// (C++ `tirx::reinterpret(target_ty, value, loc)`, `tirx.reinterpret`).
+/// Reinterpret `value` bit for bit as the primitive or pointer type `ty` 
(`tirx.reinterpret`).
 ///
 /// Besides primitive types of the same size, a string reinterprets as a 
pointer and a pointer as a
 /// scalar `int64`, and back.
 ///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{PrimType, PrimVar, UNKNOWN_LOC};
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::tirx::reinterpret;
+///
+/// let x = PrimVar::new("x", PrimType::float(32), UNKNOWN_LOC);
+/// assert_eq!(script(&reinterpret(PrimType::int(32), &x)?)?, 
"T.reinterpret(x, ty=T.int32)");
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
+///
 /// # Errors
 ///
 /// Returns an error for any other pair of types, or two primitive types of 
different sizes.
@@ -307,10 +309,10 @@ pub fn reinterpret_at(
 }
 
 /// Build a rational approximation of `erf(arg)` on `[-4, 4]` in `bits`-bit 
floats, as the Metal and
-/// WebGPU backends lower `erf` (C++ `tirx::fast_erf_float_expr(arg, bits)`).
+/// WebGPU backends lower `erf`.
 pub fn fast_erf_float_expr(arg: impl IntoExpr<PrimExpr>, bits: u8) -> 
Result<PrimExpr> {
     let ty = PrimType::float(bits);
-    // The C++ coefficients are `float` literals.
+    // The coefficients are single-precision values.
     let constant =
         |value: f32| -> ExprResult { FloatImm::new(&ty, f64::from(value), 
UNKNOWN_LOC).into() };
     // alpha_1, alpha_3, ..., alpha_13: the odd coefficients of the numerator.
@@ -335,7 +337,7 @@ pub fn fast_erf_float_expr(arg: impl IntoExpr<PrimExpr>, 
bits: u8) -> Result<Pri
     ];
     let x = arg.into_owned_expr()?.min(constant(4.0)).max(constant(-4.0));
     let x2 = &x * &x;
-    // Horner's rule from the highest coefficient down, as C++ writes it out.
+    // Horner's rule from the highest coefficient down.
     let mut p = &x2 * constant(alpha[6]) + constant(alpha[5]);
     for coefficient in alpha[..5].iter().rev() {
         p = &x2 * p + constant(*coefficient);
diff --git a/rust/src/tirx/op_attr.rs b/rust/src/tirx/op_attr.rs
index 1d15b1287..590b2610b 100644
--- a/rust/src/tirx/op_attr.rs
+++ b/rust/src/tirx/op_attr.rs
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-//! The operator attributes of TIRx (C++ `tvm::tirx::op_attr`), for
-//! [`OpDef::set_attr`](crate::ir::OpDef::set_attr).
+//! Operator attributes. The typed keys of the attributes the dialect reads 
from an operator, set
+//! with [`OpDef::set_attr`](crate::ir::OpDef::set_attr).
 
 use tvm_ffi::String;
 
 use crate::ir::AttrKey;
 
-/// The global symbol of the operator after lowering (C++ `kGlobalSymbol`, 
`TGlobalSymbol`).
+/// The global symbol the operator lowers to.
 pub const GLOBAL_SYMBOL: AttrKey<String> = AttrKey::new("TGlobalSymbol");
-/// Whether the operator is overloaded for vector forms (C++ `kVectorizable`, 
`TVectorizable`).
+/// Whether the operator is overloaded for vector operands.
 pub const VECTORIZABLE: AttrKey<bool> = AttrKey::new("TVectorizable");
diff --git a/rust/src/tirx/structural.rs b/rust/src/tirx/structural.rs
index 19b5991f8..bfc48d411 100644
--- a/rust/src/tirx/structural.rs
+++ b/rust/src/tirx/structural.rs
@@ -17,12 +17,9 @@
  * under the License.
  */
 
-//! Structural traversal with the facts in scope.
-//!
-//! A [`StructuralVisitorWithConstraintContext`] visits values and a
-//! [`StructuralMutatorWithConstraintContext`] transforms them, keeping the 
[`ConstraintContext`] of
-//! the bindings, ranges and guards in effect at the current value. From Rust, 
run them with the
-//! `structural_*_with_constraint_context` entry points.
+//! Constraint-aware traversal. A [`StructuralVisitorWithConstraintContext`] 
visits values and a
+//! [`StructuralMutatorWithConstraintContext`] rewrites them, each keeping the 
[`ConstraintContext`]
+//! of the bindings, ranges and guards in scope at the current value.
 
 // tvm-ffi-stubgen(missing-docs): allow
 
@@ -53,6 +50,9 @@ struct FfiStructuralVisitorObj {
 }
 // tvm-ffi-stubgen(end)
 
+/// A structural mutator that keeps the [`ConstraintContext`] in scope at the 
current value, read
+/// with `constraints`; Rust code runs one with
+/// 
[`structural_mutate_with_constraint_context`](mod@super::structural_mutate_with_constraint_context).
 // tvm-ffi-stubgen(begin): object/tirx.StructuralMutatorWithConstraintContext
 #[allow(missing_docs)]
 #[repr(C)]
@@ -80,6 +80,9 @@ impl StructuralMutatorWithConstraintContextObj {
 }
 // tvm-ffi-stubgen(end)
 
+/// A structural visitor that keeps the [`ConstraintContext`] in scope at the 
current value, read
+/// with `constraints`; Rust code runs one with
+/// 
[`structural_visit_with_constraint_context`](mod@super::structural_visit_with_constraint_context).
 // tvm-ffi-stubgen(begin): object/tirx.StructuralVisitorWithConstraintContext
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/structural_map_with_constraint_context.rs 
b/rust/src/tirx/structural_map_with_constraint_context.rs
index 854ea836b..26b6872b6 100644
--- a/rust/src/tirx/structural_map_with_constraint_context.rs
+++ b/rust/src/tirx/structural_map_with_constraint_context.rs
@@ -17,7 +17,9 @@
  * under the License.
  */
 
-//! Constraint-aware structural mapping: rewrite each value with the facts in 
scope.
+//! Constraint-aware structural mapping. 
[`structural_map_with_constraint_context`] calls a closure
+//! on every value with the facts in scope there and replaces the value with 
what it returns,
+//! unless that is `Unchanged`.
 
 use std::borrow::BorrowMut;
 
diff --git a/rust/src/tirx/structural_mutate_with_constraint_context.rs 
b/rust/src/tirx/structural_mutate_with_constraint_context.rs
index cfb336c72..b0447359b 100644
--- a/rust/src/tirx/structural_mutate_with_constraint_context.rs
+++ b/rust/src/tirx/structural_mutate_with_constraint_context.rs
@@ -17,7 +17,9 @@
  * under the License.
  */
 
-//! Constraint-aware structural mutation: rewrite values with the facts in 
scope.
+//! Constraint-aware structural mutation. 
[`structural_mutate_with_constraint_context`] rewrites a
+//! value with a dispatcher whose handlers read the facts in scope from the
+//! [`StructuralMutatorWithConstraintContextView`] they are given.
 
 use std::borrow::BorrowMut;
 use std::cell::UnsafeCell;
diff --git a/rust/src/tirx/structural_visit_with_constraint_context.rs 
b/rust/src/tirx/structural_visit_with_constraint_context.rs
index a46081a6d..634247087 100644
--- a/rust/src/tirx/structural_visit_with_constraint_context.rs
+++ b/rust/src/tirx/structural_visit_with_constraint_context.rs
@@ -17,7 +17,9 @@
  * under the License.
  */
 
-//! Constraint-aware structural visiting: visit values with the facts in scope.
+//! Constraint-aware structural visiting. 
[`structural_visit_with_constraint_context`] visits a
+//! value with a dispatcher whose handlers read the facts in scope from the
+//! [`StructuralVisitorWithConstraintContextView`] they are given.
 
 use std::borrow::BorrowMut;
 use std::cell::UnsafeCell;
diff --git a/rust/src/tirx/structural_walk_with_constraint_context.rs 
b/rust/src/tirx/structural_walk_with_constraint_context.rs
index c74e1d3cb..f69d07d07 100644
--- a/rust/src/tirx/structural_walk_with_constraint_context.rs
+++ b/rust/src/tirx/structural_walk_with_constraint_context.rs
@@ -17,7 +17,8 @@
  * under the License.
  */
 
-//! Constraint-aware structural walking: observe values with the facts in 
scope.
+//! Constraint-aware structural walking. 
[`structural_walk_with_constraint_context`] calls a closure
+//! on every value with the facts in scope there, to observe the value, not to 
rewrite it.
 
 use std::borrow::BorrowMut;
 use tvm_ffi::{
diff --git a/rust/src/tirx/tensor.rs b/rust/src/tirx/tensor.rs
index f8da86bf3..d28bc7516 100644
--- a/rust/src/tirx/tensor.rs
+++ b/rust/src/tirx/tensor.rs
@@ -17,35 +17,13 @@
  * under the License.
  */
 
-//! Tensor types.
-//!
-//! A [`TensorType`] is the element type, memory scope, shape, data alignment 
and optional layout of
-//! a tensor.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{Expr, PrimType, PrimVar, Subscript, Var, UNKNOWN_LOC};
-//! use tvm_tirx::runtime::MemoryScope;
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::TensorType;
-//!
-//! let scratch = MemoryScope::register("my_ext.scratch")?;
-//! let ty = TensorType::new([16, 16], PrimType::float(32), &scratch, 0, None, 
UNKNOWN_LOC)?;
-//! assert_eq!(script(&ty)?, r#"T.Tensor((16, 16), T.float32, 
scope="my_ext.scratch")"#);
-//!
-//! let a = Expr::from(Var::new("A", &ty, UNKNOWN_LOC));
-//! let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
-//! let point = [Subscript::Index(i.into()), Subscript::Index(0.into())];
-//! let element = a.subscript(point, UNKNOWN_LOC)?;
-//! assert_eq!(script(&Expr::try_from(element)?)?, "A[i, 0]");
-//!
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Tensors. A tensor is a [`Var`] whose type is a [`TensorType`], which fixes 
its shape, element
+//! type, memory scope, alignment and [`Layout`]; loads and stores subscript 
the variable, and
+//! [`TensorVar`] matches one in an operator signature.
 
 // tvm-ffi-stubgen(missing-docs): allow
-// `custom-new` reserves `new` for a constructor with the C++ one's semantics 
(defaults); the
-// generated `from_fields` allocates the fields as given.
+// `custom-new` reserves `new` for a hand-written constructor (defaults); the 
generated
+// `from_fields` allocates the fields as given.
 // tvm-ffi-stubgen(custom-new): tirx.TensorType
 
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -87,6 +65,32 @@ use tvm_ffi::ObjectCore;
 use tvm_ffi::Result;
 // tvm-ffi-stubgen(end)
 
+/// The type of a tensor of `shape` and element type `dtype` in the memory 
`scope`, with its data
+/// pointer aligned to `data_alignment` bytes and an optional `layout` from 
indices to storage.
+///
+/// The shape is the one loads and stores index. The data pointer is not part 
of the type; the
+/// definition of the tensor variable binds it.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{Expr, PrimType, PrimVar, Subscript, Var, UNKNOWN_LOC};
+/// use tvm_tirx::runtime::MemoryScope;
+/// use tvm_tirx::script::script;
+/// use tvm_tirx::tirx::TensorType;
+///
+/// let scratch = MemoryScope::register("my_ext.scratch")?;
+/// let ty = TensorType::new([16, 16], PrimType::float(32), &scratch, 0, None, 
UNKNOWN_LOC)?;
+/// assert_eq!(script(&ty)?, r#"T.Tensor((16, 16), T.float32, 
scope="my_ext.scratch")"#);
+///
+/// let a = Expr::from(Var::new("A", &ty, UNKNOWN_LOC));
+/// let i = PrimVar::new("i", PrimType::int(32), UNKNOWN_LOC);
+/// let point = [Subscript::Index(i.into()), Subscript::Index(0.into())];
+/// let element = a.subscript(point, UNKNOWN_LOC)?;
+/// assert_eq!(script(&Expr::try_from(element)?)?, "A[i, 0]");
+///
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.TensorType
 #[allow(missing_docs)]
 #[repr(C)]
@@ -169,8 +173,7 @@ tvm_ffi::unsafe_impl_object_ref_upcast!(TensorType => Type);
 
 impl TensorType {
     /// Create the type of a tensor of `shape` and `dtype` in `scope`, aligned 
to `data_alignment`
-    /// bytes (the runtime's allocation alignment when 0 or less), with 
`layout` if given (C++
-    /// `TensorType(shape, dtype, scope, data_alignment, layout, loc)`).
+    /// bytes (the runtime's allocation alignment when 0 or less), with 
`layout` if given.
     pub fn new(
         shape: impl IntoIterator<Item = impl IntoExpr<PrimExpr>>,
         dtype: impl Into<PrimType>,
@@ -206,8 +209,8 @@ impl TensorType {
     }
 }
 
-/// A signature descriptor ([`sig::ArgType`]) of a `Var`, not a subclass, 
whose type is a
-/// [`TensorType`] (C++ `tirx::TensorVar`).
+/// A signature descriptor ([`sig::ArgType`]) that matches a [`Var`] whose 
type is a
+/// [`TensorType`]; it is not a type of its own.
 pub struct TensorVar;
 
 impl sig::ArgType for TensorVar {
@@ -233,8 +236,8 @@ pub fn tensor_is_scalar(tensor: impl IntoObjectRef<Var>, 
alloc_or_decl: bool) ->
         .try_into()
 }
 
-/// The runtime's allocation alignment (C++ `runtime::kAllocAlignment`, which 
a build may set),
-/// read from the default the reflection registers for 
`TensorType::data_alignment`.
+/// The runtime's allocation alignment, which a build may configure, read from 
the default the
+/// reflection registers for `TensorType::data_alignment`.
 fn alloc_alignment() -> Result<i32> {
     static ALLOC_ALIGNMENT: std::sync::OnceLock<i32> = 
std::sync::OnceLock::new();
     if let Some(alignment) = ALLOC_ALIGNMENT.get() {
diff --git a/rust/src/tirx/thread_scope.rs b/rust/src/tirx/thread_scope.rs
index 1bc84f443..5d0d69fd0 100644
--- a/rust/src/tirx/thread_scope.rs
+++ b/rust/src/tirx/thread_scope.rs
@@ -17,22 +17,8 @@
  * under the License.
  */
 
-//! Thread scopes: the levels of a backend's thread hierarchy.
-//!
-//! A [`ThreadScope`], such as `cuda.warp`, is looked up by name with 
[`ThreadScope::get`].
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::tirx::ThreadScope;
-//!
-//! let warp = ThreadScope::get("cuda.warp")?;
-//! assert_eq!((warp.backend()?, warp.local_name()?), ("cuda".into(), 
"warp".into()));
-//!
-//! let cta = ThreadScope::get("cuda.cta")?;
-//! assert!(cta.is_higher_than(&warp)?);
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Thread scopes. A [`ThreadScope`] is one level of a backend's thread 
hierarchy, such as
+//! `cuda.warp`; each backend registers its levels, ranked from the widest 
down.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -60,6 +46,23 @@ struct FfiEnumObj {
 }
 // tvm-ffi-stubgen(end)
 
+/// A level of a backend's thread hierarchy, named `backend.level`, such as 
`cuda.warp`.
+///
+/// Backends register their scopes; [`ThreadScope::get`] only looks one up. 
Within a backend,
+/// [`rank`](ThreadScope::rank) orders the levels, a wider scope ranking lower.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::tirx::ThreadScope;
+///
+/// let warp = ThreadScope::get("cuda.warp")?;
+/// assert_eq!((warp.backend()?, warp.local_name()?), ("cuda".into(), 
"warp".into()));
+///
+/// let cta = ThreadScope::get("cuda.cta")?;
+/// assert!(cta.is_higher_than(&warp)?);
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.ThreadScope
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/tile.rs b/rust/src/tirx/tile.rs
index 153974159..1144fe2c4 100644
--- a/rust/src/tirx/tile.rs
+++ b/rust/src/tirx/tile.rs
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-//! Tile instructions: their attributes ([`instruction`]) and the context they 
are dispatched in
-//! ([`dispatch`]).
+//! Tile instructions. [`instruction`] holds the attributes their calls carry 
and their validation,
+//! and [`dispatch`] the context a lowering dispatches each call in.
 
 pub mod dispatch;
 pub mod instruction;
diff --git a/rust/src/tirx/tile/dispatch.rs b/rust/src/tirx/tile/dispatch.rs
index 45c3aaceb..ab05b394c 100644
--- a/rust/src/tirx/tile/dispatch.rs
+++ b/rust/src/tirx/tile/dispatch.rs
@@ -17,34 +17,9 @@
  * under the License.
  */
 
-//! The context of tile instruction dispatch.
-//!
-//! A [`DispatchContext`] gives a lowering its target, thread scope, launch 
parameters and variable
-//! ranges, and the state shared between dispatches.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::{Evaluate, IntImm, PrimType, PrimVar, UNKNOWN_LOC};
-//! use tvm_tirx::target::Target;
-//! use tvm_tirx::tirx::tile::DispatchContext;
-//! use tvm_tirx::tirx::ThreadScope;
-//! use tvm_tirx::tvm_ffi::Map;
-//!
-//! let tx = PrimVar::new("tx", PrimType::int(32), UNKNOWN_LOC);
-//! let ctx = DispatchContext::new(
-//!     Target::new("cuda")?,
-//!     ThreadScope::get("cuda.warp")?,
-//!     [("threadIdx.x", tx, 32.into())],
-//!     &Map::new(), false, &Map::new(), &Map::new(), &Map::new(), &Map::new(),
-//!     "warp",
-//! )?;
-//!
-//! ctx.cache_set("my_ext.zero", &IntImm::int32(0))?;
-//! assert!(ctx.cache_get("my_ext.zero")?.is_some());
-//! ctx.add_init_stmt(Evaluate::new(1, UNKNOWN_LOC), false)?;
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Tile instruction dispatch. A lowering dispatches each tile instruction 
call with a
+//! [`DispatchContext`], which describes where the call runs and collects the 
statements and
+//! allocations the dispatch requests.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -83,6 +58,38 @@ use tvm_ffi::String;
 use tvm_ffi::object::ObjectRef;
 // tvm-ffi-stubgen(end)
 
+/// The context a tile instruction call is lowered in: the `target`, the 
`thread_scope` of the
+/// call, the kernel's `launch_params`, the ranges of the loop variables in 
scope
+/// (`var_range_map`), the axis views across and within teams (`inter`, 
`intra`) and the
+/// `scope_kind`.
+///
+/// Through it a dispatch requests allocations and statements, and caches 
objects that later
+/// dispatches of the same lowering reuse. A context with `alloc_only` set is 
used only to allocate
+/// tensors.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::{Evaluate, IntImm, PrimType, PrimVar, UNKNOWN_LOC};
+/// use tvm_tirx::target::Target;
+/// use tvm_tirx::tirx::tile::DispatchContext;
+/// use tvm_tirx::tirx::ThreadScope;
+/// use tvm_tirx::tvm_ffi::Map;
+///
+/// let tx = PrimVar::new("tx", PrimType::int(32), UNKNOWN_LOC);
+/// let ctx = DispatchContext::new(
+///     Target::new("cuda")?,
+///     ThreadScope::get("cuda.warp")?,
+///     [("threadIdx.x", tx, 32.into())],
+///     &Map::new(), false, &Map::new(), &Map::new(), &Map::new(), &Map::new(),
+///     "warp",
+/// )?;
+///
+/// ctx.cache_set("my_ext.zero", &IntImm::int32(0))?;
+/// assert!(ctx.cache_get("my_ext.zero")?.is_some());
+/// ctx.add_init_stmt(Evaluate::new(1, UNKNOWN_LOC), false)?;
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.DispatchContext
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/tile/instruction.rs 
b/rust/src/tirx/tile/instruction.rs
index 06ebb3308..ab939455c 100644
--- a/rust/src/tirx/tile/instruction.rs
+++ b/rust/src/tirx/tile/instruction.rs
@@ -19,10 +19,9 @@
 // The generated constructors take one argument per reflected field.
 #![allow(clippy::too_many_arguments)]
 
-//! The attributes of tile instruction calls.
-//!
-//! [`ScopeAttrs`] holds the thread scope of an instruction and [`TrnAttrs`] 
the scope and options
-//! of a Trainium (`trn`) instruction; their type keys are `tirx.tensor.*`.
+//! Tile instruction calls. Each call carries the static attributes its 
operator registers, such as
+//! [`ScopeAttrs`] or [`TrnAttrs`], and is checked by the validator
+//! [`configure_tensor_instruction`] installs.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Every object registered under `tirx.tensor` has its block in this file.
@@ -54,6 +53,7 @@ use tvm_ffi::Result;
 use tvm_ffi::String;
 // tvm-ffi-stubgen(end)
 
+/// The attributes of a tile instruction call that needs only the thread 
`scope` it runs at.
 // tvm-ffi-stubgen(begin): object/tirx.tensor.ScopeAttrs
 #[allow(missing_docs)]
 #[repr(C)]
@@ -109,6 +109,9 @@ unsafe impl tvm_ffi::object::ImmutableObject for 
ScopeAttrsObj {}
 tvm_ffi::unsafe_impl_object_ref_upcast!(ScopeAttrs => Attrs);
 // tvm-ffi-stubgen(end)
 
+/// The attributes of a Trainium (`trn`) tile instruction call: the thread 
`scope` it runs at, an
+/// optional instruction size limit `max_inst_size`, and the options the `trn` 
backend lowers it
+/// with (`opcode`, `reduce_op`, `op0`, `op1`, `axes`, `negate`, `reverse1`).
 // tvm-ffi-stubgen(begin): object/tirx.tensor.TrnAttrs
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/transform.rs b/rust/src/tirx/transform.rs
index 212509c00..5da6d2856 100644
--- a/rust/src/tirx/transform.rs
+++ b/rust/src/tirx/transform.rs
@@ -17,7 +17,8 @@
  * under the License.
  */
 
-//! The TIRx passes ([`mod@transform`]) and function passes 
([`function_pass`]).
+//! Passes of the dialect. [`mod@transform`] holds the passes over TIRx 
functions, and
+//! [`function_pass`] builds one from a Rust closure.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // An object registered under `tirx.transform` that no file has a block for 
yet is generated here,
diff --git a/rust/src/tirx/transform/function_pass.rs 
b/rust/src/tirx/transform/function_pass.rs
index fae9161eb..839837b53 100644
--- a/rust/src/tirx/transform/function_pass.rs
+++ b/rust/src/tirx/transform/function_pass.rs
@@ -17,34 +17,8 @@
  * under the License.
  */
 
-//! Function passes.
-//!
-//! A [`FunctionPass`] is a [`Pass`] that transforms each TIRx function of a 
module.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::transform::{Pass, PassInfo};
-//! use tvm_tirx::tirx::transform::FunctionPass;
-//! # use tvm_tirx::ir::{BaseFunc, DictAttrs, Evaluate, GlobalVar, IRModule, 
MissingType, SeqStmt};
-//! # use tvm_tirx::ir::{Var, UNKNOWN_LOC};
-//! # use tvm_tirx::tirx::Function;
-//! # use tvm_tirx::tvm_ffi::Map;
-//! # let body = SeqStmt::new([Evaluate::new(1, UNKNOWN_LOC)], UNKNOWN_LOC);
-//! # let attrs = DictAttrs::new(Map::new());
-//! # let main = Function::new(Vec::<Var>::new(), &body, MissingType::new(), 
attrs, UNKNOWN_LOC);
-//! # let main_var = GlobalVar::new("main", UNKNOWN_LOC);
-//! # let functions = Map::from_iter([(main_var, BaseFunc::from(&main))]);
-//! # let module = IRModule::new(&functions, DictAttrs::new(Map::new()), 
&Map::new())?;
-//!
-//! let pass = FunctionPass::new(
-//!     |func, _module, _ctx| Ok((!func.params.is_empty()).then_some(func)),
-//!     PassInfo::new(0, "my_ext.RemoveNullary"),
-//! );
-//! let module = Pass::from(&pass).run(module)?;
-//! assert!(!module.contains("main")?);
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Function passes. A [`FunctionPass`] is a [`Pass`] that rewrites each TIRx 
function of a module
+//! on its own, given the module and the pass context.
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -71,6 +45,33 @@ use tvm_ffi::RValueRef;
 use tvm_ffi::Result;
 // tvm-ffi-stubgen(end)
 
+/// A pass that replaces each TIRx function of a module by what a function 
returns for it, or
+/// removes it for `None`; [`FunctionPass::new`] creates one from a Rust 
closure.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::ir::transform::{Pass, PassInfo};
+/// use tvm_tirx::tirx::transform::FunctionPass;
+/// # use tvm_tirx::ir::{BaseFunc, DictAttrs, Evaluate, GlobalVar, IRModule, 
MissingType, SeqStmt};
+/// # use tvm_tirx::ir::{Var, UNKNOWN_LOC};
+/// # use tvm_tirx::tirx::Function;
+/// # use tvm_tirx::tvm_ffi::Map;
+/// # let body = SeqStmt::new([Evaluate::new(1, UNKNOWN_LOC)], UNKNOWN_LOC);
+/// # let attrs = DictAttrs::new(Map::new());
+/// # let main = Function::new(Vec::<Var>::new(), &body, MissingType::new(), 
attrs, UNKNOWN_LOC);
+/// # let main_var = GlobalVar::new("main", UNKNOWN_LOC);
+/// # let functions = Map::from_iter([(main_var, BaseFunc::from(&main))]);
+/// # let module = IRModule::new(&functions, DictAttrs::new(Map::new()), 
&Map::new())?;
+///
+/// let pass = FunctionPass::new(
+///     |func, _module, _ctx| Ok((!func.params.is_empty()).then_some(func)),
+///     PassInfo::new(0, "my_ext.RemoveNullary"),
+/// );
+/// let module = Pass::from(&pass).run(module)?;
+/// assert!(!module.contains("main")?);
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 // tvm-ffi-stubgen(begin): object/tirx.FunctionPass
 #[allow(missing_docs)]
 #[repr(C)]
diff --git a/rust/src/tirx/transform/transform.rs 
b/rust/src/tirx/transform/transform.rs
index d3a6003b4..da363812a 100644
--- a/rust/src/tirx/transform/transform.rs
+++ b/rust/src/tirx/transform/transform.rs
@@ -17,31 +17,9 @@
  * under the License.
  */
 
-//! The TIRx passes and their configurations.
-//!
-//! Each function returns a [`Pass`]; [`RemoveNoOpConfig`] and 
[`UnrollLoopConfig`] configure
+//! The passes of the dialect. Each function returns a [`Pass`] over the TIRx 
functions of a module;
+//! [`RemoveNoOpConfig`] and [`UnrollLoopConfig`] are the pass context options 
that configure
 //! [`remove_no_op`] and [`unroll_loop`].
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::tirx::{transform, Function};
-//! use tvm_tirx::tvm_ffi::{Error, ObjectRefCore};
-//! # use tvm_tirx::ir::{BaseFunc, DictAttrs, Evaluate, GlobalVar, IRModule, 
MissingType, SeqStmt};
-//! # use tvm_tirx::ir::{Var, UNKNOWN_LOC};
-//! # use tvm_tirx::tvm_ffi::Map;
-//! # let body = SeqStmt::new([Evaluate::new(1, UNKNOWN_LOC)], UNKNOWN_LOC);
-//! # let attrs = DictAttrs::new(Map::new());
-//! # let main = Function::new(Vec::<Var>::new(), &body, MissingType::new(), 
attrs, UNKNOWN_LOC);
-//! # let main_var = GlobalVar::new("main", UNKNOWN_LOC);
-//! # let functions = Map::from_iter([(main_var, BaseFunc::from(&main))]);
-//! # let module = IRModule::new(&functions, DictAttrs::new(Map::new()), 
&Map::new())?;
-//!
-//! let module = transform::remove_no_op()?.run(module)?;
-//! let main: Function = module.lookup_name("main")?.try_into()?;
-//! assert!(main.body.seq.is_empty());
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
 
 // tvm-ffi-stubgen(missing-docs): allow
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
@@ -62,6 +40,10 @@ use tvm_ffi::Result;
 use tvm_ffi::String;
 // tvm-ffi-stubgen(end)
 
+/// The configuration of [`remove_no_op`], the pass context option 
`tirx.RemoveNoOp`:
+/// `ignore_profiler_call` treats profiler calls as no-ops, and a non-zero
+/// `max_simplification_steps` makes the simplifier fail after that many 
rewrite steps, for
+/// debugging.
 // tvm-ffi-stubgen(begin): object/tirx.transform.RemoveNoOpConfig
 #[allow(missing_docs)]
 #[repr(C)]
@@ -109,6 +91,12 @@ impl RemoveNoOpConfig {
 unsafe impl tvm_ffi::object::ImmutableObject for RemoveNoOpConfigObj {}
 // tvm-ffi-stubgen(end)
 
+/// The configuration of [`unroll_loop`], the pass context option 
`tirx.UnrollLoop`.
+///
+/// A loop is unrolled automatically within `auto_max_step` steps, 
`auto_max_depth` nesting levels
+/// and an extent of `auto_max_extent`; `explicit_unroll` unrolls the loop in 
the IR rather than
+/// marking it with a pragma, and `unroll_local_access` always unrolls loops 
that access local
+/// memory.
 // tvm-ffi-stubgen(begin): object/tirx.transform.UnrollLoopConfig
 #[allow(missing_docs)]
 #[repr(C)]
@@ -307,6 +295,27 @@ pub fn pointer_value_type_rewrite() -> Result<Pass> {
 }
 
 /// Remove effect-free statements and provably empty control flow 
(`tirx.transform.RemoveNoOp`).
+///
+/// # Examples
+///
+/// ```
+/// use tvm_tirx::tirx::{transform, Function};
+/// use tvm_tirx::tvm_ffi::{Error, ObjectRefCore};
+/// # use tvm_tirx::ir::{BaseFunc, DictAttrs, Evaluate, GlobalVar, IRModule, 
MissingType, SeqStmt};
+/// # use tvm_tirx::ir::{Var, UNKNOWN_LOC};
+/// # use tvm_tirx::tvm_ffi::Map;
+/// # let body = SeqStmt::new([Evaluate::new(1, UNKNOWN_LOC)], UNKNOWN_LOC);
+/// # let attrs = DictAttrs::new(Map::new());
+/// # let main = Function::new(Vec::<Var>::new(), &body, MissingType::new(), 
attrs, UNKNOWN_LOC);
+/// # let main_var = GlobalVar::new("main", UNKNOWN_LOC);
+/// # let functions = Map::from_iter([(main_var, BaseFunc::from(&main))]);
+/// # let module = IRModule::new(&functions, DictAttrs::new(Map::new()), 
&Map::new())?;
+///
+/// let module = transform::remove_no_op()?.run(module)?;
+/// let main: Function = module.lookup_name("main")?.try_into()?;
+/// assert!(main.body.seq.is_empty());
+/// # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
+/// ```
 pub fn remove_no_op() -> Result<Pass> {
     
tvm_ffi::cached_global_func!("tirx.transform.RemoveNoOp").call_tuple(())?.try_into()
 }
diff --git a/rust/src/tirx/type.rs b/rust/src/tirx/type.rs
index 5fa578238..2547cda8c 100644
--- a/rust/src/tirx/type.rs
+++ b/rust/src/tirx/type.rs
@@ -17,22 +17,8 @@
  * under the License.
  */
 
-//! Types of TIRx operations.
-//!
-//! [`TensorMapType`] is the type of a tensor map.
-//!
-//! # Examples
-//!
-//! ```
-//! use tvm_tirx::ir::UNKNOWN_LOC;
-//! use tvm_tirx::script::script;
-//! use tvm_tirx::tirx::TensorMapType;
-//!
-//! let ty = TensorMapType::new(UNKNOWN_LOC);
-//! assert_eq!(script(&ty)?, "T.TensorMap()");
-//!
-//! # Ok::<(), tvm_tirx::tvm_ffi::Error>(())
-//! ```
+//! Types of the dialect beyond tensors. A [`TensorMapType`] types the 
tensor-map descriptors that
+//! backend memory operations take; tensors are typed by 
[`TensorType`](super::TensorType).
 
 // tvm-ffi-stubgen(missing-docs): allow
 
@@ -44,6 +30,10 @@ use std::ops::Deref;
 use tvm_ffi::ObjectArc;
 // tvm-ffi-stubgen(end)
 
+/// The type of a tensor-map descriptor, which backend memory operations take.
+///
+/// A pointer to it points at the descriptor storage, not at tensor data; the 
operation that
+/// creates a descriptor defines its shape and layout.
 // tvm-ffi-stubgen(begin): object/tirx.TensorMapType
 #[allow(missing_docs)]
 #[repr(C)]

Reply via email to