MLIR 24.0.0git
GPUDialect.cpp
Go to the documentation of this file.
1//===- GPUDialect.cpp - MLIR Dialect for GPU Kernels implementation -------===//
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 file implements the GPU kernel-related dialect and its operations.
10//
11//===----------------------------------------------------------------------===//
12
14
20#include "mlir/IR/Attributes.h"
21#include "mlir/IR/Builders.h"
23#include "mlir/IR/BuiltinOps.h"
25#include "mlir/IR/Diagnostics.h"
27#include "mlir/IR/Matchers.h"
30#include "mlir/IR/SymbolTable.h"
36#include "llvm/ADT/STLExtras.h"
37#include "llvm/ADT/TypeSwitch.h"
38#include "llvm/Support/CommandLine.h"
39#include "llvm/Support/ErrorHandling.h"
40#include "llvm/Support/FormatVariadic.h"
41#include "llvm/Support/InterleavedRange.h"
42#include "llvm/Support/StringSaver.h"
43#include <cassert>
44#include <numeric>
45#include <optional>
46
47using namespace mlir;
48using namespace mlir::gpu;
49
50#include "mlir/Dialect/GPU/IR/GPUOpsDialect.cpp.inc"
51
52//===----------------------------------------------------------------------===//
53// GPU Device Mapping Attributes
54//===----------------------------------------------------------------------===//
55
56int64_t GPUBlockMappingAttr::getMappingId() const {
57 return static_cast<int64_t>(getBlock());
58}
59
60bool GPUBlockMappingAttr::isLinearMapping() const {
61 return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
62}
63
64int64_t GPUBlockMappingAttr::getRelativeIndex() const {
65 return isLinearMapping()
66 ? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
67 : getMappingId();
68}
69
70int64_t GPUWarpgroupMappingAttr::getMappingId() const {
71 return static_cast<int64_t>(getWarpgroup());
72}
73
74bool GPUWarpgroupMappingAttr::isLinearMapping() const {
75 return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
76}
77
78int64_t GPUWarpgroupMappingAttr::getRelativeIndex() const {
79 return isLinearMapping()
80 ? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
81 : getMappingId();
82}
83
84int64_t GPUWarpMappingAttr::getMappingId() const {
85 return static_cast<int64_t>(getWarp());
86}
87
88bool GPUWarpMappingAttr::isLinearMapping() const {
89 return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
90}
91
92int64_t GPUWarpMappingAttr::getRelativeIndex() const {
93 return isLinearMapping()
94 ? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
95 : getMappingId();
96}
97
98int64_t GPUThreadMappingAttr::getMappingId() const {
99 return static_cast<int64_t>(getThread());
100}
101
102bool GPUThreadMappingAttr::isLinearMapping() const {
103 return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
104}
105
106int64_t GPUThreadMappingAttr::getRelativeIndex() const {
107 return isLinearMapping()
108 ? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
109 : getMappingId();
110}
111
112int64_t GPULaneMappingAttr::getMappingId() const {
113 return static_cast<int64_t>(getLane());
114}
115
116bool GPULaneMappingAttr::isLinearMapping() const {
117 return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
118}
119
120int64_t GPULaneMappingAttr::getRelativeIndex() const {
121 return isLinearMapping()
122 ? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
123 : getMappingId();
124}
125
126int64_t GPUMappingMaskAttr::getMaxNumPhysicalIds() const { return 64; }
127
128/// 8 4 0
129/// Example mask : 0 0 0 1 1 0 1 0 0
130///
131/// Active physical (resp. logical) is 2 (0), 4 (1) and 5 (2).
132/// Logical id for e.g. 5 (2) constructs filter (1 << 5 - 1).
133///
134/// Example mask : 0 0 0 1 1 0 1 0 0
135/// Example filter: 0 0 0 0 1 1 1 1 1
136/// Intersection : 0 0 0 0 1 0 1 0 0
137/// PopCnt : 2
138Value GPUMappingMaskAttr::createLogicalLinearMappingId(
139 OpBuilder &b, Value physicalLinearMappingId) const {
140 Location loc = physicalLinearMappingId.getLoc();
141 Value mask =
142 arith::ConstantOp::create(b, loc, b.getI64IntegerAttr(getMask()));
143 Value one = arith::ConstantOp::create(b, loc, b.getI64IntegerAttr(1));
144 Value filter = arith::ShLIOp::create(b, loc, one, physicalLinearMappingId);
145 filter = arith::SubIOp::create(b, loc, filter, one);
146 Value filteredId = arith::AndIOp::create(b, loc, mask, filter);
147 return math::CtPopOp::create(b, loc, filteredId);
148}
149
150/// 8 4 0
151/// Example mask : 0 0 0 1 1 0 1 0 0
152///
153/// Active physical (resp. logical) is 2 (0), 4 (1) and 5 (2).
154/// Logical id for e.g. 5 (2) constructs filter (1 << 5).
155///
156/// Example mask : 0 0 0 1 1 0 1 0 0
157/// Example filter: 0 0 0 1 0 0 0 0 0
158/// Intersection : 0 0 0 1 0 0 0 0 0
159/// Cmp : 1
160Value GPUMappingMaskAttr::createIsActiveIdPredicate(
161 OpBuilder &b, Value physicalLinearMappingId) const {
162 Location loc = physicalLinearMappingId.getLoc();
163 Value mask =
164 arith::ConstantOp::create(b, loc, b.getI64IntegerAttr(getMask()));
165 Value one = arith::ConstantOp::create(b, loc, b.getI64IntegerAttr(1));
166 Value filter = arith::ShLIOp::create(b, loc, one, physicalLinearMappingId);
167 Value filtered = arith::AndIOp::create(b, loc, mask, filter);
168 Value zero = arith::ConstantOp::create(b, loc, b.getI64IntegerAttr(0));
169 return arith::CmpIOp::create(b, loc, arith::CmpIPredicate::ne, filtered,
170 zero);
171}
172
173int64_t GPUMemorySpaceMappingAttr::getMappingId() const {
174 return static_cast<int64_t>(getAddressSpace());
175}
176
177bool GPUMemorySpaceMappingAttr::isLinearMapping() const {
178 llvm_unreachable("GPUMemorySpaceMappingAttr does not support linear mapping");
179}
180
181int64_t GPUMemorySpaceMappingAttr::getRelativeIndex() const {
182 llvm_unreachable("GPUMemorySpaceMappingAttr does not support relative index");
183}
184
185//===----------------------------------------------------------------------===//
186// MMAMatrixType
187//===----------------------------------------------------------------------===//
188
190 StringRef operand) {
191 return Base::get(elementType.getContext(), shape, elementType, operand);
192}
193
196 ArrayRef<int64_t> shape, Type elementType,
197 StringRef operand) {
198 return Base::getChecked(emitError, elementType.getContext(), shape,
199 elementType, operand);
200}
201
202unsigned MMAMatrixType::getNumDims() const { return getImpl()->numDims; }
203
205 return getImpl()->getShape();
206}
207
208Type MMAMatrixType::getElementType() const { return getImpl()->elementType; }
209
210StringRef MMAMatrixType::getOperand() const { return getImpl()->getOperand(); }
211
213 return elementType.isF16() || elementType.isF32() || elementType.isF64() ||
214 elementType.isUnsignedInteger(8) || elementType.isSignedInteger(8) ||
215 elementType.isInteger(32);
216}
217
218LogicalResult
220 ArrayRef<int64_t> shape, Type elementType,
221 StringRef operand) {
222 if (operand != "AOp" && operand != "BOp" && operand != "COp")
223 return emitError() << "operand expected to be one of AOp, BOp or COp";
224
225 if (shape.size() != 2)
226 return emitError() << "MMAMatrixType must have exactly two dimensions";
227
228 if (!MMAMatrixType::isValidElementType(elementType))
229 return emitError()
230 << "MMAMatrixType elements must be SI8, UI8, I32, F16, F32, or F64";
231
232 return success();
233}
234
235//===----------------------------------------------------------------------===//
236// GPUDialect
237//===----------------------------------------------------------------------===//
238
239bool GPUDialect::isWorkgroupMemoryAddressSpace(Attribute memorySpace) {
240 if (!memorySpace)
241 return false;
242 if (auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
243 return gpuAttr.getValue() == getWorkgroupAddressSpace();
244 return false;
245}
246
247bool GPUDialect::hasWorkgroupMemoryAddressSpace(MemRefType type) {
248 Attribute memorySpace = type.getMemorySpace();
249 return isWorkgroupMemoryAddressSpace(memorySpace);
250}
251
252bool GPUDialect::isConstantMemoryAddressSpace(Attribute memorySpace) {
253 if (!memorySpace)
254 return false;
255 if (auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
256 return gpuAttr.getValue() == getConstantAddressSpace();
257 return false;
258}
259
260bool GPUDialect::hasConstantMemoryAddressSpace(MemRefType type) {
261 Attribute memorySpace = type.getMemorySpace();
262 return isConstantMemoryAddressSpace(memorySpace);
263}
264
265bool GPUDialect::isKernel(Operation *op) {
266 if (auto gpuFunc = dyn_cast<GPUFuncOp>(op))
267 return gpuFunc.isKernel();
268 return static_cast<bool>(
269 op->getDiscardableAttrOfType<UnitAttr>(getKernelFuncAttrName()));
270}
271
272namespace {
273/// This class defines the interface for handling inlining with gpu
274/// operations.
275struct GPUInlinerInterface : public DialectInlinerInterface {
276 using DialectInlinerInterface::DialectInlinerInterface;
277
278 /// All gpu dialect ops can be inlined.
279 bool isLegalToInline(Operation *, Region *, bool, IRMapping &) const final {
280 return true;
281 }
282};
283} // namespace
284
285void GPUDialect::initialize() {
286 addTypes<AsyncTokenType>();
287 addTypes<MMAMatrixType>();
288 addTypes<NamedBarrierType>();
289 addTypes<SparseDnTensorHandleType>();
290 addTypes<SparseSpMatHandleType>();
291 addTypes<SparseSpGEMMOpHandleType>();
292 addOperations<
293#define GET_OP_LIST
294#include "mlir/Dialect/GPU/IR/GPUOps.cpp.inc"
295 >();
296 addAttributes<
297#define GET_ATTRDEF_LIST
298#include "mlir/Dialect/GPU/IR/GPUOpsAttributes.cpp.inc"
299 >();
300 addInterfaces<GPUInlinerInterface>();
301 declarePromisedInterface<bufferization::BufferDeallocationOpInterface,
302 TerminatorOp>();
303 declarePromisedInterfaces<ValueBoundsOpInterface, ClusterDimOp,
304 ClusterDimBlocksOp, ClusterIdOp, ClusterBlockIdOp,
305 BlockDimOp, BlockIdOp, GridDimOp, ThreadIdOp,
306 LaneIdOp, SubgroupIdOp, GlobalIdOp, NumSubgroupsOp,
307 SubgroupSizeOp, LaunchOp, SubgroupBroadcastOp>();
308 declarePromisedInterfaces<memref::IndexedAccessOpInterface,
309 SubgroupMmaLoadMatrixOp,
310 SubgroupMmaStoreMatrixOp>();
311}
312
313static std::string getSparseHandleKeyword(SparseHandleKind kind) {
314 switch (kind) {
316 return "sparse.dntensor_handle";
318 return "sparse.spmat_handle";
320 return "sparse.spgemmop_handle";
321 }
322 llvm_unreachable("unknown sparse handle kind");
323 return "";
324}
325
326Type GPUDialect::parseType(DialectAsmParser &parser) const {
327 // Parse the main keyword for the type.
328 StringRef keyword;
329 if (parser.parseKeyword(&keyword))
330 return Type();
331 MLIRContext *context = getContext();
332
333 // Handle 'async token' types.
334 if (keyword == "async.token")
335 return AsyncTokenType::get(context);
336
337 if (keyword == "mma_matrix") {
338 SMLoc beginLoc = parser.getNameLoc();
339
340 // Parse '<'.
341 if (parser.parseLess())
342 return nullptr;
343
344 // Parse the size and elementType.
345 SmallVector<int64_t> shape;
346 Type elementType;
347 if (parser.parseDimensionList(shape, /*allowDynamic=*/false) ||
348 parser.parseType(elementType))
349 return nullptr;
350
351 // Parse ','
352 if (parser.parseComma())
353 return nullptr;
354
355 // Parse operand.
356 std::string operand;
357 if (failed(parser.parseOptionalString(&operand)))
358 return nullptr;
359
360 // Parse '>'.
361 if (parser.parseGreater())
362 return nullptr;
363
365 parser.getEncodedSourceLoc(beginLoc)),
366 shape, elementType, operand);
367 }
368
369 if (keyword == "named_barrier")
370 return NamedBarrierType::get(context);
371
373 return SparseDnTensorHandleType::get(context);
375 return SparseSpMatHandleType::get(context);
377 return SparseSpGEMMOpHandleType::get(context);
378
379 parser.emitError(parser.getNameLoc(), "unknown gpu type: " + keyword);
380 return Type();
381}
382// TODO: print refined type here. Notice that should be corresponding to the
383// parser
384void GPUDialect::printType(Type type, DialectAsmPrinter &os) const {
385 TypeSwitch<Type>(type)
386 .Case<AsyncTokenType>([&](Type) { os << "async.token"; })
387 .Case<NamedBarrierType>([&](Type) { os << "named_barrier"; })
388 .Case<SparseDnTensorHandleType>([&](Type) {
390 })
391 .Case<SparseSpMatHandleType>(
393 .Case<SparseSpGEMMOpHandleType>([&](Type) {
395 })
396 .Case([&](MMAMatrixType fragTy) {
397 os << "mma_matrix<";
398 auto shape = fragTy.getShape();
399 for (auto dim = shape.begin(), e = shape.end() - 1; dim != e; ++dim)
400 os << *dim << 'x';
401 os << shape.back() << 'x' << fragTy.getElementType();
402 os << ", \"" << fragTy.getOperand() << "\"" << '>';
403 })
404 .DefaultUnreachable("unexpected 'gpu' type kind");
405}
406
407static LogicalResult verifyKnownLaunchSizeAttr(Operation *op,
408 NamedAttribute attr) {
409 auto array = dyn_cast<DenseI32ArrayAttr>(attr.getValue());
410 if (!array)
411 return op->emitOpError(Twine(attr.getName()) +
412 " must be a dense i32 array");
413 if (array.size() != 3)
414 return op->emitOpError(Twine(attr.getName()) +
415 " must contain exactly 3 elements");
416 return success();
417}
418
419LogicalResult GPUDialect::verifyOperationAttribute(Operation *op,
420 NamedAttribute attr) {
421 if (attr.getName() == getKnownBlockSizeAttrHelper().getName())
422 return verifyKnownLaunchSizeAttr(op, attr);
423 if (attr.getName() == getKnownGridSizeAttrHelper().getName())
424 return verifyKnownLaunchSizeAttr(op, attr);
425 if (attr.getName() == getKnownClusterSizeAttrHelper().getName())
426 return verifyKnownLaunchSizeAttr(op, attr);
427 if (!llvm::isa<UnitAttr>(attr.getValue()) ||
428 attr.getName() != getContainerModuleAttrName())
429 return success();
430
431 auto module = dyn_cast<ModuleOp>(op);
432 if (!module)
433 return op->emitError("expected '")
434 << getContainerModuleAttrName() << "' attribute to be attached to '"
435 << ModuleOp::getOperationName() << '\'';
436 return success();
437}
438
439/// Parses an optional list of async operands with an optional leading keyword.
440/// (`async`)? (`[` ssa-id-list `]`)?
441///
442/// This method is used by the tablegen assembly format for async ops as well.
443static ParseResult parseAsyncDependencies(
444 OpAsmParser &parser, Type &asyncTokenType,
446 auto loc = parser.getCurrentLocation();
447 if (succeeded(parser.parseOptionalKeyword("async"))) {
448 if (parser.getNumResults() == 0)
449 return parser.emitError(loc, "needs to be named when marked 'async'");
450 asyncTokenType = parser.getBuilder().getType<AsyncTokenType>();
451 }
452 return parser.parseOperandList(asyncDependencies,
454}
455
456/// Prints optional async dependencies with its leading keyword.
457/// (`async`)? (`[` ssa-id-list `]`)?
458// Used by the tablegen assembly format for several async ops.
460 Type asyncTokenType,
461 OperandRange asyncDependencies) {
462 if (asyncTokenType)
463 printer << "async";
464 if (asyncDependencies.empty())
465 return;
466 if (asyncTokenType)
467 printer << ' ';
468 printer << llvm::interleaved_array(asyncDependencies);
469}
470
471// GPU Memory attributions functions shared by LaunchOp and GPUFuncOp.
472/// Parses a GPU function memory attribution.
473///
474/// memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)?
475/// (`private` `(` ssa-id-and-type-list `)`)?
476///
477/// Note that this function parses only one of the two similar parts, with the
478/// keyword provided as argument.
479static ParseResult
480parseAttributions(OpAsmParser &parser, StringRef keyword,
482 // If we could not parse the keyword, just assume empty list and succeed.
483 if (failed(parser.parseOptionalKeyword(keyword)))
484 return success();
485
487 /*allowType=*/true);
488}
489
490static void printAttributions(OpAsmPrinter &p, StringRef keyword,
492 ArrayAttr attributes = {}) {
493 if (values.empty())
494 return;
495
496 p << ' ' << keyword << '(';
497 llvm::interleaveComma(
498 llvm::enumerate(values), p, [&p, attributes](auto pair) {
499 BlockArgument v = pair.value();
500 p << v << " : " << v.getType();
501
502 size_t attributionIndex = pair.index();
503 DictionaryAttr attrs;
504 if (attributes && attributionIndex < attributes.size())
505 attrs = llvm::cast<DictionaryAttr>(attributes[attributionIndex]);
506 if (attrs)
507 p.printOptionalAttrDict(attrs.getValue());
508 });
509 p << ')';
510}
511
512/// Verifies a GPU function memory attribution.
513static LogicalResult verifyAttributions(Operation *op,
514 ArrayRef<BlockArgument> attributions,
515 gpu::AddressSpace memorySpace) {
516 for (Value v : attributions) {
517 auto type = llvm::dyn_cast<MemRefType>(v.getType());
518 if (!type)
519 return op->emitOpError() << "expected memref type in attribution";
520
521 // We can only verify the address space if it hasn't already been lowered
522 // from the AddressSpaceAttr to a target-specific numeric value.
523 auto addressSpace =
524 llvm::dyn_cast_or_null<gpu::AddressSpaceAttr>(type.getMemorySpace());
525 if (!addressSpace)
526 continue;
527 if (addressSpace.getValue() != memorySpace)
528 return op->emitOpError()
529 << "expected memory space " << stringifyAddressSpace(memorySpace)
530 << " in attribution";
531 }
532 return success();
533}
534
535//===----------------------------------------------------------------------===//
536// AllReduceOp
537//===----------------------------------------------------------------------===//
538
539static LogicalResult verifyReduceOpAndType(gpu::AllReduceOperation opName,
540 Type resType) {
541 using Kind = gpu::AllReduceOperation;
542 if (llvm::is_contained(
543 {Kind::MINNUMF, Kind::MAXNUMF, Kind::MINIMUMF, Kind::MAXIMUMF},
544 opName)) {
545 if (!isa<FloatType>(resType))
546 return failure();
547 }
548
549 if (llvm::is_contained({Kind::MINSI, Kind::MINUI, Kind::MAXSI, Kind::MAXUI,
550 Kind::AND, Kind::OR, Kind::XOR},
551 opName)) {
552 if (!isa<IntegerType>(resType))
553 return failure();
554 }
555
556 return success();
557}
558
559LogicalResult gpu::AllReduceOp::verifyRegions() {
560 if (getBody().empty() != getOp().has_value())
561 return emitError("expected either an op attribute or a non-empty body");
562 if (!getBody().empty()) {
563 if (getBody().getNumArguments() != 2)
564 return emitError("expected two region arguments");
565 for (auto argument : getBody().getArguments()) {
566 if (argument.getType() != getType())
567 return emitError("incorrect region argument type");
568 }
569 unsigned yieldCount = 0;
570 for (Block &block : getBody()) {
571 if (auto yield = dyn_cast<gpu::YieldOp>(block.getTerminator())) {
572 if (yield.getNumOperands() != 1)
573 return emitError("expected one gpu.yield operand");
574 if (yield.getOperand(0).getType() != getType())
575 return emitError("incorrect gpu.yield type");
576 ++yieldCount;
577 }
578 }
579 if (yieldCount == 0)
580 return emitError("expected gpu.yield op in region");
581 } else {
582 gpu::AllReduceOperation opName = *getOp();
583 if (failed(verifyReduceOpAndType(opName, getType()))) {
584 return emitError() << '`' << gpu::stringifyAllReduceOperation(opName)
585 << "` reduction operation is not compatible with type "
586 << getType();
587 }
588 }
589
590 return success();
591}
592
594 auto launchOp = dyn_cast<gpu::LaunchOp>(op->getParentOp());
595 if (!launchOp)
596 return false;
597
598 Region &body = launchOp.getBody();
599 assert(!body.empty() && "Invalid region");
600
601 // Only convert ops in gpu::launch entry block for now.
602 return op->getBlock() == &body.front();
603}
604
605OpFoldResult gpu::AllReduceOp::fold(FoldAdaptor /*adaptor*/) {
606 if (!getUniform() && canMakeGroupOpUniform(*this)) {
607 setUniform(true);
608 return getResult();
609 }
610
611 return nullptr;
612}
613
614//===----------------------------------------------------------------------===//
615// SubgroupReduceOp
616//===----------------------------------------------------------------------===//
617
618LogicalResult gpu::SubgroupReduceOp::verify() {
619 Type elemType = getType();
620 if (auto vecTy = dyn_cast<VectorType>(elemType)) {
621 if (vecTy.isScalable())
622 return emitOpError() << "is not compatible with scalable vector types";
623
624 elemType = vecTy.getElementType();
625 }
626
627 gpu::AllReduceOperation opName = getOp();
628 if (failed(verifyReduceOpAndType(opName, elemType))) {
629 return emitError() << '`' << gpu::stringifyAllReduceOperation(opName)
630 << "` reduction operation is not compatible with type "
631 << getType();
632 }
633
634 auto clusterSize = getClusterSize();
635 if (clusterSize) {
636 uint32_t size = *clusterSize;
637 if (!llvm::isPowerOf2_32(size)) {
638 return emitOpError() << "cluster size " << size
639 << " is not a power of two";
640 }
641 }
642
643 uint32_t stride = getClusterStride();
644 if (stride != 1 && !clusterSize) {
645 return emitOpError() << "cluster stride can only be specified if cluster "
646 "size is specified";
647 }
648 if (!llvm::isPowerOf2_32(stride)) {
649 return emitOpError() << "cluster stride " << stride
650 << " is not a power of two";
651 }
652
653 return success();
654}
655
656OpFoldResult gpu::SubgroupReduceOp::fold(FoldAdaptor /*adaptor*/) {
657 if (getClusterSize() == 1)
658 return getValue();
659
660 if (!getUniform() && canMakeGroupOpUniform(*this)) {
661 setUniform(true);
662 return getResult();
663 }
664
665 return nullptr;
666}
667
668//===----------------------------------------------------------------------===//
669// AsyncOpInterface
670//===----------------------------------------------------------------------===//
671
673 op->insertOperands(0, {token});
674 if (!op->template hasTrait<OpTrait::AttrSizedOperandSegments>())
675 return;
676 auto attrName =
678 auto sizeAttr = dyn_cast_or_null<DenseI32ArrayAttr>(
679 op->getInherentAttr(attrName).value_or(Attribute{}));
680
681 // Async dependencies is the only variadic operand.
682 if (!sizeAttr)
683 return;
684
685 SmallVector<int32_t, 8> sizes(sizeAttr.asArrayRef());
686 ++sizes.front();
687 op->setInherentAttr(StringAttr::get(op->getContext(), attrName),
689}
690
691//===----------------------------------------------------------------------===//
692// LaunchOp
693//===----------------------------------------------------------------------===//
694
695void LaunchOp::build(OpBuilder &builder, OperationState &result,
696 Value gridSizeX, Value gridSizeY, Value gridSizeZ,
697 Value getBlockSizeX, Value getBlockSizeY,
698 Value getBlockSizeZ, Value dynamicSharedMemorySize,
699 Type asyncTokenType, ValueRange asyncDependencies,
700 Value asyncObject, TypeRange workgroupAttributions,
701 TypeRange privateAttributions, Value clusterSizeX,
702 Value clusterSizeY, Value clusterSizeZ,
703 FlatSymbolRefAttr module, FlatSymbolRefAttr function) {
704 OpBuilder::InsertionGuard g(builder);
705
706 if (!workgroupAttributions.empty())
707 result.addAttribute(
708 getWorkgroupAttributionsAttrName(result.name),
709 builder.getI64IntegerAttr(workgroupAttributions.size()));
710
711 // Add Op operands.
712 result.addOperands(asyncDependencies);
713 if (asyncTokenType)
714 result.types.push_back(builder.getType<AsyncTokenType>());
715
716 // Add grid and block sizes as op operands, followed by the data operands.
717 result.addOperands({gridSizeX, gridSizeY, gridSizeZ, getBlockSizeX,
718 getBlockSizeY, getBlockSizeZ});
719 if (clusterSizeX)
720 result.addOperands(clusterSizeX);
721 if (clusterSizeY)
722 result.addOperands(clusterSizeY);
723 if (clusterSizeZ)
724 result.addOperands(clusterSizeZ);
725 if (dynamicSharedMemorySize)
726 result.addOperands(dynamicSharedMemorySize);
727 if (asyncObject)
728 result.addOperands(asyncObject);
729
730 // Add optional module and function attributes.
731 if (module)
732 result.addAttribute(getModuleAttrName(result.name), module);
733 if (function)
734 result.addAttribute(getFunctionAttrName(result.name), function);
735
736 // Create a kernel body region with kNumConfigRegionAttributes + N memory
737 // attributions, where the first kNumConfigRegionAttributes arguments have
738 // `index` type and the rest have the same types as the data operands.
739 Region *kernelRegion = result.addRegion();
740 Block *body = builder.createBlock(kernelRegion);
741 // TODO: Allow passing in proper locations here.
742 for (unsigned i = 0; i < kNumConfigRegionAttributes; ++i)
743 body->addArgument(builder.getIndexType(), result.location);
744 // Add WorkGroup & Private attributions to the region arguments.
745 for (Type argTy : workgroupAttributions)
746 body->addArgument(argTy, result.location);
747 for (Type argTy : privateAttributions)
748 body->addArgument(argTy, result.location);
749 // Fill OperandSegmentSize Attribute.
750 SmallVector<int32_t, 12> segmentSizes(12, 1);
751 segmentSizes.front() = asyncDependencies.size();
752 segmentSizes[7] = clusterSizeX ? 1 : 0;
753 segmentSizes[8] = clusterSizeY ? 1 : 0;
754 segmentSizes[9] = clusterSizeZ ? 1 : 0;
755 segmentSizes[10] = dynamicSharedMemorySize ? 1 : 0;
756 segmentSizes[11] = asyncObject ? 1 : 0;
757 result.addAttribute(getOperandSegmentSizeAttr(),
758 builder.getDenseI32ArrayAttr(segmentSizes));
759}
760
761KernelDim3 LaunchOp::getBlockIds() {
762 assert(!getBody().empty() && "LaunchOp body must not be empty.");
763 auto args = getBody().getArguments();
764 return KernelDim3{args[0], args[1], args[2]};
765}
766
767KernelDim3 LaunchOp::getThreadIds() {
768 assert(!getBody().empty() && "LaunchOp body must not be empty.");
769 auto args = getBody().getArguments();
770 return KernelDim3{args[3], args[4], args[5]};
771}
772
773KernelDim3 LaunchOp::getGridSize() {
774 assert(!getBody().empty() && "LaunchOp body must not be empty.");
775 auto args = getBody().getArguments();
776 return KernelDim3{args[6], args[7], args[8]};
777}
778
779KernelDim3 LaunchOp::getBlockSize() {
780 assert(!getBody().empty() && "LaunchOp body must not be empty.");
781 auto args = getBody().getArguments();
782 return KernelDim3{args[9], args[10], args[11]};
783}
784
785std::optional<KernelDim3> LaunchOp::getClusterIds() {
786 assert(!getBody().empty() && "LaunchOp body must not be empty.");
787 if (!hasClusterSize())
788 return std::nullopt;
789 auto args = getBody().getArguments();
790 return KernelDim3{args[12], args[13], args[14]};
791}
792
793std::optional<KernelDim3> LaunchOp::getClusterSize() {
794 assert(!getBody().empty() && "LaunchOp body must not be empty.");
795 if (!hasClusterSize())
796 return std::nullopt;
797 auto args = getBody().getArguments();
798 return KernelDim3{args[15], args[16], args[17]};
799}
800
801KernelDim3 LaunchOp::getGridSizeOperandValues() {
802 auto operands = getOperands().drop_front(getAsyncDependencies().size());
803 return KernelDim3{operands[0], operands[1], operands[2]};
804}
805
806KernelDim3 LaunchOp::getBlockSizeOperandValues() {
807 auto operands = getOperands().drop_front(getAsyncDependencies().size());
808 return KernelDim3{operands[3], operands[4], operands[5]};
809}
810
811std::optional<KernelDim3> LaunchOp::getClusterSizeOperandValues() {
812 auto operands = getOperands().drop_front(getAsyncDependencies().size());
813 if (!hasClusterSize())
814 return std::nullopt;
815 return KernelDim3{operands[6], operands[7], operands[8]};
816}
817
818template <typename OpTy>
819static LogicalResult verifyLaunchAsyncModel(OpTy op) {
820 if (!op.getAsyncDependencies().empty() && !op.getAsyncToken())
821 return op.emitOpError("dependency operands require the dependency-based "
822 "async model i.e. returning a token");
823 if (op.getAsyncToken() && op.getAsyncObject())
824 return op.emitOpError("stream-based and dependency-based async models are "
825 "mutually exclusive");
826 if (op.getNumResults() == 0 && op.getAsyncToken())
827 return op.emitOpError("needs to be named when async keyword is specified");
828 return success();
829}
830
831LogicalResult LaunchOp::verify() {
832 if (verifyLaunchAsyncModel(*this).failed())
833 return failure();
834
835 if (!(hasClusterSize()) &&
836 (getClusterSizeX() || getClusterSizeY() || getClusterSizeZ()))
837 return emitOpError() << "cluster size must be all present";
838 return success();
839}
840
841LogicalResult LaunchOp::verifyRegions() {
842 // Kernel launch takes kNumConfigOperands leading operands for grid/block
843 // sizes and transforms them into kNumConfigRegionAttributes region arguments
844 // for block/thread identifiers and grid/block sizes.
845 if (getBody().empty()) {
846 return emitOpError("body region is empty");
847 }
848 unsigned actualNumRegionArgs = getBody().getNumArguments();
849 unsigned expectedNumRegionArgs =
850 getNumConfigRegionAttributes() + getNumWorkgroupAttributions();
851 if (actualNumRegionArgs < expectedNumRegionArgs) {
852 return emitOpError("expected at least ")
853 << expectedNumRegionArgs << " region arguments, but got "
854 << actualNumRegionArgs;
855 }
856
857 // Verify Attributions Address Spaces.
858 if (failed(verifyAttributions(getOperation(), getWorkgroupAttributionBBArgs(),
859 GPUDialect::getWorkgroupAddressSpace())) ||
860 failed(verifyAttributions(getOperation(), getPrivateAttributions(),
861 GPUDialect::getPrivateAddressSpace())))
862 return failure();
863
864 // Block terminators without successors are expected to exit the kernel region
865 // and must be `gpu.terminator`.
866 for (Block &block : getBody()) {
867 if (block.empty())
868 continue;
869 if (block.back().getNumSuccessors() != 0)
870 continue;
871 if (!isa<gpu::TerminatorOp>(&block.back())) {
872 return block.back()
873 .emitError()
874 .append("expected '", gpu::TerminatorOp::getOperationName(),
875 "' or a terminator with successors")
876 .attachNote(getLoc())
877 .append("in '", LaunchOp::getOperationName(), "' body region");
878 }
879 }
880
881 return success();
882}
883
884// Pretty-print the kernel grid/block size assignment as
885// (%iter-x, %iter-y, %iter-z) in
886// (%size-x = %ssa-use, %size-y = %ssa-use, %size-z = %ssa-use)
887// where %size-* and %iter-* will correspond to the body region arguments.
889 KernelDim3 operands, KernelDim3 ids) {
890 p << '(' << ids.x << ", " << ids.y << ", " << ids.z << ") in (";
891 p << size.x << " = " << operands.x << ", ";
892 p << size.y << " = " << operands.y << ", ";
893 p << size.z << " = " << operands.z << ')';
894}
895
896void LaunchOp::print(OpAsmPrinter &p) {
897 if (auto asyncObject = getAsyncObject()) {
898 p << " <" << asyncObject << " : " << asyncObject.getType() << ">";
899 }
900 if (getAsyncToken()) {
901 p << " async";
902 if (!getAsyncDependencies().empty())
903 p << " [" << getAsyncDependencies() << ']';
904 }
905 // Print the launch configuration.
906 if (hasClusterSize()) {
907 p << ' ' << getClustersKeyword();
908 printSizeAssignment(p, getClusterSize().value(),
909 getClusterSizeOperandValues().value(),
910 getClusterIds().value());
911 }
912 p << ' ' << getBlocksKeyword();
913 printSizeAssignment(p, getGridSize(), getGridSizeOperandValues(),
914 getBlockIds());
915 p << ' ' << getThreadsKeyword();
916 printSizeAssignment(p, getBlockSize(), getBlockSizeOperandValues(),
917 getThreadIds());
918 if (getDynamicSharedMemorySize())
919 p << ' ' << getDynamicSharedMemorySizeKeyword() << ' '
920 << getDynamicSharedMemorySize();
921
922 // Print optional module attribute.
923 StringRef moduleAttrName = getModuleAttrName();
924 if (auto module = getModule()) {
925 p << ' ' << moduleAttrName << '(';
926 p.printSymbolName(*module);
927 p << ')';
928 }
929 // Print optional function attribute.
930 StringRef functionAttrName = getFunctionAttrName();
931 if (auto function = getFunction()) {
932 p << ' ' << functionAttrName << '(';
933 p.printSymbolName(*function);
934 p << ')';
935 }
936
937 if (getCooperative())
938 p << " cooperative";
939
940 printAttributions(p, getWorkgroupKeyword(), getWorkgroupAttributionBBArgs());
941 printAttributions(p, getPrivateKeyword(), getPrivateAttributions());
942
943 p << ' ';
944
945 p.printRegion(getBody(), /*printEntryBlockArgs=*/false);
947 (*this)->getDiscardableAttrDictionary().getValue(), /*elidedAttrs=*/{
948 LaunchOp::getOperandSegmentSizeAttr(),
949 getWorkgroupAttributionsAttrName(), getCooperativeAttrName(),
950 moduleAttrName, functionAttrName});
951}
952
953// Parse the size assignment blocks for blocks and threads. These have the form
954// (%region_arg, %region_arg, %region_arg) in
955// (%region_arg = %operand, %region_arg = %operand, %region_arg = %operand)
956// where %region_arg are percent-identifiers for the region arguments to be
957// introduced further (SSA defs), and %operand are percent-identifiers for the
958// SSA value uses.
959static ParseResult
964 StringRef keyword) {
965 assert(indices.size() == 3 && "space for three indices expected");
968 /*allowResultNumber=*/false) ||
969 parser.parseKeyword("in") || parser.parseLParen())
970 return failure();
971
972 if (args.size() != 3) {
973 return parser.emitError(parser.getNameLoc())
974 << keyword << " expects 3 arguments, but got " << args.size();
975 }
976 std::move(args.begin(), args.end(), indices.begin());
977
978 for (int i = 0; i < 3; ++i) {
979 if (i != 0 && parser.parseComma())
980 return failure();
981 if (parser.parseOperand(regionSizes[i], /*allowResultNumber=*/false) ||
982 parser.parseEqual() || parser.parseOperand(sizes[i]))
983 return failure();
984 }
985
986 return parser.parseRParen();
987}
988
989/// Parses a Launch operation.
990/// operation ::= `gpu.launch` (`<` ssa-use `:` type `>`)?
991/// (`async` `[` ssa-id-list `]`)?
992/// (`clusters` `(` ssa-id-list `)` `in` ssa-reassignment)?
993/// `blocks` `(` ssa-id-list `)` `in` ssa-reassignment
994/// `threads` `(` ssa-id-list `)` `in` ssa-reassignment
995/// (`dynamic_shared_memory_size` ssa-use)?
996/// (`module(` symbol-ref-id `)`)?
997/// (`function(` symbol-ref-id `)`)?
998/// memory-attribution
999/// region attr-dict?
1000/// ssa-reassignment ::= `(` ssa-id `=` ssa-use (`,` ssa-id `=` ssa-use)* `)`
1001ParseResult LaunchOp::parse(OpAsmParser &parser, OperationState &result) {
1002 // Sizes of the grid and block.
1003 SmallVector<OpAsmParser::UnresolvedOperand, LaunchOp::kNumConfigOperands>
1004 sizes(LaunchOp::kNumConfigOperands);
1005
1006 // Region arguments to be created.
1007 SmallVector<OpAsmParser::UnresolvedOperand, 16> regionArgs(
1008 LaunchOp::kNumConfigRegionAttributes);
1009
1010 // Parse optional asyncObject: < value : type >
1011 OpAsmParser::UnresolvedOperand asyncObjectOperand;
1012 Type asyncObjectType;
1013 bool hasAsyncObject = false;
1014 if (succeeded(parser.parseOptionalLess())) {
1015 hasAsyncObject = true;
1016 if (parser.parseOperand(asyncObjectOperand) || parser.parseColon() ||
1017 parser.parseType(asyncObjectType) || parser.parseGreater())
1018 return failure();
1019 }
1020
1021 // Parse optional async dependencies.
1022 SmallVector<OpAsmParser::UnresolvedOperand, 4> asyncDependencies;
1023 Type asyncTokenType;
1024 if (failed(
1025 parseAsyncDependencies(parser, asyncTokenType, asyncDependencies)) ||
1026 parser.resolveOperands(asyncDependencies, asyncTokenType,
1027 result.operands))
1028 return failure();
1029 if (parser.getNumResults() > 0) {
1030 if (!asyncTokenType)
1031 return parser.emitError(
1032 parser.getNameLoc(),
1033 "gpu.launch requires 'async' keyword to return a value");
1034 result.types.push_back(asyncTokenType);
1035 }
1036
1037 bool hasCluster = false;
1038 if (succeeded(parser.parseOptionalKeyword(LaunchOp::getClustersKeyword()))) {
1039 hasCluster = true;
1040 sizes.resize(9);
1041 regionArgs.resize(18);
1042 }
1043 MutableArrayRef<OpAsmParser::UnresolvedOperand> sizesRef(sizes);
1044 MutableArrayRef<OpAsmParser::UnresolvedOperand> regionArgsRef(regionArgs);
1045
1046 // Last three segment assigns the cluster size. In the region argument
1047 // list, this is last 6 arguments.
1048 if (hasCluster) {
1050 parser, sizesRef.drop_front(6), regionArgsRef.slice(15, 3),
1051 regionArgsRef.slice(12, 3), LaunchOp::getClustersKeyword()))
1052 return failure();
1053 }
1054 // Parse the size assignment segments: the first segment assigns grid sizes
1055 // and defines values for block identifiers; the second segment assigns block
1056 // sizes and defines values for thread identifiers. In the region argument
1057 // list, identifiers precede sizes, and block-related values precede
1058 // thread-related values.
1059 if (parser.parseKeyword(LaunchOp::getBlocksKeyword()) ||
1060 parseSizeAssignment(parser, sizesRef.take_front(3),
1061 regionArgsRef.slice(6, 3), regionArgsRef.slice(0, 3),
1062 LaunchOp::getBlocksKeyword()) ||
1063 parser.parseKeyword(LaunchOp::getThreadsKeyword()) ||
1064 parseSizeAssignment(parser, sizesRef.drop_front(3),
1065 regionArgsRef.slice(9, 3), regionArgsRef.slice(3, 3),
1066 LaunchOp::getThreadsKeyword()) ||
1067 parser.resolveOperands(sizes, parser.getBuilder().getIndexType(),
1068 result.operands))
1069 return failure();
1070
1071 OpAsmParser::UnresolvedOperand dynamicSharedMemorySize;
1072 bool hasDynamicSharedMemorySize = false;
1073 if (!parser.parseOptionalKeyword(
1074 LaunchOp::getDynamicSharedMemorySizeKeyword())) {
1075 hasDynamicSharedMemorySize = true;
1076 if (parser.parseOperand(dynamicSharedMemorySize) ||
1077 parser.resolveOperand(dynamicSharedMemorySize,
1078 parser.getBuilder().getI32Type(),
1079 result.operands))
1080 return failure();
1081 }
1082
1083 // Resolve the asyncObject operand
1084 if (hasAsyncObject && parser.resolveOperand(asyncObjectOperand,
1085 asyncObjectType, result.operands))
1086 return failure();
1087
1088 // Parse optional module attribute.
1089 StringRef moduleAttrName = getModuleAttrName(result.name);
1090 if (succeeded(parser.parseOptionalKeyword(moduleAttrName))) {
1091 FlatSymbolRefAttr moduleSymbol;
1092 if (parser.parseLParen() ||
1093 parser.parseAttribute(moduleSymbol, Type(), moduleAttrName,
1094 result.attributes) ||
1095 parser.parseRParen())
1096 return failure();
1097 }
1098 // Parse optional function attribute.
1099 StringRef functionAttrName = getFunctionAttrName(result.name);
1100 if (succeeded(parser.parseOptionalKeyword(functionAttrName))) {
1101 FlatSymbolRefAttr funcSymbol;
1102 if (parser.parseLParen() ||
1103 parser.parseAttribute(funcSymbol, Type(), functionAttrName,
1104 result.attributes) ||
1105 parser.parseRParen())
1106 return failure();
1107 }
1108
1109 // Parse optional cooperative keyword.
1110 if (succeeded(parser.parseOptionalKeyword("cooperative")))
1111 result.addAttribute("cooperative", parser.getBuilder().getUnitAttr());
1112
1113 // Create the region arguments: fixed launch-config args (`index`), then
1114 // workgroup / private attribution args. The workgroup count is stored in the
1115 // inherent `workgroup_attributions` attribute when non-zero.
1116 Type index = parser.getBuilder().getIndexType();
1117 SmallVector<Type, LaunchOp::kNumConfigRegionAttributes> dataTypes(
1118 LaunchOp::kNumConfigRegionAttributes + 6, index);
1119
1120 SmallVector<OpAsmParser::Argument> regionArguments;
1121 for (auto ssaValueAndType : llvm::zip(regionArgs, dataTypes)) {
1122 OpAsmParser::Argument arg;
1123 arg.ssaName = std::get<0>(ssaValueAndType);
1124 arg.type = std::get<1>(ssaValueAndType);
1125 regionArguments.push_back(arg);
1126 }
1127
1128 Builder &builder = parser.getBuilder();
1129 // Parse workgroup memory attributions.
1130 if (failed(parseAttributions(parser, LaunchOp::getWorkgroupKeyword(),
1131 regionArguments)))
1132 return failure();
1133
1134 // Store the number of operands we just parsed as the number of workgroup
1135 // memory attributions.
1136 unsigned numWorkgroupAttrs = regionArguments.size() -
1137 LaunchOp::kNumConfigRegionAttributes -
1138 (hasCluster ? 6 : 0);
1139 if (numWorkgroupAttrs != 0)
1140 result.addAttribute(LaunchOp::getWorkgroupAttributionsAttrName(result.name),
1141 builder.getI64IntegerAttr(numWorkgroupAttrs));
1142
1143 // Parse private memory attributions.
1144 if (failed(parseAttributions(parser, LaunchOp::getPrivateKeyword(),
1145 regionArguments)))
1146 return failure();
1147
1148 // Introduce the body region and parse it. The region has
1149 // kNumConfigRegionAttributes arguments that correspond to
1150 // block/thread identifiers and grid/block sizes, all having `index` type.
1151 Region *body = result.addRegion();
1152 if (parser.parseRegion(*body, regionArguments) ||
1153 parser.parseOptionalAttrDict(result.attributes))
1154 return failure();
1155
1156 SmallVector<int32_t, 12> segmentSizes(12, 1);
1157 segmentSizes.front() = asyncDependencies.size();
1158
1159 if (!hasCluster) {
1160 segmentSizes[7] = 0;
1161 segmentSizes[8] = 0;
1162 segmentSizes[9] = 0;
1163 }
1164 segmentSizes[10] = hasDynamicSharedMemorySize ? 1 : 0;
1165 segmentSizes[11] = hasAsyncObject ? 1 : 0;
1166 result.addAttribute(LaunchOp::getOperandSegmentSizeAttr(),
1167 parser.getBuilder().getDenseI32ArrayAttr(segmentSizes));
1168 return success();
1169}
1170
1171/// Simplify the gpu.launch when the range of a thread or block ID is
1172/// trivially known to be one.
1173struct FoldLaunchArguments : public OpRewritePattern<LaunchOp> {
1174 using OpRewritePattern<LaunchOp>::OpRewritePattern;
1175 LogicalResult matchAndRewrite(LaunchOp op,
1176 PatternRewriter &rewriter) const override {
1177 // If the range implies a single value for `id`, replace `id`'s uses by
1178 // zero.
1179 Value zero;
1180 bool simplified = false;
1181 auto constPropIdUses = [&](Value id, Value size) {
1182 // Check if size is trivially one.
1183 if (!matchPattern(size, m_One()))
1184 return;
1185 if (id.getUses().empty())
1186 return;
1187 if (!simplified) {
1188 // Create a zero value the first time.
1189 OpBuilder::InsertionGuard guard(rewriter);
1190 rewriter.setInsertionPointToStart(&op.getBody().front());
1191 zero =
1192 arith::ConstantIndexOp::create(rewriter, op.getLoc(), /*value=*/0);
1193 }
1194 rewriter.replaceAllUsesWith(id, zero);
1195 simplified = true;
1196 };
1197 constPropIdUses(op.getBlockIds().x, op.getGridSizeX());
1198 constPropIdUses(op.getBlockIds().y, op.getGridSizeY());
1199 constPropIdUses(op.getBlockIds().z, op.getGridSizeZ());
1200 constPropIdUses(op.getThreadIds().x, op.getBlockSizeX());
1201 constPropIdUses(op.getThreadIds().y, op.getBlockSizeY());
1202 constPropIdUses(op.getThreadIds().z, op.getBlockSizeZ());
1203
1204 return success(simplified);
1205 }
1206};
1207
1208void LaunchOp::getCanonicalizationPatterns(RewritePatternSet &rewrites,
1209 MLIRContext *context) {
1210 rewrites.add<FoldLaunchArguments>(context);
1211}
1212
1213/// Adds a new block argument that corresponds to buffers located in
1214/// workgroup memory.
1215BlockArgument LaunchOp::addWorkgroupAttribution(Type type, Location loc) {
1216 int64_t cur = getWorkgroupAttributions().value_or(0);
1217 setWorkgroupAttributions(std::optional<int64_t>(cur + 1));
1218 return getBody().insertArgument(
1219 getNumConfigRegionAttributes() + static_cast<unsigned>(cur), type, loc);
1220}
1221
1222/// Adds a new block argument that corresponds to buffers located in
1223/// private memory.
1224BlockArgument LaunchOp::addPrivateAttribution(Type type, Location loc) {
1225 // Buffers on the private memory always come after buffers on the workgroup
1226 // memory.
1227 return getBody().addArgument(type, loc);
1228}
1229
1230//===----------------------------------------------------------------------===//
1231// LaunchFuncOp
1232//===----------------------------------------------------------------------===//
1233
1234void LaunchFuncOp::build(OpBuilder &builder, OperationState &result,
1235 SymbolRefAttr kernelSymbol, KernelDim3 gridSize,
1236 KernelDim3 getBlockSize, Value dynamicSharedMemorySize,
1237 ValueRange kernelOperands, Type asyncTokenType,
1238 ValueRange asyncDependencies, Value asyncObject,
1239 std::optional<KernelDim3> clusterSize) {
1240 assert(kernelSymbol.getNestedReferences().size() == 1 &&
1241 "expected a symbol reference with a single nested reference");
1242 result.addOperands(asyncDependencies);
1243 if (asyncTokenType)
1244 result.types.push_back(builder.getType<AsyncTokenType>());
1245
1246 // Add grid and block sizes as op operands, followed by the data operands.
1247 result.addOperands({gridSize.x, gridSize.y, gridSize.z, getBlockSize.x,
1249 if (clusterSize.has_value())
1250 result.addOperands({clusterSize->x, clusterSize->y, clusterSize->z});
1251 if (dynamicSharedMemorySize)
1252 result.addOperands(dynamicSharedMemorySize);
1253 result.addOperands(kernelOperands);
1254 if (asyncObject)
1255 result.addOperands(asyncObject);
1256
1257 Properties &prop = result.getOrAddProperties<Properties>();
1258 prop.kernel = kernelSymbol;
1259 size_t segmentSizesLen = std::size(prop.operandSegmentSizes);
1260 // Initialize the segment sizes to 1.
1261 llvm::fill(prop.operandSegmentSizes, 1);
1262 prop.operandSegmentSizes[0] = asyncDependencies.size();
1263 if (!clusterSize.has_value()) {
1264 prop.operandSegmentSizes[segmentSizesLen - 4] = 0;
1265 prop.operandSegmentSizes[segmentSizesLen - 5] = 0;
1266 prop.operandSegmentSizes[segmentSizesLen - 6] = 0;
1267 }
1268 prop.operandSegmentSizes[segmentSizesLen - 3] =
1269 dynamicSharedMemorySize ? 1 : 0;
1270 prop.operandSegmentSizes[segmentSizesLen - 2] =
1271 static_cast<int32_t>(kernelOperands.size());
1272 prop.operandSegmentSizes[segmentSizesLen - 1] = asyncObject ? 1 : 0;
1273}
1274
1275void LaunchFuncOp::build(OpBuilder &builder, OperationState &result,
1276 GPUFuncOp kernelFunc, KernelDim3 gridSize,
1277 KernelDim3 getBlockSize, Value dynamicSharedMemorySize,
1278 ValueRange kernelOperands, Type asyncTokenType,
1279 ValueRange asyncDependencies, Value asyncObject,
1280 std::optional<KernelDim3> clusterSize) {
1281 auto kernelModule = kernelFunc->getParentOfType<GPUModuleOp>();
1282 auto kernelSymbol =
1283 SymbolRefAttr::get(kernelModule.getNameAttr(),
1284 {SymbolRefAttr::get(kernelFunc.getNameAttr())});
1285 build(builder, result, kernelSymbol, gridSize, getBlockSize,
1286 dynamicSharedMemorySize, kernelOperands, asyncTokenType,
1287 asyncDependencies, asyncObject, clusterSize);
1288}
1289
1290StringAttr LaunchFuncOp::getKernelModuleName() {
1291 return getKernel().getRootReference();
1292}
1293
1294StringAttr LaunchFuncOp::getKernelName() {
1295 return getKernel().getLeafReference();
1296}
1297
1298unsigned LaunchFuncOp::getNumKernelOperands() {
1299 return getKernelOperands().size();
1300}
1301
1302Value LaunchFuncOp::getKernelOperand(unsigned i) {
1303 return getKernelOperands()[i];
1304}
1305
1306KernelDim3 LaunchFuncOp::getGridSizeOperandValues() {
1307 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1308 return KernelDim3{operands[0], operands[1], operands[2]};
1309}
1310
1311KernelDim3 LaunchFuncOp::getBlockSizeOperandValues() {
1312 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1313 return KernelDim3{operands[3], operands[4], operands[5]};
1314}
1315
1316KernelDim3 LaunchFuncOp::getClusterSizeOperandValues() {
1317 assert(hasClusterSize() &&
1318 "cluster size is not set, check hasClusterSize() first");
1319 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1320 return KernelDim3{operands[6], operands[7], operands[8]};
1321}
1322
1323LogicalResult LaunchFuncOp::verify() {
1324 if (verifyLaunchAsyncModel(*this).failed())
1325 return failure();
1326
1327 auto module = (*this)->getParentOfType<ModuleOp>();
1328 if (!module)
1329 return emitOpError("expected to belong to a module");
1330
1331 if (!module->getDiscardableAttrOfType<UnitAttr>(
1332 GPUDialect::getContainerModuleAttrName()))
1333 return emitOpError("expected the closest surrounding module to have the '" +
1334 GPUDialect::getContainerModuleAttrName() +
1335 "' attribute");
1336
1337 if (hasClusterSize()) {
1338 if (getClusterSizeY().getType() != getClusterSizeX().getType() ||
1339 getClusterSizeZ().getType() != getClusterSizeX().getType())
1340 return emitOpError()
1341 << "expects types of the cluster dimensions must be the same";
1342 }
1343
1344 return success();
1345}
1346
1347LogicalResult
1348LaunchFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1349 LaunchFuncOp launchOp = *this;
1350 Operation *table = SymbolTable::getNearestSymbolTable(launchOp);
1351 // GPU modules cannot be nested within each other, escape to resolve the name.
1352 if (isa<GPUModuleOp>(table))
1354
1355 // Ignore launches that are nested more or less deep than functions in the
1356 // module we are currently checking.
1357 if (!launchOp->getParentOp() ||
1358 launchOp->getParentOp()->getParentOp() != table)
1359 return success();
1360
1361 // Ignore launch ops with missing attributes here. The errors will be
1362 // reported by the verifiers of those ops.
1363 if (!launchOp.getKernelAttr())
1364 return success();
1365
1366 // Check that `launch_func` refers to a well-formed GPU kernel container.
1367 StringAttr kernelContainerName = launchOp.getKernelModuleName();
1368 Operation *kernelContainer =
1369 symbolTable.lookupNearestSymbolFrom(table, kernelContainerName);
1370 if (!kernelContainer)
1371 return launchOp.emitOpError()
1372 << "kernel container '" << kernelContainerName.getValue()
1373 << "' is undefined";
1374
1375 // If the container is a GPU binary op return success.
1376 if (isa<BinaryOp>(kernelContainer))
1377 return success();
1378
1379 auto kernelModule = dyn_cast<GPUModuleOp>(kernelContainer);
1380 if (!kernelModule)
1381 return launchOp.emitOpError()
1382 << "kernel module '" << kernelContainerName.getValue()
1383 << "' is undefined";
1384
1385 // Check that `launch_func` refers to a well-formed kernel function.
1386 Operation *kernelFunc = symbolTable.lookupNearestSymbolFrom(
1387 kernelModule, launchOp.getKernelName());
1388 if (!kernelFunc)
1389 return launchOp.emitOpError("kernel function '")
1390 << launchOp.getKernel() << "' is undefined";
1391 auto kernelConvertedFunction = dyn_cast<FunctionOpInterface>(kernelFunc);
1392 if (!kernelConvertedFunction) {
1393 InFlightDiagnostic diag = launchOp.emitOpError()
1394 << "referenced kernel '" << launchOp.getKernel()
1395 << "' is not a function";
1396 diag.attachNote(kernelFunc->getLoc()) << "see the kernel definition here";
1397 return diag;
1398 }
1399
1400 if (!GPUDialect::isKernel(kernelFunc))
1401 return launchOp.emitOpError("kernel function is missing the '")
1402 << GPUDialect::getKernelFuncAttrName() << "' attribute";
1403
1404 // TODO: If the kernel isn't a GPU function (which happens during separate
1405 // compilation), do not check type correspondence as it would require the
1406 // verifier to be aware of the type conversion.
1407 auto kernelGPUFunction = dyn_cast<gpu::GPUFuncOp>(kernelFunc);
1408 if (!kernelGPUFunction)
1409 return success();
1410
1411 unsigned actualNumArguments = launchOp.getNumKernelOperands();
1412 unsigned expectedNumArguments = kernelGPUFunction.getNumArguments();
1413 if (expectedNumArguments != actualNumArguments)
1414 return launchOp.emitOpError("got ")
1415 << actualNumArguments << " kernel operands but expected "
1416 << expectedNumArguments;
1417
1418 FunctionType functionType = kernelGPUFunction.getFunctionType();
1419 for (unsigned i = 0; i < expectedNumArguments; ++i) {
1420 if (launchOp.getKernelOperand(i).getType() != functionType.getInput(i)) {
1421 return launchOp.emitOpError("type of function argument ")
1422 << i << " does not match";
1423 }
1424 }
1425
1426 return success();
1427}
1428
1429static ParseResult
1431 std::optional<OpAsmParser::UnresolvedOperand> clusterValue,
1432 Type &clusterXTy, Type &clusterYTy, Type &clusterZTy) {
1433 if (succeeded(parser.parseOptionalColon())) {
1434 if (parser.parseType(dimTy))
1435 return failure();
1436 } else {
1437 dimTy = IndexType::get(parser.getContext());
1438 }
1439 if (clusterValue.has_value()) {
1440 clusterXTy = clusterYTy = clusterZTy = dimTy;
1441 }
1442 return success();
1443}
1444
1445static void printLaunchDimType(OpAsmPrinter &printer, Operation *op, Type dimTy,
1446 Value clusterValue, Type clusterXTy,
1447 Type clusterYTy, Type clusterZTy) {
1448 if (!dimTy.isIndex())
1449 printer << ": " << dimTy;
1450}
1451
1452static ParseResult parseLaunchFuncOperands(
1453 OpAsmParser &parser,
1455 SmallVectorImpl<Type> &argTypes) {
1456 if (parser.parseOptionalKeyword("args"))
1457 return success();
1458
1459 auto parseElement = [&]() -> ParseResult {
1460 return failure(parser.parseOperand(argNames.emplace_back()) ||
1461 parser.parseColonType(argTypes.emplace_back()));
1462 };
1463
1465 parseElement, " in argument list");
1466}
1467
1469 OperandRange operands, TypeRange types) {
1470 if (operands.empty())
1471 return;
1472 printer << "args(";
1473 llvm::interleaveComma(llvm::zip_equal(operands, types), printer,
1474 [&](const auto &pair) {
1475 auto [operand, type] = pair;
1476 printer << operand << " : " << type;
1477 });
1478 printer << ")";
1479}
1480
1481//===----------------------------------------------------------------------===//
1482// ShuffleOp
1483//===----------------------------------------------------------------------===//
1484
1485void ShuffleOp::build(OpBuilder &builder, OperationState &result, Value value,
1486 int32_t offset, int32_t width, ShuffleMode mode) {
1487 build(builder, result, value,
1488 arith::ConstantOp::create(builder, result.location,
1489 builder.getI32IntegerAttr(offset)),
1490 arith::ConstantOp::create(builder, result.location,
1491 builder.getI32IntegerAttr(width)),
1492 mode);
1493}
1494
1495//===----------------------------------------------------------------------===//
1496// RotateOp
1497//===----------------------------------------------------------------------===//
1498
1499LogicalResult RotateOp::verify() {
1500 uint32_t offset = getOffset();
1501 uint32_t width = getWidth();
1502
1503 if (offset >= width) {
1504 return emitOpError() << "offset must be in the range [0, " << width << ")";
1505 }
1506
1507 return success();
1508}
1509
1510//===----------------------------------------------------------------------===//
1511// BarrierOp
1512//===----------------------------------------------------------------------===//
1513
1514LogicalResult BarrierOp::verify() {
1515 BarrierScope scope = getScope();
1516
1517 if (getNamedBarrier() && scope != BarrierScope::Workgroup)
1518 return emitOpError("named barriers require workgroup scope");
1519
1520 return success();
1521}
1522
1523/// Remove gpu.barrier after gpu.barrier, the threads are already synchronized!
1524static LogicalResult eraseRedundantGpuBarrierOps(BarrierOp op,
1525 PatternRewriter &rewriter) {
1526 auto nextOp = dyn_cast_or_null<BarrierOp>(op->getNextNode());
1527 if (!nextOp)
1528 return failure();
1529
1530 // Cannot merge barriers of different scopes.
1531 if (op.getScope() != nextOp.getScope())
1532 return failure();
1533
1534 // Cannot merge named barriers unless both refer to the same handle.
1535 if (op.getNamedBarrier() != nextOp.getNamedBarrier())
1536 return failure();
1537
1538 std::optional<ArrayAttr> thisMemfence = op.getAddressSpaces();
1539 std::optional<ArrayAttr> nextMemfence = nextOp.getAddressSpaces();
1540
1541 if (thisMemfence) {
1542 rewriter.modifyOpInPlace(op, [&]() {
1543 if (!nextMemfence) {
1544 op.removeAddressSpacesAttr();
1545 return;
1546 }
1547 // Fast path - merge where the two barriers fence the same spaces.
1548 if (*thisMemfence == *nextMemfence) {
1549 return;
1550 }
1551
1552 llvm::SmallSetVector<Attribute, 4> mergedSpaces;
1553 for (Attribute attr : *thisMemfence)
1554 mergedSpaces.insert(attr);
1555 for (Attribute attr : *nextMemfence)
1556 mergedSpaces.insert(attr);
1557 op.setAddressSpacesAttr(rewriter.getArrayAttr(mergedSpaces.takeVector()));
1558 });
1559 }
1560
1561 rewriter.eraseOp(nextOp);
1562 return success();
1563}
1564
1565void BarrierOp::getCanonicalizationPatterns(RewritePatternSet &results,
1566 MLIRContext *context) {
1568}
1569
1570void BarrierOp::build(mlir::OpBuilder &odsBuilder,
1571 mlir::OperationState &odsState,
1572 std::optional<AddressSpace> addressSpace) {
1573 ArrayAttr addressSpacesAttr;
1574 if (addressSpace)
1575 addressSpacesAttr = odsBuilder.getArrayAttr(
1576 AddressSpaceAttr::get(odsBuilder.getContext(), addressSpace.value()));
1577 build(
1578 odsBuilder, odsState, addressSpacesAttr, /*named_barrier=*/Value{},
1579 BarrierScopeAttr::get(odsBuilder.getContext(), BarrierScope::Workgroup));
1580}
1581
1582/// Builds a barrier that causes memory operations affecting `memrefToFence` to
1583/// be completed after the barrier is concluded. Currently, this means setting
1584/// the fenced address spaces to those of the given memref if it is a gpu
1585/// address space.
1586void BarrierOp::build(OpBuilder &builder, OperationState &odsState,
1587 Value memrefToFence) {
1588 std::optional<AddressSpace> addrSpaceToFence;
1589 if (auto memrefType = dyn_cast<BaseMemRefType>(memrefToFence.getType()))
1590 if (auto addrSpaceAttr = dyn_cast_if_present<gpu::AddressSpaceAttr>(
1591 memrefType.getMemorySpace()))
1592 addrSpaceToFence = addrSpaceAttr.getValue();
1593 return build(builder, odsState, addrSpaceToFence);
1594}
1595
1596//===----------------------------------------------------------------------===//
1597// GPUFuncOp
1598//===----------------------------------------------------------------------===//
1599
1600/// Adds a new block argument that corresponds to buffers located in
1601/// workgroup memory.
1602BlockArgument GPUFuncOp::addWorkgroupAttribution(Type type, Location loc) {
1603 int64_t cur = getWorkgroupAttributions().value_or(0);
1604 setWorkgroupAttributions(std::optional<int64_t>(cur + 1));
1605 return getBody().insertArgument(
1606 getFunctionType().getNumInputs() + static_cast<unsigned>(cur), type, loc);
1607}
1608
1609/// Adds a new block argument that corresponds to buffers located in
1610/// private memory.
1611BlockArgument GPUFuncOp::addPrivateAttribution(Type type, Location loc) {
1612 // Buffers on the private memory always come after buffers on the workgroup
1613 // memory.
1614 return getBody().addArgument(type, loc);
1615}
1616
1617void GPUFuncOp::build(OpBuilder &builder, OperationState &result,
1618 StringRef name, FunctionType type,
1619 TypeRange workgroupAttributions,
1620 TypeRange privateAttributions,
1621 ArrayRef<NamedAttribute> attrs) {
1622 OpBuilder::InsertionGuard g(builder);
1623
1624 result.getOrAddProperties<Properties>().sym_name =
1625 builder.getStringAttr(name);
1626 result.addAttribute(getFunctionTypeAttrName(result.name),
1627 TypeAttr::get(type));
1628 result.addAttribute(getWorkgroupAttributionsAttrName(result.name),
1629 builder.getI64IntegerAttr(workgroupAttributions.size()));
1630 result.addAttributes(attrs);
1631 Region *body = result.addRegion();
1632 Block *entryBlock = builder.createBlock(body);
1633
1634 // TODO: Allow passing in proper locations here.
1635 for (Type argTy : type.getInputs())
1636 entryBlock->addArgument(argTy, result.location);
1637 for (Type argTy : workgroupAttributions)
1638 entryBlock->addArgument(argTy, result.location);
1639 for (Type argTy : privateAttributions)
1640 entryBlock->addArgument(argTy, result.location);
1641}
1642
1643/// Parses a GPU function memory attribution.
1644///
1645/// memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)?
1646/// (`private` `(` ssa-id-and-type-list `)`)?
1647///
1648/// Note that this function parses only one of the two similar parts, with the
1649/// keyword provided as argument.
1650static ParseResult
1651parseAttributions(OpAsmParser &parser, StringRef keyword,
1653 Attribute &attributionAttrs) {
1654 // If we could not parse the keyword, just assume empty list and succeed.
1655 if (failed(parser.parseOptionalKeyword(keyword)))
1656 return success();
1657
1658 size_t existingArgs = args.size();
1659 ParseResult result =
1661 /*allowType=*/true, /*allowAttrs=*/true);
1662 if (failed(result))
1663 return result;
1664
1665 bool hadAttrs = llvm::any_of(ArrayRef(args).drop_front(existingArgs),
1666 [](const OpAsmParser::Argument &arg) -> bool {
1667 return arg.attrs && !arg.attrs.empty();
1668 });
1669 if (!hadAttrs) {
1670 attributionAttrs = nullptr;
1671 return result;
1672 }
1673
1674 Builder &builder = parser.getBuilder();
1675 SmallVector<Attribute> attributionAttrsVec;
1676 for (const auto &argument : ArrayRef(args).drop_front(existingArgs)) {
1677 if (!argument.attrs)
1678 attributionAttrsVec.push_back(builder.getDictionaryAttr({}));
1679 else
1680 attributionAttrsVec.push_back(argument.attrs);
1681 }
1682 attributionAttrs = builder.getArrayAttr(attributionAttrsVec);
1683 return result;
1684}
1685
1686/// Parses a GPU function.
1687///
1688/// <operation> ::= `gpu.func` symbol-ref-id `(` argument-list `)`
1689/// (`->` function-result-list)? memory-attribution `kernel`?
1690/// function-attributes? region
1691ParseResult GPUFuncOp::parse(OpAsmParser &parser, OperationState &result) {
1692 SmallVector<OpAsmParser::Argument> entryArgs;
1693 SmallVector<DictionaryAttr> resultAttrs;
1694 SmallVector<Type> resultTypes;
1695 bool isVariadic;
1696
1697 // Parse the function name.
1698 StringAttr nameAttr;
1699 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1700 result.attributes))
1701 return failure();
1702
1703 auto signatureLocation = parser.getCurrentLocation();
1705 parser, /*allowVariadic=*/false, entryArgs, isVariadic, resultTypes,
1706 resultAttrs)))
1707 return failure();
1708
1709 if (!entryArgs.empty() && entryArgs[0].ssaName.name.empty())
1710 return parser.emitError(signatureLocation)
1711 << "gpu.func requires named arguments";
1712
1713 // Construct the function type. More types will be added to the region, but
1714 // not to the function type.
1715 Builder &builder = parser.getBuilder();
1716
1717 SmallVector<Type> argTypes;
1718 for (auto &arg : entryArgs)
1719 argTypes.push_back(arg.type);
1720 auto type = builder.getFunctionType(argTypes, resultTypes);
1721 result.addAttribute(getFunctionTypeAttrName(result.name),
1722 TypeAttr::get(type));
1723
1725 builder, result, entryArgs, resultAttrs, getArgAttrsAttrName(result.name),
1726 getResAttrsAttrName(result.name));
1727
1728 Attribute workgroupAttributionAttrs;
1729 // Parse workgroup memory attributions.
1730 if (failed(parseAttributions(parser, GPUFuncOp::getWorkgroupKeyword(),
1731 entryArgs, workgroupAttributionAttrs)))
1732 return failure();
1733
1734 // Store the number of operands we just parsed as the number of workgroup
1735 // memory attributions.
1736 unsigned numWorkgroupAttrs = entryArgs.size() - type.getNumInputs();
1737 if (numWorkgroupAttrs != 0)
1738 result.addAttribute(
1739 GPUFuncOp::getWorkgroupAttributionsAttrName(result.name),
1740 builder.getI64IntegerAttr(numWorkgroupAttrs));
1741 if (workgroupAttributionAttrs)
1742 result.addAttribute(GPUFuncOp::getWorkgroupAttribAttrsAttrName(result.name),
1743 workgroupAttributionAttrs);
1744
1745 Attribute privateAttributionAttrs;
1746 // Parse private memory attributions.
1747 if (failed(parseAttributions(parser, GPUFuncOp::getPrivateKeyword(),
1748 entryArgs, privateAttributionAttrs)))
1749 return failure();
1750 if (privateAttributionAttrs)
1751 result.addAttribute(GPUFuncOp::getPrivateAttribAttrsAttrName(result.name),
1752 privateAttributionAttrs);
1753
1754 // Parse the kernel attribute if present.
1755 if (succeeded(parser.parseOptionalKeyword(GPUFuncOp::getKernelKeyword())))
1756 result.addAttribute(GPUFuncOp::getKernelAttrName(result.name),
1757 builder.getUnitAttr());
1758
1759 // Parse attributes.
1760 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
1761 return failure();
1762
1763 // Parse the region. If no argument names were provided, take all names
1764 // (including those of attributions) from the entry block.
1765 auto *body = result.addRegion();
1766 return parser.parseRegion(*body, entryArgs);
1767}
1768
1769void GPUFuncOp::print(OpAsmPrinter &p) {
1770 p << ' ';
1771 p.printSymbolName(getName());
1772
1773 FunctionType type = getFunctionType();
1774 function_interface_impl::printFunctionSignature(p, *this, type.getInputs(),
1775 /*isVariadic=*/false,
1776 type.getResults());
1777
1778 printAttributions(p, getWorkgroupKeyword(), getWorkgroupAttributionBBArgs(),
1779 getWorkgroupAttribAttrs().value_or(nullptr));
1780 printAttributions(p, getPrivateKeyword(), getPrivateAttributions(),
1781 getPrivateAttribAttrs().value_or(nullptr));
1782 if (isKernel())
1783 p << ' ' << getKernelKeyword();
1784
1786 p, *this,
1787 {getWorkgroupAttributionsAttrName(), getKernelAttrName(),
1788 GPUDialect::getKernelFuncAttrName(), getFunctionTypeAttrName(),
1789 getArgAttrsAttrName(), getResAttrsAttrName(),
1790 getWorkgroupAttribAttrsAttrName(), getPrivateAttribAttrsAttrName()});
1791 p << ' ';
1792 p.printRegion(getBody(), /*printEntryBlockArgs=*/false);
1793}
1794
1795static DictionaryAttr getAttributionAttrs(GPUFuncOp op, unsigned index,
1796 StringAttr attrName) {
1797 ArrayAttr allAttrs = attrName == op.getWorkgroupAttribAttrsAttrName()
1798 ? op.getWorkgroupAttribAttrsAttr()
1799 : op.getPrivateAttribAttrsAttr();
1800 if (!allAttrs || index >= allAttrs.size())
1801 return DictionaryAttr();
1802 return llvm::cast<DictionaryAttr>(allAttrs[index]);
1803}
1804
1805DictionaryAttr GPUFuncOp::getworkgroupAttributionAttrs(unsigned index) {
1806 return getAttributionAttrs(*this, index, getWorkgroupAttribAttrsAttrName());
1807}
1808
1809DictionaryAttr GPUFuncOp::getPrivateAttributionAttrs(unsigned index) {
1810 return getAttributionAttrs(*this, index, getPrivateAttribAttrsAttrName());
1811}
1812
1813static void setAttributionAttrs(GPUFuncOp op, unsigned index,
1814 DictionaryAttr value, StringAttr attrName) {
1815 MLIRContext *ctx = op.getContext();
1816 ArrayAttr allAttrs = attrName == op.getWorkgroupAttribAttrsAttrName()
1817 ? op.getWorkgroupAttribAttrsAttr()
1818 : op.getPrivateAttribAttrsAttr();
1819 SmallVector<Attribute> elements;
1820 if (allAttrs)
1821 elements.append(allAttrs.begin(), allAttrs.end());
1822 while (elements.size() <= index)
1823 elements.push_back(DictionaryAttr::get(ctx));
1824 if (!value)
1825 elements[index] = DictionaryAttr::get(ctx);
1826 else
1827 elements[index] = value;
1828 ArrayAttr newValue = ArrayAttr::get(ctx, elements);
1829 if (attrName == op.getWorkgroupAttribAttrsAttrName())
1830 op.setWorkgroupAttribAttrsAttr(newValue);
1831 else
1832 op.setPrivateAttribAttrsAttr(newValue);
1833}
1834
1835void GPUFuncOp::setworkgroupAttributionAttrs(unsigned index,
1836 DictionaryAttr value) {
1837 setAttributionAttrs(*this, index, value, getWorkgroupAttribAttrsAttrName());
1838}
1839
1840void GPUFuncOp::setPrivateAttributionAttrs(unsigned int index,
1841 DictionaryAttr value) {
1842 setAttributionAttrs(*this, index, value, getPrivateAttribAttrsAttrName());
1843}
1844
1845static Attribute getAttributionAttr(GPUFuncOp op, unsigned index,
1846 StringAttr name, StringAttr attrsName) {
1847 DictionaryAttr dict = getAttributionAttrs(op, index, attrsName);
1848 if (!dict)
1849 return Attribute();
1850 return dict.get(name);
1851}
1852
1853Attribute GPUFuncOp::getWorkgroupAttributionAttr(unsigned index,
1854 StringAttr name) {
1855 assert(index < getNumWorkgroupAttributions() &&
1856 "index must map to a workgroup attribution");
1857 return getAttributionAttr(*this, index, name,
1858 getWorkgroupAttribAttrsAttrName());
1859}
1860
1861Attribute GPUFuncOp::getPrivateAttributionAttr(unsigned index,
1862 StringAttr name) {
1863 assert(index < getNumPrivateAttributions() &&
1864 "index must map to a private attribution");
1865 return getAttributionAttr(*this, index, name,
1866 getPrivateAttribAttrsAttrName());
1867}
1868
1869static void setAttributionAttr(GPUFuncOp op, unsigned index, StringAttr name,
1870 Attribute value, StringAttr attrsName) {
1871 MLIRContext *ctx = op.getContext();
1873 DictionaryAttr oldDict = getAttributionAttrs(op, index, attrsName);
1874 if (oldDict)
1875 elems.append(oldDict.getValue().begin(), oldDict.getValue().end());
1876
1877 bool found = false;
1878 bool mustSort = true;
1879 for (unsigned i = 0, e = elems.size(); i < e; ++i) {
1880 if (elems[i].getName() == name) {
1881 found = true;
1882 if (!value) {
1883 std::swap(elems[i], elems[elems.size() - 1]);
1884 elems.pop_back();
1885 } else {
1886 mustSort = false;
1887 elems[i] = NamedAttribute(elems[i].getName(), value);
1888 }
1889 break;
1890 }
1891 }
1892 if (!found) {
1893 if (!value)
1894 return;
1895 elems.emplace_back(name, value);
1896 }
1897 if (mustSort) {
1898 DictionaryAttr::sortInPlace(elems);
1899 }
1900 auto newDict = DictionaryAttr::getWithSorted(ctx, elems);
1901 setAttributionAttrs(op, index, newDict, attrsName);
1902}
1903
1904void GPUFuncOp::setWorkgroupAttributionAttr(unsigned index, StringAttr name,
1905 Attribute value) {
1906 assert(index < getNumWorkgroupAttributions() &&
1907 "index must map to a workgroup attribution");
1908 setAttributionAttr(*this, index, name, value,
1909 getWorkgroupAttribAttrsAttrName());
1910}
1911
1912void GPUFuncOp::setPrivateAttributionAttr(unsigned index, StringAttr name,
1913 Attribute value) {
1914 assert(index < getNumPrivateAttributions() &&
1915 "index must map to a private attribution");
1916 setAttributionAttr(*this, index, name, value,
1917 getPrivateAttribAttrsAttrName());
1918}
1919
1920LogicalResult GPUFuncOp::verifyType() {
1921 if (isKernel() && getFunctionType().getNumResults() != 0)
1922 return emitOpError() << "expected void return type for kernel function";
1923
1924 return success();
1925}
1926
1927/// Verifies the body of the function.
1928LogicalResult GPUFuncOp::verifyBody() {
1929 if (empty())
1930 return emitOpError() << "expected body with at least one block";
1931 unsigned numFuncArguments = getNumArguments();
1932 unsigned numWorkgroupAttributions = getNumWorkgroupAttributions();
1933 unsigned numBlockArguments = front().getNumArguments();
1934 if (numBlockArguments < numFuncArguments + numWorkgroupAttributions)
1935 return emitOpError() << "expected at least "
1936 << numFuncArguments + numWorkgroupAttributions
1937 << " arguments to body region";
1938
1939 ArrayRef<Type> funcArgTypes = getFunctionType().getInputs();
1940 for (unsigned i = 0; i < numFuncArguments; ++i) {
1941 Type blockArgType = front().getArgument(i).getType();
1942 if (funcArgTypes[i] != blockArgType)
1943 return emitOpError() << "expected body region argument #" << i
1944 << " to be of type " << funcArgTypes[i] << ", got "
1945 << blockArgType;
1946 }
1947
1948 if (failed(verifyAttributions(getOperation(), getWorkgroupAttributionBBArgs(),
1949 GPUDialect::getWorkgroupAddressSpace())) ||
1950 failed(verifyAttributions(getOperation(), getPrivateAttributions(),
1951 GPUDialect::getPrivateAddressSpace())))
1952 return failure();
1953
1954 return success();
1955}
1956
1957//===----------------------------------------------------------------------===//
1958// ReturnOp
1959//===----------------------------------------------------------------------===//
1960
1961LogicalResult gpu::ReturnOp::verify() {
1962 GPUFuncOp function = (*this)->getParentOfType<GPUFuncOp>();
1963
1964 FunctionType funType = function.getFunctionType();
1965
1966 if (funType.getNumResults() != getOperands().size())
1967 return emitOpError()
1968 .append("expected ", funType.getNumResults(), " result operands")
1969 .attachNote(function.getLoc())
1970 .append("return type declared here");
1971
1972 for (const auto &pair : llvm::enumerate(
1973 llvm::zip(function.getFunctionType().getResults(), getOperands()))) {
1974 auto [type, operand] = pair.value();
1975 if (type != operand.getType())
1976 return emitOpError() << "unexpected type `" << operand.getType()
1977 << "' for operand #" << pair.index();
1978 }
1979 return success();
1980}
1981
1982//===----------------------------------------------------------------------===//
1983// GPUModuleOp
1984//===----------------------------------------------------------------------===//
1985
1986void GPUModuleOp::build(OpBuilder &builder, OperationState &result,
1987 StringRef name, ArrayAttr targets,
1988 Attribute offloadingHandler) {
1989 result.addRegion()->emplaceBlock();
1990 Properties &props = result.getOrAddProperties<Properties>();
1991 if (targets)
1992 props.targets = targets;
1993 props.setSymName(builder.getStringAttr(name));
1994 props.offloadingHandler = offloadingHandler;
1995}
1996
1997void GPUModuleOp::build(OpBuilder &builder, OperationState &result,
1998 StringRef name, ArrayRef<Attribute> targets,
1999 Attribute offloadingHandler) {
2000 build(builder, result, name,
2001 targets.empty() ? ArrayAttr() : builder.getArrayAttr(targets),
2002 offloadingHandler);
2003}
2004
2005bool GPUModuleOp::hasTarget(Attribute target) {
2006 if (ArrayAttr targets = getTargetsAttr())
2007 return llvm::count(targets.getValue(), target);
2008 return false;
2009}
2010
2011void GPUModuleOp::setTargets(ArrayRef<TargetAttrInterface> targets) {
2012 ArrayAttr &targetsAttr = getProperties().targets;
2013 SmallVector<Attribute> targetsVector(targets);
2014 targetsAttr = ArrayAttr::get(getContext(), targetsVector);
2015}
2016
2017LogicalResult GPUModuleOp::verify() {
2018 auto targets = getTargetsAttr();
2019
2020 if (!targets)
2021 return success();
2022
2023 for (auto target : targets) {
2024 if (auto verifyTargetAttr =
2025 llvm::dyn_cast<TargetAttrVerifyInterface>(target)) {
2026 if (verifyTargetAttr.verifyTarget(getOperation()).failed())
2027 return failure();
2028 }
2029 }
2030 return success();
2031}
2032
2033//===----------------------------------------------------------------------===//
2034// GPUBinaryOp
2035//===----------------------------------------------------------------------===//
2036void BinaryOp::build(OpBuilder &builder, OperationState &result, StringRef name,
2037 Attribute offloadingHandler, ArrayAttr objects) {
2038 auto &properties = result.getOrAddProperties<Properties>();
2039 result.attributes.push_back(builder.getNamedAttr(
2040 getSymNameAttrName(result.name), builder.getStringAttr(name)));
2041 properties.objects = objects;
2042 if (offloadingHandler)
2043 properties.offloadingHandler = offloadingHandler;
2044 else
2045 properties.offloadingHandler = builder.getAttr<SelectObjectAttr>(nullptr);
2046}
2047
2048void BinaryOp::build(OpBuilder &builder, OperationState &result, StringRef name,
2049 Attribute offloadingHandler, ArrayRef<Attribute> objects) {
2050 build(builder, result, name, offloadingHandler,
2051 objects.empty() ? ArrayAttr() : builder.getArrayAttr(objects));
2052}
2053
2054static ParseResult parseOffloadingHandler(OpAsmParser &parser,
2055 Attribute &offloadingHandler) {
2056 if (succeeded(parser.parseOptionalLess())) {
2057 if (parser.parseAttribute(offloadingHandler))
2058 return failure();
2059 if (parser.parseGreater())
2060 return failure();
2061 }
2062 if (!offloadingHandler)
2063 offloadingHandler = parser.getBuilder().getAttr<SelectObjectAttr>(nullptr);
2064 return success();
2065}
2066
2068 Attribute offloadingHandler) {
2069 if (offloadingHandler != SelectObjectAttr::get(op->getContext(), nullptr))
2070 printer << '<' << offloadingHandler << '>';
2071}
2072
2073//===----------------------------------------------------------------------===//
2074// GPUMemcpyOp
2075//===----------------------------------------------------------------------===//
2076
2077LogicalResult MemcpyOp::verify() {
2078 auto srcType = getSrc().getType();
2079 auto dstType = getDst().getType();
2080
2081 if (getElementTypeOrSelf(srcType) != getElementTypeOrSelf(dstType))
2082 return emitOpError("arguments have incompatible element type");
2083
2084 if (failed(verifyCompatibleShape(srcType, dstType)))
2085 return emitOpError("arguments have incompatible shape");
2086
2087 return success();
2088}
2089
2090namespace {
2091
2092/// Erases a common case of copy ops where a destination value is used only by
2093/// the copy op, alloc and dealloc ops.
2094struct EraseTrivialCopyOp : public OpRewritePattern<MemcpyOp> {
2095 using OpRewritePattern<MemcpyOp>::OpRewritePattern;
2096
2097 LogicalResult matchAndRewrite(MemcpyOp op,
2098 PatternRewriter &rewriter) const override {
2099 Value dest = op.getDst();
2100 Operation *destDefOp = dest.getDefiningOp();
2101 // `dest` must be defined by an op having Allocate memory effect in order to
2102 // perform the folding.
2103 if (!destDefOp ||
2105 return failure();
2106 // We can erase `op` iff `dest` has no other use apart from its
2107 // use by `op` and dealloc ops.
2108 if (llvm::any_of(dest.getUsers(), [op, dest](Operation *user) {
2109 return user != op &&
2110 !hasSingleEffect<MemoryEffects::Free>(user, dest);
2111 }))
2112 return failure();
2113 // We can perform the folding if and only if op has a single async
2114 // dependency and produces an async token as result, or if it does not have
2115 // any async dependency and does not produce any async token result.
2116 if (op.getAsyncDependencies().size() > 1 ||
2117 ((op.getAsyncDependencies().empty() && op.getAsyncToken()) ||
2118 (!op.getAsyncDependencies().empty() && !op.getAsyncToken())))
2119 return failure();
2120 rewriter.replaceOp(op, op.getAsyncDependencies());
2121 return success();
2122 }
2123};
2124
2125} // end anonymous namespace
2126
2127void MemcpyOp::getCanonicalizationPatterns(RewritePatternSet &results,
2128 MLIRContext *context) {
2129 results.add<EraseTrivialCopyOp>(context);
2130}
2131
2132//===----------------------------------------------------------------------===//
2133// GPU_SubgroupMmaLoadMatrixOp
2134//===----------------------------------------------------------------------===//
2135
2136LogicalResult SubgroupMmaLoadMatrixOp::verify() {
2137 auto srcType = getSrcMemref().getType();
2138 auto resType = getRes().getType();
2139 auto resMatrixType = llvm::cast<gpu::MMAMatrixType>(resType);
2140 auto operand = resMatrixType.getOperand();
2141 auto srcMemrefType = llvm::cast<MemRefType>(srcType);
2142
2143 if (!srcMemrefType.isLastDimUnitStride())
2144 return emitError(
2145 "expected source memref most minor dim must have unit stride");
2146
2147 if (operand != "AOp" && operand != "BOp" && operand != "COp")
2148 return emitError("only AOp, BOp and COp can be loaded");
2149
2150 return success();
2151}
2152
2153//===----------------------------------------------------------------------===//
2154// GPU_SubgroupMmaStoreMatrixOp
2155//===----------------------------------------------------------------------===//
2156
2157LogicalResult SubgroupMmaStoreMatrixOp::verify() {
2158 auto srcType = getSrc().getType();
2159 auto dstType = getDstMemref().getType();
2160 auto srcMatrixType = llvm::cast<gpu::MMAMatrixType>(srcType);
2161 auto dstMemrefType = llvm::cast<MemRefType>(dstType);
2162
2163 if (!dstMemrefType.isLastDimUnitStride())
2164 return emitError(
2165 "expected destination memref most minor dim must have unit stride");
2166
2167 if (srcMatrixType.getOperand() != "COp")
2168 return emitError(
2169 "expected the operand matrix being stored to have 'COp' operand type");
2170
2171 return success();
2172}
2173
2174//===----------------------------------------------------------------------===//
2175// GPU_SubgroupMmaComputeOp
2176//===----------------------------------------------------------------------===//
2177
2178LogicalResult SubgroupMmaComputeOp::verify() {
2179 enum OperandMap { A, B, C };
2180 SmallVector<MMAMatrixType, 3> opTypes;
2181 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpA().getType()));
2182 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpB().getType()));
2183 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpC().getType()));
2184
2185 if (opTypes[A].getOperand() != "AOp" || opTypes[B].getOperand() != "BOp" ||
2186 opTypes[C].getOperand() != "COp")
2187 return emitError("operands must be in the order AOp, BOp, COp");
2188
2189 ArrayRef<int64_t> aShape, bShape, cShape;
2190 aShape = opTypes[A].getShape();
2191 bShape = opTypes[B].getShape();
2192 cShape = opTypes[C].getShape();
2193
2194 if (aShape[1] != bShape[0] || aShape[0] != cShape[0] ||
2195 bShape[1] != cShape[1])
2196 return emitError("operand shapes do not satisfy matmul constraints");
2197
2198 return success();
2199}
2200
2201LogicalResult MemcpyOp::fold(FoldAdaptor adaptor,
2202 SmallVectorImpl<::mlir::OpFoldResult> &results) {
2203 return memref::foldMemRefCast(*this);
2204}
2205
2206LogicalResult MemsetOp::fold(FoldAdaptor adaptor,
2207 SmallVectorImpl<::mlir::OpFoldResult> &results) {
2208 return memref::foldMemRefCast(*this);
2209}
2210
2211//===----------------------------------------------------------------------===//
2212// GPU_WaitOp
2213//===----------------------------------------------------------------------===//
2214
2215namespace {
2216
2217/// Remove gpu.wait op use of gpu.wait op def without async dependencies.
2218/// %t = gpu.wait async [] // No async dependencies.
2219/// ... gpu.wait ... [%t, ...] // %t can be removed.
2220struct EraseRedundantGpuWaitOpPairs : public OpRewritePattern<WaitOp> {
2221public:
2223
2224 LogicalResult matchAndRewrite(WaitOp op,
2225 PatternRewriter &rewriter) const final {
2226 auto predicate = [](Value value) {
2227 auto waitOp = value.getDefiningOp<WaitOp>();
2228 return waitOp && waitOp->getNumOperands() == 0;
2229 };
2230 if (llvm::none_of(op.getAsyncDependencies(), predicate))
2231 return failure();
2232 SmallVector<Value> validOperands;
2233 for (Value operand : op->getOperands()) {
2234 if (predicate(operand))
2235 continue;
2236 validOperands.push_back(operand);
2237 }
2238 rewriter.modifyOpInPlace(op, [&]() { op->setOperands(validOperands); });
2239 return success();
2240 }
2241};
2242
2243/// Simplify trivial gpu.wait ops for the following patterns.
2244/// 1. %t = gpu.wait async ... ops, where %t has no uses (regardless of async
2245/// dependencies).
2246/// 2. %t1 = gpu.wait async [%t0], in this case, we can replace uses of %t1 with
2247/// %t0.
2248/// 3. gpu.wait [] ops, i.e gpu.wait ops that neither have any async
2249/// dependencies nor return any token.
2250struct SimplifyGpuWaitOp : public OpRewritePattern<WaitOp> {
2251public:
2253
2254 LogicalResult matchAndRewrite(WaitOp op,
2255 PatternRewriter &rewriter) const final {
2256 // Erase gpu.wait ops that neither have any async dependencies nor return
2257 // any async token.
2258 if (op.getAsyncDependencies().empty() && !op.getAsyncToken()) {
2259 rewriter.eraseOp(op);
2260 return success();
2261 }
2262 // Replace uses of %t1 = gpu.wait async [%t0] ops with %t0 and erase the op.
2263 if (llvm::hasSingleElement(op.getAsyncDependencies()) &&
2264 op.getAsyncToken()) {
2265 rewriter.replaceOp(op, op.getAsyncDependencies());
2266 return success();
2267 }
2268 // Erase %t = gpu.wait async ... ops, where %t has no uses.
2269 if (op.getAsyncToken() && op.getAsyncToken().use_empty()) {
2270 rewriter.eraseOp(op);
2271 return success();
2272 }
2273 return failure();
2274 }
2275};
2276
2277} // end anonymous namespace
2278
2279void WaitOp::getCanonicalizationPatterns(RewritePatternSet &results,
2280 MLIRContext *context) {
2281 results.add<EraseRedundantGpuWaitOpPairs, SimplifyGpuWaitOp>(context);
2282}
2283
2284//===----------------------------------------------------------------------===//
2285// GPU_AllocOp
2286//===----------------------------------------------------------------------===//
2287
2288LogicalResult AllocOp::verify() {
2289 auto memRefType = llvm::cast<MemRefType>(getMemref().getType());
2290
2291 if (failed(verifyDynamicDimensionCount(getOperation(), memRefType,
2292 getDynamicSizes())))
2293 return failure();
2294
2295 unsigned numSymbols = 0;
2296 if (!memRefType.getLayout().isIdentity())
2297 numSymbols = memRefType.getLayout().getAffineMap().getNumSymbols();
2298 if (getSymbolOperands().size() != numSymbols) {
2299 return emitOpError(
2300 "symbol operand count does not equal memref symbol count");
2301 }
2302
2303 return success();
2304}
2305
2306namespace {
2307
2308/// Folding of memref.dim(gpu.alloc(%size), %idx) -> %size similar to
2309/// `memref::AllocOp`.
2310struct SimplifyDimOfAllocOp : public OpRewritePattern<memref::DimOp> {
2311 using OpRewritePattern<memref::DimOp>::OpRewritePattern;
2312
2313 LogicalResult matchAndRewrite(memref::DimOp dimOp,
2314 PatternRewriter &rewriter) const override {
2315 std::optional<int64_t> index = dimOp.getConstantIndex();
2316 if (!index)
2317 return failure();
2318
2319 int64_t indexVal = index.value();
2320 auto memrefType = llvm::dyn_cast<MemRefType>(dimOp.getSource().getType());
2321 if (!memrefType || indexVal < 0 || indexVal >= memrefType.getRank() ||
2322 !memrefType.isDynamicDim(indexVal))
2323 return failure();
2324
2325 auto alloc = dimOp.getSource().getDefiningOp<AllocOp>();
2326 if (!alloc)
2327 return failure();
2328
2329 Value substituteOp = *(alloc.getDynamicSizes().begin() +
2330 memrefType.getDynamicDimIndex(indexVal));
2331 rewriter.replaceOp(dimOp, substituteOp);
2332 return success();
2333 }
2334};
2335
2336} // namespace
2337
2338void AllocOp::getCanonicalizationPatterns(RewritePatternSet &results,
2339 MLIRContext *context) {
2340 results.add<SimplifyDimOfAllocOp>(context);
2341}
2342
2343//===----------------------------------------------------------------------===//
2344// GPU object attribute
2345//===----------------------------------------------------------------------===//
2346
2347LogicalResult ObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2348 Attribute target, CompilationTarget format,
2349 StringAttr object, DictionaryAttr properties,
2350 KernelTableAttr kernels) {
2351 if (!target)
2352 return emitError() << "the target attribute cannot be null";
2353 if (target.hasPromiseOrImplementsInterface<TargetAttrInterface>())
2354 return success();
2355 return emitError() << "the target attribute must implement or promise the "
2356 "`gpu::TargetAttrInterface`";
2357}
2358
2359namespace {
2360ParseResult parseObject(AsmParser &odsParser, CompilationTarget &format,
2361 StringAttr &object) {
2362 std::optional<CompilationTarget> formatResult;
2363 StringRef enumKeyword;
2364 auto loc = odsParser.getCurrentLocation();
2365 if (failed(odsParser.parseOptionalKeyword(&enumKeyword)))
2366 formatResult = CompilationTarget::Fatbin;
2367 if (!formatResult &&
2368 (formatResult =
2369 gpu::symbolizeEnum<gpu::CompilationTarget>(enumKeyword)) &&
2370 odsParser.parseEqual())
2371 return odsParser.emitError(loc, "expected an equal sign");
2372 if (!formatResult)
2373 return odsParser.emitError(loc, "expected keyword for GPU object format");
2374 FailureOr<StringAttr> objectResult =
2375 FieldParser<StringAttr>::parse(odsParser);
2376 if (failed(objectResult))
2377 return odsParser.emitError(odsParser.getCurrentLocation(),
2378 "failed to parse GPU_ObjectAttr parameter "
2379 "'object' which is to be a `StringAttr`");
2380 format = *formatResult;
2381 object = *objectResult;
2382 return success();
2383}
2384
2385void printObject(AsmPrinter &odsParser, CompilationTarget format,
2386 StringAttr object) {
2387 if (format != CompilationTarget::Fatbin)
2388 odsParser << stringifyEnum(format) << " = ";
2389 odsParser << object;
2390}
2391} // namespace
2392
2393//===----------------------------------------------------------------------===//
2394// GPU select object attribute
2395//===----------------------------------------------------------------------===//
2396
2397LogicalResult
2398gpu::SelectObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2399 Attribute target) {
2400 // Check `target`, it can be null, an integer attr or a GPU Target attribute.
2401 if (target) {
2402 if (auto intAttr = mlir::dyn_cast<IntegerAttr>(target)) {
2403 if (intAttr.getInt() < 0) {
2404 return emitError() << "the object index must be positive";
2405 }
2406 } else if (!target.hasPromiseOrImplementsInterface<TargetAttrInterface>()) {
2407 return emitError()
2408 << "the target attribute must be a GPU Target attribute";
2409 }
2410 }
2411 return success();
2412}
2413
2414//===----------------------------------------------------------------------===//
2415// DynamicSharedMemoryOp
2416//===----------------------------------------------------------------------===//
2417
2418LogicalResult gpu::DynamicSharedMemoryOp::verify() {
2419 if (!getOperation()->getParentWithTrait<OpTrait::SymbolTable>())
2420 return emitOpError() << "must be inside an op with symbol table";
2421
2422 MemRefType memrefType = getResultMemref().getType();
2423 // Check address space
2424 if (!GPUDialect::hasWorkgroupMemoryAddressSpace(memrefType)) {
2425 return emitOpError() << "address space must be "
2426 << gpu::AddressSpaceAttr::getMnemonic() << "<"
2427 << stringifyEnum(gpu::AddressSpace::Workgroup) << ">";
2428 }
2429 if (memrefType.hasStaticShape()) {
2430 return emitOpError() << "result memref type must be memref<?xi8, "
2431 "#gpu.address_space<workgroup>>";
2432 }
2433 return success();
2434}
2435
2436//===----------------------------------------------------------------------===//
2437// GPU WarpExecuteOnLane0Op
2438//===----------------------------------------------------------------------===//
2439
2440void WarpExecuteOnLane0Op::print(OpAsmPrinter &p) {
2441 p << "(" << getLaneid() << ")";
2442
2443 SmallVector<StringRef> coreAttr = {getWarpSizeAttrName()};
2444 p << "[" << getWarpSize() << "]";
2445
2446 if (!getArgs().empty())
2447 p << " args(" << getArgs() << " : " << getArgs().getTypes() << ")";
2448 if (!getResults().empty())
2449 p << " -> (" << getResults().getTypes() << ')';
2450 p << " ";
2451 p.printRegion(getRegion(),
2452 /*printEntryBlockArgs=*/true,
2453 /*printBlockTerminators=*/!getResults().empty());
2455 getOperation()->getDiscardableAttrDictionary().getValue(), coreAttr);
2456}
2457
2458ParseResult WarpExecuteOnLane0Op::parse(OpAsmParser &parser,
2459 OperationState &result) {
2460 // Create the region.
2461 result.regions.reserve(1);
2462 Region *warpRegion = result.addRegion();
2463
2464 auto &builder = parser.getBuilder();
2465 OpAsmParser::UnresolvedOperand laneId;
2466
2467 // Parse predicate operand.
2468 if (parser.parseLParen() ||
2469 parser.parseOperand(laneId, /*allowResultNumber=*/false) ||
2470 parser.parseRParen())
2471 return failure();
2472
2473 int64_t warpSize;
2474 if (parser.parseLSquare() || parser.parseInteger(warpSize) ||
2475 parser.parseRSquare())
2476 return failure();
2477 result.addAttribute(getWarpSizeAttrName(OperationName(getOperationName(),
2478 builder.getContext())),
2479 builder.getI64IntegerAttr(warpSize));
2480
2481 if (parser.resolveOperand(laneId, builder.getIndexType(), result.operands))
2482 return failure();
2483
2484 llvm::SMLoc inputsOperandsLoc;
2485 SmallVector<OpAsmParser::UnresolvedOperand> inputsOperands;
2486 SmallVector<Type> inputTypes;
2487 if (succeeded(parser.parseOptionalKeyword("args"))) {
2488 if (parser.parseLParen())
2489 return failure();
2490
2491 inputsOperandsLoc = parser.getCurrentLocation();
2492 if (parser.parseOperandList(inputsOperands) ||
2493 parser.parseColonTypeList(inputTypes) || parser.parseRParen())
2494 return failure();
2495 }
2496 if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
2497 result.operands))
2498 return failure();
2499
2500 // Parse optional results type list.
2501 if (parser.parseOptionalArrowTypeList(result.types))
2502 return failure();
2503 // Parse the region.
2504 if (parser.parseRegion(*warpRegion, /*arguments=*/{},
2505 /*argTypes=*/{}))
2506 return failure();
2507 WarpExecuteOnLane0Op::ensureTerminator(*warpRegion, builder, result.location);
2508
2509 // Parse the optional attribute list.
2510 if (parser.parseOptionalAttrDict(result.attributes))
2511 return failure();
2512 return success();
2513}
2514
2515void WarpExecuteOnLane0Op::getSuccessorRegions(
2516 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
2517 if (!point.isParent()) {
2518 regions.push_back(RegionSuccessor(getOperation()));
2519 return;
2520 }
2521
2522 // The warp region is always executed
2523 regions.push_back(RegionSuccessor(&getWarpRegion()));
2524}
2525
2526ValueRange WarpExecuteOnLane0Op::getSuccessorInputs(RegionSuccessor successor) {
2527 return successor.isOperation() ? ValueRange(getResults()) : ValueRange();
2528}
2529void WarpExecuteOnLane0Op::build(OpBuilder &builder, OperationState &result,
2530 TypeRange resultTypes, Value laneId,
2531 int64_t warpSize) {
2532 build(builder, result, resultTypes, laneId, warpSize,
2533 /*operands=*/{}, /*argTypes=*/{});
2534}
2535
2536void WarpExecuteOnLane0Op::build(OpBuilder &builder, OperationState &result,
2537 TypeRange resultTypes, Value laneId,
2538 int64_t warpSize, ValueRange args,
2539 TypeRange blockArgTypes) {
2540 result.addOperands(laneId);
2541 result.addAttribute(getAttributeNames()[0],
2542 builder.getI64IntegerAttr(warpSize));
2543 result.addTypes(resultTypes);
2544 result.addOperands(args);
2545 assert(args.size() == blockArgTypes.size());
2546 OpBuilder::InsertionGuard guard(builder);
2547 Region *warpRegion = result.addRegion();
2548 Block *block = builder.createBlock(warpRegion);
2549 for (auto [type, arg] : llvm::zip_equal(blockArgTypes, args))
2550 block->addArgument(type, arg.getLoc());
2551}
2552
2553/// Helper check if the distributed vector type is consistent with the expanded
2554/// type and distributed size.
2555static LogicalResult verifyDistributedType(Type expanded, Type distributed,
2556 int64_t warpSize, Operation *op) {
2557 // If the types matches there is no distribution.
2558 if (expanded == distributed)
2559 return success();
2560 auto expandedVecType = llvm::dyn_cast<VectorType>(expanded);
2561 auto distributedVecType = llvm::dyn_cast<VectorType>(distributed);
2562 if (!expandedVecType || !distributedVecType)
2563 return op->emitOpError("expected vector type for distributed operands.");
2564 if (expandedVecType.getRank() != distributedVecType.getRank() ||
2565 expandedVecType.getElementType() != distributedVecType.getElementType())
2566 return op->emitOpError(
2567 "expected distributed vectors to have same rank and element type.");
2568
2569 SmallVector<int64_t> scales(expandedVecType.getRank(), 1);
2570 for (int64_t i = 0, e = expandedVecType.getRank(); i < e; i++) {
2571 int64_t eDim = expandedVecType.getDimSize(i);
2572 int64_t dDim = distributedVecType.getDimSize(i);
2573 if (eDim == dDim)
2574 continue;
2575 if (eDim % dDim != 0)
2576 return op->emitOpError()
2577 << "expected expanded vector dimension #" << i << " (" << eDim
2578 << ") to be a multipler of the distributed vector dimension ("
2579 << dDim << ")";
2580 scales[i] = eDim / dDim;
2581 }
2582 if (llvm::product_of(scales) != warpSize)
2583 return op->emitOpError()
2584 << "incompatible distribution dimensions from " << expandedVecType
2585 << " to " << distributedVecType << " with warp size = " << warpSize;
2586
2587 return success();
2588}
2589
2590LogicalResult WarpExecuteOnLane0Op::verify() {
2591 if (getArgs().size() != getWarpRegion().getNumArguments())
2592 return emitOpError(
2593 "expected same number op arguments and block arguments.");
2594 auto yield = dyn_cast<gpu::YieldOp>(getBody()->getTerminator());
2595 if (!yield)
2596 return emitOpError("expected body to be terminated with 'gpu.yield'");
2597 if (yield.getNumOperands() != getNumResults())
2598 return emitOpError(
2599 "expected same number of yield operands and return values.");
2600 int64_t warpSize = getWarpSize();
2601 for (auto [regionArg, arg] :
2602 llvm::zip_equal(getWarpRegion().getArguments(), getArgs())) {
2603 if (failed(verifyDistributedType(regionArg.getType(), arg.getType(),
2604 warpSize, getOperation())))
2605 return failure();
2606 }
2607 for (auto [yieldOperand, result] :
2608 llvm::zip_equal(yield.getOperands(), getResults())) {
2609 if (failed(verifyDistributedType(yieldOperand.getType(), result.getType(),
2610 warpSize, getOperation())))
2611 return failure();
2612 }
2613 return success();
2614}
2615bool WarpExecuteOnLane0Op::areTypesCompatible(Type lhs, Type rhs) {
2616 return succeeded(
2617 verifyDistributedType(lhs, rhs, getWarpSize(), getOperation()));
2618}
2619
2620gpu::YieldOp WarpExecuteOnLane0Op::getTerminator() {
2621 return cast<gpu::YieldOp>(getBody()->getTerminator());
2622}
2623
2624//===----------------------------------------------------------------------===//
2625// GPU_SubgroupBroadcastOp
2626//===----------------------------------------------------------------------===//
2627
2628void gpu::SubgroupBroadcastOp::inferResultRanges(
2629 ArrayRef<ConstantIntRanges> argRanges, SetIntRangeFn setResultRange) {
2630 setResultRange(getResult(), argRanges.front());
2631}
2632
2633Speculation::Speculatability gpu::SubgroupBroadcastOp::getSpeculatability() {
2634 switch (getBroadcastType()) {
2635 case BroadcastType::first_active_lane:
2636 // Cannot speculate first_lane broadcast, because speculating it across
2637 // control flow can change the active lanes.
2639 case BroadcastType::specific_lane:
2640 // Speculation should be safe as long as we inside structured control flow.
2642 }
2643 llvm_unreachable("Unknown BroadcastType");
2644}
2645
2646LogicalResult gpu::SubgroupBroadcastOp::verify() {
2647 switch (getBroadcastType()) {
2648 case BroadcastType::first_active_lane:
2649 if (getLane())
2650 return emitOpError()
2651 << "lane can only be specified for `specific_lane` broadcast";
2652 return success();
2653 case BroadcastType::specific_lane:
2654 if (!getLane())
2655 return emitOpError()
2656 << "lane must be specified for `specific_lane` broadcast";
2657 return success();
2658 }
2659 llvm_unreachable("Unknown BroadcastType");
2660}
2661
2662OpFoldResult gpu::SubgroupBroadcastOp::fold(FoldAdaptor /*adaptor*/) {
2663 // Broadcast result is always uniform.
2664 if (auto prev = getSrc().getDefiningOp<SubgroupBroadcastOp>())
2665 return prev.getResult();
2666
2667 return nullptr;
2668}
2669
2670//===----------------------------------------------------------------------===//
2671// GPU_BallotOp
2672//===----------------------------------------------------------------------===//
2673
2674// No custom implementations needed; ballot uses default behavior from ODS.
2675
2676//===----------------------------------------------------------------------===//
2677// GPU KernelMetadataAttr
2678//===----------------------------------------------------------------------===//
2679
2680KernelMetadataAttr KernelMetadataAttr::get(FunctionOpInterface kernel,
2681 DictionaryAttr metadata) {
2682 assert(kernel && "invalid kernel");
2683 return get(kernel.getNameAttr(), kernel.getFunctionType(),
2684 kernel.getAllArgAttrs(), metadata);
2685}
2686
2687KernelMetadataAttr
2688KernelMetadataAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
2689 FunctionOpInterface kernel,
2690 DictionaryAttr metadata) {
2691 assert(kernel && "invalid kernel");
2692 return getChecked(emitError, kernel.getNameAttr(), kernel.getFunctionType(),
2693 kernel.getAllArgAttrs(), metadata);
2694}
2695
2696KernelMetadataAttr
2697KernelMetadataAttr::appendMetadata(ArrayRef<NamedAttribute> attrs) const {
2698 if (attrs.empty())
2699 return *this;
2700 NamedAttrList attrList;
2701 if (DictionaryAttr dict = getMetadata())
2702 attrList.append(dict);
2703 attrList.append(attrs);
2704 return KernelMetadataAttr::get(getName(), getFunctionType(), getArgAttrs(),
2705 attrList.getDictionary(getContext()));
2706}
2707
2708LogicalResult
2709KernelMetadataAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2710 StringAttr name, Type functionType,
2711 ArrayAttr argAttrs, DictionaryAttr metadata) {
2712 if (name.empty())
2713 return emitError() << "the kernel name can't be empty";
2714 if (argAttrs) {
2715 if (llvm::any_of(argAttrs, [](Attribute attr) {
2716 return !llvm::isa<DictionaryAttr>(attr);
2717 }))
2718 return emitError()
2719 << "all attributes in the array must be a dictionary attribute";
2720 }
2721 return success();
2722}
2723
2724//===----------------------------------------------------------------------===//
2725// GPU KernelTableAttr
2726//===----------------------------------------------------------------------===//
2727
2728KernelTableAttr KernelTableAttr::get(MLIRContext *context,
2729 ArrayRef<KernelMetadataAttr> kernels,
2730 bool isSorted) {
2731 // Note that `is_sorted` is always only invoked once even with assertions ON.
2732 assert((!isSorted || llvm::is_sorted(kernels)) &&
2733 "expected a sorted kernel array");
2734 // Immediately return the attribute if the array is sorted.
2735 if (isSorted || llvm::is_sorted(kernels))
2736 return Base::get(context, kernels);
2737 // Sort the array.
2738 SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
2739 llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
2740 return Base::get(context, kernelsTmp);
2741}
2742
2743KernelTableAttr KernelTableAttr::getChecked(
2744 function_ref<InFlightDiagnostic()> emitError, MLIRContext *context,
2745 ArrayRef<KernelMetadataAttr> kernels, bool isSorted) {
2746 // Note that `is_sorted` is always only invoked once even with assertions ON.
2747 assert((!isSorted || llvm::is_sorted(kernels)) &&
2748 "expected a sorted kernel array");
2749 // Immediately return the attribute if the array is sorted.
2750 if (isSorted || llvm::is_sorted(kernels))
2751 return Base::getChecked(emitError, context, kernels);
2752 // Sort the array.
2753 SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
2754 llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
2755 return Base::getChecked(emitError, context, kernelsTmp);
2756}
2757
2758LogicalResult
2759KernelTableAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2760 ArrayRef<KernelMetadataAttr> kernels) {
2761 if (kernels.size() < 2)
2762 return success();
2763 // Check that the kernels are uniquely named.
2764 if (std::adjacent_find(kernels.begin(), kernels.end(),
2765 [](KernelMetadataAttr l, KernelMetadataAttr r) {
2766 return l.getName() == r.getName();
2767 }) != kernels.end()) {
2768 return emitError() << "expected all kernels to be uniquely named";
2769 }
2770 return success();
2771}
2772
2773KernelMetadataAttr KernelTableAttr::lookup(StringRef key) const {
2774 auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
2775 return found ? *iterator : KernelMetadataAttr();
2776}
2777
2778KernelMetadataAttr KernelTableAttr::lookup(StringAttr key) const {
2779 auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
2780 return found ? *iterator : KernelMetadataAttr();
2781}
2782
2783//===----------------------------------------------------------------------===//
2784// GPU target options
2785//===----------------------------------------------------------------------===//
2786
2801
2819
2820TypeID TargetOptions::getTypeID() const { return typeID; }
2821
2822StringRef TargetOptions::getToolkitPath() const { return toolkitPath; }
2823
2827
2828StringRef TargetOptions::getCmdOptions() const { return cmdOptions; }
2829
2830StringRef TargetOptions::getELFSection() const { return elfSection; }
2831
2835
2836function_ref<void(llvm::Module &)>
2840
2841function_ref<void(llvm::Module &)>
2845
2846function_ref<void(llvm::Module &)>
2850
2852 return isaCallback;
2853}
2854
2855CompilationTarget TargetOptions::getCompilationTarget() const {
2856 return compilationTarget;
2857}
2858
2860 return CompilationTarget::Fatbin;
2861}
2862
2863std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2865 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> options;
2866 llvm::StringSaver stringSaver(options.first);
2867 StringRef opts = cmdOptions;
2868 // For a correct tokenization of the command line options `opts` must be
2869 // unquoted, otherwise the tokenization function returns a single string: the
2870 // unquoted `cmdOptions` -which is not the desired behavior.
2871 // Remove any quotes if they are at the beginning and end of the string:
2872 if (!opts.empty() && opts.front() == '"' && opts.back() == '"')
2873 opts.consume_front("\""), opts.consume_back("\"");
2874 if (!opts.empty() && opts.front() == '\'' && opts.back() == '\'')
2875 opts.consume_front("'"), opts.consume_back("'");
2876#ifdef _WIN32
2877 llvm::cl::TokenizeWindowsCommandLine(opts, stringSaver, options.second,
2878 /*MarkEOLs=*/false);
2879#else
2880 llvm::cl::TokenizeGNUCommandLine(opts, stringSaver, options.second,
2881 /*MarkEOLs=*/false);
2882#endif // _WIN32
2883 return options;
2884}
2885
2886std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2890
2891std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2893 size_t startPos = cmdOptions.find(startsWith);
2894 if (startPos == std::string::npos)
2895 return {llvm::BumpPtrAllocator(), SmallVector<const char *>()};
2896
2897 auto tokenized =
2898 tokenizeCmdOptions(cmdOptions.substr(startPos + startsWith.size()));
2899 cmdOptions.resize(startPos);
2900 return tokenized;
2901}
2902
2904
2905#include "mlir/Dialect/GPU/IR/GPUOpInterfaces.cpp.inc"
2906#include "mlir/Dialect/GPU/IR/GPUOpsEnums.cpp.inc"
2907
2908#define GET_ATTRDEF_CLASSES
2909#include "mlir/Dialect/GPU/IR/GPUOpsAttributes.cpp.inc"
2910
2911#define GET_OP_CLASSES
2912#include "mlir/Dialect/GPU/IR/GPUOps.cpp.inc"
2913
2914#include "mlir/Dialect/GPU/IR/CompilationAttrInterfaces.cpp.inc"
return success()
static void printLaunchFuncOperands(OpAsmPrinter &printer, Operation *, OperandRange operands, TypeRange types)
static ParseResult parseAsyncDependencies(OpAsmParser &parser, Type &asyncTokenType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &asyncDependencies)
Parses an optional list of async operands with an optional leading keyword.
static void setAttributionAttrs(GPUFuncOp op, unsigned index, DictionaryAttr value, StringAttr attrName)
static void printAttributions(OpAsmPrinter &p, StringRef keyword, ArrayRef< BlockArgument > values, ArrayAttr attributes={})
static LogicalResult verifyDistributedType(Type expanded, Type distributed, int64_t warpSize, Operation *op)
Helper check if the distributed vector type is consistent with the expanded type and distributed size...
static void printAsyncDependencies(OpAsmPrinter &printer, Operation *op, Type asyncTokenType, OperandRange asyncDependencies)
Prints optional async dependencies with its leading keyword.
static ParseResult parseSizeAssignment(OpAsmParser &parser, MutableArrayRef< OpAsmParser::UnresolvedOperand > sizes, MutableArrayRef< OpAsmParser::UnresolvedOperand > regionSizes, MutableArrayRef< OpAsmParser::UnresolvedOperand > indices, StringRef keyword)
static LogicalResult eraseRedundantGpuBarrierOps(BarrierOp op, PatternRewriter &rewriter)
Remove gpu.barrier after gpu.barrier, the threads are already synchronized!
static ParseResult parseOffloadingHandler(OpAsmParser &parser, Attribute &offloadingHandler)
static DictionaryAttr getAttributionAttrs(GPUFuncOp op, unsigned index, StringAttr attrName)
static void printLaunchDimType(OpAsmPrinter &printer, Operation *op, Type dimTy, Value clusterValue, Type clusterXTy, Type clusterYTy, Type clusterZTy)
static bool canMakeGroupOpUniform(Operation *op)
static std::string getSparseHandleKeyword(SparseHandleKind kind)
static LogicalResult verifyKnownLaunchSizeAttr(Operation *op, NamedAttribute attr)
static LogicalResult verifyLaunchAsyncModel(OpTy op)
static ParseResult parseAttributions(OpAsmParser &parser, StringRef keyword, SmallVectorImpl< OpAsmParser::Argument > &args)
Parses a GPU function memory attribution.
static ParseResult parseLaunchDimType(OpAsmParser &parser, Type &dimTy, std::optional< OpAsmParser::UnresolvedOperand > clusterValue, Type &clusterXTy, Type &clusterYTy, Type &clusterZTy)
static void setAttributionAttr(GPUFuncOp op, unsigned index, StringAttr name, Attribute value, StringAttr attrsName)
static ParseResult parseLaunchFuncOperands(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &argNames, SmallVectorImpl< Type > &argTypes)
static void printOffloadingHandler(OpAsmPrinter &printer, Operation *op, Attribute offloadingHandler)
static LogicalResult verifyReduceOpAndType(gpu::AllReduceOperation opName, Type resType)
static void printSizeAssignment(OpAsmPrinter &p, KernelDim3 size, KernelDim3 operands, KernelDim3 ids)
static Attribute getAttributionAttr(GPUFuncOp op, unsigned index, StringAttr name, StringAttr attrsName)
static LogicalResult verifyAttributions(Operation *op, ArrayRef< BlockArgument > attributions, gpu::AddressSpace memorySpace)
Verifies a GPU function memory attribution.
lhs
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
template bool mlir::hasSingleEffect< MemoryEffects::Allocate >(Operation *)
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
static sycl::kernel * getKernel(ze_module_handle_t zeModule, const char *name)
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition TypeID.h:323
ParseResult parseSymbolName(StringAttr &result)
Parse an -identifier and store it (without the '@' symbol) in a string attribute.
@ Paren
Parens surrounding zero or more operands.
@ OptionalSquare
Square brackets supporting zero or more ops, or nothing.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual Location getEncodedSourceLoc(SMLoc loc)=0
Re-encode the given source location as an MLIR location and return it.
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
virtual ParseResult parseLSquare()=0
Parse a [ token.
virtual ParseResult parseRSquare()=0
Parse a ] token.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseDimensionList(SmallVectorImpl< int64_t > &dimensions, bool allowDynamic=true, bool withTrailingX=true)=0
Parse a dimension list of a tensor or memref type.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalString(std::string *string)=0
Parse a quoted string token if present.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printSymbolName(StringRef symbolRef)
Print the given string as a symbol reference, i.e.
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
UnitAttr getUnitAttr()
Definition Builders.cpp:106
IntegerAttr getI32IntegerAttr(int32_t value)
Definition Builders.cpp:208
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:84
IntegerType getI32Type()
Definition Builders.cpp:71
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:120
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
Definition Builders.h:94
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition Builders.cpp:112
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition Builders.cpp:102
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:101
A symbol reference with a reference path containing a single element.
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Definition Attributes.h:179
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual size_t getNumResults() const =0
Return the number of declared SSA results.
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
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:439
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void setInherentAttr(StringAttr name, Attribute value)
Set an inherent attribute by name.
void insertOperands(unsigned index, ValueRange operands)
Insert the given operands into the operand list at the given 'index'.
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Definition Operation.h:493
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
bool isParent() const
Returns true if branching from the parent op.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
bool empty()
Definition Region.h:60
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 replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:24
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
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 isF64() const
Definition Types.cpp:41
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
Definition Types.cpp:78
bool isIndex() const
Definition Types.cpp:56
bool isF32() const
Definition Types.cpp:40
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
Definition Types.cpp:90
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isF16() const
Definition Types.cpp:38
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
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
MMAMatrix represents a matrix held by a subgroup for matrix-matrix multiply accumulate operations.
Definition GPUDialect.h:143
ArrayRef< int64_t > getShape() const
Get shape of the matrix.
static MMAMatrixType get(ArrayRef< int64_t > shape, Type elementType, StringRef operand)
Get MMAMatrixType and verify construction Invariants.
Type getElementType() const
Get elementType of a single element.
static bool isValidElementType(Type elementType)
Check if a type is valid a MMAMatrixType elementType.
static LogicalResult verifyInvariants(function_ref< InFlightDiagnostic()> emitError, ArrayRef< int64_t > shape, Type elementType, StringRef operand)
Verify that shape and elementType are actually allowed for the MMAMatrixType.
StringRef getOperand() const
The general form of operation this type supports is given by the equation C += A*B.
static MMAMatrixType getChecked(function_ref< InFlightDiagnostic()> emitError, ArrayRef< int64_t > shape, Type elementType, StringRef operand)
Get MMAMatrixType at a particular location and verify construction Invariants.
unsigned getNumDims() const
Get number of dims.
This class serves as an opaque interface for passing options to the TargetAttrInterface methods.
function_ref< void(llvm::Module &)> optimizedLlvmIRCallback
Callback invoked with LLVM IR for the device module after LLVM optimizations but before codegen.
function_ref< void(StringRef)> getISACallback() const
Returns the callback invoked with the target ISA for the device, for example PTX assembly.
TypeID getTypeID() const
Returns the typeID.
std::string toolkitPath
Path to the target toolkit.
SymbolTable * getSymbolTable() const
Returns the result of the getSymbolTableCallback callback or a nullptr if no callback was provided.
StringRef getELFSection() const
Returns the ELF section.
StringRef getCmdOptions() const
Returns the command line options.
std::string cmdOptions
An optional set of command line options to be used by the compilation process.
function_ref< void(StringRef)> isaCallback
Callback invoked with the target ISA for the device, for example PTX assembly.
CompilationTarget compilationTarget
Compilation process target format.
std::pair< llvm::BumpPtrAllocator, SmallVector< const char * > > tokenizeCmdOptions() const
Returns a tokenization of the command line options.
function_ref< void(llvm::Module &)> initialLlvmIRCallback
Callback invoked with the initial LLVM IR for the device module.
ArrayRef< Attribute > getLibrariesToLink() const
Returns the LLVM libraries to link to.
TargetOptions(StringRef toolkitPath={}, ArrayRef< Attribute > librariesToLink={}, StringRef cmdOptions={}, StringRef elfSection={}, CompilationTarget compilationTarget=getDefaultCompilationTarget(), function_ref< SymbolTable *()> getSymbolTableCallback={}, function_ref< void(llvm::Module &)> initialLlvmIRCallback={}, function_ref< void(llvm::Module &)> linkedLlvmIRCallback={}, function_ref< void(llvm::Module &)> optimizedLlvmIRCallback={}, function_ref< void(StringRef)> isaCallback={})
Constructor initializing the toolkit path, the list of files to link to, extra command line options,...
function_ref< void(llvm::Module &)> getOptimizedLlvmIRCallback() const
Returns the callback invoked with LLVM IR for the device module after LLVM optimizations but before c...
std::pair< llvm::BumpPtrAllocator, SmallVector< const char * > > tokenizeAndRemoveSuffixCmdOptions(llvm::StringRef startsWith)
Returns a tokenization of the substr of the command line options that starts with startsWith and ends...
StringRef getToolkitPath() const
Returns the toolkit path.
SmallVector< Attribute > librariesToLink
List of files to link with the LLVM module.
function_ref< void(llvm::Module &)> linkedLlvmIRCallback
Callback invoked with LLVM IR for the device module after linking the device libraries.
function_ref< void(llvm::Module &)> getInitialLlvmIRCallback() const
Returns the callback invoked with the initial LLVM IR for the device module.
function_ref< SymbolTable *()> getSymbolTableCallback
Callback for obtaining the parent symbol table of all the GPU modules being serialized.
static CompilationTarget getDefaultCompilationTarget()
Returns the default compilation target: CompilationTarget::Fatbin.
function_ref< void(llvm::Module &)> getLinkedLlvmIRCallback() const
Returns the callback invoked with LLVM IR for the device module after linking the device libraries.
std::string elfSection
ELF Section where the binary needs to be located.
CompilationTarget getCompilationTarget() const
Returns the compilation target.
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
void addArgAndResultAttrs(Builder &builder, OperationState &result, ArrayRef< DictionaryAttr > argAttrs, ArrayRef< DictionaryAttr > resultAttrs, StringAttr argAttrsName, StringAttr resAttrsName)
Adds argument and result attributes, provided as argAttrs and resultAttrs arguments,...
llvm::unique_function< InFlightDiagnostic()> getDefaultDiagnosticEmitFn(MLIRContext *ctx)
Utility method to generate a callback that can be used to generate a diagnostic when checking the con...
ArrayRef< NamedAttribute > getArgAttrs(FunctionOpInterface op, unsigned index)
Return all of the attributes for the argument at 'index'.
ParseResult parseFunctionSignatureWithArguments(OpAsmParser &parser, bool allowVariadic, SmallVectorImpl< OpAsmParser::Argument > &arguments, bool &isVariadic, SmallVectorImpl< Type > &resultTypes, SmallVectorImpl< DictionaryAttr > &resultAttrs)
Parses a function signature using parser.
void printFunctionAttributes(OpAsmPrinter &p, Operation *op, ArrayRef< StringRef > elided={})
Prints the list of function prefixed with the "attributes" keyword.
void printFunctionSignature(OpAsmPrinter &p, FunctionOpInterface op, ArrayRef< Type > argTypes, bool isVariadic, ArrayRef< Type > resultTypes)
Prints the signature of the function-like operation op.
void addAsyncDependency(Operation *op, Value token)
std::pair< IteratorT, bool > findAttrSorted(IteratorT first, IteratorT last, StringRef name)
Using llvm::lower_bound requires an extra string comparison to check whether the returned iterator po...
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
Definition MemRefOps.cpp:47
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:732
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
llvm::function_ref< void(Value, const ConstantIntRanges &)> SetIntRangeFn
The type of the setResultRanges callback provided to ops implementing InferIntRangeInterface.
LogicalResult verifyDynamicDimensionCount(Operation *op, ShapedType type, ValueRange dynamicSizes)
Verify that the number of dynamic size operands matches the number of dynamic dimensions in the shape...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto getChecked(function_ref< InFlightDiagnostic()> emitError, MLIRContext *context, Ts &&...params)
Helper method analogous to get, but uses getChecked when available to allow graceful failure on inval...
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition Matchers.h:478
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
Simplify the gpu.launch when the range of a thread or block ID is trivially known to be one.
LogicalResult matchAndRewrite(LaunchOp op, PatternRewriter &rewriter) const override
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Utility class for the GPU dialect to represent triples of Values accessible through ....
Definition GPUDialect.h:39