diff --git a/mlir/docs/Tools/mlir-reduce.md b/mlir/docs/Tools/mlir-reduce.md new file mode 100644 --- /dev/null +++ b/mlir/docs/Tools/mlir-reduce.md @@ -0,0 +1,126 @@ +# MLIR Reduce + +[TOC] + +An MLIR input may trigger bugs after series of transformations. To root cause +the problem or help verification after fixes, developers want to be able to +reduce the size of a reproducer for a bug. This document describes +`mlir-reduce`, which is similar to +[bugpoint](https://llvm.org/docs/CommandGuide/bugpoint.html), a tool that can +reduce the size of the input needed to trigger the error. + +`mlir-reduce` supports reducing the input in several ways, including simply +deleting code not required to reproduce an error, applying the reducer +patterns heuristically or run with optimization passes to reduce the input. To +use it, the first thing you need to do is, provide a command which tells if an +input is interesting, e.g., exhibits the characteristics that you would like to +focus on. For example, you may want to see if `mlir-opt` invocation fails after +it runs on the certain MLIR input. Afterwards, select your reduction strategy +then `mlir-reduce` will do the remining works for you. + +## How to Use it + +`mlir-reduce` adopts reduction-tree algorithm to reduce the input. it generates +several reduced outputs and do the further reduction in between them according +to the tree traversal strategy. The different strategies may lead to different +result and different time complexity. You can run as +`-reduction-tree='traversal-mode=0'` to select the mode for example. + +### Write the script for testing interesting + +As mentioned, you need to provide a command to specify `mlir-reduce` which case +you're interesting. For each intermediate output generated during reduction, +`mlir-reduce` will run the command over the it, the script should returns 1 for +interesting case, 0 otherwise. The sample script, + +```shell +mlir-opt -convert-vector-to-spirv $1 | grep "failed to materialize" +if [[ $? -eq 1 ]]; then + exit 1 +else + exit 0 +fi +``` + +The sample usage will be like, note that the `test` argument is part of the mode +argument. + +```shell +mlir-reduce $INPUT -reduction-tree='traversal-mode=0 test=$TEST_SCRIPT' +``` + +## Available reduction strategies + +### Operation elimination + +`mlir-reduce` will try to remove the operations directly. This is the most +aggressive reduction as it may result in an invalid output as long as it ends up +retaining the error message that the test script is interesting. To avoid that, +`mlir-reduce` always checks the validity and it expects the user will provide a +valid input as well. + +### Rewrite patterns into simpler forms + +In some cases, rewrite an operation into a simpler or smaller form can still +retain the interestingness. For example, `mlir-reduce` will try to rewrite a +`tensor` with unknown rank into a constant rank one like +`tensor<1xi32>`. Not only produce a simpler operation, it may introduce further +reduction chances because of precise type information. + +MLIR supports dialects and `mlir-reduce` supports rewrite patterns for every +dialect as well. Which means you can have the dialect specific rewrite patterns. +To do that, you need to implement the `DialectReductionPatternInterface`. For +example, + +```c++ +#include "mlir/Reducer/ReductionPatternInterface.h" + +struct MyReductionPatternInterface : public DialectReductionPatternInterface { + virtual void + populateReductionPatterns(RewritePatternSet &patterns) const final { + populateMyReductionPatterns(patterns); + } +} +``` + +`mlir-reduce` will call `populateReductionPatterns` to collect the reduction +rewrite patterns provided by each dialect. Here's a hint, if you use +[DRR](../DeclarativeRewrites.md) to write the reduction patterns, you can +leverage the method `populateWithGenerated` generated by `mlir-tblgen`. + +### Reduce with built-in optimization passes + +MLIR provides amount of transformation passes and some of them are useful for +reducing the input size, e.g., Symbol-DCE. `mlir-reduce` will schedule them +along with above two strategies. + +## Build a custom mlir-reduce + +In the cases of, 1. have defined a custom syntax, 2. the failure is specific to +certain dialects or 3. there's a dialect specific reducer patterns, you need to +build your own `mlir-reduce`. Link it with `MLIRReduceLib` and implement it +like, + +```c++ +#include "mlir/Tools/mlir-reduce/MlirReduceMain.h" +using namespace mlir; + +int main(int argc, char **argv) { + DialectRegistry registry; + registerMyDialects(registry); + // Register the DialectReductionPatternInterface if any. + MLIRContext context(registry); + return failed(mlirReduceMain(argc, argv, context)); +} + +``` + +## Future works + +`mlir-reduce` is missing several features, + +* `-reduction-tree` now only supports `Single-Path` traversal mode, extends it +with different traveral strategies may reduce the input better. +* Produce the optimial result when interruped. The reduction process may take +a quite long time, it'll be better to get an optimal result so far while an +interrup is triggered.