Pretty print on -dump-pass-pipeline (#143223)
This PR makes `dump-pass-pipeline` pretty-print the dumped pipeline. For
large pipelines the current behavior produces a wall of text that is
hard to visually navigate.
For the command
```bash
mlir-opt --pass-pipeline="builtin.module(flatten-memref, expand-strided-metadata,func.func(arith-expand,func.func(affine-scalrep)))" --dump-pass-pipeline
```
Before:
```bash
Pass Manager with 3 passes:
builtin.module(flatten-memref,expand-strided-metadata,func.func(arith-expand{include-bf16=false include-f8e8m0=false},func.func(affine-scalrep)))
```
After:
```bash
Pass Manager with 3 passes:
builtin.module(
flatten-memref,
expand-strided-metadata,
func.func(
arith-expand{include-bf16=false include-f8e8m0=false},
func.func(
affine-scalrep
)
)
)
```
Another nice feature of this is that the pretty-printed string can still
be copy/pasted into `-pass-pipeline` using a quote:
```bash
$ bin/mlir-opt --dump-pass-pipeline test.mlir --pass-pipeline='
builtin.module(
flatten-memref,
expand-strided-metadata,
func.func(
arith-expand{include-bf16=false include-f8e8m0=false},
func.func(
affine-scalrep
)
)
)'
```
---------
Co-authored-by: Jeremy Kun <j2kun@users.noreply.github.com>
This commit is contained in:
@@ -118,8 +118,9 @@ public:
|
||||
function_ref<LogicalResult(const Twine &)> errorHandler);
|
||||
|
||||
/// Prints out the pass in the textual representation of pipelines. If this is
|
||||
/// an adaptor pass, print its pass managers.
|
||||
void printAsTextualPipeline(raw_ostream &os);
|
||||
/// an adaptor pass, print its pass managers. When `pretty` is true, the
|
||||
/// printed pipeline is formatted for readability.
|
||||
void printAsTextualPipeline(raw_ostream &os, bool pretty = false);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Statistics
|
||||
|
||||
@@ -140,10 +140,12 @@ public:
|
||||
detail::OpPassManagerImpl &getImpl();
|
||||
|
||||
/// Prints out the passes of the pass manager as the textual representation
|
||||
/// of pipelines.
|
||||
/// of pipelines. When `pretty` is true, the printed pipeline is formatted
|
||||
/// for readability.
|
||||
///
|
||||
/// Note: The quality of the string representation depends entirely on the
|
||||
/// the correctness of per-pass overrides of Pass::printAsTextualPipeline.
|
||||
void printAsTextualPipeline(raw_ostream &os) const;
|
||||
void printAsTextualPipeline(raw_ostream &os, bool pretty = false) const;
|
||||
|
||||
/// Raw dump of the pass manager to llvm::errs().
|
||||
void dump();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "mlir/IR/Threading.h"
|
||||
#include "mlir/IR/Verifier.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "mlir/Support/IndentedOstream.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
@@ -80,14 +81,19 @@ void Pass::copyOptionValuesFrom(const Pass *other) {
|
||||
}
|
||||
|
||||
/// Prints out the pass in the textual representation of pipelines. If this is
|
||||
/// an adaptor pass, print its pass managers.
|
||||
void Pass::printAsTextualPipeline(raw_ostream &os) {
|
||||
/// an adaptor pass, print its pass managers. When `pretty` is true, the
|
||||
/// printed pipeline is formatted for readability.
|
||||
void Pass::printAsTextualPipeline(raw_ostream &os, bool pretty) {
|
||||
// Special case for adaptors to print its pass managers.
|
||||
if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(this)) {
|
||||
llvm::interleave(
|
||||
adaptor->getPassManagers(),
|
||||
[&](OpPassManager &pm) { pm.printAsTextualPipeline(os); },
|
||||
[&] { os << ","; });
|
||||
[&](OpPassManager &pm) { pm.printAsTextualPipeline(os, pretty); },
|
||||
[&] {
|
||||
os << ",";
|
||||
if (pretty)
|
||||
os << "\n";
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Otherwise, print the pass argument followed by its options. If the pass
|
||||
@@ -390,27 +396,51 @@ StringRef OpPassManager::getOpAnchorName() const {
|
||||
}
|
||||
|
||||
/// Prints out the passes of the pass manager as the textual representation
|
||||
/// of pipelines.
|
||||
/// of pipelines. When `pretty` is true, the printed pipeline is formatted for
|
||||
/// readability.
|
||||
void printAsTextualPipeline(
|
||||
raw_ostream &os, StringRef anchorName,
|
||||
const llvm::iterator_range<OpPassManager::pass_iterator> &passes) {
|
||||
raw_indented_ostream &os, StringRef anchorName,
|
||||
const llvm::iterator_range<OpPassManager::pass_iterator> &passes,
|
||||
bool pretty = false) {
|
||||
os << anchorName << "(";
|
||||
if (pretty) {
|
||||
os << "\n";
|
||||
os.indent();
|
||||
}
|
||||
llvm::interleave(
|
||||
passes, [&](mlir::Pass &pass) { pass.printAsTextualPipeline(os); },
|
||||
[&]() { os << ","; });
|
||||
passes,
|
||||
[&](mlir::Pass &pass) { pass.printAsTextualPipeline(os, pretty); },
|
||||
[&]() {
|
||||
os << ",";
|
||||
if (pretty)
|
||||
os << "\n";
|
||||
});
|
||||
if (pretty) {
|
||||
os << "\n";
|
||||
os.unindent();
|
||||
}
|
||||
os << ")";
|
||||
}
|
||||
void OpPassManager::printAsTextualPipeline(raw_ostream &os) const {
|
||||
void printAsTextualPipeline(
|
||||
raw_ostream &os, StringRef anchorName,
|
||||
const llvm::iterator_range<OpPassManager::pass_iterator> &passes,
|
||||
bool pretty) {
|
||||
raw_indented_ostream indentedOS(os);
|
||||
printAsTextualPipeline(indentedOS, anchorName, passes, pretty);
|
||||
}
|
||||
void OpPassManager::printAsTextualPipeline(raw_ostream &os, bool pretty) const {
|
||||
StringRef anchorName = getOpAnchorName();
|
||||
raw_indented_ostream indentedOS(os);
|
||||
::printAsTextualPipeline(
|
||||
os, anchorName,
|
||||
indentedOS, anchorName,
|
||||
{MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(),
|
||||
MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end()});
|
||||
MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end()},
|
||||
pretty);
|
||||
}
|
||||
|
||||
void OpPassManager::dump() {
|
||||
llvm::errs() << "Pass Manager with " << impl->passes.size() << " passes:\n";
|
||||
printAsTextualPipeline(llvm::errs());
|
||||
printAsTextualPipeline(llvm::errs(), /*pretty=*/true);
|
||||
llvm::errs() << "\n";
|
||||
}
|
||||
|
||||
@@ -466,7 +496,6 @@ llvm::hash_code OpPassManager::hash() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// OpToOpPassAdaptor
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -871,7 +900,8 @@ LogicalResult PassManager::run(Operation *op) {
|
||||
// Initialize all of the passes within the pass manager with a new generation.
|
||||
llvm::hash_code newInitKey = context->getRegistryHash();
|
||||
llvm::hash_code pipelineKey = hash();
|
||||
if (newInitKey != initializationKey || pipelineKey != pipelineInitializationKey) {
|
||||
if (newInitKey != initializationKey ||
|
||||
pipelineKey != pipelineInitializationKey) {
|
||||
if (failed(initialize(context, impl->initializationGeneration + 1)))
|
||||
return failure();
|
||||
initializationKey = newInitKey;
|
||||
|
||||
@@ -443,7 +443,8 @@ makeReproducerStreamFactory(StringRef outputFile) {
|
||||
|
||||
void printAsTextualPipeline(
|
||||
raw_ostream &os, StringRef anchorName,
|
||||
const llvm::iterator_range<OpPassManager::pass_iterator> &passes);
|
||||
const llvm::iterator_range<OpPassManager::pass_iterator> &passes,
|
||||
bool pretty = false);
|
||||
|
||||
std::string mlir::makeReproducer(
|
||||
StringRef anchorName,
|
||||
|
||||
@@ -38,11 +38,71 @@
|
||||
|
||||
// CHECK_1: test-options-pass{enum=zero list={1,2,3,4,5} string=nested_pipeline{arg1=10 arg2=" {} " arg3=true} string-list={a,b,c,d}}
|
||||
// CHECK_2: test-options-pass{enum=one list={1} string= string-list={a,b}}
|
||||
// CHECK_3: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string= })))
|
||||
// CHECK_4: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string=foobar })))
|
||||
// CHECK_5: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string={foo bar baz} })))
|
||||
// CHECK_6: builtin.module(builtin.module(func.func(test-options-pass{enum=zero list={3} string= }),func.func(test-options-pass{enum=one list={1,2,3,4} string=foo"bar"baz })))
|
||||
// CHECK_7{LITERAL}: builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}}))
|
||||
// CHECK_8{LITERAL}: builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one string=bar }}}))
|
||||
// CHECK_9: builtin.module(func.func(test-options-pass{enum=zero string= string-list={}}))
|
||||
// CHECK_10: builtin.module(func.func(test-options-pass{enum=zero string= string-list={,}}))
|
||||
|
||||
// CHECK_3: builtin.module(
|
||||
// CHECK_3-NEXT: builtin.module(
|
||||
// CHECK_3-NEXT: func.func(
|
||||
// CHECK_3-NEXT: test-options-pass{enum=zero list={3} string= }
|
||||
// CHECK_3-NEXT: ),
|
||||
// CHECK_3-NEXT: func.func(
|
||||
// CHECK_3-NEXT: test-options-pass{enum=one list={1,2,3,4} string= }
|
||||
// CHECK_3-NEXT: )
|
||||
// CHECK_3-NEXT: )
|
||||
// CHECK_3-NEXT: )
|
||||
|
||||
// CHECK_4: builtin.module(
|
||||
// CHECK_4-NEXT: builtin.module(
|
||||
// CHECK_4-NEXT: func.func(
|
||||
// CHECK_4-NEXT: test-options-pass{enum=zero list={3} string= }
|
||||
// CHECK_4-NEXT: ),
|
||||
// CHECK_4-NEXT: func.func(
|
||||
// CHECK_4-NEXT: test-options-pass{enum=one list={1,2,3,4} string=foobar }
|
||||
// CHECK_4-NEXT: )
|
||||
// CHECK_4-NEXT: )
|
||||
// CHECK_4-NEXT: )
|
||||
|
||||
// CHECK_5: builtin.module(
|
||||
// CHECK_5-NEXT: builtin.module(
|
||||
// CHECK_5-NEXT: func.func(
|
||||
// CHECK_5-NEXT: test-options-pass{enum=zero list={3} string= }
|
||||
// CHECK_5-NEXT: ),
|
||||
// CHECK_5-NEXT: func.func(
|
||||
// CHECK_5-NEXT: test-options-pass{enum=one list={1,2,3,4} string={foo bar baz} }
|
||||
// CHECK_5-NEXT: )
|
||||
// CHECK_5-NEXT: )
|
||||
// CHECK_5-NEXT: )
|
||||
|
||||
// CHECK_6: builtin.module(
|
||||
// CHECK_6-NEXT: builtin.module(
|
||||
// CHECK_6-NEXT: func.func(
|
||||
// CHECK_6-NEXT: test-options-pass{enum=zero list={3} string= }
|
||||
// CHECK_6-NEXT: ),
|
||||
// CHECK_6-NEXT: func.func(
|
||||
// CHECK_6-NEXT: test-options-pass{enum=one list={1,2,3,4} string=foo"bar"baz }
|
||||
// CHECK_6-NEXT: )
|
||||
// CHECK_6-NEXT: )
|
||||
// CHECK_6-NEXT: )
|
||||
|
||||
// CHECK_7{LITERAL}: builtin.module(
|
||||
// CHECK_7{LITERAL}-NEXT: func.func(
|
||||
// CHECK_7{LITERAL}-NEXT: test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}}
|
||||
// CHECK_7{LITERAL}-NEXT: )
|
||||
// CHECK_7{LITERAL}-NEXT: )
|
||||
|
||||
// CHECK_8{LITERAL}: builtin.module(
|
||||
// CHECK_8{LITERAL}-NEXT: func.func(
|
||||
// CHECK_8{LITERAL}-NEXT: test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one string=bar }}}
|
||||
// CHECK_8{LITERAL}-NEXT: )
|
||||
// CHECK_8{LITERAL}-NEXT: )
|
||||
|
||||
// CHECK_9: builtin.module(
|
||||
// CHECK_9-NEXT: func.func(
|
||||
// CHECK_9-NEXT: test-options-pass{enum=zero string= string-list={}}
|
||||
// CHECK_9-NEXT: )
|
||||
// CHECK_9-NEXT: )
|
||||
|
||||
// CHECK_10: builtin.module(
|
||||
// CHECK_10-NEXT: func.func(
|
||||
// CHECK_10-NEXT: test-options-pass{enum=zero string= string-list={,}}
|
||||
// CHECK_10-NEXT: )
|
||||
// CHECK_10-NEXT: )
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
// CHECK_ERROR_7: can't run 'wrong-op' pass manager on 'builtin.module' op
|
||||
|
||||
// RUN: mlir-opt %s -pass-pipeline='any(cse)' -dump-pass-pipeline 2>&1 | FileCheck %s -check-prefix=CHECK_ROUNDTRIP
|
||||
// CHECK_ROUNDTRIP: any(cse)
|
||||
// CHECK_ROUNDTRIP: any(
|
||||
// CHECK_ROUNDTRIP-NEXT: cse
|
||||
// CHECK_ROUNDTRIP-NEXT: )
|
||||
|
||||
func.func @foo() {
|
||||
return
|
||||
|
||||
@@ -14,7 +14,12 @@ func.func @bar() {
|
||||
external_resources: {
|
||||
mlir_reproducer: {
|
||||
verify_each: true,
|
||||
// CHECK: builtin.module(func.func(cse,canonicalize{ max-iterations=1 max-num-rewrites=-1 region-simplify=normal test-convergence=false top-down=false}))
|
||||
// CHECK: builtin.module(
|
||||
// CHECK-NEXT: func.func(
|
||||
// CHECK-NEXT: cse,
|
||||
// CHECK-NEXT: canonicalize{ max-iterations=1 max-num-rewrites=-1 region-simplify=normal test-convergence=false top-down=false}
|
||||
// CHECK-NEXT: )
|
||||
// CHECK-NEXT: )
|
||||
pipeline: "builtin.module(func.func(cse,canonicalize{max-iterations=1 max-num-rewrites=-1 region-simplify=normal top-down=false}))",
|
||||
disable_threading: true
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// RUN: mlir-opt %s --log-actions-to=- --composite-fixed-point-pass='name=TestCompositePass pipeline=any(canonicalize,cse)' -split-input-file | FileCheck %s
|
||||
|
||||
// Ensure the composite pass correctly prints its options.
|
||||
// PIPELINE: builtin.module(composite-fixed-point-pass{max-iterations=10 name=TestCompositePass
|
||||
// PIPELINE-SAME: pipeline=canonicalize{ max-iterations=10 max-num-rewrites=-1 region-simplify=normal test-convergence=false top-down=true},cse})
|
||||
// PIPELINE: builtin.module(
|
||||
// PIPELINE-NEXT: composite-fixed-point-pass{max-iterations=10 name=TestCompositePass
|
||||
// PIPELINE-SAME: pipeline=canonicalize{ max-iterations=10 max-num-rewrites=-1 region-simplify=normal test-convergence=false top-down=true},cse}
|
||||
|
||||
// CHECK-LABEL: running `TestCompositePass`
|
||||
// CHECK: running `Canonicalizer`
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
// RUN: mlir-opt %s -pass-pipeline="builtin.module(inline)" -dump-pass-pipeline 2>&1 | FileCheck %s
|
||||
// CHECK: builtin.module(inline{default-pipeline=canonicalize inlining-threshold=4294967295 max-iterations=4 })
|
||||
// CHECK: builtin.module(
|
||||
// CHECK-NEXT: inline{default-pipeline=canonicalize inlining-threshold=4294967295 max-iterations=4 }
|
||||
// CHECK-NEXT: )
|
||||
|
||||
Reference in New Issue
Block a user