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->getAttrOfType<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// TODO: Support optional custom attributes (without dialect prefix).
615static ParseResult parseAllReduceOperation(AsmParser &parser,
616 AllReduceOperationAttr &attr) {
617 StringRef enumStr;
618 if (!parser.parseOptionalKeyword(&enumStr)) {
619 std::optional<AllReduceOperation> op =
620 gpu::symbolizeAllReduceOperation(enumStr);
621 if (!op)
622 return parser.emitError(parser.getCurrentLocation(), "invalid op kind");
623 attr = AllReduceOperationAttr::get(parser.getContext(), *op);
624 }
625 return success();
626}
627
629 AllReduceOperationAttr attr) {
630 if (attr)
631 attr.print(printer);
632}
633
634//===----------------------------------------------------------------------===//
635// SubgroupReduceOp
636//===----------------------------------------------------------------------===//
637
638LogicalResult gpu::SubgroupReduceOp::verify() {
639 Type elemType = getType();
640 if (auto vecTy = dyn_cast<VectorType>(elemType)) {
641 if (vecTy.isScalable())
642 return emitOpError() << "is not compatible with scalable vector types";
643
644 elemType = vecTy.getElementType();
645 }
646
647 gpu::AllReduceOperation opName = getOp();
648 if (failed(verifyReduceOpAndType(opName, elemType))) {
649 return emitError() << '`' << gpu::stringifyAllReduceOperation(opName)
650 << "` reduction operation is not compatible with type "
651 << getType();
652 }
653
654 auto clusterSize = getClusterSize();
655 if (clusterSize) {
656 uint32_t size = *clusterSize;
657 if (!llvm::isPowerOf2_32(size)) {
658 return emitOpError() << "cluster size " << size
659 << " is not a power of two";
660 }
661 }
662
663 uint32_t stride = getClusterStride();
664 if (stride != 1 && !clusterSize) {
665 return emitOpError() << "cluster stride can only be specified if cluster "
666 "size is specified";
667 }
668 if (!llvm::isPowerOf2_32(stride)) {
669 return emitOpError() << "cluster stride " << stride
670 << " is not a power of two";
671 }
672
673 return success();
674}
675
676OpFoldResult gpu::SubgroupReduceOp::fold(FoldAdaptor /*adaptor*/) {
677 if (getClusterSize() == 1)
678 return getValue();
679
680 if (!getUniform() && canMakeGroupOpUniform(*this)) {
681 setUniform(true);
682 return getResult();
683 }
684
685 return nullptr;
686}
687
688//===----------------------------------------------------------------------===//
689// AsyncOpInterface
690//===----------------------------------------------------------------------===//
691
693 op->insertOperands(0, {token});
694 if (!op->template hasTrait<OpTrait::AttrSizedOperandSegments>())
695 return;
696 auto attrName =
698 auto sizeAttr = op->template getAttrOfType<DenseI32ArrayAttr>(attrName);
699
700 // Async dependencies is the only variadic operand.
701 if (!sizeAttr)
702 return;
703
704 SmallVector<int32_t, 8> sizes(sizeAttr.asArrayRef());
705 ++sizes.front();
706 op->setAttr(attrName, Builder(op->getContext()).getDenseI32ArrayAttr(sizes));
707}
708
709//===----------------------------------------------------------------------===//
710// LaunchOp
711//===----------------------------------------------------------------------===//
712
713void LaunchOp::build(OpBuilder &builder, OperationState &result,
714 Value gridSizeX, Value gridSizeY, Value gridSizeZ,
715 Value getBlockSizeX, Value getBlockSizeY,
716 Value getBlockSizeZ, Value dynamicSharedMemorySize,
717 Type asyncTokenType, ValueRange asyncDependencies,
718 Value asyncObject, TypeRange workgroupAttributions,
719 TypeRange privateAttributions, Value clusterSizeX,
720 Value clusterSizeY, Value clusterSizeZ,
721 FlatSymbolRefAttr module, FlatSymbolRefAttr function) {
722 OpBuilder::InsertionGuard g(builder);
723
724 if (!workgroupAttributions.empty())
725 result.addAttribute(
726 getWorkgroupAttributionsAttrName(result.name),
727 builder.getI64IntegerAttr(workgroupAttributions.size()));
728
729 // Add Op operands.
730 result.addOperands(asyncDependencies);
731 if (asyncTokenType)
732 result.types.push_back(builder.getType<AsyncTokenType>());
733
734 // Add grid and block sizes as op operands, followed by the data operands.
735 result.addOperands({gridSizeX, gridSizeY, gridSizeZ, getBlockSizeX,
736 getBlockSizeY, getBlockSizeZ});
737 if (clusterSizeX)
738 result.addOperands(clusterSizeX);
739 if (clusterSizeY)
740 result.addOperands(clusterSizeY);
741 if (clusterSizeZ)
742 result.addOperands(clusterSizeZ);
743 if (dynamicSharedMemorySize)
744 result.addOperands(dynamicSharedMemorySize);
745 if (asyncObject)
746 result.addOperands(asyncObject);
747
748 // Add optional module and function attributes.
749 if (module)
750 result.addAttribute(getModuleAttrName(result.name), module);
751 if (function)
752 result.addAttribute(getFunctionAttrName(result.name), function);
753
754 // Create a kernel body region with kNumConfigRegionAttributes + N memory
755 // attributions, where the first kNumConfigRegionAttributes arguments have
756 // `index` type and the rest have the same types as the data operands.
757 Region *kernelRegion = result.addRegion();
758 Block *body = builder.createBlock(kernelRegion);
759 // TODO: Allow passing in proper locations here.
760 for (unsigned i = 0; i < kNumConfigRegionAttributes; ++i)
761 body->addArgument(builder.getIndexType(), result.location);
762 // Add WorkGroup & Private attributions to the region arguments.
763 for (Type argTy : workgroupAttributions)
764 body->addArgument(argTy, result.location);
765 for (Type argTy : privateAttributions)
766 body->addArgument(argTy, result.location);
767 // Fill OperandSegmentSize Attribute.
768 SmallVector<int32_t, 12> segmentSizes(12, 1);
769 segmentSizes.front() = asyncDependencies.size();
770 segmentSizes[7] = clusterSizeX ? 1 : 0;
771 segmentSizes[8] = clusterSizeY ? 1 : 0;
772 segmentSizes[9] = clusterSizeZ ? 1 : 0;
773 segmentSizes[10] = dynamicSharedMemorySize ? 1 : 0;
774 segmentSizes[11] = asyncObject ? 1 : 0;
775 result.addAttribute(getOperandSegmentSizeAttr(),
776 builder.getDenseI32ArrayAttr(segmentSizes));
777}
778
779KernelDim3 LaunchOp::getBlockIds() {
780 assert(!getBody().empty() && "LaunchOp body must not be empty.");
781 auto args = getBody().getArguments();
782 return KernelDim3{args[0], args[1], args[2]};
783}
784
785KernelDim3 LaunchOp::getThreadIds() {
786 assert(!getBody().empty() && "LaunchOp body must not be empty.");
787 auto args = getBody().getArguments();
788 return KernelDim3{args[3], args[4], args[5]};
789}
790
791KernelDim3 LaunchOp::getGridSize() {
792 assert(!getBody().empty() && "LaunchOp body must not be empty.");
793 auto args = getBody().getArguments();
794 return KernelDim3{args[6], args[7], args[8]};
795}
796
797KernelDim3 LaunchOp::getBlockSize() {
798 assert(!getBody().empty() && "LaunchOp body must not be empty.");
799 auto args = getBody().getArguments();
800 return KernelDim3{args[9], args[10], args[11]};
801}
802
803std::optional<KernelDim3> LaunchOp::getClusterIds() {
804 assert(!getBody().empty() && "LaunchOp body must not be empty.");
805 if (!hasClusterSize())
806 return std::nullopt;
807 auto args = getBody().getArguments();
808 return KernelDim3{args[12], args[13], args[14]};
809}
810
811std::optional<KernelDim3> LaunchOp::getClusterSize() {
812 assert(!getBody().empty() && "LaunchOp body must not be empty.");
813 if (!hasClusterSize())
814 return std::nullopt;
815 auto args = getBody().getArguments();
816 return KernelDim3{args[15], args[16], args[17]};
817}
818
819KernelDim3 LaunchOp::getGridSizeOperandValues() {
820 auto operands = getOperands().drop_front(getAsyncDependencies().size());
821 return KernelDim3{operands[0], operands[1], operands[2]};
822}
823
824KernelDim3 LaunchOp::getBlockSizeOperandValues() {
825 auto operands = getOperands().drop_front(getAsyncDependencies().size());
826 return KernelDim3{operands[3], operands[4], operands[5]};
827}
828
829std::optional<KernelDim3> LaunchOp::getClusterSizeOperandValues() {
830 auto operands = getOperands().drop_front(getAsyncDependencies().size());
831 if (!hasClusterSize())
832 return std::nullopt;
833 return KernelDim3{operands[6], operands[7], operands[8]};
834}
835
836template <typename OpTy>
837static LogicalResult verifyLaunchAsyncModel(OpTy op) {
838 if (!op.getAsyncDependencies().empty() && !op.getAsyncToken())
839 return op.emitOpError("dependency operands require the dependency-based "
840 "async model i.e. returning a token");
841 if (op.getAsyncToken() && op.getAsyncObject())
842 return op.emitOpError("stream-based and dependency-based async models are "
843 "mutually exclusive");
844 if (op.getNumResults() == 0 && op.getAsyncToken())
845 return op.emitOpError("needs to be named when async keyword is specified");
846 return success();
847}
848
849LogicalResult LaunchOp::verify() {
850 if (verifyLaunchAsyncModel(*this).failed())
851 return failure();
852
853 if (!(hasClusterSize()) &&
854 (getClusterSizeX() || getClusterSizeY() || getClusterSizeZ()))
855 return emitOpError() << "cluster size must be all present";
856 return success();
857}
858
859LogicalResult LaunchOp::verifyRegions() {
860 // Kernel launch takes kNumConfigOperands leading operands for grid/block
861 // sizes and transforms them into kNumConfigRegionAttributes region arguments
862 // for block/thread identifiers and grid/block sizes.
863 if (getBody().empty()) {
864 return emitOpError("body region is empty");
865 }
866 unsigned actualNumRegionArgs = getBody().getNumArguments();
867 unsigned expectedNumRegionArgs =
868 getNumConfigRegionAttributes() + getNumWorkgroupAttributions();
869 if (actualNumRegionArgs < expectedNumRegionArgs) {
870 return emitOpError("expected at least ")
871 << expectedNumRegionArgs << " region arguments, but got "
872 << actualNumRegionArgs;
873 }
874
875 // Verify Attributions Address Spaces.
876 if (failed(verifyAttributions(getOperation(), getWorkgroupAttributionBBArgs(),
877 GPUDialect::getWorkgroupAddressSpace())) ||
878 failed(verifyAttributions(getOperation(), getPrivateAttributions(),
879 GPUDialect::getPrivateAddressSpace())))
880 return failure();
881
882 // Block terminators without successors are expected to exit the kernel region
883 // and must be `gpu.terminator`.
884 for (Block &block : getBody()) {
885 if (block.empty())
886 continue;
887 if (block.back().getNumSuccessors() != 0)
888 continue;
889 if (!isa<gpu::TerminatorOp>(&block.back())) {
890 return block.back()
891 .emitError()
892 .append("expected '", gpu::TerminatorOp::getOperationName(),
893 "' or a terminator with successors")
894 .attachNote(getLoc())
895 .append("in '", LaunchOp::getOperationName(), "' body region");
896 }
897 }
898
899 return success();
900}
901
902// Pretty-print the kernel grid/block size assignment as
903// (%iter-x, %iter-y, %iter-z) in
904// (%size-x = %ssa-use, %size-y = %ssa-use, %size-z = %ssa-use)
905// where %size-* and %iter-* will correspond to the body region arguments.
907 KernelDim3 operands, KernelDim3 ids) {
908 p << '(' << ids.x << ", " << ids.y << ", " << ids.z << ") in (";
909 p << size.x << " = " << operands.x << ", ";
910 p << size.y << " = " << operands.y << ", ";
911 p << size.z << " = " << operands.z << ')';
912}
913
914void LaunchOp::print(OpAsmPrinter &p) {
915 if (auto asyncObject = getAsyncObject()) {
916 p << " <" << asyncObject << " : " << asyncObject.getType() << ">";
917 }
918 if (getAsyncToken()) {
919 p << " async";
920 if (!getAsyncDependencies().empty())
921 p << " [" << getAsyncDependencies() << ']';
922 }
923 // Print the launch configuration.
924 if (hasClusterSize()) {
925 p << ' ' << getClustersKeyword();
926 printSizeAssignment(p, getClusterSize().value(),
927 getClusterSizeOperandValues().value(),
928 getClusterIds().value());
929 }
930 p << ' ' << getBlocksKeyword();
931 printSizeAssignment(p, getGridSize(), getGridSizeOperandValues(),
932 getBlockIds());
933 p << ' ' << getThreadsKeyword();
934 printSizeAssignment(p, getBlockSize(), getBlockSizeOperandValues(),
935 getThreadIds());
936 if (getDynamicSharedMemorySize())
937 p << ' ' << getDynamicSharedMemorySizeKeyword() << ' '
938 << getDynamicSharedMemorySize();
939
940 // Print optional module attribute.
941 StringRef moduleAttrName = getModuleAttrName();
942 if (auto module = getModule()) {
943 p << ' ' << moduleAttrName << '(';
944 p.printSymbolName(*module);
945 p << ')';
946 }
947 // Print optional function attribute.
948 StringRef functionAttrName = getFunctionAttrName();
949 if (auto function = getFunction()) {
950 p << ' ' << functionAttrName << '(';
951 p.printSymbolName(*function);
952 p << ')';
953 }
954
955 if (getCooperative())
956 p << " cooperative";
957
958 printAttributions(p, getWorkgroupKeyword(), getWorkgroupAttributionBBArgs());
959 printAttributions(p, getPrivateKeyword(), getPrivateAttributions());
960
961 p << ' ';
962
963 p.printRegion(getBody(), /*printEntryBlockArgs=*/false);
964 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{
965 LaunchOp::getOperandSegmentSizeAttr(),
966 getWorkgroupAttributionsAttrName(),
967 getCooperativeAttrName(), moduleAttrName,
968 functionAttrName});
969}
970
971// Parse the size assignment blocks for blocks and threads. These have the form
972// (%region_arg, %region_arg, %region_arg) in
973// (%region_arg = %operand, %region_arg = %operand, %region_arg = %operand)
974// where %region_arg are percent-identifiers for the region arguments to be
975// introduced further (SSA defs), and %operand are percent-identifiers for the
976// SSA value uses.
977static ParseResult
982 StringRef keyword) {
983 assert(indices.size() == 3 && "space for three indices expected");
986 /*allowResultNumber=*/false) ||
987 parser.parseKeyword("in") || parser.parseLParen())
988 return failure();
989
990 if (args.size() != 3) {
991 return parser.emitError(parser.getNameLoc())
992 << keyword << " expects 3 arguments, but got " << args.size();
993 }
994 std::move(args.begin(), args.end(), indices.begin());
995
996 for (int i = 0; i < 3; ++i) {
997 if (i != 0 && parser.parseComma())
998 return failure();
999 if (parser.parseOperand(regionSizes[i], /*allowResultNumber=*/false) ||
1000 parser.parseEqual() || parser.parseOperand(sizes[i]))
1001 return failure();
1002 }
1003
1004 return parser.parseRParen();
1005}
1006
1007/// Parses a Launch operation.
1008/// operation ::= `gpu.launch` (`<` ssa-use `:` type `>`)?
1009/// (`async` `[` ssa-id-list `]`)?
1010/// (`clusters` `(` ssa-id-list `)` `in` ssa-reassignment)?
1011/// `blocks` `(` ssa-id-list `)` `in` ssa-reassignment
1012/// `threads` `(` ssa-id-list `)` `in` ssa-reassignment
1013/// (`dynamic_shared_memory_size` ssa-use)?
1014/// (`module(` symbol-ref-id `)`)?
1015/// (`function(` symbol-ref-id `)`)?
1016/// memory-attribution
1017/// region attr-dict?
1018/// ssa-reassignment ::= `(` ssa-id `=` ssa-use (`,` ssa-id `=` ssa-use)* `)`
1019ParseResult LaunchOp::parse(OpAsmParser &parser, OperationState &result) {
1020 // Sizes of the grid and block.
1021 SmallVector<OpAsmParser::UnresolvedOperand, LaunchOp::kNumConfigOperands>
1022 sizes(LaunchOp::kNumConfigOperands);
1023
1024 // Region arguments to be created.
1025 SmallVector<OpAsmParser::UnresolvedOperand, 16> regionArgs(
1026 LaunchOp::kNumConfigRegionAttributes);
1027
1028 // Parse optional asyncObject: < value : type >
1029 OpAsmParser::UnresolvedOperand asyncObjectOperand;
1030 Type asyncObjectType;
1031 bool hasAsyncObject = false;
1032 if (succeeded(parser.parseOptionalLess())) {
1033 hasAsyncObject = true;
1034 if (parser.parseOperand(asyncObjectOperand) || parser.parseColon() ||
1035 parser.parseType(asyncObjectType) || parser.parseGreater())
1036 return failure();
1037 }
1038
1039 // Parse optional async dependencies.
1040 SmallVector<OpAsmParser::UnresolvedOperand, 4> asyncDependencies;
1041 Type asyncTokenType;
1042 if (failed(
1043 parseAsyncDependencies(parser, asyncTokenType, asyncDependencies)) ||
1044 parser.resolveOperands(asyncDependencies, asyncTokenType,
1045 result.operands))
1046 return failure();
1047 if (parser.getNumResults() > 0) {
1048 if (!asyncTokenType)
1049 return parser.emitError(
1050 parser.getNameLoc(),
1051 "gpu.launch requires 'async' keyword to return a value");
1052 result.types.push_back(asyncTokenType);
1053 }
1054
1055 bool hasCluster = false;
1056 if (succeeded(parser.parseOptionalKeyword(LaunchOp::getClustersKeyword()))) {
1057 hasCluster = true;
1058 sizes.resize(9);
1059 regionArgs.resize(18);
1060 }
1061 MutableArrayRef<OpAsmParser::UnresolvedOperand> sizesRef(sizes);
1062 MutableArrayRef<OpAsmParser::UnresolvedOperand> regionArgsRef(regionArgs);
1063
1064 // Last three segment assigns the cluster size. In the region argument
1065 // list, this is last 6 arguments.
1066 if (hasCluster) {
1068 parser, sizesRef.drop_front(6), regionArgsRef.slice(15, 3),
1069 regionArgsRef.slice(12, 3), LaunchOp::getClustersKeyword()))
1070 return failure();
1071 }
1072 // Parse the size assignment segments: the first segment assigns grid sizes
1073 // and defines values for block identifiers; the second segment assigns block
1074 // sizes and defines values for thread identifiers. In the region argument
1075 // list, identifiers precede sizes, and block-related values precede
1076 // thread-related values.
1077 if (parser.parseKeyword(LaunchOp::getBlocksKeyword()) ||
1078 parseSizeAssignment(parser, sizesRef.take_front(3),
1079 regionArgsRef.slice(6, 3), regionArgsRef.slice(0, 3),
1080 LaunchOp::getBlocksKeyword()) ||
1081 parser.parseKeyword(LaunchOp::getThreadsKeyword()) ||
1082 parseSizeAssignment(parser, sizesRef.drop_front(3),
1083 regionArgsRef.slice(9, 3), regionArgsRef.slice(3, 3),
1084 LaunchOp::getThreadsKeyword()) ||
1085 parser.resolveOperands(sizes, parser.getBuilder().getIndexType(),
1086 result.operands))
1087 return failure();
1088
1089 OpAsmParser::UnresolvedOperand dynamicSharedMemorySize;
1090 bool hasDynamicSharedMemorySize = false;
1091 if (!parser.parseOptionalKeyword(
1092 LaunchOp::getDynamicSharedMemorySizeKeyword())) {
1093 hasDynamicSharedMemorySize = true;
1094 if (parser.parseOperand(dynamicSharedMemorySize) ||
1095 parser.resolveOperand(dynamicSharedMemorySize,
1096 parser.getBuilder().getI32Type(),
1097 result.operands))
1098 return failure();
1099 }
1100
1101 // Resolve the asyncObject operand
1102 if (hasAsyncObject && parser.resolveOperand(asyncObjectOperand,
1103 asyncObjectType, result.operands))
1104 return failure();
1105
1106 // Parse optional module attribute.
1107 StringRef moduleAttrName = getModuleAttrName(result.name);
1108 if (succeeded(parser.parseOptionalKeyword(moduleAttrName))) {
1109 FlatSymbolRefAttr moduleSymbol;
1110 if (parser.parseLParen() ||
1111 parser.parseAttribute(moduleSymbol, Type(), moduleAttrName,
1112 result.attributes) ||
1113 parser.parseRParen())
1114 return failure();
1115 }
1116 // Parse optional function attribute.
1117 StringRef functionAttrName = getFunctionAttrName(result.name);
1118 if (succeeded(parser.parseOptionalKeyword(functionAttrName))) {
1119 FlatSymbolRefAttr funcSymbol;
1120 if (parser.parseLParen() ||
1121 parser.parseAttribute(funcSymbol, Type(), functionAttrName,
1122 result.attributes) ||
1123 parser.parseRParen())
1124 return failure();
1125 }
1126
1127 // Parse optional cooperative keyword.
1128 if (succeeded(parser.parseOptionalKeyword("cooperative")))
1129 result.addAttribute("cooperative", parser.getBuilder().getUnitAttr());
1130
1131 // Create the region arguments: fixed launch-config args (`index`), then
1132 // workgroup / private attribution args. The workgroup count is stored in the
1133 // inherent `workgroup_attributions` attribute when non-zero.
1134 Type index = parser.getBuilder().getIndexType();
1135 SmallVector<Type, LaunchOp::kNumConfigRegionAttributes> dataTypes(
1136 LaunchOp::kNumConfigRegionAttributes + 6, index);
1137
1138 SmallVector<OpAsmParser::Argument> regionArguments;
1139 for (auto ssaValueAndType : llvm::zip(regionArgs, dataTypes)) {
1140 OpAsmParser::Argument arg;
1141 arg.ssaName = std::get<0>(ssaValueAndType);
1142 arg.type = std::get<1>(ssaValueAndType);
1143 regionArguments.push_back(arg);
1144 }
1145
1146 Builder &builder = parser.getBuilder();
1147 // Parse workgroup memory attributions.
1148 if (failed(parseAttributions(parser, LaunchOp::getWorkgroupKeyword(),
1149 regionArguments)))
1150 return failure();
1151
1152 // Store the number of operands we just parsed as the number of workgroup
1153 // memory attributions.
1154 unsigned numWorkgroupAttrs = regionArguments.size() -
1155 LaunchOp::kNumConfigRegionAttributes -
1156 (hasCluster ? 6 : 0);
1157 if (numWorkgroupAttrs != 0)
1158 result.addAttribute(LaunchOp::getWorkgroupAttributionsAttrName(result.name),
1159 builder.getI64IntegerAttr(numWorkgroupAttrs));
1160
1161 // Parse private memory attributions.
1162 if (failed(parseAttributions(parser, LaunchOp::getPrivateKeyword(),
1163 regionArguments)))
1164 return failure();
1165
1166 // Introduce the body region and parse it. The region has
1167 // kNumConfigRegionAttributes arguments that correspond to
1168 // block/thread identifiers and grid/block sizes, all having `index` type.
1169 Region *body = result.addRegion();
1170 if (parser.parseRegion(*body, regionArguments) ||
1171 parser.parseOptionalAttrDict(result.attributes))
1172 return failure();
1173
1174 SmallVector<int32_t, 12> segmentSizes(12, 1);
1175 segmentSizes.front() = asyncDependencies.size();
1176
1177 if (!hasCluster) {
1178 segmentSizes[7] = 0;
1179 segmentSizes[8] = 0;
1180 segmentSizes[9] = 0;
1181 }
1182 segmentSizes[10] = hasDynamicSharedMemorySize ? 1 : 0;
1183 segmentSizes[11] = hasAsyncObject ? 1 : 0;
1184 result.addAttribute(LaunchOp::getOperandSegmentSizeAttr(),
1185 parser.getBuilder().getDenseI32ArrayAttr(segmentSizes));
1186 return success();
1187}
1188
1189/// Simplify the gpu.launch when the range of a thread or block ID is
1190/// trivially known to be one.
1191struct FoldLaunchArguments : public OpRewritePattern<LaunchOp> {
1192 using OpRewritePattern<LaunchOp>::OpRewritePattern;
1193 LogicalResult matchAndRewrite(LaunchOp op,
1194 PatternRewriter &rewriter) const override {
1195 // If the range implies a single value for `id`, replace `id`'s uses by
1196 // zero.
1197 Value zero;
1198 bool simplified = false;
1199 auto constPropIdUses = [&](Value id, Value size) {
1200 // Check if size is trivially one.
1201 if (!matchPattern(size, m_One()))
1202 return;
1203 if (id.getUses().empty())
1204 return;
1205 if (!simplified) {
1206 // Create a zero value the first time.
1207 OpBuilder::InsertionGuard guard(rewriter);
1208 rewriter.setInsertionPointToStart(&op.getBody().front());
1209 zero =
1210 arith::ConstantIndexOp::create(rewriter, op.getLoc(), /*value=*/0);
1211 }
1212 rewriter.replaceAllUsesWith(id, zero);
1213 simplified = true;
1214 };
1215 constPropIdUses(op.getBlockIds().x, op.getGridSizeX());
1216 constPropIdUses(op.getBlockIds().y, op.getGridSizeY());
1217 constPropIdUses(op.getBlockIds().z, op.getGridSizeZ());
1218 constPropIdUses(op.getThreadIds().x, op.getBlockSizeX());
1219 constPropIdUses(op.getThreadIds().y, op.getBlockSizeY());
1220 constPropIdUses(op.getThreadIds().z, op.getBlockSizeZ());
1221
1222 return success(simplified);
1223 }
1224};
1225
1226void LaunchOp::getCanonicalizationPatterns(RewritePatternSet &rewrites,
1227 MLIRContext *context) {
1228 rewrites.add<FoldLaunchArguments>(context);
1229}
1230
1231/// Adds a new block argument that corresponds to buffers located in
1232/// workgroup memory.
1233BlockArgument LaunchOp::addWorkgroupAttribution(Type type, Location loc) {
1234 int64_t cur = getWorkgroupAttributions().value_or(0);
1235 setWorkgroupAttributions(std::optional<int64_t>(cur + 1));
1236 return getBody().insertArgument(
1237 getNumConfigRegionAttributes() + static_cast<unsigned>(cur), type, loc);
1238}
1239
1240/// Adds a new block argument that corresponds to buffers located in
1241/// private memory.
1242BlockArgument LaunchOp::addPrivateAttribution(Type type, Location loc) {
1243 // Buffers on the private memory always come after buffers on the workgroup
1244 // memory.
1245 return getBody().addArgument(type, loc);
1246}
1247
1248//===----------------------------------------------------------------------===//
1249// LaunchFuncOp
1250//===----------------------------------------------------------------------===//
1251
1252void LaunchFuncOp::build(OpBuilder &builder, OperationState &result,
1253 SymbolRefAttr kernelSymbol, KernelDim3 gridSize,
1254 KernelDim3 getBlockSize, Value dynamicSharedMemorySize,
1255 ValueRange kernelOperands, Type asyncTokenType,
1256 ValueRange asyncDependencies, Value asyncObject,
1257 std::optional<KernelDim3> clusterSize) {
1258 assert(kernelSymbol.getNestedReferences().size() == 1 &&
1259 "expected a symbol reference with a single nested reference");
1260 result.addOperands(asyncDependencies);
1261 if (asyncTokenType)
1262 result.types.push_back(builder.getType<AsyncTokenType>());
1263
1264 // Add grid and block sizes as op operands, followed by the data operands.
1265 result.addOperands({gridSize.x, gridSize.y, gridSize.z, getBlockSize.x,
1267 if (clusterSize.has_value())
1268 result.addOperands({clusterSize->x, clusterSize->y, clusterSize->z});
1269 if (dynamicSharedMemorySize)
1270 result.addOperands(dynamicSharedMemorySize);
1271 result.addOperands(kernelOperands);
1272 if (asyncObject)
1273 result.addOperands(asyncObject);
1274
1275 Properties &prop = result.getOrAddProperties<Properties>();
1276 prop.kernel = kernelSymbol;
1277 size_t segmentSizesLen = std::size(prop.operandSegmentSizes);
1278 // Initialize the segment sizes to 1.
1279 llvm::fill(prop.operandSegmentSizes, 1);
1280 prop.operandSegmentSizes[0] = asyncDependencies.size();
1281 if (!clusterSize.has_value()) {
1282 prop.operandSegmentSizes[segmentSizesLen - 4] = 0;
1283 prop.operandSegmentSizes[segmentSizesLen - 5] = 0;
1284 prop.operandSegmentSizes[segmentSizesLen - 6] = 0;
1285 }
1286 prop.operandSegmentSizes[segmentSizesLen - 3] =
1287 dynamicSharedMemorySize ? 1 : 0;
1288 prop.operandSegmentSizes[segmentSizesLen - 2] =
1289 static_cast<int32_t>(kernelOperands.size());
1290 prop.operandSegmentSizes[segmentSizesLen - 1] = asyncObject ? 1 : 0;
1291}
1292
1293void LaunchFuncOp::build(OpBuilder &builder, OperationState &result,
1294 GPUFuncOp kernelFunc, KernelDim3 gridSize,
1295 KernelDim3 getBlockSize, Value dynamicSharedMemorySize,
1296 ValueRange kernelOperands, Type asyncTokenType,
1297 ValueRange asyncDependencies, Value asyncObject,
1298 std::optional<KernelDim3> clusterSize) {
1299 auto kernelModule = kernelFunc->getParentOfType<GPUModuleOp>();
1300 auto kernelSymbol =
1301 SymbolRefAttr::get(kernelModule.getNameAttr(),
1302 {SymbolRefAttr::get(kernelFunc.getNameAttr())});
1303 build(builder, result, kernelSymbol, gridSize, getBlockSize,
1304 dynamicSharedMemorySize, kernelOperands, asyncTokenType,
1305 asyncDependencies, asyncObject, clusterSize);
1306}
1307
1308StringAttr LaunchFuncOp::getKernelModuleName() {
1309 return getKernel().getRootReference();
1310}
1311
1312StringAttr LaunchFuncOp::getKernelName() {
1313 return getKernel().getLeafReference();
1314}
1315
1316unsigned LaunchFuncOp::getNumKernelOperands() {
1317 return getKernelOperands().size();
1318}
1319
1320Value LaunchFuncOp::getKernelOperand(unsigned i) {
1321 return getKernelOperands()[i];
1322}
1323
1324KernelDim3 LaunchFuncOp::getGridSizeOperandValues() {
1325 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1326 return KernelDim3{operands[0], operands[1], operands[2]};
1327}
1328
1329KernelDim3 LaunchFuncOp::getBlockSizeOperandValues() {
1330 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1331 return KernelDim3{operands[3], operands[4], operands[5]};
1332}
1333
1334KernelDim3 LaunchFuncOp::getClusterSizeOperandValues() {
1335 assert(hasClusterSize() &&
1336 "cluster size is not set, check hasClusterSize() first");
1337 auto operands = getOperands().drop_front(getAsyncDependencies().size());
1338 return KernelDim3{operands[6], operands[7], operands[8]};
1339}
1340
1341LogicalResult LaunchFuncOp::verify() {
1342 if (verifyLaunchAsyncModel(*this).failed())
1343 return failure();
1344
1345 auto module = (*this)->getParentOfType<ModuleOp>();
1346 if (!module)
1347 return emitOpError("expected to belong to a module");
1348
1349 if (!module->getAttrOfType<UnitAttr>(
1350 GPUDialect::getContainerModuleAttrName()))
1351 return emitOpError("expected the closest surrounding module to have the '" +
1352 GPUDialect::getContainerModuleAttrName() +
1353 "' attribute");
1354
1355 if (hasClusterSize()) {
1356 if (getClusterSizeY().getType() != getClusterSizeX().getType() ||
1357 getClusterSizeZ().getType() != getClusterSizeX().getType())
1358 return emitOpError()
1359 << "expects types of the cluster dimensions must be the same";
1360 }
1361
1362 return success();
1363}
1364
1365LogicalResult
1366LaunchFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1367 LaunchFuncOp launchOp = *this;
1368 Operation *table = SymbolTable::getNearestSymbolTable(launchOp);
1369 // GPU modules cannot be nested within each other, escape to resolve the name.
1370 if (isa<GPUModuleOp>(table))
1372
1373 // Ignore launches that are nested more or less deep than functions in the
1374 // module we are currently checking.
1375 if (!launchOp->getParentOp() ||
1376 launchOp->getParentOp()->getParentOp() != table)
1377 return success();
1378
1379 // Ignore launch ops with missing attributes here. The errors will be
1380 // reported by the verifiers of those ops.
1381 if (!launchOp->getAttrOfType<SymbolRefAttr>(
1382 LaunchFuncOp::getKernelAttrName(launchOp->getName())))
1383 return success();
1384
1385 // Check that `launch_func` refers to a well-formed GPU kernel container.
1386 StringAttr kernelContainerName = launchOp.getKernelModuleName();
1387 Operation *kernelContainer =
1388 symbolTable.lookupNearestSymbolFrom(table, kernelContainerName);
1389 if (!kernelContainer)
1390 return launchOp.emitOpError()
1391 << "kernel container '" << kernelContainerName.getValue()
1392 << "' is undefined";
1393
1394 // If the container is a GPU binary op return success.
1395 if (isa<BinaryOp>(kernelContainer))
1396 return success();
1397
1398 auto kernelModule = dyn_cast<GPUModuleOp>(kernelContainer);
1399 if (!kernelModule)
1400 return launchOp.emitOpError()
1401 << "kernel module '" << kernelContainerName.getValue()
1402 << "' is undefined";
1403
1404 // Check that `launch_func` refers to a well-formed kernel function.
1405 Operation *kernelFunc = symbolTable.lookupNearestSymbolFrom(
1406 kernelModule, launchOp.getKernelName());
1407 if (!kernelFunc)
1408 return launchOp.emitOpError("kernel function '")
1409 << launchOp.getKernel() << "' is undefined";
1410 auto kernelConvertedFunction = dyn_cast<FunctionOpInterface>(kernelFunc);
1411 if (!kernelConvertedFunction) {
1412 InFlightDiagnostic diag = launchOp.emitOpError()
1413 << "referenced kernel '" << launchOp.getKernel()
1414 << "' is not a function";
1415 diag.attachNote(kernelFunc->getLoc()) << "see the kernel definition here";
1416 return diag;
1417 }
1418
1419 if (!GPUDialect::isKernel(kernelFunc))
1420 return launchOp.emitOpError("kernel function is missing the '")
1421 << GPUDialect::getKernelFuncAttrName() << "' attribute";
1422
1423 // TODO: If the kernel isn't a GPU function (which happens during separate
1424 // compilation), do not check type correspondence as it would require the
1425 // verifier to be aware of the type conversion.
1426 auto kernelGPUFunction = dyn_cast<gpu::GPUFuncOp>(kernelFunc);
1427 if (!kernelGPUFunction)
1428 return success();
1429
1430 unsigned actualNumArguments = launchOp.getNumKernelOperands();
1431 unsigned expectedNumArguments = kernelGPUFunction.getNumArguments();
1432 if (expectedNumArguments != actualNumArguments)
1433 return launchOp.emitOpError("got ")
1434 << actualNumArguments << " kernel operands but expected "
1435 << expectedNumArguments;
1436
1437 FunctionType functionType = kernelGPUFunction.getFunctionType();
1438 for (unsigned i = 0; i < expectedNumArguments; ++i) {
1439 if (launchOp.getKernelOperand(i).getType() != functionType.getInput(i)) {
1440 return launchOp.emitOpError("type of function argument ")
1441 << i << " does not match";
1442 }
1443 }
1444
1445 return success();
1446}
1447
1448static ParseResult
1450 std::optional<OpAsmParser::UnresolvedOperand> clusterValue,
1451 Type &clusterXTy, Type &clusterYTy, Type &clusterZTy) {
1452 if (succeeded(parser.parseOptionalColon())) {
1453 if (parser.parseType(dimTy))
1454 return failure();
1455 } else {
1456 dimTy = IndexType::get(parser.getContext());
1457 }
1458 if (clusterValue.has_value()) {
1459 clusterXTy = clusterYTy = clusterZTy = dimTy;
1460 }
1461 return success();
1462}
1463
1464static void printLaunchDimType(OpAsmPrinter &printer, Operation *op, Type dimTy,
1465 Value clusterValue, Type clusterXTy,
1466 Type clusterYTy, Type clusterZTy) {
1467 if (!dimTy.isIndex())
1468 printer << ": " << dimTy;
1469}
1470
1471static ParseResult parseLaunchFuncOperands(
1472 OpAsmParser &parser,
1474 SmallVectorImpl<Type> &argTypes) {
1475 if (parser.parseOptionalKeyword("args"))
1476 return success();
1477
1478 auto parseElement = [&]() -> ParseResult {
1479 return failure(parser.parseOperand(argNames.emplace_back()) ||
1480 parser.parseColonType(argTypes.emplace_back()));
1481 };
1482
1484 parseElement, " in argument list");
1485}
1486
1488 OperandRange operands, TypeRange types) {
1489 if (operands.empty())
1490 return;
1491 printer << "args(";
1492 llvm::interleaveComma(llvm::zip_equal(operands, types), printer,
1493 [&](const auto &pair) {
1494 auto [operand, type] = pair;
1495 printer << operand << " : " << type;
1496 });
1497 printer << ")";
1498}
1499
1500//===----------------------------------------------------------------------===//
1501// ShuffleOp
1502//===----------------------------------------------------------------------===//
1503
1504void ShuffleOp::build(OpBuilder &builder, OperationState &result, Value value,
1505 int32_t offset, int32_t width, ShuffleMode mode) {
1506 build(builder, result, value,
1507 arith::ConstantOp::create(builder, result.location,
1508 builder.getI32IntegerAttr(offset)),
1509 arith::ConstantOp::create(builder, result.location,
1510 builder.getI32IntegerAttr(width)),
1511 mode);
1512}
1513
1514//===----------------------------------------------------------------------===//
1515// RotateOp
1516//===----------------------------------------------------------------------===//
1517
1518LogicalResult RotateOp::verify() {
1519 uint32_t offset = getOffset();
1520 uint32_t width = getWidth();
1521
1522 if (offset >= width) {
1523 return emitOpError() << "offset must be in the range [0, " << width << ")";
1524 }
1525
1526 return success();
1527}
1528
1529//===----------------------------------------------------------------------===//
1530// BarrierOp
1531//===----------------------------------------------------------------------===//
1532
1533LogicalResult BarrierOp::verify() {
1534 BarrierScope scope = getScope();
1535
1536 if (getNamedBarrier() && scope != BarrierScope::Workgroup)
1537 return emitOpError("named barriers require workgroup scope");
1538
1539 return success();
1540}
1541
1542/// Remove gpu.barrier after gpu.barrier, the threads are already synchronized!
1543static LogicalResult eraseRedundantGpuBarrierOps(BarrierOp op,
1544 PatternRewriter &rewriter) {
1545 auto nextOp = dyn_cast_or_null<BarrierOp>(op->getNextNode());
1546 if (!nextOp)
1547 return failure();
1548
1549 // Cannot merge barriers of different scopes.
1550 if (op.getScope() != nextOp.getScope())
1551 return failure();
1552
1553 // Cannot merge named barriers unless both refer to the same handle.
1554 if (op.getNamedBarrier() != nextOp.getNamedBarrier())
1555 return failure();
1556
1557 std::optional<ArrayAttr> thisMemfence = op.getAddressSpaces();
1558 std::optional<ArrayAttr> nextMemfence = nextOp.getAddressSpaces();
1559
1560 if (thisMemfence) {
1561 rewriter.modifyOpInPlace(op, [&]() {
1562 if (!nextMemfence) {
1563 op.removeAddressSpacesAttr();
1564 return;
1565 }
1566 // Fast path - merge where the two barriers fence the same spaces.
1567 if (*thisMemfence == *nextMemfence) {
1568 return;
1569 }
1570
1571 llvm::SmallSetVector<Attribute, 4> mergedSpaces;
1572 for (Attribute attr : *thisMemfence)
1573 mergedSpaces.insert(attr);
1574 for (Attribute attr : *nextMemfence)
1575 mergedSpaces.insert(attr);
1576 op.setAddressSpacesAttr(rewriter.getArrayAttr(mergedSpaces.takeVector()));
1577 });
1578 }
1579
1580 rewriter.eraseOp(nextOp);
1581 return success();
1582}
1583
1584void BarrierOp::getCanonicalizationPatterns(RewritePatternSet &results,
1585 MLIRContext *context) {
1587}
1588
1589void BarrierOp::build(mlir::OpBuilder &odsBuilder,
1590 mlir::OperationState &odsState,
1591 std::optional<AddressSpace> addressSpace) {
1592 ArrayAttr addressSpacesAttr;
1593 if (addressSpace)
1594 addressSpacesAttr = odsBuilder.getArrayAttr(
1595 AddressSpaceAttr::get(odsBuilder.getContext(), addressSpace.value()));
1596 build(
1597 odsBuilder, odsState, addressSpacesAttr, /*named_barrier=*/Value{},
1598 BarrierScopeAttr::get(odsBuilder.getContext(), BarrierScope::Workgroup));
1599}
1600
1601/// Builds a barrier that causes memory operations affecting `memrefToFence` to
1602/// be completed after the barrier is concluded. Currently, this means setting
1603/// the fenced address spaces to those of the given memref if it is a gpu
1604/// address space.
1605void BarrierOp::build(OpBuilder &builder, OperationState &odsState,
1606 Value memrefToFence) {
1607 std::optional<AddressSpace> addrSpaceToFence;
1608 if (auto memrefType = dyn_cast<BaseMemRefType>(memrefToFence.getType()))
1609 if (auto addrSpaceAttr = dyn_cast_if_present<gpu::AddressSpaceAttr>(
1610 memrefType.getMemorySpace()))
1611 addrSpaceToFence = addrSpaceAttr.getValue();
1612 return build(builder, odsState, addrSpaceToFence);
1613}
1614
1615//===----------------------------------------------------------------------===//
1616// GPUFuncOp
1617//===----------------------------------------------------------------------===//
1618
1619/// Adds a new block argument that corresponds to buffers located in
1620/// workgroup memory.
1621BlockArgument GPUFuncOp::addWorkgroupAttribution(Type type, Location loc) {
1622 int64_t cur = getWorkgroupAttributions().value_or(0);
1623 setWorkgroupAttributions(std::optional<int64_t>(cur + 1));
1624 return getBody().insertArgument(
1625 getFunctionType().getNumInputs() + static_cast<unsigned>(cur), type, loc);
1626}
1627
1628/// Adds a new block argument that corresponds to buffers located in
1629/// private memory.
1630BlockArgument GPUFuncOp::addPrivateAttribution(Type type, Location loc) {
1631 // Buffers on the private memory always come after buffers on the workgroup
1632 // memory.
1633 return getBody().addArgument(type, loc);
1634}
1635
1636void GPUFuncOp::build(OpBuilder &builder, OperationState &result,
1637 StringRef name, FunctionType type,
1638 TypeRange workgroupAttributions,
1639 TypeRange privateAttributions,
1640 ArrayRef<NamedAttribute> attrs) {
1641 OpBuilder::InsertionGuard g(builder);
1642
1644 builder.getStringAttr(name));
1645 result.addAttribute(getFunctionTypeAttrName(result.name),
1646 TypeAttr::get(type));
1647 result.addAttribute(getWorkgroupAttributionsAttrName(result.name),
1648 builder.getI64IntegerAttr(workgroupAttributions.size()));
1649 result.addAttributes(attrs);
1650 Region *body = result.addRegion();
1651 Block *entryBlock = builder.createBlock(body);
1652
1653 // TODO: Allow passing in proper locations here.
1654 for (Type argTy : type.getInputs())
1655 entryBlock->addArgument(argTy, result.location);
1656 for (Type argTy : workgroupAttributions)
1657 entryBlock->addArgument(argTy, result.location);
1658 for (Type argTy : privateAttributions)
1659 entryBlock->addArgument(argTy, result.location);
1660}
1661
1662/// Parses a GPU function memory attribution.
1663///
1664/// memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)?
1665/// (`private` `(` ssa-id-and-type-list `)`)?
1666///
1667/// Note that this function parses only one of the two similar parts, with the
1668/// keyword provided as argument.
1669static ParseResult
1670parseAttributions(OpAsmParser &parser, StringRef keyword,
1672 Attribute &attributionAttrs) {
1673 // If we could not parse the keyword, just assume empty list and succeed.
1674 if (failed(parser.parseOptionalKeyword(keyword)))
1675 return success();
1676
1677 size_t existingArgs = args.size();
1678 ParseResult result =
1680 /*allowType=*/true, /*allowAttrs=*/true);
1681 if (failed(result))
1682 return result;
1683
1684 bool hadAttrs = llvm::any_of(ArrayRef(args).drop_front(existingArgs),
1685 [](const OpAsmParser::Argument &arg) -> bool {
1686 return arg.attrs && !arg.attrs.empty();
1687 });
1688 if (!hadAttrs) {
1689 attributionAttrs = nullptr;
1690 return result;
1691 }
1692
1693 Builder &builder = parser.getBuilder();
1694 SmallVector<Attribute> attributionAttrsVec;
1695 for (const auto &argument : ArrayRef(args).drop_front(existingArgs)) {
1696 if (!argument.attrs)
1697 attributionAttrsVec.push_back(builder.getDictionaryAttr({}));
1698 else
1699 attributionAttrsVec.push_back(argument.attrs);
1700 }
1701 attributionAttrs = builder.getArrayAttr(attributionAttrsVec);
1702 return result;
1703}
1704
1705/// Parses a GPU function.
1706///
1707/// <operation> ::= `gpu.func` symbol-ref-id `(` argument-list `)`
1708/// (`->` function-result-list)? memory-attribution `kernel`?
1709/// function-attributes? region
1710ParseResult GPUFuncOp::parse(OpAsmParser &parser, OperationState &result) {
1711 SmallVector<OpAsmParser::Argument> entryArgs;
1712 SmallVector<DictionaryAttr> resultAttrs;
1713 SmallVector<Type> resultTypes;
1714 bool isVariadic;
1715
1716 // Parse the function name.
1717 StringAttr nameAttr;
1719 result.attributes))
1720 return failure();
1721
1722 auto signatureLocation = parser.getCurrentLocation();
1724 parser, /*allowVariadic=*/false, entryArgs, isVariadic, resultTypes,
1725 resultAttrs)))
1726 return failure();
1727
1728 if (!entryArgs.empty() && entryArgs[0].ssaName.name.empty())
1729 return parser.emitError(signatureLocation)
1730 << "gpu.func requires named arguments";
1731
1732 // Construct the function type. More types will be added to the region, but
1733 // not to the function type.
1734 Builder &builder = parser.getBuilder();
1735
1736 SmallVector<Type> argTypes;
1737 for (auto &arg : entryArgs)
1738 argTypes.push_back(arg.type);
1739 auto type = builder.getFunctionType(argTypes, resultTypes);
1740 result.addAttribute(getFunctionTypeAttrName(result.name),
1741 TypeAttr::get(type));
1742
1744 builder, result, entryArgs, resultAttrs, getArgAttrsAttrName(result.name),
1745 getResAttrsAttrName(result.name));
1746
1747 Attribute workgroupAttributionAttrs;
1748 // Parse workgroup memory attributions.
1749 if (failed(parseAttributions(parser, GPUFuncOp::getWorkgroupKeyword(),
1750 entryArgs, workgroupAttributionAttrs)))
1751 return failure();
1752
1753 // Store the number of operands we just parsed as the number of workgroup
1754 // memory attributions.
1755 unsigned numWorkgroupAttrs = entryArgs.size() - type.getNumInputs();
1756 if (numWorkgroupAttrs != 0)
1757 result.addAttribute(
1758 GPUFuncOp::getWorkgroupAttributionsAttrName(result.name),
1759 builder.getI64IntegerAttr(numWorkgroupAttrs));
1760 if (workgroupAttributionAttrs)
1761 result.addAttribute(GPUFuncOp::getWorkgroupAttribAttrsAttrName(result.name),
1762 workgroupAttributionAttrs);
1763
1764 Attribute privateAttributionAttrs;
1765 // Parse private memory attributions.
1766 if (failed(parseAttributions(parser, GPUFuncOp::getPrivateKeyword(),
1767 entryArgs, privateAttributionAttrs)))
1768 return failure();
1769 if (privateAttributionAttrs)
1770 result.addAttribute(GPUFuncOp::getPrivateAttribAttrsAttrName(result.name),
1771 privateAttributionAttrs);
1772
1773 // Parse the kernel attribute if present.
1774 if (succeeded(parser.parseOptionalKeyword(GPUFuncOp::getKernelKeyword())))
1775 result.addAttribute(GPUFuncOp::getKernelAttrName(result.name),
1776 builder.getUnitAttr());
1777
1778 // Parse attributes.
1779 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
1780 return failure();
1781
1782 // Parse the region. If no argument names were provided, take all names
1783 // (including those of attributions) from the entry block.
1784 auto *body = result.addRegion();
1785 return parser.parseRegion(*body, entryArgs);
1786}
1787
1788void GPUFuncOp::print(OpAsmPrinter &p) {
1789 p << ' ';
1790 p.printSymbolName(getName());
1791
1792 FunctionType type = getFunctionType();
1793 function_interface_impl::printFunctionSignature(p, *this, type.getInputs(),
1794 /*isVariadic=*/false,
1795 type.getResults());
1796
1797 printAttributions(p, getWorkgroupKeyword(), getWorkgroupAttributionBBArgs(),
1798 getWorkgroupAttribAttrs().value_or(nullptr));
1799 printAttributions(p, getPrivateKeyword(), getPrivateAttributions(),
1800 getPrivateAttribAttrs().value_or(nullptr));
1801 if (isKernel())
1802 p << ' ' << getKernelKeyword();
1803
1805 p, *this,
1806 {getWorkgroupAttributionsAttrName(), getKernelAttrName(),
1807 GPUDialect::getKernelFuncAttrName(), getFunctionTypeAttrName(),
1808 getArgAttrsAttrName(), getResAttrsAttrName(),
1809 getWorkgroupAttribAttrsAttrName(), getPrivateAttribAttrsAttrName()});
1810 p << ' ';
1811 p.printRegion(getBody(), /*printEntryBlockArgs=*/false);
1812}
1813
1814static DictionaryAttr getAttributionAttrs(GPUFuncOp op, unsigned index,
1815 StringAttr attrName) {
1816 auto allAttrs = llvm::dyn_cast_or_null<ArrayAttr>(op->getAttr(attrName));
1817 if (!allAttrs || index >= allAttrs.size())
1818 return DictionaryAttr();
1819 return llvm::cast<DictionaryAttr>(allAttrs[index]);
1820}
1821
1822DictionaryAttr GPUFuncOp::getworkgroupAttributionAttrs(unsigned index) {
1823 return getAttributionAttrs(*this, index, getWorkgroupAttribAttrsAttrName());
1824}
1825
1826DictionaryAttr GPUFuncOp::getPrivateAttributionAttrs(unsigned index) {
1827 return getAttributionAttrs(*this, index, getPrivateAttribAttrsAttrName());
1828}
1829
1830static void setAttributionAttrs(GPUFuncOp op, unsigned index,
1831 DictionaryAttr value, StringAttr attrName) {
1832 MLIRContext *ctx = op.getContext();
1833 auto allAttrs = llvm::dyn_cast_or_null<ArrayAttr>(op->getAttr(attrName));
1834 SmallVector<Attribute> elements;
1835 if (allAttrs)
1836 elements.append(allAttrs.begin(), allAttrs.end());
1837 while (elements.size() <= index)
1838 elements.push_back(DictionaryAttr::get(ctx));
1839 if (!value)
1840 elements[index] = DictionaryAttr::get(ctx);
1841 else
1842 elements[index] = value;
1843 ArrayAttr newValue = ArrayAttr::get(ctx, elements);
1844 op->setAttr(attrName, newValue);
1845}
1846
1847void GPUFuncOp::setworkgroupAttributionAttrs(unsigned index,
1848 DictionaryAttr value) {
1849 setAttributionAttrs(*this, index, value, getWorkgroupAttribAttrsAttrName());
1850}
1851
1852void GPUFuncOp::setPrivateAttributionAttrs(unsigned int index,
1853 DictionaryAttr value) {
1854 setAttributionAttrs(*this, index, value, getPrivateAttribAttrsAttrName());
1855}
1856
1857static Attribute getAttributionAttr(GPUFuncOp op, unsigned index,
1858 StringAttr name, StringAttr attrsName) {
1859 DictionaryAttr dict = getAttributionAttrs(op, index, attrsName);
1860 if (!dict)
1861 return Attribute();
1862 return dict.get(name);
1863}
1864
1865Attribute GPUFuncOp::getWorkgroupAttributionAttr(unsigned index,
1866 StringAttr name) {
1867 assert(index < getNumWorkgroupAttributions() &&
1868 "index must map to a workgroup attribution");
1869 return getAttributionAttr(*this, index, name,
1870 getWorkgroupAttribAttrsAttrName());
1871}
1872
1873Attribute GPUFuncOp::getPrivateAttributionAttr(unsigned index,
1874 StringAttr name) {
1875 assert(index < getNumPrivateAttributions() &&
1876 "index must map to a private attribution");
1877 return getAttributionAttr(*this, index, name,
1878 getPrivateAttribAttrsAttrName());
1879}
1880
1881static void setAttributionAttr(GPUFuncOp op, unsigned index, StringAttr name,
1882 Attribute value, StringAttr attrsName) {
1883 MLIRContext *ctx = op.getContext();
1885 DictionaryAttr oldDict = getAttributionAttrs(op, index, attrsName);
1886 if (oldDict)
1887 elems.append(oldDict.getValue().begin(), oldDict.getValue().end());
1888
1889 bool found = false;
1890 bool mustSort = true;
1891 for (unsigned i = 0, e = elems.size(); i < e; ++i) {
1892 if (elems[i].getName() == name) {
1893 found = true;
1894 if (!value) {
1895 std::swap(elems[i], elems[elems.size() - 1]);
1896 elems.pop_back();
1897 } else {
1898 mustSort = false;
1899 elems[i] = NamedAttribute(elems[i].getName(), value);
1900 }
1901 break;
1902 }
1903 }
1904 if (!found) {
1905 if (!value)
1906 return;
1907 elems.emplace_back(name, value);
1908 }
1909 if (mustSort) {
1910 DictionaryAttr::sortInPlace(elems);
1911 }
1912 auto newDict = DictionaryAttr::getWithSorted(ctx, elems);
1913 setAttributionAttrs(op, index, newDict, attrsName);
1914}
1915
1916void GPUFuncOp::setWorkgroupAttributionAttr(unsigned index, StringAttr name,
1917 Attribute value) {
1918 assert(index < getNumWorkgroupAttributions() &&
1919 "index must map to a workgroup attribution");
1920 setAttributionAttr(*this, index, name, value,
1921 getWorkgroupAttribAttrsAttrName());
1922}
1923
1924void GPUFuncOp::setPrivateAttributionAttr(unsigned index, StringAttr name,
1925 Attribute value) {
1926 assert(index < getNumPrivateAttributions() &&
1927 "index must map to a private attribution");
1928 setAttributionAttr(*this, index, name, value,
1929 getPrivateAttribAttrsAttrName());
1930}
1931
1932LogicalResult GPUFuncOp::verifyType() {
1933 if (isKernel() && getFunctionType().getNumResults() != 0)
1934 return emitOpError() << "expected void return type for kernel function";
1935
1936 return success();
1937}
1938
1939/// Verifies the body of the function.
1940LogicalResult GPUFuncOp::verifyBody() {
1941 if (empty())
1942 return emitOpError() << "expected body with at least one block";
1943 unsigned numFuncArguments = getNumArguments();
1944 unsigned numWorkgroupAttributions = getNumWorkgroupAttributions();
1945 unsigned numBlockArguments = front().getNumArguments();
1946 if (numBlockArguments < numFuncArguments + numWorkgroupAttributions)
1947 return emitOpError() << "expected at least "
1948 << numFuncArguments + numWorkgroupAttributions
1949 << " arguments to body region";
1950
1951 ArrayRef<Type> funcArgTypes = getFunctionType().getInputs();
1952 for (unsigned i = 0; i < numFuncArguments; ++i) {
1953 Type blockArgType = front().getArgument(i).getType();
1954 if (funcArgTypes[i] != blockArgType)
1955 return emitOpError() << "expected body region argument #" << i
1956 << " to be of type " << funcArgTypes[i] << ", got "
1957 << blockArgType;
1958 }
1959
1960 if (failed(verifyAttributions(getOperation(), getWorkgroupAttributionBBArgs(),
1961 GPUDialect::getWorkgroupAddressSpace())) ||
1962 failed(verifyAttributions(getOperation(), getPrivateAttributions(),
1963 GPUDialect::getPrivateAddressSpace())))
1964 return failure();
1965
1966 return success();
1967}
1968
1969//===----------------------------------------------------------------------===//
1970// ReturnOp
1971//===----------------------------------------------------------------------===//
1972
1973LogicalResult gpu::ReturnOp::verify() {
1974 GPUFuncOp function = (*this)->getParentOfType<GPUFuncOp>();
1975
1976 FunctionType funType = function.getFunctionType();
1977
1978 if (funType.getNumResults() != getOperands().size())
1979 return emitOpError()
1980 .append("expected ", funType.getNumResults(), " result operands")
1981 .attachNote(function.getLoc())
1982 .append("return type declared here");
1983
1984 for (const auto &pair : llvm::enumerate(
1985 llvm::zip(function.getFunctionType().getResults(), getOperands()))) {
1986 auto [type, operand] = pair.value();
1987 if (type != operand.getType())
1988 return emitOpError() << "unexpected type `" << operand.getType()
1989 << "' for operand #" << pair.index();
1990 }
1991 return success();
1992}
1993
1994//===----------------------------------------------------------------------===//
1995// GPUModuleOp
1996//===----------------------------------------------------------------------===//
1997
1998void GPUModuleOp::build(OpBuilder &builder, OperationState &result,
1999 StringRef name, ArrayAttr targets,
2000 Attribute offloadingHandler) {
2001 result.addRegion()->emplaceBlock();
2002 Properties &props = result.getOrAddProperties<Properties>();
2003 if (targets)
2004 props.targets = targets;
2005 props.setSymName(builder.getStringAttr(name));
2006 props.offloadingHandler = offloadingHandler;
2007}
2008
2009void GPUModuleOp::build(OpBuilder &builder, OperationState &result,
2010 StringRef name, ArrayRef<Attribute> targets,
2011 Attribute offloadingHandler) {
2012 build(builder, result, name,
2013 targets.empty() ? ArrayAttr() : builder.getArrayAttr(targets),
2014 offloadingHandler);
2015}
2016
2017bool GPUModuleOp::hasTarget(Attribute target) {
2018 if (ArrayAttr targets = getTargetsAttr())
2019 return llvm::count(targets.getValue(), target);
2020 return false;
2021}
2022
2023void GPUModuleOp::setTargets(ArrayRef<TargetAttrInterface> targets) {
2024 ArrayAttr &targetsAttr = getProperties().targets;
2025 SmallVector<Attribute> targetsVector(targets);
2026 targetsAttr = ArrayAttr::get(getContext(), targetsVector);
2027}
2028
2029LogicalResult GPUModuleOp::verify() {
2030 auto targets = getOperation()->getAttrOfType<ArrayAttr>("targets");
2031
2032 if (!targets)
2033 return success();
2034
2035 for (auto target : targets) {
2036 if (auto verifyTargetAttr =
2037 llvm::dyn_cast<TargetAttrVerifyInterface>(target)) {
2038 if (verifyTargetAttr.verifyTarget(getOperation()).failed())
2039 return failure();
2040 }
2041 }
2042 return success();
2043}
2044
2045//===----------------------------------------------------------------------===//
2046// GPUBinaryOp
2047//===----------------------------------------------------------------------===//
2048void BinaryOp::build(OpBuilder &builder, OperationState &result, StringRef name,
2049 Attribute offloadingHandler, ArrayAttr objects) {
2050 auto &properties = result.getOrAddProperties<Properties>();
2051 result.attributes.push_back(builder.getNamedAttr(
2053 properties.objects = objects;
2054 if (offloadingHandler)
2055 properties.offloadingHandler = offloadingHandler;
2056 else
2057 properties.offloadingHandler = builder.getAttr<SelectObjectAttr>(nullptr);
2058}
2059
2060void BinaryOp::build(OpBuilder &builder, OperationState &result, StringRef name,
2061 Attribute offloadingHandler, ArrayRef<Attribute> objects) {
2062 build(builder, result, name, offloadingHandler,
2063 objects.empty() ? ArrayAttr() : builder.getArrayAttr(objects));
2064}
2065
2066static ParseResult parseOffloadingHandler(OpAsmParser &parser,
2067 Attribute &offloadingHandler) {
2068 if (succeeded(parser.parseOptionalLess())) {
2069 if (parser.parseAttribute(offloadingHandler))
2070 return failure();
2071 if (parser.parseGreater())
2072 return failure();
2073 }
2074 if (!offloadingHandler)
2075 offloadingHandler = parser.getBuilder().getAttr<SelectObjectAttr>(nullptr);
2076 return success();
2077}
2078
2080 Attribute offloadingHandler) {
2081 if (offloadingHandler != SelectObjectAttr::get(op->getContext(), nullptr))
2082 printer << '<' << offloadingHandler << '>';
2083}
2084
2085//===----------------------------------------------------------------------===//
2086// GPUMemcpyOp
2087//===----------------------------------------------------------------------===//
2088
2089LogicalResult MemcpyOp::verify() {
2090 auto srcType = getSrc().getType();
2091 auto dstType = getDst().getType();
2092
2093 if (getElementTypeOrSelf(srcType) != getElementTypeOrSelf(dstType))
2094 return emitOpError("arguments have incompatible element type");
2095
2096 if (failed(verifyCompatibleShape(srcType, dstType)))
2097 return emitOpError("arguments have incompatible shape");
2098
2099 return success();
2100}
2101
2102namespace {
2103
2104/// Erases a common case of copy ops where a destination value is used only by
2105/// the copy op, alloc and dealloc ops.
2106struct EraseTrivialCopyOp : public OpRewritePattern<MemcpyOp> {
2107 using OpRewritePattern<MemcpyOp>::OpRewritePattern;
2108
2109 LogicalResult matchAndRewrite(MemcpyOp op,
2110 PatternRewriter &rewriter) const override {
2111 Value dest = op.getDst();
2112 Operation *destDefOp = dest.getDefiningOp();
2113 // `dest` must be defined by an op having Allocate memory effect in order to
2114 // perform the folding.
2115 if (!destDefOp ||
2117 return failure();
2118 // We can erase `op` iff `dest` has no other use apart from its
2119 // use by `op` and dealloc ops.
2120 if (llvm::any_of(dest.getUsers(), [op, dest](Operation *user) {
2121 return user != op &&
2122 !hasSingleEffect<MemoryEffects::Free>(user, dest);
2123 }))
2124 return failure();
2125 // We can perform the folding if and only if op has a single async
2126 // dependency and produces an async token as result, or if it does not have
2127 // any async dependency and does not produce any async token result.
2128 if (op.getAsyncDependencies().size() > 1 ||
2129 ((op.getAsyncDependencies().empty() && op.getAsyncToken()) ||
2130 (!op.getAsyncDependencies().empty() && !op.getAsyncToken())))
2131 return failure();
2132 rewriter.replaceOp(op, op.getAsyncDependencies());
2133 return success();
2134 }
2135};
2136
2137} // end anonymous namespace
2138
2139void MemcpyOp::getCanonicalizationPatterns(RewritePatternSet &results,
2140 MLIRContext *context) {
2141 results.add<EraseTrivialCopyOp>(context);
2142}
2143
2144//===----------------------------------------------------------------------===//
2145// GPU_SubgroupMmaLoadMatrixOp
2146//===----------------------------------------------------------------------===//
2147
2148LogicalResult SubgroupMmaLoadMatrixOp::verify() {
2149 auto srcType = getSrcMemref().getType();
2150 auto resType = getRes().getType();
2151 auto resMatrixType = llvm::cast<gpu::MMAMatrixType>(resType);
2152 auto operand = resMatrixType.getOperand();
2153 auto srcMemrefType = llvm::cast<MemRefType>(srcType);
2154
2155 if (!srcMemrefType.isLastDimUnitStride())
2156 return emitError(
2157 "expected source memref most minor dim must have unit stride");
2158
2159 if (operand != "AOp" && operand != "BOp" && operand != "COp")
2160 return emitError("only AOp, BOp and COp can be loaded");
2161
2162 return success();
2163}
2164
2165//===----------------------------------------------------------------------===//
2166// GPU_SubgroupMmaStoreMatrixOp
2167//===----------------------------------------------------------------------===//
2168
2169LogicalResult SubgroupMmaStoreMatrixOp::verify() {
2170 auto srcType = getSrc().getType();
2171 auto dstType = getDstMemref().getType();
2172 auto srcMatrixType = llvm::cast<gpu::MMAMatrixType>(srcType);
2173 auto dstMemrefType = llvm::cast<MemRefType>(dstType);
2174
2175 if (!dstMemrefType.isLastDimUnitStride())
2176 return emitError(
2177 "expected destination memref most minor dim must have unit stride");
2178
2179 if (srcMatrixType.getOperand() != "COp")
2180 return emitError(
2181 "expected the operand matrix being stored to have 'COp' operand type");
2182
2183 return success();
2184}
2185
2186//===----------------------------------------------------------------------===//
2187// GPU_SubgroupMmaComputeOp
2188//===----------------------------------------------------------------------===//
2189
2190LogicalResult SubgroupMmaComputeOp::verify() {
2191 enum OperandMap { A, B, C };
2192 SmallVector<MMAMatrixType, 3> opTypes;
2193 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpA().getType()));
2194 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpB().getType()));
2195 opTypes.push_back(llvm::cast<MMAMatrixType>(getOpC().getType()));
2196
2197 if (opTypes[A].getOperand() != "AOp" || opTypes[B].getOperand() != "BOp" ||
2198 opTypes[C].getOperand() != "COp")
2199 return emitError("operands must be in the order AOp, BOp, COp");
2200
2201 ArrayRef<int64_t> aShape, bShape, cShape;
2202 aShape = opTypes[A].getShape();
2203 bShape = opTypes[B].getShape();
2204 cShape = opTypes[C].getShape();
2205
2206 if (aShape[1] != bShape[0] || aShape[0] != cShape[0] ||
2207 bShape[1] != cShape[1])
2208 return emitError("operand shapes do not satisfy matmul constraints");
2209
2210 return success();
2211}
2212
2213LogicalResult MemcpyOp::fold(FoldAdaptor adaptor,
2214 SmallVectorImpl<::mlir::OpFoldResult> &results) {
2215 return memref::foldMemRefCast(*this);
2216}
2217
2218LogicalResult MemsetOp::fold(FoldAdaptor adaptor,
2219 SmallVectorImpl<::mlir::OpFoldResult> &results) {
2220 return memref::foldMemRefCast(*this);
2221}
2222
2223//===----------------------------------------------------------------------===//
2224// GPU_WaitOp
2225//===----------------------------------------------------------------------===//
2226
2227namespace {
2228
2229/// Remove gpu.wait op use of gpu.wait op def without async dependencies.
2230/// %t = gpu.wait async [] // No async dependencies.
2231/// ... gpu.wait ... [%t, ...] // %t can be removed.
2232struct EraseRedundantGpuWaitOpPairs : public OpRewritePattern<WaitOp> {
2233public:
2235
2236 LogicalResult matchAndRewrite(WaitOp op,
2237 PatternRewriter &rewriter) const final {
2238 auto predicate = [](Value value) {
2239 auto waitOp = value.getDefiningOp<WaitOp>();
2240 return waitOp && waitOp->getNumOperands() == 0;
2241 };
2242 if (llvm::none_of(op.getAsyncDependencies(), predicate))
2243 return failure();
2244 SmallVector<Value> validOperands;
2245 for (Value operand : op->getOperands()) {
2246 if (predicate(operand))
2247 continue;
2248 validOperands.push_back(operand);
2249 }
2250 rewriter.modifyOpInPlace(op, [&]() { op->setOperands(validOperands); });
2251 return success();
2252 }
2253};
2254
2255/// Simplify trivial gpu.wait ops for the following patterns.
2256/// 1. %t = gpu.wait async ... ops, where %t has no uses (regardless of async
2257/// dependencies).
2258/// 2. %t1 = gpu.wait async [%t0], in this case, we can replace uses of %t1 with
2259/// %t0.
2260/// 3. gpu.wait [] ops, i.e gpu.wait ops that neither have any async
2261/// dependencies nor return any token.
2262struct SimplifyGpuWaitOp : public OpRewritePattern<WaitOp> {
2263public:
2265
2266 LogicalResult matchAndRewrite(WaitOp op,
2267 PatternRewriter &rewriter) const final {
2268 // Erase gpu.wait ops that neither have any async dependencies nor return
2269 // any async token.
2270 if (op.getAsyncDependencies().empty() && !op.getAsyncToken()) {
2271 rewriter.eraseOp(op);
2272 return success();
2273 }
2274 // Replace uses of %t1 = gpu.wait async [%t0] ops with %t0 and erase the op.
2275 if (llvm::hasSingleElement(op.getAsyncDependencies()) &&
2276 op.getAsyncToken()) {
2277 rewriter.replaceOp(op, op.getAsyncDependencies());
2278 return success();
2279 }
2280 // Erase %t = gpu.wait async ... ops, where %t has no uses.
2281 if (op.getAsyncToken() && op.getAsyncToken().use_empty()) {
2282 rewriter.eraseOp(op);
2283 return success();
2284 }
2285 return failure();
2286 }
2287};
2288
2289} // end anonymous namespace
2290
2291void WaitOp::getCanonicalizationPatterns(RewritePatternSet &results,
2292 MLIRContext *context) {
2293 results.add<EraseRedundantGpuWaitOpPairs, SimplifyGpuWaitOp>(context);
2294}
2295
2296//===----------------------------------------------------------------------===//
2297// GPU_AllocOp
2298//===----------------------------------------------------------------------===//
2299
2300LogicalResult AllocOp::verify() {
2301 auto memRefType = llvm::cast<MemRefType>(getMemref().getType());
2302
2303 if (failed(verifyDynamicDimensionCount(getOperation(), memRefType,
2304 getDynamicSizes())))
2305 return failure();
2306
2307 unsigned numSymbols = 0;
2308 if (!memRefType.getLayout().isIdentity())
2309 numSymbols = memRefType.getLayout().getAffineMap().getNumSymbols();
2310 if (getSymbolOperands().size() != numSymbols) {
2311 return emitOpError(
2312 "symbol operand count does not equal memref symbol count");
2313 }
2314
2315 return success();
2316}
2317
2318namespace {
2319
2320/// Folding of memref.dim(gpu.alloc(%size), %idx) -> %size similar to
2321/// `memref::AllocOp`.
2322struct SimplifyDimOfAllocOp : public OpRewritePattern<memref::DimOp> {
2323 using OpRewritePattern<memref::DimOp>::OpRewritePattern;
2324
2325 LogicalResult matchAndRewrite(memref::DimOp dimOp,
2326 PatternRewriter &rewriter) const override {
2327 std::optional<int64_t> index = dimOp.getConstantIndex();
2328 if (!index)
2329 return failure();
2330
2331 int64_t indexVal = index.value();
2332 auto memrefType = llvm::dyn_cast<MemRefType>(dimOp.getSource().getType());
2333 if (!memrefType || indexVal < 0 || indexVal >= memrefType.getRank() ||
2334 !memrefType.isDynamicDim(indexVal))
2335 return failure();
2336
2337 auto alloc = dimOp.getSource().getDefiningOp<AllocOp>();
2338 if (!alloc)
2339 return failure();
2340
2341 Value substituteOp = *(alloc.getDynamicSizes().begin() +
2342 memrefType.getDynamicDimIndex(indexVal));
2343 rewriter.replaceOp(dimOp, substituteOp);
2344 return success();
2345 }
2346};
2347
2348} // namespace
2349
2350void AllocOp::getCanonicalizationPatterns(RewritePatternSet &results,
2351 MLIRContext *context) {
2352 results.add<SimplifyDimOfAllocOp>(context);
2353}
2354
2355//===----------------------------------------------------------------------===//
2356// GPU object attribute
2357//===----------------------------------------------------------------------===//
2358
2359LogicalResult ObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2360 Attribute target, CompilationTarget format,
2361 StringAttr object, DictionaryAttr properties,
2362 KernelTableAttr kernels) {
2363 if (!target)
2364 return emitError() << "the target attribute cannot be null";
2365 if (target.hasPromiseOrImplementsInterface<TargetAttrInterface>())
2366 return success();
2367 return emitError() << "the target attribute must implement or promise the "
2368 "`gpu::TargetAttrInterface`";
2369}
2370
2371namespace {
2372ParseResult parseObject(AsmParser &odsParser, CompilationTarget &format,
2373 StringAttr &object) {
2374 std::optional<CompilationTarget> formatResult;
2375 StringRef enumKeyword;
2376 auto loc = odsParser.getCurrentLocation();
2377 if (failed(odsParser.parseOptionalKeyword(&enumKeyword)))
2378 formatResult = CompilationTarget::Fatbin;
2379 if (!formatResult &&
2380 (formatResult =
2381 gpu::symbolizeEnum<gpu::CompilationTarget>(enumKeyword)) &&
2382 odsParser.parseEqual())
2383 return odsParser.emitError(loc, "expected an equal sign");
2384 if (!formatResult)
2385 return odsParser.emitError(loc, "expected keyword for GPU object format");
2386 FailureOr<StringAttr> objectResult =
2387 FieldParser<StringAttr>::parse(odsParser);
2388 if (failed(objectResult))
2389 return odsParser.emitError(odsParser.getCurrentLocation(),
2390 "failed to parse GPU_ObjectAttr parameter "
2391 "'object' which is to be a `StringAttr`");
2392 format = *formatResult;
2393 object = *objectResult;
2394 return success();
2395}
2396
2397void printObject(AsmPrinter &odsParser, CompilationTarget format,
2398 StringAttr object) {
2399 if (format != CompilationTarget::Fatbin)
2400 odsParser << stringifyEnum(format) << " = ";
2401 odsParser << object;
2402}
2403} // namespace
2404
2405//===----------------------------------------------------------------------===//
2406// GPU select object attribute
2407//===----------------------------------------------------------------------===//
2408
2409LogicalResult
2410gpu::SelectObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2411 Attribute target) {
2412 // Check `target`, it can be null, an integer attr or a GPU Target attribute.
2413 if (target) {
2414 if (auto intAttr = mlir::dyn_cast<IntegerAttr>(target)) {
2415 if (intAttr.getInt() < 0) {
2416 return emitError() << "the object index must be positive";
2417 }
2418 } else if (!target.hasPromiseOrImplementsInterface<TargetAttrInterface>()) {
2419 return emitError()
2420 << "the target attribute must be a GPU Target attribute";
2421 }
2422 }
2423 return success();
2424}
2425
2426//===----------------------------------------------------------------------===//
2427// DynamicSharedMemoryOp
2428//===----------------------------------------------------------------------===//
2429
2430LogicalResult gpu::DynamicSharedMemoryOp::verify() {
2431 if (!getOperation()->getParentWithTrait<OpTrait::SymbolTable>())
2432 return emitOpError() << "must be inside an op with symbol table";
2433
2434 MemRefType memrefType = getResultMemref().getType();
2435 // Check address space
2436 if (!GPUDialect::hasWorkgroupMemoryAddressSpace(memrefType)) {
2437 return emitOpError() << "address space must be "
2438 << gpu::AddressSpaceAttr::getMnemonic() << "<"
2439 << stringifyEnum(gpu::AddressSpace::Workgroup) << ">";
2440 }
2441 if (memrefType.hasStaticShape()) {
2442 return emitOpError() << "result memref type must be memref<?xi8, "
2443 "#gpu.address_space<workgroup>>";
2444 }
2445 return success();
2446}
2447
2448//===----------------------------------------------------------------------===//
2449// GPU WarpExecuteOnLane0Op
2450//===----------------------------------------------------------------------===//
2451
2452void WarpExecuteOnLane0Op::print(OpAsmPrinter &p) {
2453 p << "(" << getLaneid() << ")";
2454
2455 SmallVector<StringRef> coreAttr = {getWarpSizeAttrName()};
2456 auto warpSizeAttr = getOperation()->getAttr(getWarpSizeAttrName());
2457 p << "[" << llvm::cast<IntegerAttr>(warpSizeAttr).getInt() << "]";
2458
2459 if (!getArgs().empty())
2460 p << " args(" << getArgs() << " : " << getArgs().getTypes() << ")";
2461 if (!getResults().empty())
2462 p << " -> (" << getResults().getTypes() << ')';
2463 p << " ";
2464 p.printRegion(getRegion(),
2465 /*printEntryBlockArgs=*/true,
2466 /*printBlockTerminators=*/!getResults().empty());
2467 p.printOptionalAttrDict(getOperation()->getAttrs(), coreAttr);
2468}
2469
2470ParseResult WarpExecuteOnLane0Op::parse(OpAsmParser &parser,
2471 OperationState &result) {
2472 // Create the region.
2473 result.regions.reserve(1);
2474 Region *warpRegion = result.addRegion();
2475
2476 auto &builder = parser.getBuilder();
2477 OpAsmParser::UnresolvedOperand laneId;
2478
2479 // Parse predicate operand.
2480 if (parser.parseLParen() ||
2481 parser.parseOperand(laneId, /*allowResultNumber=*/false) ||
2482 parser.parseRParen())
2483 return failure();
2484
2485 int64_t warpSize;
2486 if (parser.parseLSquare() || parser.parseInteger(warpSize) ||
2487 parser.parseRSquare())
2488 return failure();
2489 result.addAttribute(getWarpSizeAttrName(OperationName(getOperationName(),
2490 builder.getContext())),
2491 builder.getI64IntegerAttr(warpSize));
2492
2493 if (parser.resolveOperand(laneId, builder.getIndexType(), result.operands))
2494 return failure();
2495
2496 llvm::SMLoc inputsOperandsLoc;
2497 SmallVector<OpAsmParser::UnresolvedOperand> inputsOperands;
2498 SmallVector<Type> inputTypes;
2499 if (succeeded(parser.parseOptionalKeyword("args"))) {
2500 if (parser.parseLParen())
2501 return failure();
2502
2503 inputsOperandsLoc = parser.getCurrentLocation();
2504 if (parser.parseOperandList(inputsOperands) ||
2505 parser.parseColonTypeList(inputTypes) || parser.parseRParen())
2506 return failure();
2507 }
2508 if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
2509 result.operands))
2510 return failure();
2511
2512 // Parse optional results type list.
2513 if (parser.parseOptionalArrowTypeList(result.types))
2514 return failure();
2515 // Parse the region.
2516 if (parser.parseRegion(*warpRegion, /*arguments=*/{},
2517 /*argTypes=*/{}))
2518 return failure();
2519 WarpExecuteOnLane0Op::ensureTerminator(*warpRegion, builder, result.location);
2520
2521 // Parse the optional attribute list.
2522 if (parser.parseOptionalAttrDict(result.attributes))
2523 return failure();
2524 return success();
2525}
2526
2527void WarpExecuteOnLane0Op::getSuccessorRegions(
2528 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
2529 if (!point.isParent()) {
2530 regions.push_back(RegionSuccessor(getOperation()));
2531 return;
2532 }
2533
2534 // The warp region is always executed
2535 regions.push_back(RegionSuccessor(&getWarpRegion()));
2536}
2537
2538ValueRange WarpExecuteOnLane0Op::getSuccessorInputs(RegionSuccessor successor) {
2539 return successor.isOperation() ? ValueRange(getResults()) : ValueRange();
2540}
2541void WarpExecuteOnLane0Op::build(OpBuilder &builder, OperationState &result,
2542 TypeRange resultTypes, Value laneId,
2543 int64_t warpSize) {
2544 build(builder, result, resultTypes, laneId, warpSize,
2545 /*operands=*/{}, /*argTypes=*/{});
2546}
2547
2548void WarpExecuteOnLane0Op::build(OpBuilder &builder, OperationState &result,
2549 TypeRange resultTypes, Value laneId,
2550 int64_t warpSize, ValueRange args,
2551 TypeRange blockArgTypes) {
2552 result.addOperands(laneId);
2553 result.addAttribute(getAttributeNames()[0],
2554 builder.getI64IntegerAttr(warpSize));
2555 result.addTypes(resultTypes);
2556 result.addOperands(args);
2557 assert(args.size() == blockArgTypes.size());
2558 OpBuilder::InsertionGuard guard(builder);
2559 Region *warpRegion = result.addRegion();
2560 Block *block = builder.createBlock(warpRegion);
2561 for (auto [type, arg] : llvm::zip_equal(blockArgTypes, args))
2562 block->addArgument(type, arg.getLoc());
2563}
2564
2565/// Helper check if the distributed vector type is consistent with the expanded
2566/// type and distributed size.
2567static LogicalResult verifyDistributedType(Type expanded, Type distributed,
2568 int64_t warpSize, Operation *op) {
2569 // If the types matches there is no distribution.
2570 if (expanded == distributed)
2571 return success();
2572 auto expandedVecType = llvm::dyn_cast<VectorType>(expanded);
2573 auto distributedVecType = llvm::dyn_cast<VectorType>(distributed);
2574 if (!expandedVecType || !distributedVecType)
2575 return op->emitOpError("expected vector type for distributed operands.");
2576 if (expandedVecType.getRank() != distributedVecType.getRank() ||
2577 expandedVecType.getElementType() != distributedVecType.getElementType())
2578 return op->emitOpError(
2579 "expected distributed vectors to have same rank and element type.");
2580
2581 SmallVector<int64_t> scales(expandedVecType.getRank(), 1);
2582 for (int64_t i = 0, e = expandedVecType.getRank(); i < e; i++) {
2583 int64_t eDim = expandedVecType.getDimSize(i);
2584 int64_t dDim = distributedVecType.getDimSize(i);
2585 if (eDim == dDim)
2586 continue;
2587 if (eDim % dDim != 0)
2588 return op->emitOpError()
2589 << "expected expanded vector dimension #" << i << " (" << eDim
2590 << ") to be a multipler of the distributed vector dimension ("
2591 << dDim << ")";
2592 scales[i] = eDim / dDim;
2593 }
2594 if (llvm::product_of(scales) != warpSize)
2595 return op->emitOpError()
2596 << "incompatible distribution dimensions from " << expandedVecType
2597 << " to " << distributedVecType << " with warp size = " << warpSize;
2598
2599 return success();
2600}
2601
2602LogicalResult WarpExecuteOnLane0Op::verify() {
2603 if (getArgs().size() != getWarpRegion().getNumArguments())
2604 return emitOpError(
2605 "expected same number op arguments and block arguments.");
2606 auto yield = dyn_cast<gpu::YieldOp>(getBody()->getTerminator());
2607 if (!yield)
2608 return emitOpError("expected body to be terminated with 'gpu.yield'");
2609 if (yield.getNumOperands() != getNumResults())
2610 return emitOpError(
2611 "expected same number of yield operands and return values.");
2612 int64_t warpSize = getWarpSize();
2613 for (auto [regionArg, arg] :
2614 llvm::zip_equal(getWarpRegion().getArguments(), getArgs())) {
2615 if (failed(verifyDistributedType(regionArg.getType(), arg.getType(),
2616 warpSize, getOperation())))
2617 return failure();
2618 }
2619 for (auto [yieldOperand, result] :
2620 llvm::zip_equal(yield.getOperands(), getResults())) {
2621 if (failed(verifyDistributedType(yieldOperand.getType(), result.getType(),
2622 warpSize, getOperation())))
2623 return failure();
2624 }
2625 return success();
2626}
2627bool WarpExecuteOnLane0Op::areTypesCompatible(Type lhs, Type rhs) {
2628 return succeeded(
2629 verifyDistributedType(lhs, rhs, getWarpSize(), getOperation()));
2630}
2631
2632gpu::YieldOp WarpExecuteOnLane0Op::getTerminator() {
2633 return cast<gpu::YieldOp>(getBody()->getTerminator());
2634}
2635
2636//===----------------------------------------------------------------------===//
2637// GPU_SubgroupBroadcastOp
2638//===----------------------------------------------------------------------===//
2639
2640void gpu::SubgroupBroadcastOp::inferResultRanges(
2641 ArrayRef<ConstantIntRanges> argRanges, SetIntRangeFn setResultRange) {
2642 setResultRange(getResult(), argRanges.front());
2643}
2644
2645Speculation::Speculatability gpu::SubgroupBroadcastOp::getSpeculatability() {
2646 switch (getBroadcastType()) {
2647 case BroadcastType::first_active_lane:
2648 // Cannot speculate first_lane broadcast, because speculating it across
2649 // control flow can change the active lanes.
2651 case BroadcastType::specific_lane:
2652 // Speculation should be safe as long as we inside structured control flow.
2654 }
2655 llvm_unreachable("Unknown BroadcastType");
2656}
2657
2658LogicalResult gpu::SubgroupBroadcastOp::verify() {
2659 switch (getBroadcastType()) {
2660 case BroadcastType::first_active_lane:
2661 if (getLane())
2662 return emitOpError()
2663 << "lane can only be specified for `specific_lane` broadcast";
2664 return success();
2665 case BroadcastType::specific_lane:
2666 if (!getLane())
2667 return emitOpError()
2668 << "lane must be specified for `specific_lane` broadcast";
2669 return success();
2670 }
2671 llvm_unreachable("Unknown BroadcastType");
2672}
2673
2674OpFoldResult gpu::SubgroupBroadcastOp::fold(FoldAdaptor /*adaptor*/) {
2675 // Broadcast result is always uniform.
2676 if (auto prev = getSrc().getDefiningOp<SubgroupBroadcastOp>())
2677 return prev.getResult();
2678
2679 return nullptr;
2680}
2681
2682//===----------------------------------------------------------------------===//
2683// GPU_BallotOp
2684//===----------------------------------------------------------------------===//
2685
2686// No custom implementations needed; ballot uses default behavior from ODS.
2687
2688//===----------------------------------------------------------------------===//
2689// GPU KernelMetadataAttr
2690//===----------------------------------------------------------------------===//
2691
2692KernelMetadataAttr KernelMetadataAttr::get(FunctionOpInterface kernel,
2693 DictionaryAttr metadata) {
2694 assert(kernel && "invalid kernel");
2695 return get(kernel.getNameAttr(), kernel.getFunctionType(),
2696 kernel.getAllArgAttrs(), metadata);
2697}
2698
2699KernelMetadataAttr
2700KernelMetadataAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
2701 FunctionOpInterface kernel,
2702 DictionaryAttr metadata) {
2703 assert(kernel && "invalid kernel");
2704 return getChecked(emitError, kernel.getNameAttr(), kernel.getFunctionType(),
2705 kernel.getAllArgAttrs(), metadata);
2706}
2707
2708KernelMetadataAttr
2709KernelMetadataAttr::appendMetadata(ArrayRef<NamedAttribute> attrs) const {
2710 if (attrs.empty())
2711 return *this;
2712 NamedAttrList attrList;
2713 if (DictionaryAttr dict = getMetadata())
2714 attrList.append(dict);
2715 attrList.append(attrs);
2716 return KernelMetadataAttr::get(getName(), getFunctionType(), getArgAttrs(),
2717 attrList.getDictionary(getContext()));
2718}
2719
2720LogicalResult
2721KernelMetadataAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2722 StringAttr name, Type functionType,
2723 ArrayAttr argAttrs, DictionaryAttr metadata) {
2724 if (name.empty())
2725 return emitError() << "the kernel name can't be empty";
2726 if (argAttrs) {
2727 if (llvm::any_of(argAttrs, [](Attribute attr) {
2728 return !llvm::isa<DictionaryAttr>(attr);
2729 }))
2730 return emitError()
2731 << "all attributes in the array must be a dictionary attribute";
2732 }
2733 return success();
2734}
2735
2736//===----------------------------------------------------------------------===//
2737// GPU KernelTableAttr
2738//===----------------------------------------------------------------------===//
2739
2740KernelTableAttr KernelTableAttr::get(MLIRContext *context,
2741 ArrayRef<KernelMetadataAttr> kernels,
2742 bool isSorted) {
2743 // Note that `is_sorted` is always only invoked once even with assertions ON.
2744 assert((!isSorted || llvm::is_sorted(kernels)) &&
2745 "expected a sorted kernel array");
2746 // Immediately return the attribute if the array is sorted.
2747 if (isSorted || llvm::is_sorted(kernels))
2748 return Base::get(context, kernels);
2749 // Sort the array.
2750 SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
2751 llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
2752 return Base::get(context, kernelsTmp);
2753}
2754
2755KernelTableAttr KernelTableAttr::getChecked(
2756 function_ref<InFlightDiagnostic()> emitError, MLIRContext *context,
2757 ArrayRef<KernelMetadataAttr> kernels, bool isSorted) {
2758 // Note that `is_sorted` is always only invoked once even with assertions ON.
2759 assert((!isSorted || llvm::is_sorted(kernels)) &&
2760 "expected a sorted kernel array");
2761 // Immediately return the attribute if the array is sorted.
2762 if (isSorted || llvm::is_sorted(kernels))
2763 return Base::getChecked(emitError, context, kernels);
2764 // Sort the array.
2765 SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
2766 llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
2767 return Base::getChecked(emitError, context, kernelsTmp);
2768}
2769
2770LogicalResult
2771KernelTableAttr::verify(function_ref<InFlightDiagnostic()> emitError,
2772 ArrayRef<KernelMetadataAttr> kernels) {
2773 if (kernels.size() < 2)
2774 return success();
2775 // Check that the kernels are uniquely named.
2776 if (std::adjacent_find(kernels.begin(), kernels.end(),
2777 [](KernelMetadataAttr l, KernelMetadataAttr r) {
2778 return l.getName() == r.getName();
2779 }) != kernels.end()) {
2780 return emitError() << "expected all kernels to be uniquely named";
2781 }
2782 return success();
2783}
2784
2785KernelMetadataAttr KernelTableAttr::lookup(StringRef key) const {
2786 auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
2787 return found ? *iterator : KernelMetadataAttr();
2788}
2789
2790KernelMetadataAttr KernelTableAttr::lookup(StringAttr key) const {
2791 auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
2792 return found ? *iterator : KernelMetadataAttr();
2793}
2794
2795//===----------------------------------------------------------------------===//
2796// GPU target options
2797//===----------------------------------------------------------------------===//
2798
2813
2831
2832TypeID TargetOptions::getTypeID() const { return typeID; }
2833
2834StringRef TargetOptions::getToolkitPath() const { return toolkitPath; }
2835
2839
2840StringRef TargetOptions::getCmdOptions() const { return cmdOptions; }
2841
2842StringRef TargetOptions::getELFSection() const { return elfSection; }
2843
2847
2848function_ref<void(llvm::Module &)>
2852
2853function_ref<void(llvm::Module &)>
2857
2858function_ref<void(llvm::Module &)>
2862
2864 return isaCallback;
2865}
2866
2867CompilationTarget TargetOptions::getCompilationTarget() const {
2868 return compilationTarget;
2869}
2870
2872 return CompilationTarget::Fatbin;
2873}
2874
2875std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2877 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> options;
2878 llvm::StringSaver stringSaver(options.first);
2879 StringRef opts = cmdOptions;
2880 // For a correct tokenization of the command line options `opts` must be
2881 // unquoted, otherwise the tokenization function returns a single string: the
2882 // unquoted `cmdOptions` -which is not the desired behavior.
2883 // Remove any quotes if they are at the beginning and end of the string:
2884 if (!opts.empty() && opts.front() == '"' && opts.back() == '"')
2885 opts.consume_front("\""), opts.consume_back("\"");
2886 if (!opts.empty() && opts.front() == '\'' && opts.back() == '\'')
2887 opts.consume_front("'"), opts.consume_back("'");
2888#ifdef _WIN32
2889 llvm::cl::TokenizeWindowsCommandLine(opts, stringSaver, options.second,
2890 /*MarkEOLs=*/false);
2891#else
2892 llvm::cl::TokenizeGNUCommandLine(opts, stringSaver, options.second,
2893 /*MarkEOLs=*/false);
2894#endif // _WIN32
2895 return options;
2896}
2897
2898std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2902
2903std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
2905 size_t startPos = cmdOptions.find(startsWith);
2906 if (startPos == std::string::npos)
2907 return {llvm::BumpPtrAllocator(), SmallVector<const char *>()};
2908
2909 auto tokenized =
2910 tokenizeCmdOptions(cmdOptions.substr(startPos + startsWith.size()));
2911 cmdOptions.resize(startPos);
2912 return tokenized;
2913}
2914
2916
2917#include "mlir/Dialect/GPU/IR/GPUOpInterfaces.cpp.inc"
2918#include "mlir/Dialect/GPU/IR/GPUOpsEnums.cpp.inc"
2919
2920#define GET_ATTRDEF_CLASSES
2921#include "mlir/Dialect/GPU/IR/GPUOpsAttributes.cpp.inc"
2922
2923#define GET_OP_CLASSES
2924#include "mlir/Dialect/GPU/IR/GPUOps.cpp.inc"
2925
2926#include "mlir/Dialect/GPU/IR/CompilationAttrInterfaces.cpp.inc"
return success()
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 ParseResult parseAllReduceOperation(AsmParser &parser, AllReduceOperationAttr &attr)
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 void printAllReduceOperation(AsmPrinter &printer, Operation *op, AllReduceOperationAttr attr)
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
This base class exposes generic asm parser hooks, usable across the various derived parsers.
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.
This base class exposes generic asm printer hooks, usable across the various derived printers.
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 insertOperands(unsigned index, ValueRange operands)
Insert the given operands into the operand list at the given 'index'.
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:575
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
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...
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition Operation.h:607
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 StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition SymbolTable.h:76
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:397
MMAMatrix represents a matrix held by a subgroup for matrix-matrix multiply accumulate operations.
Definition GPUDialect.h:139
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:717
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:307
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