--- name: dump-intermediate-ir description: >- Augments a `bazel run` CLI command for `heir-opt` with additional flags that dump the IR before each compiler pass, allowing the agent to inspect intermediate IR during compilation, identify a particular pass at which the pipeline fails, or extract the corresponding IR input to a particular pass for further debugging. --- # Dump intermediate IR during compilation ## Overview This skill guides the agent in adding flags to the `heir-opt` tool to dump the compiled IR after each compiler pass to a series of files. These files can then be inspected to identify the failing pass as well as the IR input to the failing pass. ## Usage ### Converting a `bazel run` command To convert a `bazel run` command, add the flags `--mlir-print-ir-before-all` and `--mlir-print-ir-tree-dir=`, where `` is a path to a temporary directory. For example, given the following input command (which can be produced by the `lit-to-bazel` skill), ```bash bazel run //tools:heir-opt -- --mlir-to-ckks='ciphertext-degree=8' --scheme-to-openfhe='entry-function=dot_product' /path/to/file ``` The agent should produce the following (again where `` is chosen appropriately) ```bash bazel run //tools:heir-opt -- --mlir-to-ckks='ciphertext-degree=8' --scheme-to-openfhe='entry-function=dot_product' --mlir-print-ir-before-all --mlir-print-ir-tree-dir= /path/to/file ``` ### Tree structure `--mlir-print-ir-tree-dir` nests the dumped IR in a tree structure that matches the nesting structure of the pass pipeline. In particular, by default all passes will be nested under a `builtin_module_no-symbol-name/` folder, and any passes in the pipeline that are anchored on nested operations like `func.func` will be placed in subdirectories based on the name, such as `func_func_matvec__decrypt__result0` for a pass anchored on `func.func` when the IR contains a function called `matvec__decrypt__result0`. For example, if the flag `--mlir-print-ir-tree-dir=/tmp/mlir` is used, then the dumped files might look like: ``` /tmp/mlir/ └── builtin_module_no-symbol-name ├── 0_annotate-module.mlir ├── 10_compare-to-sign-rewrite.mlir ├── 11_canonicalize.mlir ├── 12_cse.mlir ├── 13_apply-folders.mlir ├── 14_canonicalize.mlir ├── 15_insert-rotate.mlir ├── 16_cse.mlir ├── 17_canonicalize.mlir ├── 18_cse.mlir ├── 19_collapse-insertion-chains.mlir ├── 1_debug-validate-names.mlir ├── 20_sccp.mlir ├── 21_canonicalize.mlir ... ├── func_func__assign_layout_9911323688340753831 │   ├── 47_0_affine-expand-index-ops.mlir │   ├── 47_1_affine-simplify-structures.mlir │   ├── 47_2_affine-loop-normalize.mlir │   └── 47_3_forward-insert-slice-to-extract-slice.mlir ├── func_func_matvec │   ├── 47_0_affine-expand-index-ops.mlir │   ├── 47_1_affine-simplify-structures.mlir │   ├── 47_2_affine-loop-normalize.mlir │   └── 47_3_forward-insert-slice-to-extract-slice.mlir ├── func_func_matvec__decrypt__result0 │   ├── 47_0_affine-expand-index-ops.mlir │   ├── 47_1_affine-simplify-structures.mlir │   ├── 47_2_affine-loop-normalize.mlir │   └── 47_3_forward-insert-slice-to-extract-slice.mlir └── func_func_matvec__encrypt__arg0 ├── 47_0_affine-expand-index-ops.mlir ├── 47_1_affine-simplify-structures.mlir ├── 47_2_affine-loop-normalize.mlir └── 47_3_forward-insert-slice-to-extract-slice.mlir ``` ### Dumped file contents At the top of each file, a comment is printed indicating how the file was generated, containing e.g., `IR Dump Before CSEPass (cse)`. This will say whether the IR was dumped before or after a pass, as well as whether (in the case of `--mlir-print-ir-after-failure`) the pass failed. Below the comment, the MLIR is printed in textual format. ## Gotchas - **Stale files**: `--mlir-print-ir-tree-dir` does not delete existing files, so if the command is re-run, be sure to remove the existing temporary files first. - **Dump before vs after**: `--mlir-print-ir-before-all` prints the IR before a pass runs, which is required to extract the IR if a pass has a hard error like a segfault. To dump the IR *after* each pass, use `--mlir-print-ir-after-all`. - **Dumping after failure**: For some errors without stack traces (like a dialect conversion failure where the compiler fails gracefully), you can use `--mlir-print-ir-after-failure` to dump the IR at failure time, which can provide additional debugging information. One notable example is when dialect conversion fails, the IR dumped by `--mlir-print-ir-after-failure` includes the partially-converted IR including any `unrealized_conversion_cast` ops that failed to be removed. - **Dumping after change**: The number of files can be reduced by using `--mlir-print-ir-after-change`, which only prints the IR if the compiler pass changed it. There is no "before change" analogue. - **Dumping after change**: The number of files can be reduced by using `--mlir-print-ir-before=`, which only prints the IR before the target pass. Since a pass can be run multiple times in a pipeline, this may still produce many files if, for example, the pass is a common one like `cse`. ```markdown Copy this checklist and track progress: - [ ] Step 1: Identify the command to convert, perhaps with `lit-to-bazel` - [ ] Step 2: Add the relevant flags, defaulting to `--mlir-print-ir-before-all` and `--mlir-print-ir-tree-dir` if there is no specific reason to use the more specific flags. - [ ] Step 3: Inspect the generated command and run it. - [ ] Step 4: Inspect the dumped files for information. ```