MLIR 24.0.0git
SparseGPUCodegen.cpp
Go to the documentation of this file.
1//===- SparseGPUCodegen.cpp - Generates GPU code --------------------------===//
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// This is a prototype GPU codegenerator for the sparsifier.
10// The objective is to eventually use the right combination of
11// direct code generation and libary calls into vendor-specific
12// highly optimized sparse libraries (e.g. cuSparse for CUDA).
13//
14//===----------------------------------------------------------------------===//
15
16#include "Utils/CodegenUtils.h"
17#include "Utils/LoopEmitter.h"
18
28#include "mlir/IR/IRMapping.h"
29#include "mlir/IR/Matchers.h"
31#include "llvm/Support/Casting.h"
32
33using namespace mlir;
34using namespace mlir::sparse_tensor;
35
36namespace {
37
38// Sparse formats supported by cuSparse.
39enum class CuSparseFormat {
40 kNone,
41 kCOO,
42 kCSR,
43 kCSC,
44 kBSR,
45};
46
47//===----------------------------------------------------------------------===//
48// Helper methods.
49//===----------------------------------------------------------------------===//
50
51/// Marks the given top module as a GPU container module.
52static void markAsGPUContainer(ModuleOp topModule) {
53 topModule->setDiscardableAttr(gpu::GPUDialect::getContainerModuleAttrName(),
54 UnitAttr::get(topModule->getContext()));
55}
56
57/// Constructs a new GPU module (for GPU kernels) inside the given top module,
58/// or returns an existing GPU module if one was built previously.
59static gpu::GPUModuleOp genGPUModule(OpBuilder &builder, ModuleOp topModule) {
60 for (auto op : topModule.getBodyRegion().getOps<gpu::GPUModuleOp>())
61 return op; // existing
62 markAsGPUContainer(topModule);
63 builder.setInsertionPointToStart(topModule.getBody());
64 return gpu::GPUModuleOp::create(builder, topModule->getLoc(),
65 "sparse_kernels");
66}
67
68/// Constructs a new GPU kernel in the given GPU module.
69static gpu::GPUFuncOp genGPUFunc(OpBuilder &builder, gpu::GPUModuleOp gpuModule,
71 // Get a unique kernel name. Not very creative,
72 // but we simply try kernel0, kernel1, etc.
73 unsigned kernelNumber = 0;
74 SmallString<16> kernelName;
75 do {
76 kernelName.clear();
77 ("kernel" + Twine(kernelNumber++)).toStringRef(kernelName);
78 } while (gpuModule.lookupSymbol(kernelName));
79 // Then we insert a new kernel with given arguments into the module.
80 builder.setInsertionPointToStart(gpuModule.getBody());
81 SmallVector<Type> argsTp;
82 for (auto arg : args)
83 argsTp.push_back(arg.getType());
84 FunctionType type = FunctionType::get(gpuModule->getContext(), argsTp, {});
85 auto gpuFunc =
86 gpu::GPUFuncOp::create(builder, gpuModule->getLoc(), kernelName, type);
87 gpuFunc.setKernel(true);
88 return gpuFunc;
89}
90
91/// Constructs code to launch GPU kernel.
92static Value genLaunchGPUFunc(OpBuilder &builder, gpu::GPUFuncOp gpuFunc,
95 unsigned numThreads) {
96 Location loc = gpuFunc->getLoc();
98 Value one = constantIndex(builder, loc, 1);
99 Value numT = constantIndex(builder, loc, numThreads);
100 gpu::KernelDim3 gridSize = {one, one, one};
101 gpu::KernelDim3 blckSize = {numT, one, one};
102 return gpu::LaunchFuncOp::create(builder, loc, gpuFunc, gridSize, blckSize,
103 /*dynSharedMemSz*/ none, args,
104 builder.getType<gpu::AsyncTokenType>(),
105 tokens)
106 .getAsyncToken();
107}
108
109/// Maps the provided ranked host buffer into the device address space.
110/// Writes from the host are guaranteed to be visible to device kernels
111/// that are launched afterwards. Writes from the device are guaranteed
112/// to be visible on the host after synchronizing with the device kernel
113/// completion. Needs to cast the buffer to a unranked buffer.
114static Value genHostRegisterMemref(OpBuilder &builder, Location loc,
115 Value mem) {
116 MemRefType memTp = cast<MemRefType>(mem.getType());
117 UnrankedMemRefType resTp =
118 UnrankedMemRefType::get(memTp.getElementType(), /*memorySpace=*/0);
119 Value cast = memref::CastOp::create(builder, loc, resTp, mem);
120 gpu::HostRegisterOp::create(builder, loc, cast);
121 return cast;
122}
123
124/// Unmaps the provided buffer, expecting the casted buffer.
125static void genHostUnregisterMemref(OpBuilder &builder, Location loc,
126 Value cast) {
127 gpu::HostUnregisterOp::create(builder, loc, cast);
128}
129
130/// Generates first wait in an asynchronous chain.
131static Value genFirstWait(OpBuilder &builder, Location loc) {
132 Type tokenType = builder.getType<gpu::AsyncTokenType>();
133 return gpu::WaitOp::create(builder, loc, tokenType, ValueRange())
134 .getAsyncToken();
135}
136
137/// Generates last, blocking wait in an asynchronous chain.
138static void genBlockingWait(OpBuilder &builder, Location loc,
139 ValueRange operands) {
140 gpu::WaitOp::create(builder, loc, Type(), operands);
141}
142
143/// Allocates memory on the device.
144/// TODO: A `host_shared` attribute could be used to indicate that
145/// the buffer is visible by both host and device, but lowering
146/// that feature does not seem to be fully supported yet.
147static gpu::AllocOp genAllocMemRef(OpBuilder &builder, Location loc, Value mem,
148 Value token) {
149 auto tp = cast<ShapedType>(mem.getType());
150 auto elemTp = tp.getElementType();
151 auto shape = tp.getShape();
152 auto memTp = MemRefType::get(shape, elemTp);
153 SmallVector<Value> dynamicSizes;
154 for (unsigned r = 0, rank = tp.getRank(); r < rank; r++) {
155 if (shape[r] == ShapedType::kDynamic) {
156 Value dimOp = linalg::createOrFoldDimOp(builder, loc, mem, r);
157 dynamicSizes.push_back(dimOp);
158 }
159 }
160 return gpu::AllocOp::create(builder, loc, TypeRange({memTp, token.getType()}),
161 token, dynamicSizes, ValueRange());
162}
163
164// Allocates a typed buffer on the host with given size.
165static Value genHostBuffer(OpBuilder &builder, Location loc, Type type,
166 Value size) {
167 const auto memTp = MemRefType::get({ShapedType::kDynamic}, type);
168 return memref::AllocOp::create(builder, loc, memTp, size).getResult();
169}
170
171// Allocates a typed buffer on the device with given size.
172static gpu::AllocOp genAllocBuffer(OpBuilder &builder, Location loc, Type type,
173 Value size, Value token) {
174 const auto memTp = MemRefType::get({ShapedType::kDynamic}, type);
175 return gpu::AllocOp::create(builder, loc, TypeRange({memTp, token.getType()}),
176 token, size, ValueRange());
177}
178
179// Allocates a void buffer on the device with given size.
180static gpu::AllocOp genAllocBuffer(OpBuilder &builder, Location loc, Value size,
181 Value token) {
182 return genAllocBuffer(builder, loc, builder.getI8Type(), size, token);
183}
184
185/// Deallocates memory from the device.
186static Value genDeallocMemRef(OpBuilder &builder, Location loc, Value mem,
187 Value token) {
188 return gpu::DeallocOp::create(builder, loc, token.getType(), token, mem)
189 .getAsyncToken();
190}
191
192/// Copies memory between host and device (direction is implicit).
193static Value genCopyMemRef(OpBuilder &builder, Location loc, Value dst,
194 Value src, Value token) {
195 return gpu::MemcpyOp::create(builder, loc, token.getType(), token, dst, src)
196 .getAsyncToken();
197}
198
199/// Generates an alloc/copy pair.
200static Value genAllocCopy(OpBuilder &builder, Location loc, Value b,
201 SmallVectorImpl<Value> &tokens) {
202 Value firstToken = genFirstWait(builder, loc);
203 auto alloc = genAllocMemRef(builder, loc, b, firstToken);
204 Value devMem = alloc.getResult(0);
205 Value depToken = alloc.getAsyncToken(); // copy-after-alloc
206 tokens.push_back(genCopyMemRef(builder, loc, devMem, b, depToken));
207 return devMem;
208}
209
210/// Generates a memref from tensor operation.
211static Value genTensorToMemref(PatternRewriter &rewriter, Location loc,
212 Value tensor) {
213 auto tensorType = llvm::cast<ShapedType>(tensor.getType());
214 auto memrefType =
215 MemRefType::get(tensorType.getShape(), tensorType.getElementType());
216 return bufferization::ToBufferOp::create(rewriter, loc, memrefType, tensor);
217}
218
219/// Prepares the outlined arguments, passing scalars and buffers in. Here we
220/// assume that the first buffer is the one allocated for output. We create
221/// a set of properly chained asynchronous allocation/copy pairs to increase
222/// overlap before launching the kernel.
223static Value genParametersIn(OpBuilder &builder, Location loc,
224 SmallVectorImpl<Value> &scalars,
225 SmallVectorImpl<Value> &buffers,
228 bool useHostRegistrationForOut) {
229 Value out;
230 // Scalars are passed by value.
231 for (Value s : scalars)
232 args.push_back(s);
233 // Buffers are need to be made visible on device.
234 for (Value b : buffers) {
235 if (useHostRegistrationForOut) {
236 out = genHostRegisterMemref(builder, loc, b);
237 args.push_back(b);
238 useHostRegistrationForOut = false;
239 continue;
240 }
241 args.push_back(genAllocCopy(builder, loc, b, tokens));
242 }
243 return out;
244}
245
246/// Finalizes the outlined arguments. The output buffer is copied depending
247/// on the kernel token and then deallocated. All other buffers are simply
248/// deallocated. Then we wait for all operations to complete.
249///
250/// `copyBack` maps 1:1 to the `buffers` array. It tracks which buffers were
251/// mutated by the kernel and require a device-to-host copy. An empty
252/// `copyBack` array implies no buffers are "copied back".
253static void genParametersOut(OpBuilder &builder, Location loc, Value out,
254 Value kernelToken, SmallVectorImpl<Value> &scalars,
255 SmallVectorImpl<Value> &buffers,
258 ArrayRef<bool> copyBack) {
259 unsigned base = scalars.size();
260
261 // `args` stores scalars followed by buffers. `base` is the index of the first
262 // buffer. `bufIdx` maps the current buffer to its exact 1:1 counterpart in
263 // the `copyBack` mask.
264 for (unsigned i = base, e = args.size(); i < e; i++) {
265 unsigned bufIdx = i - base;
266 Value firstToken;
267
268 // Checks if the current buffer needs a device-to-host copy.
269 if (copyBack[bufIdx]) {
270 if (out && bufIdx == 0) {
271 genHostUnregisterMemref(builder, loc, out);
272 out = Value();
273 continue;
274 }
275 firstToken =
276 genCopyMemRef(builder, loc, buffers[bufIdx], args[i], kernelToken);
277 } else {
278 firstToken = genFirstWait(builder, loc);
279 }
280 tokens.push_back(genDeallocMemRef(builder, loc, args[i], firstToken));
281 }
282}
283
284/// Constructs code for new GPU kernel.
285static void genGPUCode(PatternRewriter &rewriter, gpu::GPUFuncOp gpuFunc,
286 scf::ParallelOp forallOp,
287 SmallVectorImpl<Value> &constants,
288 SmallVectorImpl<Value> &scalars,
289 SmallVectorImpl<Value> &buffers) {
290 Location loc = gpuFunc->getLoc();
291 Block &block = gpuFunc.getBody().front();
292 rewriter.setInsertionPointToStart(&block);
293
294 // Re-generate the constants, recapture all arguments.
295 unsigned arg = 0;
296 IRMapping irMap;
297 for (Value c : constants)
298 irMap.map(c, rewriter.clone(*c.getDefiningOp())->getResult(0));
299 for (Value s : scalars)
300 irMap.map(s, block.getArgument(arg++));
301 for (Value b : buffers)
302 irMap.map(b, block.getArgument(arg++));
303
304 // Assume 1-dimensional grid/block configuration (only x dimension),
305 // so that:
306 // row = blockIdx.x * blockDim.x + threadIdx.x
307 // inc = blockDim.x * gridDim.x
308 Value bid = gpu::BlockIdOp::create(rewriter, loc, gpu::Dimension::x);
309 Value bsz = gpu::BlockDimOp::create(rewriter, loc, gpu::Dimension::x);
310 Value tid = gpu::ThreadIdOp::create(rewriter, loc, gpu::Dimension::x);
311 Value gsz = gpu::GridDimOp::create(rewriter, loc, gpu::Dimension::x);
312 Value mul = arith::MulIOp::create(rewriter, loc, bid, bsz);
313 Value row = arith::AddIOp::create(rewriter, loc, mul, tid);
314 Value inc = arith::MulIOp::create(rewriter, loc, bsz, gsz);
315
316 // Construct the iteration over the computational space that
317 // accounts for the fact that the total number of threads and
318 // the amount of work to be done usually do not match precisely.
319 // for (r = row; r < N; r += inc) {
320 // <loop-body>
321 // }
322 Value upper = irMap.lookup(forallOp.getUpperBound()[0]);
323 scf::ForOp forOp = scf::ForOp::create(rewriter, loc, row, upper, inc);
324 // The scf.for builder creates an empty block. scf.for does not allow multiple
325 // blocks in its region, so delete the block before `cloneRegionBefore` adds
326 // an additional block.
327 rewriter.eraseBlock(forOp.getBody());
328 rewriter.cloneRegionBefore(forallOp.getRegion(), forOp.getRegion(),
329 forOp.getRegion().begin(), irMap);
330 // Replace the scf.reduce terminator.
331 rewriter.setInsertionPoint(forOp.getBody()->getTerminator());
332 rewriter.replaceOpWithNewOp<scf::YieldOp>(forOp.getBody()->getTerminator());
333
334 // Done.
335 rewriter.setInsertionPointAfter(forOp);
336 gpu::ReturnOp::create(rewriter, gpuFunc->getLoc());
337}
338
339//===----------------------------------------------------------------------===//
340// Library helper methods.
341//===----------------------------------------------------------------------===//
342
343/// Helper to detect a + b with arguments taken from given block.
344static bool matchAddOfArgs(Block *block, Value val) {
345 if (auto *def = val.getDefiningOp()) {
346 if (isa<arith::AddFOp, arith::AddIOp>(def)) {
347 Value a = block->getArguments()[0];
348 Value b = block->getArguments()[1];
349 return (def->getOperand(0) == a && def->getOperand(1) == b) ||
350 (def->getOperand(0) == b && def->getOperand(1) == a);
351 }
352 }
353 return false;
354}
355
356/// Helper to detect a * b with arguments taken from given block.
357static bool matchMulOfArgs(Block *block, Value val) {
358 if (auto *def = val.getDefiningOp()) {
359 if (isa<arith::MulFOp, arith::MulIOp>(def)) {
360 Value a = block->getArguments()[0];
361 Value b = block->getArguments()[1];
362 return (def->getOperand(0) == a && def->getOperand(1) == b) ||
363 (def->getOperand(0) == b && def->getOperand(1) == a);
364 }
365 }
366 return false;
367}
368
369/// Helper to detect x = x + a * b
370static bool matchSumOfMultOfArgs(linalg::GenericOp op) {
371 auto yieldOp = cast<linalg::YieldOp>(op.getRegion().front().getTerminator());
372 if (auto *def = yieldOp.getOperand(0).getDefiningOp()) {
373 if (isa<arith::AddFOp, arith::AddIOp>(def)) {
374 Value x = op.getBlock()->getArguments()[2];
375 return (def->getOperand(0) == x &&
376 matchMulOfArgs(op.getBlock(), def->getOperand(1))) ||
377 (def->getOperand(1) == x &&
378 matchMulOfArgs(op.getBlock(), def->getOperand(0)));
379 }
380 }
381 return false;
382}
383
384// Helper to detect c += spy(s) x (a * b)
385static bool matchSumReductionOfMulUnary(linalg::GenericOp op) {
386 auto yieldOp = cast<linalg::YieldOp>(op.getRegion().front().getTerminator());
387 // The linalg yields a custom reduce result.
388 Value s_out = op.getBlock()->getArguments()[2];
389 if (auto redOp =
390 yieldOp.getOperand(0).getDefiningOp<sparse_tensor::ReduceOp>()) {
391 // The reduce consumes the output.
392 Value other;
393 if (s_out == redOp->getOperand(0))
394 other = redOp->getOperand(1);
395 else if (s_out == redOp->getOperand(1))
396 other = redOp->getOperand(0);
397 else
398 return false;
399 // The reduce op also consumes an unary which also consumes the output
400 // and does not define an absent value.
401 if (auto unOp = other.getDefiningOp<sparse_tensor::UnaryOp>()) {
402 if (s_out != unOp->getOperand(0) || !unOp.getAbsentRegion().empty())
403 return false;
404 // And the bodies are as expected.
405 auto yieldUn = cast<sparse_tensor::YieldOp>(
406 unOp.getRegion(0).front().getTerminator());
407 auto yieldRed = cast<sparse_tensor::YieldOp>(
408 redOp.getRegion().front().getTerminator());
409 return matchMulOfArgs(op.getBlock(), yieldUn.getOperand(0)) &&
410 matchAddOfArgs(&redOp.getRegion().front(), yieldRed.getOperand(0));
411 }
412 }
413 return false;
414}
415
416/// Test for dense tensor.
417static bool isDenseTensor(Value v) {
418 auto sTp = getSparseTensorType(v);
419 return sTp.getDimRank() == sTp.getLvlRank() && sTp.isAllDense();
420}
421
422/// Test for suitable positions/coordinates width.
423static bool isAdmissibleMetaData(SparseTensorType &aTp) {
424 return (aTp.getPosWidth() == 0 || aTp.getPosWidth() >= 16) &&
425 (aTp.getCrdWidth() == 0 || aTp.getCrdWidth() >= 16);
426}
427
428/// Test for sorted COO matrix with suitable metadata.
429static bool isAdmissibleCOO(SparseTensorType &aTp) {
430 return aTp.getDimRank() == 2 && aTp.getLvlRank() == 2 && aTp.isIdentity() &&
431 aTp.isCompressedLvl(0) && aTp.isOrderedLvl(0) && !aTp.isUniqueLvl(0) &&
432 aTp.isSingletonLvl(1) && aTp.isOrderedLvl(1) && aTp.isUniqueLvl(1) &&
433 isAdmissibleMetaData(aTp);
434}
435
436/// Test for CSR matrix with suitable metadata.
437static bool isAdmissibleCSR(SparseTensorType &aTp) {
438 return aTp.getDimRank() == 2 && aTp.getLvlRank() == 2 && aTp.isIdentity() &&
439 aTp.isDenseLvl(0) && aTp.isCompressedLvl(1) && aTp.isOrderedLvl(1) &&
440 aTp.isUniqueLvl(1) && isAdmissibleMetaData(aTp);
441}
442
443/// Test for CSC matrix with suitable metadata.
444static bool isAdmissibleCSC(SparseTensorType &aTp) {
445 return aTp.getDimRank() == 2 && aTp.getLvlRank() == 2 && !aTp.isIdentity() &&
446 aTp.isPermutation() && aTp.isDenseLvl(0) && aTp.isCompressedLvl(1) &&
447 aTp.isOrderedLvl(1) && aTp.isUniqueLvl(1) && isAdmissibleMetaData(aTp);
448}
449
450/// Test for BSR matrix with suitable metadata.
451static bool isAdmissibleBSR(SparseTensorType &aTp) {
452 if (aTp.getDimRank() == 2 && aTp.getLvlRank() == 4 && aTp.isDenseLvl(0) &&
453 aTp.isCompressedLvl(1) && aTp.isOrderedLvl(1) && aTp.isUniqueLvl(1) &&
454 aTp.isDenseLvl(2) && aTp.isDenseLvl(3) && isAdmissibleMetaData(aTp)) {
455 // CuSparse only supports "square" blocks currently.
457 assert(dims.size() == 2);
458 return dims[0] == dims[1] && dims[0] > 1;
459 }
460 return false;
461}
462
463/// Test for 2:4 matrix with suitable metadata.
464static bool isAdmissible24(SparseTensorType &aTp) {
465 return aTp.getDimRank() == 2 && aTp.getLvlRank() == 3 && aTp.isDenseLvl(0) &&
466 aTp.isDenseLvl(1) && aTp.isNOutOfMLvl(2) && isAdmissibleMetaData(aTp);
467}
468
469/// Test for conversion into 2:4 matrix.
470static bool isConversionInto24(Value v) {
471 if (auto cnv = v.getDefiningOp<ConvertOp>()) {
472 Value a = cnv.getResult();
473 Value d = cnv.getSource();
475 return isDenseTensor(d) && isAdmissible24(aTp);
476 }
477 return false;
478}
479
480/// Returns a suitable sparse format for the operation and given operand
481/// types with cuSparse, or kNone if none is available.
482static CuSparseFormat getCuSparseFormat(SparseTensorType aTp,
484 SparseTensorType cTp, bool enableRT,
485 bool isMatVec) {
486 // The other operands have a dense type.
487 if (bTp.hasEncoding() || cTp.hasEncoding())
488 return CuSparseFormat::kNone;
489 // Now check for suitable operand type for the main operand.
490 if (isAdmissibleCOO(aTp))
491#ifdef CUSPARSE_COO_AOS
492 return isMatVec ? CuSparseFormat::kCOO : CuSparseFormat::kNone;
493#else
494 return enableRT ? CuSparseFormat::kCOO : CuSparseFormat::kNone;
495#endif
496 if (isAdmissibleCSR(aTp))
497 return CuSparseFormat::kCSR;
498 if (isAdmissibleCSC(aTp))
499 return CuSparseFormat::kCSC;
500 if (isAdmissibleBSR(aTp))
501 return CuSparseFormat::kBSR;
502 return CuSparseFormat::kNone;
503}
504
505/// Generates the first positions/coordinates of a sparse matrix.
506static Value genFirstPosOrCrds(OpBuilder &builder, Location loc, Value a,
507 CuSparseFormat format, bool enableRT) {
508 if (format == CuSparseFormat::kCOO) {
509 // Library uses SoA COO, direct IR uses AoS COO.
510 if (enableRT)
511 return ToCoordinatesOp::create(builder, loc, a, 0);
512 return ToCoordinatesBufferOp::create(builder, loc, a);
513 }
514 // Formats CSR/CSC and BSR use positions at 1.
515 return ToPositionsOp::create(builder, loc, a, 1);
516}
517
518/// Generates the second coordinates of a sparse matrix.
519static Value genSecondCrds(OpBuilder &builder, Location loc, Value a,
520 CuSparseFormat format, bool enableRT) {
521 bool isCOO = format == CuSparseFormat::kCOO;
522 if (isCOO && !enableRT)
523 return Value(); // nothing needed
524 // Formats CSR/CSC and BSR use coordinates at 1.
525 return ToCoordinatesOp::create(builder, loc, a, 1);
526}
527
528/// Generates the sparse matrix handle.
529static Operation *genSpMat(OpBuilder &builder, Location loc,
530 SparseTensorType &aTp, Type handleTp, Type tokenTp,
531 Value token, Value sz1, Value sz2, Value nseA,
532 Value rowA, Value colA, Value valA,
533 CuSparseFormat format, bool enableRT) {
534 if (format == CuSparseFormat::kCOO) {
535 // Library uses SoA COO, direct IR uses AoS COO.
536 if (enableRT) {
537 assert(colA);
538 return gpu::CreateCooOp::create(builder, loc, handleTp, tokenTp, token,
539 sz1, sz2, nseA, rowA, colA, valA);
540 }
541#ifdef CUSPARSE_COO_AOS
542 assert(!colA);
543 return gpu::CreateCooAoSOp::create(builder, loc, handleTp, tokenTp, token,
544 sz1, sz2, nseA, rowA, valA);
545#else
546 llvm_unreachable("gpu::CreateCooAoSOp is deprecated");
547#endif
548 }
549 assert(colA);
550 if (format == CuSparseFormat::kCSR)
551 return gpu::CreateCsrOp::create(builder, loc, handleTp, tokenTp, token, sz1,
552 sz2, nseA, rowA, colA, valA);
553 if (format == CuSparseFormat::kCSC)
554 return gpu::CreateCscOp::create(builder, loc, handleTp, tokenTp, token, sz1,
555 sz2, nseA, rowA, colA, valA);
556 // BSR requires a bit more work since we need to pass in the block size
557 // and all others sizes in terms of blocks (#block-rows, #block-cols,
558 // #nonzero-blocks).
559 assert(format == CuSparseFormat::kBSR);
561 assert(dims.size() == 2 && dims[0] == dims[1]);
562 uint64_t b = dims[0];
563 Value bSz = constantIndex(builder, loc, b);
564 Value bRows = arith::DivUIOp::create(builder, loc, sz1, bSz);
565 Value bCols = arith::DivUIOp::create(builder, loc, sz2, bSz);
566 Value bNum = arith::DivUIOp::create(builder, loc, nseA,
567 constantIndex(builder, loc, b * b));
568 return gpu::CreateBsrOp::create(builder, loc, handleTp, tokenTp, token, bRows,
569 bCols, bNum, bSz, bSz, rowA, colA, valA);
570}
571
572/// Returns whether the operation swaps matrix dimensions.
573static bool isTransposed(gpu::TransposeMode mode) {
574 switch (mode) {
575 case gpu::TransposeMode::NON_TRANSPOSE:
576 return false;
577 case gpu::TransposeMode::TRANSPOSE:
578 case gpu::TransposeMode::CONJUGATE_TRANSPOSE:
579 return true;
580 }
581 llvm_unreachable("unhandled transpose mode");
582}
583
584/// Match and rewrite SpMV kernel.
585static LogicalResult rewriteSpMV(PatternRewriter &rewriter,
586 linalg::GenericOp op, bool enableRT,
587 gpu::TransposeMode modeA) {
588 Location loc = op.getLoc();
589 Value a = op.getOperand(0);
590 Value x = op.getOperand(1);
591 Value y = op.getOperand(2); // we have y = op(A)x
592 SmallVector<Value> tokens;
593
594 // Only admissible sparse matrix format and dense vectors (no BSR).
598 auto format = getCuSparseFormat(aTp, xTp, yTp, enableRT, /*isMatVec=*/true);
599 if (format == CuSparseFormat::kNone || format == CuSparseFormat::kBSR)
600 return failure();
601
602 // Start sparse kernel and copy data from host to device.
603 // a : memR/memC/memV -> rowA,colA,valA
604 // x : memX -> vecX
605 // y : memY -> vecY
606 Value nseA = NumberOfEntriesOp::create(rewriter, loc, a);
607 Value rows = linalg::createOrFoldDimOp(rewriter, loc, a, 0);
608 Value cols = linalg::createOrFoldDimOp(rewriter, loc, a, 1);
609 bool isTranspose = isTransposed(modeA);
610 Value szX = isTranspose ? rows : cols;
611 Value szY = isTranspose ? cols : rows;
612 Value memR = genFirstPosOrCrds(rewriter, loc, a, format, enableRT);
613 Value memC = genSecondCrds(rewriter, loc, a, format, enableRT); // or empty
614 Value memV = ToValuesOp::create(rewriter, loc, a);
615 Value rowA = genAllocCopy(rewriter, loc, memR, tokens);
616 Value colA = memC ? genAllocCopy(rewriter, loc, memC, tokens) : Value();
617 Value valA = genAllocCopy(rewriter, loc, memV, tokens);
618 Value memX = genTensorToMemref(rewriter, loc, x);
619 Value vecX = genAllocCopy(rewriter, loc, memX, tokens);
620 Value memY = genTensorToMemref(rewriter, loc, y);
621 Value vecY = genAllocCopy(rewriter, loc, memY, tokens);
622 genBlockingWait(rewriter, loc, tokens);
623 tokens.clear();
624
625 // Create sparse environment and sparse matrix/dense vector handles.
626 Type indexTp = rewriter.getIndexType();
627 Type dnTensorHandleTp = rewriter.getType<gpu::SparseDnTensorHandleType>();
628 Type spmatHandleTp = rewriter.getType<gpu::SparseSpMatHandleType>();
629 Type tokenTp = rewriter.getType<gpu::AsyncTokenType>();
630 Value token = genFirstWait(rewriter, loc);
631 Operation *spGenA =
632 genSpMat(rewriter, loc, aTp, spmatHandleTp, tokenTp, token, rows, cols,
633 nseA, rowA, colA, valA, format, enableRT);
634 Value spMatA = spGenA->getResult(0);
635 token = spGenA->getResult(1);
636 auto dvecX = gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp,
637 tokenTp, token, vecX, szX);
638 Value dnX = dvecX.getResult(0);
639 token = dvecX.getAsyncToken();
640 auto dvecY = gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp,
641 tokenTp, token, vecY, szY);
642 Value dnY = dvecY.getResult(0);
643 token = dvecY.getAsyncToken();
644 auto dnYType = llvm::cast<ShapedType>(y.getType()).getElementType();
645
646 // Precompute buffersize for SpMV.
647 auto bufferComp = gpu::SpMVBufferSizeOp::create(
648 rewriter, loc, indexTp, tokenTp, token, modeA, spMatA, dnX, dnY,
649 /*computeType=*/dnYType);
650 Value bufferSz = bufferComp.getResult(0);
651 token = bufferComp.getAsyncToken();
652 auto buf = genAllocBuffer(rewriter, loc, bufferSz, token);
653 Value buffer = buf.getResult(0);
654 token = buf.getAsyncToken();
655
656 // Perform the SpMV.
657 auto spmvComp = gpu::SpMVOp::create(rewriter, loc, tokenTp, token, modeA,
658 spMatA, dnX, dnY,
659 /*computeType=*/dnYType, buffer);
660 token = spmvComp.getAsyncToken();
661
662 // Copy data back to host and free all the resoures.
663 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatA)
664 .getAsyncToken();
665 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnX)
666 .getAsyncToken();
667 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnY)
668 .getAsyncToken();
669 token = genDeallocMemRef(rewriter, loc, rowA, token);
670 if (colA)
671 token = genDeallocMemRef(rewriter, loc, colA, token);
672 token = genDeallocMemRef(rewriter, loc, valA, token);
673 token = genDeallocMemRef(rewriter, loc, buffer, token);
674 token = genDeallocMemRef(rewriter, loc, vecX, token);
675 token = genCopyMemRef(rewriter, loc, memY, vecY, token);
676 token = genDeallocMemRef(rewriter, loc, vecY, token);
677 tokens.push_back(token);
678 genBlockingWait(rewriter, loc, tokens);
679 tokens.clear();
680
681 // Done.
682 rewriter.replaceOpWithNewOp<bufferization::ToTensorOp>(op, y.getType(), memY);
683 return success();
684}
685
686/// Match and rewrite SpMM kernel.
687static LogicalResult rewriteSpMM(PatternRewriter &rewriter,
688 linalg::GenericOp op, bool enableRT,
689 gpu::TransposeMode modeA,
690 gpu::TransposeMode modeB) {
691 Location loc = op.getLoc();
692 Value a = op.getOperand(0);
693 Value b = op.getOperand(1);
694 Value c = op.getOperand(2); // we have C = op(A)op(B)
695 SmallVector<Value> tokens;
696
697 // Only admissible sparse matrix format and dense matrices (no BSR).
701 auto format = getCuSparseFormat(aTp, bTp, cTp, enableRT, /*isMatVec=*/false);
702 if (format == CuSparseFormat::kNone || format == CuSparseFormat::kBSR)
703 return failure();
704
705 // Start sparse kernel and copy data from host to device.
706 // a : memR/memC/memV -> rowA,colA,valA
707 // b : bufB -> matB
708 // c : bufC -> matC
709 Value nseA = NumberOfEntriesOp::create(rewriter, loc, a);
710 Value rows = linalg::createOrFoldDimOp(rewriter, loc, a, 0);
711 Value cols = linalg::createOrFoldDimOp(rewriter, loc, a, 1);
712 bool isTransposeA = isTransposed(modeA);
713 bool isTransposeB = isTransposed(modeB);
714 Value szm = isTransposeA ? cols : rows;
715 Value szk = isTransposeA ? rows : cols;
716 Value szn = linalg::createOrFoldDimOp(rewriter, loc, b, isTransposeB ? 0 : 1);
717 SmallVector<Value> bSizes = isTransposeB ? SmallVector<Value>{szn, szk}
718 : SmallVector<Value>{szk, szn};
719 Value memR = genFirstPosOrCrds(rewriter, loc, a, format, enableRT);
720 Value memC = genSecondCrds(rewriter, loc, a, format, enableRT); // or empty
721 Value memV = ToValuesOp::create(rewriter, loc, a);
722 Value rowA = genAllocCopy(rewriter, loc, memR, tokens);
723 Value colA = memC ? genAllocCopy(rewriter, loc, memC, tokens) : Value();
724 Value valA = genAllocCopy(rewriter, loc, memV, tokens);
725 Value bufB = genTensorToMemref(rewriter, loc, b);
726 Value matB = genAllocCopy(rewriter, loc, bufB, tokens);
727 Value bufC = genTensorToMemref(rewriter, loc, c);
728 Value matC = genAllocCopy(rewriter, loc, bufC, tokens);
729 genBlockingWait(rewriter, loc, tokens);
730 tokens.clear();
731
732 // Create sparse environment and sparse matrix/dense matrix handles.
733 Type indexTp = rewriter.getIndexType();
734 Type dnTensorHandleTp = rewriter.getType<gpu::SparseDnTensorHandleType>();
735 Type spMatHandleTp = rewriter.getType<gpu::SparseSpMatHandleType>();
736 Type tokenTp = rewriter.getType<gpu::AsyncTokenType>();
737 Value token = genFirstWait(rewriter, loc);
738 Operation *spGenA =
739 genSpMat(rewriter, loc, aTp, spMatHandleTp, tokenTp, token, rows, cols,
740 nseA, rowA, colA, valA, format, enableRT);
741 Value spMatA = spGenA->getResult(0);
742 token = spGenA->getResult(1);
743 auto dmatB = gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp,
744 tokenTp, token, matB, bSizes);
745 Value dnB = dmatB.getResult(0);
746 token = dmatB.getAsyncToken();
747 auto dmatC =
748 gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp, tokenTp,
749 token, matC, SmallVector<Value>{szm, szn});
750 Value dnC = dmatC.getResult(0);
751 token = dmatC.getAsyncToken();
752 auto dmatCType = llvm::cast<ShapedType>(c.getType()).getElementType();
753
754 // Precompute buffersize for SpMM.
755 auto bufferComp = gpu::SpMMBufferSizeOp::create(
756 rewriter, loc, indexTp, tokenTp, token, modeA, modeB, spMatA, dnB, dnC,
757 /*computeType=*/dmatCType);
758 Value bufferSz = bufferComp.getResult(0);
759 token = bufferComp.getAsyncToken();
760 auto buf = genAllocBuffer(rewriter, loc, bufferSz, token);
761 Value buffer = buf.getResult(0);
762 token = buf.getAsyncToken();
763 auto dnCType = llvm::cast<ShapedType>(c.getType()).getElementType();
764
765 // Perform the SpMM.
766 auto spmmComp = gpu::SpMMOp::create(rewriter, loc, tokenTp, token, modeA,
767 modeB, spMatA, dnB, dnC,
768 /*computeType=*/dnCType, buffer);
769 token = spmmComp.getAsyncToken();
770
771 // Copy data back to host and free all the resoures.
772 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatA)
773 .getAsyncToken();
774 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnB)
775 .getAsyncToken();
776 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnC)
777 .getAsyncToken();
778 token = genDeallocMemRef(rewriter, loc, rowA, token);
779 if (colA)
780 token = genDeallocMemRef(rewriter, loc, colA, token);
781 token = genDeallocMemRef(rewriter, loc, valA, token);
782 token = genDeallocMemRef(rewriter, loc, buffer, token);
783 token = genDeallocMemRef(rewriter, loc, matB, token);
784 token = genCopyMemRef(rewriter, loc, bufC, matC, token);
785 token = genDeallocMemRef(rewriter, loc, matC, token);
786 tokens.push_back(token);
787 genBlockingWait(rewriter, loc, tokens);
788 tokens.clear();
789
790 // Done.
791 rewriter.replaceOpWithNewOp<bufferization::ToTensorOp>(op, c.getType(), bufC);
792 return success();
793}
794
795// Match and rewrite SpGEMM kernel.
796static LogicalResult rewriteSpGEMM(PatternRewriter &rewriter,
797 linalg::GenericOp op, bool enableRT) {
798 Location loc = op.getLoc();
799 Value a = op.getOperand(0);
800 Value b = op.getOperand(1);
801 Value c = op.getOperand(2); // we have C = AB
802 SmallVector<Value> tokens;
803
804 // Only CSR <- CSR x CSR supported.
805 auto format = CuSparseFormat::kCSR;
809 if (!isAdmissibleCSR(aTp) || !isAdmissibleCSR(bTp) || !isAdmissibleCSR(cTp))
810 return failure();
811
812 // Start sparse kernel and copy data from host to device.
813 // a : amemR/amemC/amemV -> rowA,colA,valA
814 // b : bmemR/bmemC/bmemV -> rowB,colB,valB
815 // c : materializes
816 auto dnCType = cTp.getElementType();
817 Value nseA = NumberOfEntriesOp::create(rewriter, loc, a);
818 Value nseB = NumberOfEntriesOp::create(rewriter, loc, b);
819 Value szm = linalg::createOrFoldDimOp(rewriter, loc, a, 0);
820 Value szk = linalg::createOrFoldDimOp(rewriter, loc, a, 1);
821 Value szn = linalg::createOrFoldDimOp(rewriter, loc, b, 1);
822 Value amemR = genFirstPosOrCrds(rewriter, loc, a, format, enableRT);
823 Value amemC = genSecondCrds(rewriter, loc, a, format, enableRT); // not empty
824 Value amemV = ToValuesOp::create(rewriter, loc, a);
825 Value bmemR = genFirstPosOrCrds(rewriter, loc, b, format, enableRT);
826 Value bmemC = genSecondCrds(rewriter, loc, b, format, enableRT); // not empty
827 Value bmemV = ToValuesOp::create(rewriter, loc, b);
828 Value rowA = genAllocCopy(rewriter, loc, amemR, tokens);
829 Value colA = genAllocCopy(rewriter, loc, amemC, tokens);
830 Value valA = genAllocCopy(rewriter, loc, amemV, tokens);
831 Value rowB = genAllocCopy(rewriter, loc, bmemR, tokens);
832 Value colB = genAllocCopy(rewriter, loc, bmemC, tokens);
833 Value valB = genAllocCopy(rewriter, loc, bmemV, tokens);
834 genBlockingWait(rewriter, loc, tokens);
835 tokens.clear();
836
837 // Create sparse environment and sparse matrix/dense vector handles.
838 Type indexTp = rewriter.getIndexType();
839 Type spmatHandleTp = rewriter.getType<gpu::SparseSpMatHandleType>();
840 Type descTp = rewriter.getType<gpu::SparseSpGEMMOpHandleType>();
841 Type tokenTp = rewriter.getType<gpu::AsyncTokenType>();
842 Value token = genFirstWait(rewriter, loc);
843 Operation *spGenA =
844 genSpMat(rewriter, loc, aTp, spmatHandleTp, tokenTp, token, szm, szk,
845 nseA, rowA, colA, valA, format, enableRT);
846 Value spMatA = spGenA->getResult(0);
847 token = spGenA->getResult(1);
848 Operation *spGenB =
849 genSpMat(rewriter, loc, bTp, spmatHandleTp, tokenTp, token, szk, szn,
850 nseB, rowB, colB, valB, format, enableRT);
851 Value spMatB = spGenB->getResult(0);
852 token = spGenB->getResult(1);
853
854 // Sparse matrix C materializes (also assumes beta == 0).
855 Value zero = constantIndex(rewriter, loc, 0);
856 Value one = constantIndex(rewriter, loc, 1);
857 Value mplus1 = arith::AddIOp::create(rewriter, loc, szm, one);
858 auto e1 = genAllocBuffer(rewriter, loc, cTp.getPosType(), mplus1, token);
859 Value rowC = e1.getResult(0);
860 token = e1.getAsyncToken();
861 auto e2 = genAllocBuffer(rewriter, loc, cTp.getCrdType(), zero, token);
862 Value colC = e2.getResult(0); // no free needed
863 token = e2.getAsyncToken();
864 auto e3 = genAllocBuffer(rewriter, loc, dnCType, zero, token);
865 Value valC = e3.getResult(0); // no free needed
866 token = e3.getAsyncToken();
867 Operation *spGenC =
868 genSpMat(rewriter, loc, cTp, spmatHandleTp, tokenTp, token, szm, szn,
869 zero, rowC, colC, valC, format, enableRT);
870 Value spMatC = spGenC->getResult(0);
871 token = spGenC->getResult(1);
872
873 // Precompute buffersizes for SpGEMM.
874 Operation *descOp =
875 gpu::SpGEMMCreateDescrOp::create(rewriter, loc, descTp, tokenTp, token);
876 Value desc = descOp->getResult(0);
877 token = descOp->getResult(1);
878 Operation *work1 = gpu::SpGEMMWorkEstimationOrComputeOp::create(
879 rewriter, loc, indexTp, tokenTp, token, desc,
880 gpu::TransposeMode::NON_TRANSPOSE, gpu::TransposeMode::NON_TRANSPOSE,
881 spMatA, spMatB, spMatC, dnCType, zero, valC,
882 gpu::SpGEMMWorkEstimationOrComputeKind::WORK_ESTIMATION);
883 Value bufferSz1 = work1->getResult(0);
884 token = work1->getResult(1);
885 auto buf1 = genAllocBuffer(rewriter, loc, bufferSz1, token);
886 Value buffer1 = buf1.getResult(0);
887 token = buf1.getAsyncToken();
888 Operation *work2 = gpu::SpGEMMWorkEstimationOrComputeOp::create(
889 rewriter, loc, indexTp, tokenTp, token, desc,
890 gpu::TransposeMode::NON_TRANSPOSE, gpu::TransposeMode::NON_TRANSPOSE,
891 spMatA, spMatB, spMatC, dnCType, bufferSz1, buffer1,
892 gpu::SpGEMMWorkEstimationOrComputeKind::WORK_ESTIMATION);
893 token = work2->getResult(1);
894
895 // Compute step.
896 Operation *compute1 = gpu::SpGEMMWorkEstimationOrComputeOp::create(
897 rewriter, loc, indexTp, tokenTp, token, desc,
898 gpu::TransposeMode::NON_TRANSPOSE, gpu::TransposeMode::NON_TRANSPOSE,
899 spMatA, spMatB, spMatC, dnCType, zero, valC,
900 gpu::SpGEMMWorkEstimationOrComputeKind::COMPUTE);
901 Value bufferSz2 = compute1->getResult(0);
902 token = compute1->getResult(1);
903 auto buf2 = genAllocBuffer(rewriter, loc, bufferSz2, token);
904 Value buffer2 = buf2.getResult(0);
905 token = buf2.getAsyncToken();
906 Operation *compute2 = gpu::SpGEMMWorkEstimationOrComputeOp::create(
907 rewriter, loc, indexTp, tokenTp, token, desc,
908 gpu::TransposeMode::NON_TRANSPOSE, gpu::TransposeMode::NON_TRANSPOSE,
909 spMatA, spMatB, spMatC, dnCType, bufferSz2, buffer2,
910 gpu::SpGEMMWorkEstimationOrComputeKind::COMPUTE);
911 token = compute2->getResult(1);
912
913 // Get sizes.
914 Operation *sizes = gpu::SpMatGetSizeOp::create(
915 rewriter, loc, indexTp, indexTp, indexTp, tokenTp, token, spMatC);
916 Value nnz = sizes->getResult(2);
917 token = sizes->getResult(3);
918 auto a2 = genAllocBuffer(rewriter, loc, cTp.getCrdType(), nnz, token);
919 colC = a2.getResult(0);
920 token = a2.getAsyncToken();
921 auto a3 = genAllocBuffer(rewriter, loc, dnCType, nnz, token);
922 valC = a3.getResult(0);
923 token = a3.getAsyncToken();
924
925 // Update C with new pointers and copy final product back into C.
926 Operation *update = gpu::SetCsrPointersOp::create(
927 rewriter, loc, tokenTp, token, spMatC, rowC, colC, valC);
928 token = update->getResult(0);
929 Operation *copy = gpu::SpGEMMCopyOp::create(
930 rewriter, loc, tokenTp, token, desc, gpu::TransposeMode::NON_TRANSPOSE,
931 gpu::TransposeMode::NON_TRANSPOSE, spMatA, spMatB, spMatC, dnCType);
932 token = copy->getResult(0);
933
934 // Allocate buffers on host.
935 Value rowH = genHostBuffer(rewriter, loc, cTp.getPosType(), mplus1);
936 Value colH = genHostBuffer(rewriter, loc, cTp.getCrdType(), nnz);
937 Value valH = genHostBuffer(rewriter, loc, dnCType, nnz);
938
939 // Copy data back to host and free all the resoures.
940 token = gpu::SpGEMMDestroyDescrOp::create(rewriter, loc, tokenTp, token, desc)
941 .getAsyncToken();
942 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatA)
943 .getAsyncToken();
944 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatB)
945 .getAsyncToken();
946 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatC)
947 .getAsyncToken();
948 token = genCopyMemRef(rewriter, loc, rowH, rowC, token);
949 token = genCopyMemRef(rewriter, loc, colH, colC, token);
950 token = genCopyMemRef(rewriter, loc, valH, valC, token);
951 token = genDeallocMemRef(rewriter, loc, rowA, token);
952 token = genDeallocMemRef(rewriter, loc, colA, token);
953 token = genDeallocMemRef(rewriter, loc, valA, token);
954 token = genDeallocMemRef(rewriter, loc, rowB, token);
955 token = genDeallocMemRef(rewriter, loc, colB, token);
956 token = genDeallocMemRef(rewriter, loc, valB, token);
957 token = genDeallocMemRef(rewriter, loc, rowC, token);
958 token = genDeallocMemRef(rewriter, loc, colC, token);
959 token = genDeallocMemRef(rewriter, loc, valC, token);
960 token = genDeallocMemRef(rewriter, loc, buffer1, token);
961 token = genDeallocMemRef(rewriter, loc, buffer2, token);
962 tokens.push_back(token);
963 genBlockingWait(rewriter, loc, tokens);
964 tokens.clear();
965
966 // Done.
967 Value vt = bufferization::ToTensorOp::create(
968 rewriter, loc, memref::getTensorTypeFromMemRefType(valH.getType()), valH);
969 Value rt = bufferization::ToTensorOp::create(
970 rewriter, loc, memref::getTensorTypeFromMemRefType(rowH.getType()), rowH);
971 Value ct = bufferization::ToTensorOp::create(
972 rewriter, loc, memref::getTensorTypeFromMemRefType(colH.getType()), colH);
973 rewriter.replaceOpWithNewOp<AssembleOp>(op, c.getType(), ValueRange{rt, ct},
974 vt);
975 return success();
976}
977
978// Match and rewrite 2:4 SpMM kernel.
979static LogicalResult rewrite2To4SpMM(PatternRewriter &rewriter,
980 linalg::GenericOp op) {
981 Location loc = op.getLoc();
982 Value A = op.getOperand(0);
983 Value B = op.getOperand(1);
984 Value C = op.getOperand(2); // we have C = AB
985 SmallVector<Value> tokens;
986
987 // The cuSparselt API currently only allows pruning and compression
988 // to occur on the device. So we recognize the pattern
989 // A' = convert A ; dense to 2:4
990 // C = A'B ; 2:4 matrix mult
991 // and then perform compression and matrix multiplication on device.
992 auto cnv = A.getDefiningOp<ConvertOp>();
993 assert(cnv);
994 A = cnv.getSource();
995
996 // All input should be dense tensors.
997 if (!isDenseTensor(A) || !isDenseTensor(B) || !isDenseTensor(C))
998 return failure();
999
1000 // Start sparse kernel and copy data from host to device.
1001 // a : bufA -> matA
1002 // b : bufB -> matB
1003 // c : bufC -> matC
1004 Value bufA = genTensorToMemref(rewriter, loc, A);
1005 Value matA = genAllocCopy(rewriter, loc, bufA, tokens);
1006 Value bufB = genTensorToMemref(rewriter, loc, B);
1007 Value matB = genAllocCopy(rewriter, loc, bufB, tokens);
1008 Value bufC = genTensorToMemref(rewriter, loc, C);
1009 Value matC = genAllocCopy(rewriter, loc, bufC, tokens);
1010 genBlockingWait(rewriter, loc, tokens);
1011 tokens.clear();
1012
1013 // Create sparse environment and sparse matrix/dense vector handles.
1014 Value szm = linalg::createOrFoldDimOp(rewriter, loc, matA, 0);
1015 Value szk = linalg::createOrFoldDimOp(rewriter, loc, matB, 0);
1016 Value szn = linalg::createOrFoldDimOp(rewriter, loc, matC, 1);
1017 Type indexTp = rewriter.getIndexType();
1018 Type dnTensorHandleTp = rewriter.getType<gpu::SparseDnTensorHandleType>();
1019 Type spMatHandleTp = rewriter.getType<gpu::SparseSpMatHandleType>();
1020 Type tokenTp = rewriter.getType<gpu::AsyncTokenType>();
1021 Value token = genFirstWait(rewriter, loc);
1022 Operation *spGenA = gpu::Create2To4SpMatOp::create(
1023 rewriter, loc, spMatHandleTp, tokenTp, token, szm, szk,
1024 gpu::Prune2To4SpMatFlag::PRUNE_AND_CHECK, matA);
1025 Value spMatA = spGenA->getResult(0);
1026 token = spGenA->getResult(1);
1027 auto dmatB =
1028 gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp, tokenTp,
1029 token, matB, SmallVector<Value>{szk, szn});
1030 Value dnB = dmatB.getResult(0);
1031 token = dmatB.getAsyncToken();
1032 auto dmatC =
1033 gpu::CreateDnTensorOp::create(rewriter, loc, dnTensorHandleTp, tokenTp,
1034 token, matC, SmallVector<Value>{szm, szn});
1035 Value dnC = dmatC.getResult(0);
1036 token = dmatC.getAsyncToken();
1037 auto dmatCType = llvm::cast<ShapedType>(matC.getType()).getElementType();
1038
1039 // Precompute buffersize for SpMM.
1040 SmallVector<Type> bufferTypes_{indexTp, indexTp, indexTp};
1041 TypeRange bufferTypes(bufferTypes_);
1042 auto bufferComp = gpu::SpMMBufferSizeOp::create(
1043 rewriter, loc, bufferTypes, tokenTp, token,
1044 gpu::TransposeMode::NON_TRANSPOSE, gpu::TransposeMode::NON_TRANSPOSE,
1045 spMatA, dnB, dnC,
1046 /*computeType=*/dmatCType);
1047 token = bufferComp.getAsyncToken();
1048
1049 // Allocate buffers on host.
1050 Value bufferSz1 = bufferComp.getResult(0);
1051 auto buf1 = genAllocBuffer(rewriter, loc, bufferSz1, token);
1052 Value buffer1 = buf1.getResult(0);
1053 token = buf1.getAsyncToken();
1054 Value bufferSz2 = bufferComp.getResult(1);
1055 auto buf2 = genAllocBuffer(rewriter, loc, bufferSz2, token);
1056 Value buffer2 = buf2.getResult(0);
1057 token = buf2.getAsyncToken();
1058 Value bufferSz3 = bufferComp.getResult(2);
1059 auto buf3 = genAllocBuffer(rewriter, loc, bufferSz3, token);
1060 Value buffer3 = buf3.getResult(0);
1061 token = buf3.getAsyncToken();
1062
1063 // Perform the SpMM.
1064 auto dnCType = llvm::cast<ShapedType>(matC.getType()).getElementType();
1065 auto spmmComp = gpu::SpMMOp::create(
1066 rewriter, loc, tokenTp, token, spMatA, dnB, dnC, /*computeType=*/dnCType,
1067 SmallVector<Value>{buffer1, buffer2, buffer3});
1068 token = spmmComp.getAsyncToken();
1069
1070 // Copy data back to host and free all the resources.
1071 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatA)
1072 .getAsyncToken();
1073 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnB)
1074 .getAsyncToken();
1075 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnC)
1076 .getAsyncToken();
1077 token = genDeallocMemRef(rewriter, loc, buffer1, token);
1078 token = genDeallocMemRef(rewriter, loc, buffer2, token);
1079 token = genDeallocMemRef(rewriter, loc, buffer3, token);
1080 token = genDeallocMemRef(rewriter, loc, matA, token);
1081 token = genDeallocMemRef(rewriter, loc, matB, token);
1082 token = genCopyMemRef(rewriter, loc, bufC, matC, token);
1083 token = genDeallocMemRef(rewriter, loc, matC, token);
1084 tokens.push_back(token);
1085 genBlockingWait(rewriter, loc, tokens);
1086 tokens.clear();
1087
1088 // Done.
1089 rewriter.replaceOpWithNewOp<bufferization::ToTensorOp>(op, C.getType(), bufC);
1090 return success();
1091}
1092
1093/// Match and rewrite SDDMM kernel.
1094static LogicalResult rewriteSDDMM(PatternRewriter &rewriter,
1095 linalg::GenericOp op, bool enableRT) {
1096 Location loc = op.getLoc();
1097 Value a = op.getOperand(0);
1098 Value b = op.getOperand(1);
1099 Value c = op.getOperand(2);
1100 SmallVector<Value> tokens;
1101
1102 // Only admissible sparse matrix format (no COO/CSC) and dense matrices.
1106 auto format = getCuSparseFormat(cTp, bTp, aTp, enableRT, /*isMatVec=*/false);
1107 if (format == CuSparseFormat::kNone || format == CuSparseFormat::kCOO ||
1108 format == CuSparseFormat::kCSC)
1109 return failure();
1110
1111 // The SDDMM does the in-place operation.
1112 // Start sparse kernel and copy data from host to device.
1113 // a : bufA -> matA
1114 // b : bufB -> matB
1115 // c : memR/memC/memV -> rowC,colC,valC
1116 Value nseC = NumberOfEntriesOp::create(rewriter, loc, c);
1117 Value szm = linalg::createOrFoldDimOp(rewriter, loc, a, 0);
1118 Value szk = linalg::createOrFoldDimOp(rewriter, loc, a, 1);
1119 Value szn = linalg::createOrFoldDimOp(rewriter, loc, b, 1);
1120 Value bufA = genTensorToMemref(rewriter, loc, a);
1121 Value matA = genAllocCopy(rewriter, loc, bufA, tokens);
1122 Value bufB = genTensorToMemref(rewriter, loc, b);
1123 Value matB = genAllocCopy(rewriter, loc, bufB, tokens);
1124 Value memR = genFirstPosOrCrds(rewriter, loc, c, format, enableRT);
1125 Value memC = genSecondCrds(rewriter, loc, c, format, enableRT); // or empty
1126 Value memV = ToValuesOp::create(rewriter, loc, c);
1127 Value rowC = genAllocCopy(rewriter, loc, memR, tokens);
1128 Value colC = memC ? genAllocCopy(rewriter, loc, memC, tokens) : Value();
1129 Value valC = genAllocCopy(rewriter, loc, memV, tokens);
1130 genBlockingWait(rewriter, loc, tokens);
1131 tokens.clear();
1132
1133 // Create sparse environment and sparse matrix/dense matrix handles.
1134 Type indexTp = rewriter.getIndexType();
1135 Type dnMatHandleTp = rewriter.getType<gpu::SparseDnTensorHandleType>();
1136 Type spMatHandleTp = rewriter.getType<gpu::SparseSpMatHandleType>();
1137 Type tokenTp = rewriter.getType<gpu::AsyncTokenType>();
1138 Value token = genFirstWait(rewriter, loc);
1139 auto dmatA =
1140 gpu::CreateDnTensorOp::create(rewriter, loc, dnMatHandleTp, tokenTp,
1141 token, matA, SmallVector<Value>{szm, szk});
1142 Value dnA = dmatA.getResult(0);
1143 token = dmatA.getAsyncToken();
1144 auto dmatB =
1145 gpu::CreateDnTensorOp::create(rewriter, loc, dnMatHandleTp, tokenTp,
1146 token, matB, SmallVector<Value>{szk, szn});
1147 Value dnB = dmatB.getResult(0);
1148 token = dmatB.getAsyncToken();
1149 Operation *spGenC =
1150 genSpMat(rewriter, loc, cTp, spMatHandleTp, tokenTp, token, szm, szn,
1151 nseC, rowC, colC, valC, format, enableRT);
1152 Value spMatC = spGenC->getResult(0);
1153 token = spGenC->getResult(1);
1154 auto dnCType = llvm::cast<ShapedType>(c.getType()).getElementType();
1155
1156 // Precompute buffersize for SDDMM.
1157 auto bufferComp = gpu::SDDMMBufferSizeOp::create(
1158 rewriter, loc, indexTp, tokenTp, token, dnA, dnB, spMatC, dnCType);
1159 Value bufferSz = bufferComp.getResult(0);
1160 token = bufferComp.getAsyncToken();
1161 auto buf = genAllocBuffer(rewriter, loc, bufferSz, token);
1162 Value buffer = buf.getResult(0);
1163 token = buf.getAsyncToken();
1164
1165 // Perform the SDDMM.
1166 auto sddmmComp = gpu::SDDMMOp::create(rewriter, loc, tokenTp, token, dnA, dnB,
1167 spMatC, dnCType, buffer);
1168 token = sddmmComp.getAsyncToken();
1169
1170 // Copy data back to host and free all the resoures.
1171 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnA)
1172 .getAsyncToken();
1173 token = gpu::DestroyDnTensorOp::create(rewriter, loc, tokenTp, token, dnB)
1174 .getAsyncToken();
1175 token = gpu::DestroySpMatOp::create(rewriter, loc, tokenTp, token, spMatC)
1176 .getAsyncToken();
1177 token = genDeallocMemRef(rewriter, loc, buffer, token);
1178 token = genDeallocMemRef(rewriter, loc, matA, token);
1179 token = genDeallocMemRef(rewriter, loc, matB, token);
1180 token = genDeallocMemRef(rewriter, loc, rowC, token);
1181 if (colC)
1182 token = genDeallocMemRef(rewriter, loc, colC, token);
1183 token = genCopyMemRef(rewriter, loc, memV, valC, token);
1184 token = genDeallocMemRef(rewriter, loc, valC, token);
1185 tokens.push_back(token);
1186 genBlockingWait(rewriter, loc, tokens);
1187 tokens.clear();
1188
1189 // Done.
1190 rewriter.replaceOpWithNewOp<sparse_tensor::LoadOp>(op, c);
1191 return success();
1192}
1193
1194//===----------------------------------------------------------------------===//
1195// Rewriting rules for direct code generation.
1196//===----------------------------------------------------------------------===//
1197
1198/// Proof-of-concept rewriter. This rule generates a GPU implementation
1199/// for each outermost forall loop generated by the sparsifier.
1200/// TODO: right now works with parallelization-strategy=dense-outer-loop
1201/// but give this its own flags in the future
1202struct ForallRewriter : public OpRewritePattern<scf::ParallelOp> {
1203 using OpRewritePattern<scf::ParallelOp>::OpRewritePattern;
1204
1205 ForallRewriter(MLIRContext *context, unsigned nT)
1206 : OpRewritePattern(context), numThreads(nT) {};
1207
1208 LogicalResult matchAndRewrite(scf::ParallelOp forallOp,
1209 PatternRewriter &rewriter) const override {
1210 // Reject inadmissible loop form.
1211 // Essentially only accept a loop, generated by the sparsifier,
1212 // of the form
1213 // forall (i = 0; i < N; i++)
1214 // so that cyclic scheduling over the threads is easy.
1215 if (!forallOp->hasDiscardableAttr(
1217 forallOp.getNumReductions() != 0 || forallOp.getNumLoops() != 1 ||
1218 !matchPattern(forallOp.getLowerBound()[0], m_Zero()) ||
1219 !matchPattern(forallOp.getStep()[0], m_One()))
1220 return failure();
1221 // Collect every value that is computed outside the parallel loop.
1222 SetVector<Value> invariants; // stable iteration!
1223 forallOp->walk([&](Operation *op) {
1224 // Collect all values of admissible ops.
1225 for (OpOperand &o : op->getOpOperands()) {
1226 Value val = o.get();
1227 Block *block;
1228 if (auto arg = dyn_cast<BlockArgument>(val))
1229 block = arg.getOwner();
1230 else
1231 block = val.getDefiningOp()->getBlock();
1232 if (!forallOp.getRegion().findAncestorBlockInRegion(*block))
1233 invariants.insert(val);
1234 }
1235 });
1236 // Outline the outside values as proper parameters. Fail when sharing
1237 // value between host and device is not straightforward.
1238 SmallVector<Value> constants;
1239 SmallVector<Value> scalars;
1240 SmallVector<Value> buffers;
1241 // A boolean mask aligned 1:1 with the `buffers` array, tracking which
1242 // of those buffers were mutated by the loop. If true, the corresponding
1243 // buffer needs to be "copied back" using a device-to-host copy.
1244 SmallVector<bool> copyBack;
1245 for (Value val : invariants) {
1246 Type tp = val.getType();
1247 if (val.getDefiningOp<arith::ConstantOp>())
1248 constants.push_back(val);
1249 else if (isa<FloatType>(tp) || tp.isIntOrIndex())
1250 scalars.push_back(val);
1251 else if (isa<MemRefType>(tp)) {
1252 buffers.push_back(val);
1253
1254 // Determine if the buffer needs to be "copied back" from device
1255 // to host by checking for `memref.store` and the write memory effect.
1256 bool isWrite = false;
1257 for (Operation *user : val.getUsers()) {
1258 if (isa<memref::StoreOp>(user)) {
1259 isWrite = true;
1260 break;
1261 }
1262 if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(user)) {
1263 if (memInterface.getEffectOnValue<MemoryEffects::Write>(val)) {
1264 isWrite = true;
1265 break;
1266 }
1267 }
1268 }
1269 copyBack.push_back(isWrite);
1270 } else
1271 return failure(); // don't know how to share
1272 }
1273 // Pass outlined non-constant values.
1274 // TODO: Experiment with `useHostRegistrationForOut` to see if we want to
1275 // keep the feature at all (either through a heuristic or compiler
1276 // option for gpu codegen).
1277 Location loc = forallOp->getLoc();
1278 SmallVector<Value> args;
1279 SmallVector<Value> tokens;
1280 Value out = genParametersIn(rewriter, loc, scalars, buffers, args, tokens,
1281 /*useHostRegistrationForOut=*/false);
1282 // Set up GPU module and construct GPU function.
1283 auto saveIp = rewriter.saveInsertionPoint();
1284 ModuleOp topModule = forallOp->getParentOfType<ModuleOp>();
1285 auto gpuModule = genGPUModule(rewriter, topModule);
1286 auto gpuFunc = genGPUFunc(rewriter, gpuModule, args);
1287 genGPUCode(rewriter, gpuFunc, forallOp, constants, scalars, buffers);
1288 // Generate code that launches the kernel asynchronously, blocking on all
1289 // opens tokens and yielding a new token for the output.
1290 // TODO: Passing in tokens to launch up does not seem to be properly lowered
1291 // by cubin yet, hence the current blocking wait.
1292 rewriter.restoreInsertionPoint(saveIp);
1293 genBlockingWait(rewriter, loc, tokens);
1294 tokens.clear();
1295 Value kernelToken =
1296 genLaunchGPUFunc(rewriter, gpuFunc, args, tokens, numThreads);
1297 // Finalize the outlined arguments.
1298 genParametersOut(rewriter, loc, out, kernelToken, scalars, buffers, args,
1299 tokens, copyBack);
1300 genBlockingWait(rewriter, loc, tokens);
1301 rewriter.eraseOp(forallOp);
1302 return success();
1303 }
1304
1305private:
1306 unsigned numThreads;
1307};
1308
1309//===----------------------------------------------------------------------===//
1310// Rewriting rules for library recognition and code generation.
1311//===----------------------------------------------------------------------===//
1312
1313/// Proof-of-concept rewriter. This rule recognizes certain math kernels
1314/// and replaces these with corresponding calls into a sparse library.
1315struct LinalgOpRewriter : public OpRewritePattern<linalg::GenericOp> {
1316 using OpRewritePattern<linalg::GenericOp>::OpRewritePattern;
1317
1318 LinalgOpRewriter(MLIRContext *context, bool rt)
1319 : OpRewritePattern(context), enableRT(rt) {}
1320
1321 LogicalResult matchAndRewrite(linalg::GenericOp op,
1322 PatternRewriter &rewriter) const override {
1323 if (op.getNumDpsInits() != 1)
1324 return failure(); // reject multi-output
1325 // The rewrites below query the sparse tensor encoding of the operands,
1326 // which is only defined for ranked tensor types. Reject buffer semantics
1327 // rather than casting a memref operand to a tensor type.
1328 if (!op.hasPureTensorSemantics())
1329 return failure();
1330
1331 const unsigned numLoops = op.getNumLoops();
1332 const unsigned numTensors = op->getNumOperands();
1333 const auto iteratorTypes = op.getIteratorTypesArray();
1334 SmallVector<AffineMap, 4> maps = op.getIndexingMapsArray();
1335
1336 using MapList = ArrayRef<ArrayRef<AffineExpr>>;
1337 auto infer = [&](MapList m) {
1338 return AffineMap::inferFromExprList(m, op.getContext());
1339 };
1340 AffineExpr i, j, k;
1341 bindDims(getContext(), i, j, k);
1342
1343 // TODO: more robust patterns, transposed versions, more kernels,
1344 // identify alpha and beta and pass them to the CUDA calls.
1345
1346 // Recognize a SpMV kernel.
1347 if (numLoops == 2 && numTensors == 3 &&
1348 linalg::isParallelIterator(iteratorTypes[0]) &&
1349 linalg::isReductionIterator(iteratorTypes[1]) &&
1350 matchSumOfMultOfArgs(op)) {
1351 if (maps == infer({{i, j}, {j}, {i}}))
1352 return rewriteSpMV(rewriter, op, enableRT,
1353 gpu::TransposeMode::NON_TRANSPOSE);
1354 // TODO: recognize conjugate transpose patterns for SpMV.
1355 if (maps == infer({{j, i}, {j}, {i}}))
1356 return rewriteSpMV(rewriter, op, enableRT,
1357 gpu::TransposeMode::TRANSPOSE);
1358 }
1359
1360 // Recognize a SpGEMM, 2:4-SpMM, or SpMM kernel.
1361 if (numLoops == 3 && numTensors == 3 &&
1362 linalg::isParallelIterator(iteratorTypes[0]) &&
1363 linalg::isParallelIterator(iteratorTypes[1]) &&
1364 linalg::isReductionIterator(iteratorTypes[2]) &&
1365 matchSumOfMultOfArgs(op)) {
1366 if (maps == infer({{i, k}, {k, j}, {i, j}})) {
1367 if (!isDenseTensor(op.getOperand(0)) &&
1368 !isDenseTensor(op.getOperand(1)))
1369 return rewriteSpGEMM(rewriter, op, enableRT);
1370 if (isConversionInto24(op.getOperand(0)))
1371 return rewrite2To4SpMM(rewriter, op);
1372 return rewriteSpMM(rewriter, op, enableRT,
1373 gpu::TransposeMode::NON_TRANSPOSE,
1374 gpu::TransposeMode::NON_TRANSPOSE);
1375 }
1376 // TODO: recognize conjugate transpose patterns for either SpMM input.
1377 if (maps == infer({{k, i}, {k, j}, {i, j}}))
1378 return rewriteSpMM(rewriter, op, enableRT,
1379 gpu::TransposeMode::TRANSPOSE,
1380 gpu::TransposeMode::NON_TRANSPOSE);
1381 if (maps == infer({{i, k}, {j, k}, {i, j}}))
1382 return rewriteSpMM(rewriter, op, enableRT,
1383 gpu::TransposeMode::NON_TRANSPOSE,
1384 gpu::TransposeMode::TRANSPOSE);
1385 if (maps == infer({{k, i}, {j, k}, {i, j}}))
1386 return rewriteSpMM(rewriter, op, enableRT,
1387 gpu::TransposeMode::TRANSPOSE,
1388 gpu::TransposeMode::TRANSPOSE);
1389 }
1390
1391 // Recognize a SDDMM kernel.
1392 if (numLoops == 3 && numTensors == 3 &&
1393 linalg::isParallelIterator(iteratorTypes[0]) &&
1394 linalg::isParallelIterator(iteratorTypes[1]) &&
1395 linalg::isReductionIterator(iteratorTypes[2]) &&
1396 maps == infer({{i, k}, {k, j}, {i, j}}) &&
1397 matchSumReductionOfMulUnary(op)) {
1398 return rewriteSDDMM(rewriter, op, enableRT);
1399 }
1400
1401 return failure();
1402 }
1403
1404private:
1405 bool enableRT;
1406};
1407
1408} // namespace
1409
1410//===----------------------------------------------------------------------===//
1411// Public method for populating GPU rewriting rules.
1412//
1413// Currently two set of rewriting rules are made available. The first set
1414// implements direct code generation, currently by means of convering the
1415// outermost paralell loop into GPU threads. The second set implements
1416// libary recognition of a set of sparse operations. Eventually, the right
1417// combination of these two approaches has to be found.
1418//===----------------------------------------------------------------------===//
1419
1421 unsigned numThreads) {
1422 patterns.add<ForallRewriter>(patterns.getContext(), numThreads);
1423}
1424
1426 bool enableRT) {
1427 patterns.add<LinalgOpRewriter>(patterns.getContext(), enableRT);
1428}
return success()
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static FailureOr< bool > isTransposed(vector::TransferReadOp op)
Check if the loaded matrix operand requires transposed.
#define mul(a, b)
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
Block represents an ordered list of Operations.
Definition Block.h:34
BlockArgument getArgument(unsigned i)
Definition Block.h:154
Operation & front()
Definition Block.h:178
BlockArgListType getArguments()
Definition Block.h:112
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
Definition Builders.h:94
IndexType getIndexType()
Definition Builders.cpp:59
IntegerType getI8Type()
Definition Builders.cpp:67
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookup(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:72
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
This class helps build Operations.
Definition Builders.h:210
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
Definition Builders.h:388
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
Definition Builders.h:393
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:608
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:415
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
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
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
Definition Types.cpp:114
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
Type getType() const
Return the type of this value.
Definition Value.h:105
user_range getUsers() const
Definition Value.h:218
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static constexpr llvm::StringLiteral getLoopEmitterLoopAttrName()
A wrapper around RankedTensorType, which has three goals:
unsigned getCrdWidth() const
Returns the coordinate-overhead bitwidth, defaulting to zero.
bool hasEncoding() const
Returns true for tensors which have an encoding, and false for those which do not.
Dimension getDimRank() const
Returns the dimension-rank.
Type getCrdType() const
Returns the coordinate-overhead MLIR type, defaulting to IndexType.
bool isIdentity() const
Returns true if the dimToLvl mapping is the identity.
Level getLvlRank() const
Returns the level-rank.
unsigned getPosWidth() const
Returns the position-overhead bitwidth, defaulting to zero.
bool isPermutation() const
Returns true if the dimToLvl mapping is a permutation.
AffineMap getDimToLvl() const
Returns the dimToLvl mapping (or the null-map for the identity).
Type getPosType() const
Returns the position-overhead MLIR type, defaulting to IndexType.
bool isParallelIterator(utils::IteratorType iteratorType)
Check if iterator type has "parallel" semantics.
Definition Utils.cpp:232
bool isReductionIterator(utils::IteratorType iteratorType)
Check if iterator type has "reduction" semantics.
Definition Utils.cpp:236
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
Type getTensorTypeFromMemRefType(Type type)
Return an unranked/ranked tensor type for the given unranked/ranked memref type.
Definition MemRefOps.cpp:63
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
SparseTensorType getSparseTensorType(Value val)
Convenience methods to obtain a SparseTensorType from a Value.
SmallVector< unsigned > getBlockSize(AffineMap dimToLvl)
Given the dimToLvl map, returns the block sizes in a vector.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
Definition AffineExpr.h:311
void populateSparseGPULibgenPatterns(RewritePatternSet &patterns, bool enableRT)
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Definition Matchers.h:442
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition Value.h:494
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition Matchers.h:478
void populateSparseGPUCodegenPatterns(RewritePatternSet &patterns, unsigned numThreads)
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Utility class for the GPU dialect to represent triples of Values accessible through ....
Definition GPUDialect.h:40