MLIR 24.0.0git
SparseIterationToScf.cpp
Go to the documentation of this file.
1
3#include "Utils/LoopEmitter.h"
5
11
12using namespace mlir;
13using namespace mlir::sparse_tensor;
14
15static void convertLevelType(SparseTensorEncodingAttr enc, Level lvl,
16 SmallVectorImpl<Type> &fields) {
17 // Position and coordinate buffer in the sparse structure.
18 if (enc.getLvlType(lvl).isWithPosLT())
19 fields.push_back(enc.getPosMemRefType());
20 if (enc.getLvlType(lvl).isWithCrdLT())
21 fields.push_back(enc.getCrdMemRefType());
22 // One index for shape bound (result from lvlOp).
23 fields.push_back(IndexType::get(enc.getContext()));
24}
25
26static std::optional<LogicalResult>
27convertIterSpaceType(IterSpaceType itSp, SmallVectorImpl<Type> &fields) {
28
29 auto idxTp = IndexType::get(itSp.getContext());
30 for (Level l = itSp.getLoLvl(); l < itSp.getHiLvl(); l++)
31 convertLevelType(itSp.getEncoding(), l, fields);
32
33 // Two indices for lower and upper bound (we only need one pair for the last
34 // iteration space).
35 fields.append({idxTp, idxTp});
36 return success();
37}
38
39static std::optional<LogicalResult>
40convertIteratorType(IteratorType itTp, SmallVectorImpl<Type> &fields) {
41 // The actually Iterator Values (that are updated every iteration).
42 auto idxTp = IndexType::get(itTp.getContext());
43 // TODO: handle batch dimension.
44 assert(itTp.getEncoding().getBatchLvlRank() == 0);
45 if (!itTp.isUnique()) {
46 // Segment high for non-unique iterator.
47 fields.push_back(idxTp);
48 }
49 fields.push_back(idxTp);
50 return success();
51}
52
53static ValueRange
54genCoIterateBranchNest(PatternRewriter &rewriter, Location loc, CoIterateOp op,
55 Value loopCrd,
56 ArrayRef<std::unique_ptr<SparseIterator>> iters,
57 ArrayRef<Block *> newBlocks, ArrayRef<Block *> oldBlocks,
58 ArrayRef<Value> userReduc) {
59 if (newBlocks.empty())
60 return userReduc;
61
62 // The current branch that we are handling.
63 Block *newBlock = newBlocks.front();
64 Block *oldBlock = oldBlocks.front();
65 Value casePred = constantI1(rewriter, loc, true);
66 I64BitSet caseBits =
67 op.getRegionDefinedSpace(newBlock->getParent()->getRegionNumber());
68 for (unsigned i : caseBits.bits()) {
69 SparseIterator *it = iters[i].get();
70 Value pred = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
71 it->getCrd(), loopCrd);
72 casePred = arith::AndIOp::create(rewriter, loc, casePred, pred);
73 }
74 scf::IfOp ifOp = scf::IfOp::create(
75 rewriter, loc, ValueRange(userReduc).getTypes(), casePred, /*else=*/true);
76 rewriter.setInsertionPointToStart(&ifOp.getThenRegion().front());
77
78 // Erase the empty block.
79 rewriter.eraseBlock(&ifOp.getThenRegion().front());
80 // Set up block arguments: user-provided values -> loop coord -> iterators.
81 SmallVector<Value> blockArgs(userReduc);
82 blockArgs.push_back(loopCrd);
83 for (unsigned idx : caseBits.bits())
84 llvm::append_range(blockArgs, iters[idx]->getCursor());
85
86 // Map the old block arguments, because the dialect conversion driver does
87 // not immediately perform SSA value replacements. This function is still
88 // seeing the old uses.
89 IRMapping mapping;
90 for (auto [from, to] : llvm::zip_equal(oldBlock->getArguments(), blockArgs)) {
91 mapping.map(from, to);
92 }
93
94 // Clone the region, we can not erase the region now because the same region
95 // might be a subcase for multiple lattice point.
96 rewriter.cloneRegionBefore(*newBlock->getParent(), ifOp.getThenRegion(),
97 ifOp.getThenRegion().begin(), mapping);
98 // Remove the block arguments, they were already replaced via `mapping`.
99 ifOp.getThenRegion().front().eraseArguments(0, blockArgs.size());
100
101 // replace sparse_tensor::YieldOp -> scf::YieldOp
102 auto spY = cast<sparse_tensor::YieldOp>(&ifOp.getThenRegion().front().back());
103 ValueRange yields = spY.getResults();
104 rewriter.eraseOp(spY);
105 rewriter.setInsertionPointToEnd(&ifOp.getThenRegion().front());
106 scf::YieldOp::create(rewriter, loc, yields);
107
108 // Generates remaining case recursively.
109 rewriter.setInsertionPointToStart(&ifOp.getElseRegion().front());
110 ValueRange res = genCoIterateBranchNest(rewriter, loc, op, loopCrd, iters,
111 newBlocks.drop_front(),
112 oldBlocks.drop_front(), userReduc);
113 if (!res.empty())
114 scf::YieldOp::create(rewriter, loc, res);
115
116 rewriter.setInsertionPointAfter(ifOp);
117 return ifOp.getResults();
118}
119
121 PatternRewriter &rewriter, Location loc, SparseIterator *it,
122 ValueRange reduc,
124 Region &loopBody, SparseIterator *it,
125 ValueRange reduc)>
126 bodyBuilder) {
127 if (it->iteratableByFor()) {
128 auto [lo, hi] = it->genForCond(rewriter, loc);
129 Value step = constantIndex(rewriter, loc, 1);
130 scf::ForOp forOp = scf::ForOp::create(
131 rewriter, loc, lo, hi, step, reduc,
132 [&](OpBuilder &b, Location loc, Value iv, ValueRange iterArgs) {
133 // Empty builder function to ensure that no terminator is created.
134 });
135 {
136 OpBuilder::InsertionGuard guard(rewriter);
137 rewriter.setInsertionPointToStart(forOp.getBody());
138 if (it->randomAccessible())
139 it->locate(rewriter, loc, forOp.getInductionVar());
140 else
141 it->linkNewScope(forOp.getInductionVar());
142 SmallVector<Value> ret = bodyBuilder(rewriter, loc, forOp.getBodyRegion(),
143 it, forOp.getRegionIterArgs());
144
145 rewriter.setInsertionPointToEnd(forOp.getBody());
146 scf::YieldOp::create(rewriter, loc, ret);
147 }
148 return forOp.getResults();
149 }
150
151 SmallVector<Value> ivs(reduc);
152 llvm::append_range(ivs, it->getCursor());
153
154 TypeRange types = ValueRange(ivs).getTypes();
155 auto whileOp = scf::WhileOp::create(rewriter, loc, types, ivs);
156 {
157 OpBuilder::InsertionGuard guard(rewriter);
158 // Generates loop conditions.
159 SmallVector<Location> l(types.size(), loc);
160 Block *before = rewriter.createBlock(&whileOp.getBefore(), {}, types, l);
161 rewriter.setInsertionPointToStart(before);
162 ValueRange bArgs = before->getArguments();
163 auto [whileCond, remArgs] = it->genWhileCond(rewriter, loc, bArgs);
164 scf::ConditionOp::create(rewriter, loc, whileCond, before->getArguments());
165
166 // Delegates loop body generation.
167 Region &dstRegion = whileOp.getAfter();
168 Block *after = rewriter.createBlock(&dstRegion, {}, types, l);
169 ValueRange aArgs = whileOp.getAfterArguments();
170 it->linkNewScope(aArgs.drop_front(reduc.size()));
171 aArgs = aArgs.take_front(reduc.size());
172
173 rewriter.setInsertionPointToStart(after);
174 SmallVector<Value> ret = bodyBuilder(rewriter, loc, dstRegion, it, aArgs);
175 rewriter.setInsertionPointToEnd(after);
176
177 // Forward loops
178 SmallVector<Value> yields;
179 llvm::append_range(yields, ret);
180 llvm::append_range(yields, it->forward(rewriter, loc));
181 scf::YieldOp::create(rewriter, loc, yields);
182 }
183 return whileOp.getResults().drop_front(it->getCursor().size());
184}
185
186namespace {
187
188/// Sparse codegen rule for number of entries operator.
189class ExtractIterSpaceConverter
190 : public OpConversionPattern<ExtractIterSpaceOp> {
191public:
192 using OpConversionPattern::OpConversionPattern;
193 LogicalResult
194 matchAndRewrite(ExtractIterSpaceOp op, OneToNOpAdaptor adaptor,
195 ConversionPatternRewriter &rewriter) const override {
196 Location loc = op.getLoc();
197
198 // Construct the iteration space.
199 SparseIterationSpace space(loc, rewriter,
200 llvm::getSingleElement(adaptor.getTensor()), 0,
201 op.getLvlRange(), adaptor.getParentIter());
202
203 SmallVector<Value> result = space.toValues();
204 rewriter.replaceOpWithMultiple(op, {result});
205 return success();
206 }
207};
208
209/// Sparse codegen rule for number of entries operator.
210class ExtractValOpConverter : public OpConversionPattern<ExtractValOp> {
211public:
212 using OpConversionPattern::OpConversionPattern;
213 LogicalResult
214 matchAndRewrite(ExtractValOp op, OneToNOpAdaptor adaptor,
215 ConversionPatternRewriter &rewriter) const override {
216 Location loc = op.getLoc();
217 Value pos = adaptor.getIterator().back();
218 Value valBuf = ToValuesOp::create(
219 rewriter, loc, llvm::getSingleElement(adaptor.getTensor()));
220 rewriter.replaceOpWithNewOp<memref::LoadOp>(op, valBuf, pos);
221 return success();
222 }
223};
224
225class SparseIterateOpConverter : public OpConversionPattern<IterateOp> {
226public:
227 using OpConversionPattern::OpConversionPattern;
228 LogicalResult
229 matchAndRewrite(IterateOp op, OneToNOpAdaptor adaptor,
230 ConversionPatternRewriter &rewriter) const override {
231 if (!op.getCrdUsedLvls().empty())
232 return rewriter.notifyMatchFailure(
233 op, "non-empty coordinates list not implemented.");
234
235 Location loc = op.getLoc();
236
237 auto iterSpace = SparseIterationSpace::fromValues(
238 op.getIterSpace().getType(), adaptor.getIterSpace(), 0);
239
240 std::unique_ptr<SparseIterator> it =
241 iterSpace.extractIterator(rewriter, loc);
242
243 SmallVector<Value> ivs;
244 for (ValueRange inits : adaptor.getInitArgs())
245 llvm::append_range(ivs, inits);
246
247 // Type conversion on iterate op block.
248 unsigned numOrigArgs = op.getBody()->getArgumentTypes().size();
249 TypeConverter::SignatureConversion signatureConversion(numOrigArgs);
250 if (failed(typeConverter->convertSignatureArgs(
251 op.getBody()->getArgumentTypes(), signatureConversion)))
252 return rewriter.notifyMatchFailure(
253 op, "failed to convert iterate region argurment types");
254
255 Block *block = rewriter.applySignatureConversion(
256 op.getBody(), signatureConversion, getTypeConverter());
258 rewriter, loc, it.get(), ivs,
259 [block](PatternRewriter &rewriter, Location loc, Region &loopBody,
260 SparseIterator *it, ValueRange reduc) -> SmallVector<Value> {
261 SmallVector<Value> blockArgs(reduc);
262 // TODO: Also appends coordinates if used.
263 // blockArgs.push_back(it->deref(rewriter, loc));
264 llvm::append_range(blockArgs, it->getCursor());
265
266 Block *dstBlock = &loopBody.getBlocks().front();
267 rewriter.inlineBlockBefore(block, dstBlock, dstBlock->end(),
268 blockArgs);
269 auto yield = llvm::cast<sparse_tensor::YieldOp>(dstBlock->back());
270 // We can not use ValueRange as the operation holding the values will
271 // be destroyed.
272 SmallVector<Value> result(yield.getResults());
273 rewriter.eraseOp(yield);
274 return result;
275 });
276
277 rewriter.replaceOp(op, ret);
278 return success();
279 }
280};
281
282class SparseCoIterateOpConverter : public OpConversionPattern<CoIterateOp> {
283 using OpConversionPattern::OpConversionPattern;
284
285 LogicalResult
286 matchAndRewrite(CoIterateOp op, OneToNOpAdaptor adaptor,
287 ConversionPatternRewriter &rewriter) const override {
288 assert(op.getSpaceDim() == 1 && "Not implemented");
289 Location loc = op.getLoc();
290
291 I64BitSet denseBits(0);
292 for (auto [idx, spaceTp] : llvm::enumerate(op.getIterSpaces().getTypes()))
293 if (all_of(cast<IterSpaceType>(spaceTp).getLvlTypes(), isDenseLT))
294 denseBits.set(idx);
295
296 // If there exists a case that only contains dense spaces. I.e., case
297 // bits is a subset of dense bits, or when there is a full empty case (due
298 // to complements), we need a universal pointer to forward the coiteration
299 // loop.
300 bool needUniv =
301 any_of(op.getRegionDefinedSpaces(), [denseBits](I64BitSet caseBits) {
302 // A case for complement.
303 if (caseBits.count() == 0)
304 return true;
305 // An all-dense case.
306 return caseBits.isSubSetOf(denseBits);
307 });
308 assert(!needUniv && "Not implemented");
309 (void)needUniv;
310
311 SmallVector<Block *> newBlocks;
312 DenseMap<Block *, Block *> newToOldBlockMap;
313 for (Region &region : op.getCaseRegions()) {
314 // Do a one-shot type conversion on all region blocks, since the same
315 // region might be used multiple time.
316 Block *block = &region.getBlocks().front();
317 TypeConverter::SignatureConversion blockTypeMapping(
318 block->getArgumentTypes().size());
319 if (failed(typeConverter->convertSignatureArgs(block->getArgumentTypes(),
320 blockTypeMapping))) {
321 return rewriter.notifyMatchFailure(
322 op, "failed to convert coiterate region argurment types");
323 }
324
325 newBlocks.push_back(rewriter.applySignatureConversion(
326 block, blockTypeMapping, getTypeConverter()));
327 newToOldBlockMap[newBlocks.back()] = block;
328 }
329
330 SmallVector<SparseIterationSpace> spaces;
331 SmallVector<std::unique_ptr<SparseIterator>> iters;
332 for (auto [spaceTp, spaceVals] : llvm::zip_equal(
333 op.getIterSpaces().getTypes(), adaptor.getIterSpaces())) {
334 // TODO: do we really need tid?
335 spaces.push_back(SparseIterationSpace::fromValues(
336 cast<IterSpaceType>(spaceTp), spaceVals, /*tid=*/0));
337 // Extract the iterator.
338 iters.push_back(spaces.back().extractIterator(rewriter, loc));
339 }
340
341 auto getFilteredIters = [&iters](I64BitSet caseBits) {
342 // Retrives a vector of pointers to the iterators used in the case.
343 SmallVector<SparseIterator *> validIters;
344 for (auto idx : caseBits.bits())
345 validIters.push_back(iters[idx].get());
346 return validIters;
347 };
348
349 // Get a flattened user-provided loop reduction values.
350 SmallVector<Value> userReduc;
351 for (ValueRange r : adaptor.getInitArgs())
352 llvm::append_range(userReduc, r);
353
354 // TODO: we need to sort the cases such that they appears in lexical order.
355 // Although sparsification always generates cases in that order, it might
356 // not be the case for human-written code.
357
358 // Generates a loop sequence, one loop per case.
359 for (auto [r, caseBits] :
360 llvm::zip_equal(newBlocks, op.getRegionDefinedSpaces())) {
361 assert(caseBits.count() > 0 && "Complement space not implemented");
362
363 // Retrives a vector of pointers to the iterators used in the case.
364 SmallVector<SparseIterator *> validIters = getFilteredIters(caseBits);
365
366 if (validIters.size() > 1) {
367 auto [loop, loopCrd] =
368 genCoIteration(rewriter, loc, validIters, userReduc,
369 /*uniIdx=*/nullptr, /*userReducFirst=*/true);
370
371 // 1st. find all the cases that is a strict subset of the current case
372 // condition, for which we generate one branch per case inside the loop.
373 // The subcases are never empty, it must contains at least the current
374 // region itself.
375 // TODO: these cases should be sorted.
376 SmallVector<Region *> subCases =
377 op.getSubCasesOf(r->getParent()->getRegionNumber());
378 SmallVector<Block *> newBlocks, oldBlocks;
379 for (Region *r : subCases) {
380 newBlocks.push_back(&r->front());
381 oldBlocks.push_back(newToOldBlockMap[newBlocks.back()]);
382 }
383 assert(!subCases.empty());
384
386 rewriter, loc, op, loopCrd, iters, newBlocks, oldBlocks, userReduc);
387
388 SmallVector<Value> nextIterYields(res);
389 // 2nd. foward the loop.
390 for (SparseIterator *it : validIters) {
391 Value cmp = arith::CmpIOp::create(
392 rewriter, loc, arith::CmpIPredicate::eq, it->getCrd(), loopCrd);
393 it->forwardIf(rewriter, loc, cmp);
394 llvm::append_range(nextIterYields, it->getCursor());
395 }
396 scf::YieldOp::create(rewriter, loc, nextIterYields);
397
398 // Exit the loop, relink the iterator SSA value.
399 rewriter.setInsertionPointAfter(loop);
400 ValueRange iterVals = loop->getResults().drop_front(userReduc.size());
401 for (SparseIterator *it : validIters)
402 iterVals = it->linkNewScope(iterVals);
403 assert(iterVals.empty());
404
405 ValueRange curResult = loop->getResults().take_front(userReduc.size());
406 userReduc.assign(curResult.begin(), curResult.end());
407 } else {
408 // This is a simple iteration loop.
409 assert(caseBits.count() == 1);
410
411 Block *block = r;
413 rewriter, loc, validIters.front(), userReduc,
414 /*bodyBuilder=*/
415 [block](PatternRewriter &rewriter, Location loc, Region &dstRegion,
416 SparseIterator *it,
417 ValueRange reduc) -> SmallVector<Value> {
418 SmallVector<Value> blockArgs(reduc);
419 blockArgs.push_back(it->deref(rewriter, loc));
420 llvm::append_range(blockArgs, it->getCursor());
421
422 Block *dstBlock = &dstRegion.getBlocks().front();
423 rewriter.inlineBlockBefore(
424 block, dstBlock, rewriter.getInsertionPoint(), blockArgs);
425 auto yield = llvm::cast<sparse_tensor::YieldOp>(dstBlock->back());
426 SmallVector<Value> result(yield.getResults());
427 rewriter.eraseOp(yield);
428 return result;
429 });
430
431 userReduc.assign(curResult.begin(), curResult.end());
432 }
433 }
434
435 rewriter.replaceOp(op, userReduc);
436 return success();
437 }
438};
439
440} // namespace
441
443 addConversion([](Type type) { return type; });
444 addConversion(convertIteratorType);
445 addConversion(convertIterSpaceType);
446
447 addSourceMaterialization([](OpBuilder &builder, IterSpaceType spTp,
448 ValueRange inputs, Location loc) -> Value {
449 return UnrealizedConversionCastOp::create(builder, loc, TypeRange(spTp),
450 inputs)
451 .getResult(0);
452 });
453}
454
456 const TypeConverter &converter, RewritePatternSet &patterns) {
457
458 IterateOp::getCanonicalizationPatterns(patterns, patterns.getContext());
459 patterns.add<ExtractIterSpaceConverter, ExtractValOpConverter,
460 SparseIterateOpConverter, SparseCoIterateOpConverter>(
461 converter, patterns.getContext());
462}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static ValueRange genLoopWithIterator(PatternRewriter &rewriter, Location loc, SparseIterator *it, ValueRange reduc, function_ref< SmallVector< Value >(PatternRewriter &rewriter, Location loc, Region &loopBody, SparseIterator *it, ValueRange reduc)> bodyBuilder)
static void convertLevelType(SparseTensorEncodingAttr enc, Level lvl, SmallVectorImpl< Type > &fields)
static std::optional< LogicalResult > convertIteratorType(IteratorType itTp, SmallVectorImpl< Type > &fields)
static ValueRange genCoIterateBranchNest(PatternRewriter &rewriter, Location loc, CoIterateOp op, Value loopCrd, ArrayRef< std::unique_ptr< SparseIterator > > iters, ArrayRef< Block * > newBlocks, ArrayRef< Block * > oldBlocks, ArrayRef< Value > userReduc)
static std::optional< LogicalResult > convertIterSpaceType(IterSpaceType itSp, SmallVectorImpl< Type > &fields)
Block represents an ordered list of Operations.
Definition Block.h:33
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation & front()
Definition Block.h:177
BlockArgListType getArguments()
Definition Block.h:111
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:350
This class helps build Operations.
Definition Builders.h:209
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:435
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:433
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:438
void cloneRegionBefore(Region &region, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
Definition Builders.cpp:594
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:414
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition Region.cpp:62
iterator begin()
Definition Region.h:55
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
A simple wrapper to encode a bitset of (at most 64) levels, currently used by sparse_tensor....
iterator_range< const_set_bits_iterator > bits() const
static SparseIterationSpace fromValues(IterSpaceType dstTp, ValueRange values, unsigned tid)
Helper class that generates loop conditions, etc, to traverse a sparse tensor level.
ValueRange forward(OpBuilder &b, Location l)
void locate(OpBuilder &b, Location l, Value crd)
std::pair< Value, ValueRange > genWhileCond(OpBuilder &b, Location l, ValueRange vs)
virtual std::pair< Value, Value > genForCond(OpBuilder &b, Location l)
ValueRange linkNewScope(ValueRange pos)
virtual bool randomAccessible() const =0
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
Value constantI1(OpBuilder &builder, Location loc, bool b)
Generates a constant of i1 type.
uint64_t Level
The type of level identifiers and level-ranks.
std::pair< Operation *, Value > genCoIteration(OpBuilder &builder, Location loc, ArrayRef< SparseIterator * > iters, MutableArrayRef< Value > reduc, Value uniIdx, bool userReducFirst=false)
bool isDenseLT(LevelType lt)
Definition Enums.h:413
Include the generated interface declarations.
void populateLowerSparseIterationToSCFPatterns(const TypeConverter &converter, RewritePatternSet &patterns)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147