Skip to main content

tilelang-layout

Use when working with TileLang layouts — the Layout/Fragment classes, layout inference and its cost model, the CuTe layout algebra and TileLang-to-CuTe conversion, swizzles, or when debugging layout-related passes and their annotations.

Source facts

Repository
tile-ai/tilelang
Last source activity
August 20, 2026 at 07:09
Detected SKILL.md language
English
Stars
8,627
Forks
891

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
tilelang-layout
description
Use when working with TileLang layouts — the Layout/Fragment classes, layout inference and its cost model, the CuTe layout algebra and TileLang-to-CuTe conversion, swizzles, or when debugging layout-related passes and their annotations.
# TileLang Layout System ## Core types (`src/layout/layout.h`, Python `tilelang/layout/`) **`tl::Layout`** maps logical coordinates to physical coordinates: `input_size_` (logical shape) + `forward_index_` (one PrimExpr per output axis). Expressions are written in process-wide placeholder Vars `InputPlaceholder(i)` (printed `_i`, `_j`, ...). Key APIs: `InputShape/OutputShape/GetForwardIndex/Forward(vars)/Inverse/ InverseWithLevel/Reshape/DetectInjective/DebugOutput`. `OutputShape()` is DERIVED from the expressions' ranges by the Analyzer, not stored. **`tl::Fragment : Layout`** adds a thread dimension for register buffers: `forward_thread_` (PrimExpr, may reference `ReplicationPlaceholder()`, printed `_rep`) and `replicate_size_`. `ThreadExtent()` = `max(forward_thread)+1` under bound domains — also derived, not stored. `thread_range_` (via `BindThreadRange`) offsets the thread space for warp specialization; the forward map itself stays in normalized `[0, T)`. Replication semantics: each logical point is held by `replicate_size` threads; `Replicate/DeReplicate/CondenseReplicateVar` manipulate the rep axis. **Gotchas (verified, easy to trip on):** - `FragmentNode::GetForwardVars()` PREPENDS `ReplicationPlaceholder()` when `replicate_size > 1` (`src/layout/layout.cc:1103`). The input placeholders are always the TRAILING `InputDim()` entries. Python code that zips `get_forward_vars()` against the shape positionally is wrong for replicated fragments. - The canonical (thread, slot, rep) packing lives in `FragmentNode::InverseWithLevel` (`src/layout/layout.cc:1133`): rep becomes a trailing ORDINARY input dim of extent `ReplicateExtent()` (substituting the placeholder), thread a trailing output. The inverse then maps `(slot..., thread) -> (coords..., rep)` with rep last; `loop_partition.cc` consumes exactly this ordering and guards replica-zero for stores. Mirror this packing when you need a fragment as a plain multi-output layout. - Layouts print via `DebugOutput()` / Python `repr`: `Fragment((2,) -> (2,), replicate: 256, thread: _rep, index: (_i,), thread_range: I.Range(0, 256))`. Structured fields are directly accessible from Python: `replicate_size`, `get_thread_size()`, `forward_thread`, `forward_index`, `thread_range`, `get_input_shape()`. **Swizzles** (`tilelang/layout/swizzle.py`, `src/layout/swizzle_mode.*`) are XOR-based shared-memory layouts. They are NOT expressible as strided layouts; the CuTe side models them as a separate `Swizzle` functor (`ComposedLayout`), never as (shape, stride) modes. ## CuTe layout algebra (`src/layout/cute_layout.{h,cc}`, `tilelang/layout/cute.py`) A full CuTe implementation maintained for the MMA/TMA backends, exposed to C++ (`namespace tvm::tl::cute`) and Python (`tilelang.layout.cute`): - Layouts are `(shape, stride)` IntTuple trees, **COLUMN-major (first mode fastest)** — opposite of TileLang's row-major intuition. Multi-output codomains use `ScaledBasis` strides (`v@axis`, `E<i>`). - Algebra: `Coalesce`, `RightInverse`, `LeftInverse`, `Composition`, `Complement`, `Cosize`, `LogicalDivide`, `Filter`, `Restrict`, `Layout::WithShape`, `Parse`/`Print` (exact CuTe spelling). - Converters: `LayoutFromTileLang` (single flat output; multi-output tl::Layouts are serialized ROW-major, so outputs `[thread, slot]` yield `thread * slots + slot`), `LayoutFromTileLangHierarchical` (per-axis recovery onto basis axes), `ComposedLayoutFromTileLang` (swizzle recovery). All use probe-then-prove: numerically probe strides at one-hot points, then symbolically PROVE equivalence — a wrong recovery cannot slip through; failure returns `None`/nullopt. **Failure conventions (must respect):** only the three `*FromTileLang` converters return Optional. Every other algebra op ICHECK-crashes on its preconditions (composition divisibility, non-constant extents in the probe, complement non-injectivity, restrict rank...). Wrap calls when the input is untrusted. `RightInverse` is PARTIAL: it silently drops stride-0 and non-const-stride modes and inverts only the maximal contiguous chain — check bijectivity via `size(right_inverse(F)) == size(F)`. **Conversion caveats:** the converters read only `GetForwardIndex()`; a Fragment's `forward_thread` and replication are silently ignored unless you pack them into a plain multi-output Layout first (see the canonical packing above; substitute `ReplicationPlaceholder` with a trailing input var — if it leaks into the probe, conversion gracefully returns None). ## Layout inference (`src/transform/layout_inference/`) `tl.LayoutInference` assigns a layout to every fragment buffer and parallel loop nest. Three levels: **strict** (annotations, MMA-imposed) → **common** (BFS propagation through shared buffers) → **free** (per connected component, try every member as inference root, keep the cheapest attempt). Results are stored as IR annotations: `layout_map` (Buffer -> Layout, on the SBlock) and `parallel_loop_layout` (Fragment, on the outermost parallel For); `ParallelLoopLayoutValidator` enforces the annotation contract. "Cheapest" is a pluggable policy (`layout_cost_model.{h,cc}`): - `tl.layout_cost_model="io-aware"` (opt-in): every fragment<->global copy and global-touching parallel loop is charged `max(bandwidth bytes, issue bytes)` under the attempt's layouts, scored symbolically on the CuTe algebra (pack -> `LayoutFromTileLang` -> `RightInverse` -> `Composition`; vector width read off coalesced modes; segments counted at warp/step granularity). Registers are the tiebreak. Statements outside the model are charged a conservative worst case — an attempt must never profit from opacity. - `tl.layout_cost_model="register-count"` (default): register-slots-only ordering. - The scoring formulas are guarded by the Python parity check `maint/layout_inference/run.py --cute` (symbolic scorer vs an independent NumPy enumeration oracle); keep `cute_model.py` in lockstep when changing `layout_cost_model.cc`. Hardware geometry is parameterized: lane width via `MaxVectorLoadBits` (`src/transform/loop_vectorize.h`, SHARED with the vectorizer so the model's width beliefs match codegen), warp size from the target's `thread_warp_size`, segment granularity 128B (see `BindMemoryGeometry`). ## Downstream consumers - `loop_partition.cc`: partitions parallel loops per-thread via the fragment inverse; emits replica-zero guards for replicated stores. - `loop_vectorize.cc`: plans vector widths (`GetVectorizeSize`, `IndicesCanVectorize`); the cost model mirrors its judgment. - TMA/MMA lowering: `src/cuda/op/tma_layout.cc` and `producer_consumer_ws.cc` recover shared-buffer swizzles via `ComposedLayoutFromTileLang`; tcgen05/wgmma macro generators (`tilelang/cuda/intrinsics/macro/`) round-trip TMEM layouts through `to_tilelang`/`from_tilelang_hierarchical`. ## Tools - **`maint/layout_inference/`** — the layout verification harness (see its README): golden layout snapshots per constructed case (`run.py`), lowered vector-width anchors (`--anchor`), and symbolic-vs-oracle parity (`--cute`, backed by `cute_model.py` + the numpy `oracle.py`). Extend it with a new case whenever you change inference, the cost model, or the converters; goldens are recorded then human-reviewed (`--record`). - **Diagnostics**: the inference and cost-model passes log decisions via DLOG (debug builds); `tl.layout_visualization_enable` renders layouts. - **Python inspection**: run `tl.transform.LayoutInference()` on a module and read the annotations (see `maint/layout_inference/common.py` for the extraction idiom), or `cute.Layout.from_tilelang(...)` to see a layout's (shape, stride) normal form. ## File map | area | files | |---|---| | core types | `src/layout/layout.{h,cc}`, `tilelang/layout/{layout,fragment}.py` | | swizzle | `src/layout/swizzle_mode.*`, `tilelang/layout/swizzle*.py` | | CuTe algebra | `src/layout/cute_layout.{h,cc}`, `tilelang/layout/cute.py` | | inference | `src/transform/layout_inference/layout_inference.cc` | | cost model | `src/transform/layout_inference/layout_cost_model.{h,cc}` | | MMA layouts | `src/layout/gemm_layouts.cc`, `src/layout/tcgen05_layout.*` | | verification | `maint/layout_inference/` |
View on GitHub