MLIR 24.0.0git
NestedMatcher.h
Go to the documentation of this file.
1//===- NestedMacher.h - Nested matcher for Function -------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef MLIR_DIALECT_AFFINE_ANALYSIS_NESTEDMATCHER_H
10#define MLIR_DIALECT_AFFINE_ANALYSIS_NESTEDMATCHER_H
11
12#include "mlir/IR/BuiltinOps.h"
13#include "mlir/IR/Operation.h"
14#include "llvm/Support/Allocator.h"
15
16namespace mlir {
17class Operation;
18
19namespace affine {
20class NestedPattern;
21
22/// An NestedPattern captures nested patterns in the IR.
23/// It is used in conjunction with a scoped NestedPatternContext which is an
24/// llvm::BumpPtrAllocator that handles memory allocations efficiently and
25/// avoids ownership issues.
26///
27/// In order to use NestedPatterns, first create a scoped context.
28/// When the context goes out of scope, everything is freed.
29/// This design simplifies the API by avoiding references to the context and
30/// makes it clear that references to matchers must not escape.
31///
32/// Example:
33/// {
34/// NestedPatternContext context;
35/// auto gemmLike = Doall(Doall(Red(LoadStores())));
36/// auto matches = gemmLike.match(f);
37/// // do work on matches
38/// } // everything is freed
39///
40///
41/// Nested abstraction for matching results.
42/// Provides access to the nested Operation* captured by a Matcher.
43///
44/// A NestedMatch contains an Operation* and the children NestedMatch and is
45/// thus cheap to copy. NestedMatch is stored in a scoped bumper allocator whose
46/// lifetime is managed by an RAII NestedPatternContext.
48public:
49 static NestedMatch build(Operation *operation,
50 ArrayRef<NestedMatch> nestedMatches);
51 NestedMatch(const NestedMatch &) = default;
52 NestedMatch &operator=(const NestedMatch &) = default;
53
54 explicit operator bool() { return matchedOperation != nullptr; }
55
56 Operation *getMatchedOperation() const { return matchedOperation; }
57 ArrayRef<NestedMatch> getMatchedChildren() { return matchedChildren; }
58
59private:
60 friend class NestedPattern;
62
63 /// Underlying global bump allocator managed by a NestedPatternContext.
64 static llvm::BumpPtrAllocator *&allocator();
65
66 NestedMatch() = default;
67
68 /// Payload, holds a NestedMatch and all its children along this branch.
69 Operation *matchedOperation = nullptr;
70 ArrayRef<NestedMatch> matchedChildren;
71};
72
73/// A NestedPattern is a nested operation walker that:
74/// 1. recursively matches a substructure in the tree;
75/// 2. uses a filter function to refine matches with extra semantic
76/// constraints (passed via a lambda of type FilterFunctionType);
77/// 3. TODO: optionally applies actions (lambda).
78///
79/// Nested patterns are meant to capture imperfectly nested loops while matching
80/// properties over the whole loop nest. For instance, in vectorization we are
81/// interested in capturing all the imperfectly nested loops of a certain type
82/// and such that all the load and stores have certain access patterns along the
83/// loops' induction variables). Such NestedMatches are first captured using the
84/// `match` function and are later processed to analyze properties and apply
85/// transformations in a non-greedy way.
86///
87/// The NestedMatches captured in the IR can grow large, especially after
88/// aggressive unrolling. As experience has shown, it is generally better to use
89/// a plain walk over operations to match flat patterns but the current
90/// implementation is competitive nonetheless.
91using FilterFunctionType = std::function<bool(Operation &)>;
92inline bool defaultFilterFunction(Operation &) { return true; }
94public:
97 NestedPattern(const NestedPattern &other);
99
101 // Call destructors manually, ArrayRef is non-owning so it wouldn't call
102 // them, but we should free the memory allocated by std::function outside of
103 // the arena allocator.
104 freeNested();
105 }
106
107 /// Returns all the top-level matches in `op`.
109 op->walk([&](Operation *child) { matchOne(child, matches); });
110 }
111
112 /// Returns the depth of the pattern.
113 unsigned getDepth() const;
114
115private:
117 friend class NestedMatch;
118
119 /// Copies the list of nested patterns to the arena allocator associated with
120 /// this pattern.
121 void copyNestedToThis(ArrayRef<NestedPattern> nested);
122
123 /// Calls destructors on nested patterns.
124 void freeNested();
125
126 /// Underlying global bump allocator managed by a NestedPatternContext.
127 static llvm::BumpPtrAllocator *&allocator();
128
129 /// Matches this pattern against a single `op` and fills matches with the
130 /// result.
131 void matchOne(Operation *op, SmallVectorImpl<NestedMatch> *matches);
132
133 /// Nested patterns to be matched.
134 ArrayRef<NestedPattern> nestedPatterns;
135
136 /// Extra filter function to apply to prune patterns as the IR is walked.
137 FilterFunctionType filter;
138
139 /// skip is an implementation detail needed so that we can implement match
140 /// without switching on the type of the Operation. The idea is that a
141 /// NestedPattern first checks if it matches locally and then recursively
142 /// applies its nested matchers to its elem->nested. Since we want to rely on
143 /// the existing operation walking functionality rather than duplicate
144 /// it, we allow an off-by-one traversal to account for the fact that we
145 /// write:
146 ///
147 /// void match(Operation *elem) {
148 /// for (auto &c : getNestedPatterns()) {
149 /// NestedPattern childPattern(...);
150 /// ^~~~ Needs off-by-one skip.
151 ///
152 Operation *skip;
153};
154
155/// RAII structure to transparently manage the bump allocator for
156/// NestedPattern and NestedMatch classes. This avoids passing a context to
157/// all the API functions.
159public:
161 assert(NestedMatch::allocator() == nullptr &&
162 "Only a single NestedPatternContext is supported");
163 assert(NestedPattern::allocator() == nullptr &&
164 "Only a single NestedPatternContext is supported");
165 NestedMatch::allocator() = &allocator;
166 NestedPattern::allocator() = &allocator;
167 }
169 NestedMatch::allocator() = nullptr;
170 NestedPattern::allocator() = nullptr;
171 }
172 llvm::BumpPtrAllocator allocator;
173};
174
175namespace matcher {
176// Syntactic sugar NestedPattern builder functions.
178NestedPattern If(const NestedPattern &child);
179NestedPattern If(const FilterFunctionType &filter, const NestedPattern &child);
182 ArrayRef<NestedPattern> nested = {});
183NestedPattern For(const NestedPattern &child);
184NestedPattern For(const FilterFunctionType &filter, const NestedPattern &child);
187 ArrayRef<NestedPattern> nested = {});
188
189bool isLoadOrStore(Operation &op);
190
191} // namespace matcher
192} // namespace affine
193} // namespace mlir
194
195#endif // MLIR_DIALECT_AFFINE_ANALYSIS_NESTEDMATCHER_H
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:851
NestedMatch & operator=(const NestedMatch &)=default
ArrayRef< NestedMatch > getMatchedChildren()
NestedMatch(const NestedMatch &)=default
Operation * getMatchedOperation() const
friend class NestedPatternContext
static NestedMatch build(Operation *operation, ArrayRef< NestedMatch > nestedMatches)
llvm::BumpPtrAllocator allocator
void match(Operation *op, SmallVectorImpl< NestedMatch > *matches)
Returns all the top-level matches in op.
unsigned getDepth() const
Returns the depth of the pattern.
NestedPattern & operator=(const NestedPattern &other)
NestedPattern(ArrayRef< NestedPattern > nested, FilterFunctionType filter=defaultFilterFunction)
NestedPattern If(const NestedPattern &child)
NestedPattern For(const NestedPattern &child)
bool isLoadOrStore(Operation &op)
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
std::function< bool(Operation &)> FilterFunctionType
A NestedPattern is a nested operation walker that:
bool defaultFilterFunction(Operation &)
Include the generated interface declarations.