brintos

brintos / llvm-project-archived public Read only

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