-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mlir-reduce] Add doc for usage of mlir-reduce
Reviewed By: jpienaar Differential Revision: https://reviews.llvm.org/D103683
- Loading branch information
1 parent
371ee32
commit 7dec20d
Showing
1 changed file
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<?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. |