# Pass Diff Pass Diff shows how TileLang's TIR changes as compiler passes run. It captures the IR before and after each pass, computes a unified diff, and writes the result to the terminal, an interactive HTML report, or both. Use the environment-variable workflow to observe the complete lowering pipeline without modifying a program. Use the Python API when you want to apply and inspect a specific pass sequence. ## Observe the Lowering Pipeline Set `TILELANG_PASS_DIFF` before starting the Python process: ```bash # Print colored diffs while passes run. TILELANG_PASS_DIFF=terminal python my_script.py # Write an HTML report. TILELANG_PASS_DIFF=html python my_script.py # Print terminal diffs and write an HTML report. TILELANG_PASS_DIFF=both python my_script.py ``` TileLang reads this setting when `tilelang` is imported and installs a process- wide hook around TVM pass execution. Setting the variable after importing TileLang does not enable the hook for that process. | Variable | Accepted values | Default | | --- | --- | --- | | `TILELANG_PASS_DIFF` | `terminal`, `html`, `both`; `1`, `true`, `yes`, and `on` also select terminal output | `0` (disabled) | | `TILELANG_PASS_DIFF_OUTPUT` | Directory for pipeline HTML reports | `tmp/pass_diff_output` | False-like values (`0`, `false`, `no`, `off`, or an empty string) disable the hook. An unrecognized nonempty value falls back to terminal output. HTML reports use a timestamped filename such as `pass_diff_20260710_153000.html`. The report is refreshed after each pass so a partial result is available even during a long compilation, then finalized at process exit. ```{figure} ../_static/img/pass_diff_html.png :width: 600 :alt: Pass Diff HTML report showing TIR before and after a compiler pass :align: center Pass Diff HTML report. ``` The HTML viewer provides side-by-side before and after source, collapsible pass sections, expandable unchanged context, copy controls, light and dark themes, and aggregate insertion and deletion counts. :::{note} The pipeline hook wraps `tvm.ir.transform.Pass.__call__` for the entire process, so it observes TVM passes outside the immediate `tilelang.compile()` call as well. Capturing IR and, in HTML mode, rewriting the report after every pass adds debugging overhead. Leave the feature disabled for normal builds and benchmarks. ::: ## Compare Selected Passes The `pass_diff` Python API accepts a `PrimFunc` or `IRModule` and applies the provided passes in order: ```python import tilelang from tilelang import tvm from tilelang.utils.pass_diff import pass_diff steps = pass_diff( func, [ ("AnnotateDeviceRegions", tvm.tirx.transform.AnnotateDeviceRegions()), ("SplitHostDevice", tvm.tirx.transform.SplitHostDevice()), ("ThreadSync", tilelang.transform.ThreadSync("shared")), ], mode="both", context=5, html_path="tmp/selected_passes.html", ) ``` A single unnamed pass is also accepted: ```python steps = pass_diff(func, tilelang.transform.ThreadSync("shared")) ``` | Parameter | Description | Default | | --- | --- | --- | | `func_or_mod` | Starting `PrimFunc` or `IRModule`; a `PrimFunc` is wrapped as `main` | Required | | `passes` | One pass, a list of passes, or a list of `(name, pass)` pairs | Required | | `mode` | `terminal`, `html`, or `both` | `terminal` | | `context` | Unchanged context lines included around each diff hunk | `3` | | `html_path` | Output file used by `html` and `both` modes | `pass_diff_report.html` | The return value contains one dictionary per pass with these fields: | Field | Meaning | | --- | --- | | `name` | Explicit step name, or a name derived from the pass type | | `before_script` | Complete TIR script before the pass | | `after_script` | Complete TIR script after the pass | | `diff_lines` | Unified diff as a list of strings | | `insertions` | Number of added lines | | `deletions` | Number of removed lines | | `changed` | Whether the pass changed the rendered TIR | For example, a test can assert that a transformation introduced an expected intrinsic: ```python steps = pass_diff(func, tilelang.transform.ThreadSync("shared")) assert steps[0]["changed"] assert "tvm_storage_sync" in steps[0]["after_script"] ``` Do not enable the process-wide `TILELANG_PASS_DIFF` hook when using the programmatic API unless you intentionally want both layers of reporting. The API invokes each pass normally, so the global hook would capture those same invocations again. ## Choosing an Output Mode - Use `terminal` for a small number of passes and immediate feedback. - Use `html` when a lowering pipeline produces many changes or you need to compare complete before and after scripts. - Use `both` when iterating interactively but retaining a shareable report. - Increase `context` only for the programmatic API; the process-wide hook uses three context lines. Pass Diff compares the textual form returned by `IRModule.script()`. A textual change is useful evidence about a pass, but it does not by itself establish a semantic or performance change.