285 lines
12 KiB
C++
285 lines
12 KiB
C++
//===- GreedyPatternRewriteDriver.h - Greedy Pattern Driver -----*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file declares methods for applying a set of patterns greedily, choosing
|
|
// the patterns with the highest local benefit, until a fixed point is reached.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef MLIR_TRANSFORMS_GREEDYPATTERNREWRITEDRIVER_H_
|
|
#define MLIR_TRANSFORMS_GREEDYPATTERNREWRITEDRIVER_H_
|
|
|
|
#include "mlir/Rewrite/FrozenRewritePatternSet.h"
|
|
|
|
namespace mlir {
|
|
|
|
/// This enum controls which ops are put on the worklist during a greedy
|
|
/// pattern rewrite.
|
|
enum class GreedyRewriteStrictness {
|
|
/// No restrictions wrt. which ops are processed.
|
|
AnyOp,
|
|
/// Only pre-existing and newly created ops are processed.
|
|
ExistingAndNewOps,
|
|
/// Only pre-existing ops are processed.
|
|
ExistingOps
|
|
};
|
|
|
|
enum class GreedySimplifyRegionLevel {
|
|
/// Disable region control-flow simplification.
|
|
Disabled,
|
|
/// Run the normal simplification (e.g. dead args elimination).
|
|
Normal,
|
|
/// Run extra simplificiations (e.g. block merging), these can be
|
|
/// more costly or have some tradeoffs associated.
|
|
Aggressive
|
|
};
|
|
|
|
/// This class allows control over how the GreedyPatternRewriteDriver works.
|
|
class GreedyRewriteConfig {
|
|
public:
|
|
/// This specifies the order of initial traversal that populates the rewriters
|
|
/// worklist. When set to true, it walks the operations top-down, which is
|
|
/// generally more efficient in compile time. When set to false, its initial
|
|
/// traversal of the region tree is bottom up on each block, which may match
|
|
/// larger patterns when given an ambiguous pattern set.
|
|
///
|
|
/// Note: Only applicable when simplifying entire regions.
|
|
bool getUseTopDownTraversal() const { return useTopDownTraversal; }
|
|
GreedyRewriteConfig &setUseTopDownTraversal(bool use = true) {
|
|
useTopDownTraversal = use;
|
|
return *this;
|
|
}
|
|
|
|
/// Perform control flow optimizations to the region tree after applying all
|
|
/// patterns.
|
|
///
|
|
/// Note: Only applicable when simplifying entire regions.
|
|
GreedySimplifyRegionLevel getRegionSimplificationLevel() const {
|
|
return regionSimplificationLevel;
|
|
}
|
|
GreedyRewriteConfig &
|
|
setRegionSimplificationLevel(GreedySimplifyRegionLevel level) {
|
|
regionSimplificationLevel = level;
|
|
return *this;
|
|
}
|
|
|
|
/// This specifies the maximum number of times the rewriter will iterate
|
|
/// between applying patterns and simplifying regions. Use `kNoLimit` to
|
|
/// disable this iteration limit.
|
|
///
|
|
/// Note: Only applicable when simplifying entire regions.
|
|
int64_t getMaxIterations() const { return maxIterations; }
|
|
GreedyRewriteConfig &setMaxIterations(int64_t iterations) {
|
|
maxIterations = iterations;
|
|
return *this;
|
|
}
|
|
|
|
/// This specifies the maximum number of rewrites within an iteration. Use
|
|
/// `kNoLimit` to disable this limit.
|
|
int64_t getMaxNumRewrites() const { return maxNumRewrites; }
|
|
GreedyRewriteConfig &setMaxNumRewrites(int64_t limit) {
|
|
maxNumRewrites = limit;
|
|
return *this;
|
|
}
|
|
|
|
static constexpr int64_t kNoLimit = -1;
|
|
|
|
/// Only ops within the scope are added to the worklist. If no scope is
|
|
/// specified, the closest enclosing region around the initial list of ops
|
|
/// (or the specified region, depending on which greedy rewrite entry point
|
|
/// is used) is used as a scope. Any region being simplified must be enclosed
|
|
/// by the scope (or equal to it).
|
|
Region *getScope() const { return scope; }
|
|
GreedyRewriteConfig &setScope(Region *scope) {
|
|
this->scope = scope;
|
|
return *this;
|
|
}
|
|
|
|
/// Strict mode can restrict the ops that are added to the worklist during
|
|
/// the rewrite.
|
|
///
|
|
/// * GreedyRewriteStrictness::AnyOp: No ops are excluded.
|
|
/// * GreedyRewriteStrictness::ExistingAndNewOps: Only pre-existing ops (that
|
|
/// were on the worklist at the very beginning) and newly created ops are
|
|
/// enqueued. All other ops are excluded.
|
|
/// * GreedyRewriteStrictness::ExistingOps: Only pre-existing ops (that were
|
|
/// were on the worklist at the very beginning) enqueued. All other ops are
|
|
/// excluded.
|
|
GreedyRewriteStrictness getStrictness() const { return strictness; }
|
|
GreedyRewriteConfig &setStrictness(GreedyRewriteStrictness mode) {
|
|
strictness = mode;
|
|
return *this;
|
|
}
|
|
|
|
/// An optional listener that should be notified about IR modifications.
|
|
RewriterBase::Listener *getListener() const { return listener; }
|
|
GreedyRewriteConfig &setListener(RewriterBase::Listener *listener) {
|
|
this->listener = listener;
|
|
return *this;
|
|
}
|
|
|
|
/// Whether this should fold while greedily rewriting.
|
|
bool isFoldingEnabled() const { return fold; }
|
|
GreedyRewriteConfig &enableFolding(bool enable = true) {
|
|
fold = enable;
|
|
return *this;
|
|
}
|
|
|
|
/// If set to "true", constants are CSE'd (even across multiple regions that
|
|
/// are in a parent-ancestor relationship).
|
|
bool isConstantCSEEnabled() const { return cseConstants; }
|
|
GreedyRewriteConfig &enableConstantCSE(bool enable = true) {
|
|
cseConstants = enable;
|
|
return *this;
|
|
}
|
|
|
|
/// If set to "true", full common-subexpression elimination is run on the
|
|
/// scoped region between each pattern-application iteration. Unlike
|
|
/// `cseConstants` (which only deduplicates constant ops via the operation
|
|
/// folder) this runs the standard CSE algorithm and can unblock further
|
|
/// canonicalizations on the next iteration. Off by default because it
|
|
/// rebuilds dominance info each iteration.
|
|
///
|
|
/// Caveats when enabling this option:
|
|
/// - Any listener attached via `setListener` will be notified of
|
|
/// `notifyOperationReplaced` / `notifyOperationErased` events generated
|
|
/// by CSE. Pattern authors relying on operation identity (e.g., the
|
|
/// transform dialect's handle tracking) must account for this.
|
|
/// - CSE-driven changes feed back into the iteration loop: a pattern that
|
|
/// re-materializes duplicates that CSE keeps collapsing can extend the
|
|
/// iteration count and, in the worst case, hit `maxIterations`. Under
|
|
/// `testConvergence=true` such pipelines will be reported as
|
|
/// non-convergent.
|
|
bool isCSEBetweenIterationsEnabled() const { return cseBetweenIterations; }
|
|
GreedyRewriteConfig &enableCSEBetweenIterations(bool enable = true) {
|
|
cseBetweenIterations = enable;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Region *scope = nullptr;
|
|
bool useTopDownTraversal = false;
|
|
GreedySimplifyRegionLevel regionSimplificationLevel =
|
|
GreedySimplifyRegionLevel::Aggressive;
|
|
int64_t maxIterations = 10;
|
|
int64_t maxNumRewrites = kNoLimit;
|
|
GreedyRewriteStrictness strictness = GreedyRewriteStrictness::AnyOp;
|
|
RewriterBase::Listener *listener = nullptr;
|
|
bool fold = true;
|
|
bool cseConstants = true;
|
|
bool cseBetweenIterations = false;
|
|
};
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// applyPatternsGreedily
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Rewrite ops in the given region, which must be isolated from above, by
|
|
/// repeatedly applying the highest benefit patterns in a greedy worklist
|
|
/// driven manner until a fixpoint is reached.
|
|
///
|
|
/// The greedy rewrite may prematurely stop after a maximum number of
|
|
/// iterations, which can be configured in the configuration parameter.
|
|
///
|
|
/// Also performs simple dead-code elimination before attempting to match any of
|
|
/// the provided patterns.
|
|
///
|
|
/// A region scope can be set in the configuration parameter. By default, the
|
|
/// scope is set to the specified region. Only in-scope ops are added to the
|
|
/// worklist and only in-scope ops are allowed to be modified by the patterns.
|
|
/// If a scope is set explicitly, it must enclose `region` (i.e., `region`
|
|
/// must be nested within the scope, or equal to it).
|
|
///
|
|
/// Returns "success" if the iterative process converged (i.e., fixpoint was
|
|
/// reached) and no more patterns can be matched within the region. `changed`
|
|
/// is set to "true" if the IR was modified at all.
|
|
///
|
|
/// Note: This method does not apply patterns to the region's parent operation.
|
|
LogicalResult
|
|
applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns,
|
|
GreedyRewriteConfig config = GreedyRewriteConfig(),
|
|
bool *changed = nullptr);
|
|
/// Rewrite ops nested under the given operation, which must be isolated from
|
|
/// above, by repeatedly applying the highest benefit patterns in a greedy
|
|
/// worklist driven manner until a fixpoint is reached.
|
|
///
|
|
/// The greedy rewrite may prematurely stop after a maximum number of
|
|
/// iterations, which can be configured in the configuration parameter.
|
|
///
|
|
/// Also performs simple dead-code elimination before attempting to match any of
|
|
/// the provided patterns.
|
|
///
|
|
/// This overload runs a separate greedy rewrite for each region of the
|
|
/// specified op. A region scope can be set in the configuration parameter. By
|
|
/// default, the scope is set to the region of the current greedy rewrite. Only
|
|
/// in-scope ops are added to the worklist and only in-scope ops and the
|
|
/// specified op itself are allowed to be modified by the patterns. If a scope
|
|
/// is set explicitly, it must enclose `op` (i.e., `op` must be nested within
|
|
/// the scope).
|
|
///
|
|
/// Note: The specified op may be modified, but it may not be removed by the
|
|
/// patterns.
|
|
///
|
|
/// Returns "success" if the iterative process converged (i.e., fixpoint was
|
|
/// reached) and no more patterns can be matched within the region. `changed`
|
|
/// is set to "true" if the IR was modified at all.
|
|
///
|
|
/// Note: This method does not apply patterns to the given operation itself.
|
|
inline LogicalResult
|
|
applyPatternsGreedily(Operation *op, const FrozenRewritePatternSet &patterns,
|
|
GreedyRewriteConfig config = GreedyRewriteConfig(),
|
|
bool *changed = nullptr) {
|
|
bool anyRegionChanged = false;
|
|
bool failed = false;
|
|
for (Region ®ion : op->getRegions()) {
|
|
bool regionChanged;
|
|
failed |= applyPatternsGreedily(region, patterns, config, ®ionChanged)
|
|
.failed();
|
|
anyRegionChanged |= regionChanged;
|
|
}
|
|
if (changed)
|
|
*changed = anyRegionChanged;
|
|
return failure(failed);
|
|
}
|
|
/// Rewrite the specified ops by repeatedly applying the highest benefit
|
|
/// patterns in a greedy worklist driven manner until a fixpoint is reached.
|
|
///
|
|
/// The greedy rewrite may prematurely stop after a maximum number of
|
|
/// iterations, which can be configured in the configuration parameter.
|
|
///
|
|
/// Also performs simple dead-code elimination before attempting to match any of
|
|
/// the provided patterns.
|
|
///
|
|
/// Newly created ops and other pre-existing ops that use results of rewritten
|
|
/// ops or supply operands to such ops are also processed, unless such ops are
|
|
/// excluded via `config.strictMode`. Any other ops remain unmodified (i.e.,
|
|
/// regardless of `strictMode`).
|
|
///
|
|
/// In addition to strictness, a region scope can be specified. Only ops within
|
|
/// the scope are simplified. This is similar to `applyPatternsGreedily`,
|
|
/// where only ops within the given region/op are simplified by default. If no
|
|
/// scope is specified, it is assumed to be the first common enclosing region of
|
|
/// the given ops.
|
|
///
|
|
/// Note that ops in `ops` could be erased as result of folding, becoming dead,
|
|
/// or via pattern rewrites. If more far reaching simplification is desired,
|
|
/// `applyPatternsGreedily` should be used.
|
|
///
|
|
/// Returns "success" if the iterative process converged (i.e., fixpoint was
|
|
/// reached) and no more patterns can be matched. `changed` is set to "true" if
|
|
/// the IR was modified at all. `allOpsErased` is set to "true" if all ops in
|
|
/// `ops` were erased.
|
|
LogicalResult
|
|
applyOpPatternsGreedily(ArrayRef<Operation *> ops,
|
|
const FrozenRewritePatternSet &patterns,
|
|
GreedyRewriteConfig config = GreedyRewriteConfig(),
|
|
bool *changed = nullptr, bool *allErased = nullptr);
|
|
} // namespace mlir
|
|
|
|
#endif // MLIR_TRANSFORMS_GREEDYPATTERNREWRITEDRIVER_H_
|