forked from OSchip/llvm-project
127 lines
4.8 KiB
Markdown
127 lines
4.8 KiB
Markdown
|
# 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<?xindex>` 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.
|