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, We 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 + +So far we provide two modes, the first one is reduction-tree, it will generate +several reduced outputs and try to do further reduction in between them. It will +form a tree structure which tells how each intermediate output is reduced. +According to different tree traversal mode, it may lead to different result. You +can run with `-reduction-tree='traversal-mode=0'` for example. + +The second mode is opt-reduction-pass, it applies the given passes to reduce the +input. Run it with arg `-opt-reduction-pass='opt-pass=symbol-dce'`. + +### Write the script for testing interesting + +As mentioned, you need to provide a command to specify `mlir-reduce` which case +you're intersting. 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 tranformation passes and some of them are useful for +reducing the input size, e.g., Symbol-DCE. You can just use them to reduce your +case. + +## 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); + 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.