4688 lines · cpp
1//===- OpenACC.cpp - OpenACC MLIR Operations ------------------------------===//2//3// Part of the MLIR 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#include "mlir/Dialect/OpenACC/OpenACC.h"10#include "mlir/Dialect/Arith/IR/Arith.h"11#include "mlir/Dialect/LLVMIR/LLVMDialect.h"12#include "mlir/Dialect/LLVMIR/LLVMTypes.h"13#include "mlir/Dialect/MemRef/IR/MemRef.h"14#include "mlir/IR/Builders.h"15#include "mlir/IR/BuiltinAttributes.h"16#include "mlir/IR/BuiltinTypes.h"17#include "mlir/IR/DialectImplementation.h"18#include "mlir/IR/Matchers.h"19#include "mlir/IR/OpImplementation.h"20#include "mlir/IR/SymbolTable.h"21#include "mlir/Support/LLVM.h"22#include "mlir/Transforms/DialectConversion.h"23#include "llvm/ADT/SmallSet.h"24#include "llvm/ADT/TypeSwitch.h"25#include "llvm/Support/LogicalResult.h"26#include <variant>27 28using namespace mlir;29using namespace acc;30 31#include "mlir/Dialect/OpenACC/OpenACCOpsDialect.cpp.inc"32#include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc"33#include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.cpp.inc"34#include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.cpp.inc"35#include "mlir/Dialect/OpenACCMPCommon/Interfaces/OpenACCMPOpsInterfaces.cpp.inc"36 37namespace {38 39static bool isScalarLikeType(Type type) {40 return type.isIntOrIndexOrFloat() || isa<ComplexType>(type);41}42 43/// Helper function to attach the `VarName` attribute to an operation44/// if a variable name is provided.45static void attachVarNameAttr(Operation *op, OpBuilder &builder,46 StringRef varName) {47 if (!varName.empty()) {48 auto varNameAttr = acc::VarNameAttr::get(builder.getContext(), varName);49 op->setAttr(acc::getVarNameAttrName(), varNameAttr);50 }51}52 53template <typename T>54struct MemRefPointerLikeModel55 : public PointerLikeType::ExternalModel<MemRefPointerLikeModel<T>, T> {56 Type getElementType(Type pointer) const {57 return cast<T>(pointer).getElementType();58 }59 60 mlir::acc::VariableTypeCategory61 getPointeeTypeCategory(Type pointer, TypedValue<PointerLikeType> varPtr,62 Type varType) const {63 if (auto mappableTy = dyn_cast<MappableType>(varType)) {64 return mappableTy.getTypeCategory(varPtr);65 }66 auto memrefTy = cast<T>(pointer);67 if (!memrefTy.hasRank()) {68 // This memref is unranked - aka it could have any rank, including a69 // rank of 0 which could mean scalar. For now, return uncategorized.70 return mlir::acc::VariableTypeCategory::uncategorized;71 }72 73 if (memrefTy.getRank() == 0) {74 if (isScalarLikeType(memrefTy.getElementType())) {75 return mlir::acc::VariableTypeCategory::scalar;76 }77 // Zero-rank non-scalar - need further analysis to determine the type78 // category. For now, return uncategorized.79 return mlir::acc::VariableTypeCategory::uncategorized;80 }81 82 // It has a rank - must be an array.83 assert(memrefTy.getRank() > 0 && "rank expected to be positive");84 return mlir::acc::VariableTypeCategory::array;85 }86 87 mlir::Value genAllocate(Type pointer, OpBuilder &builder, Location loc,88 StringRef varName, Type varType, Value originalVar,89 bool &needsFree) const {90 auto memrefTy = cast<MemRefType>(pointer);91 92 // Check if this is a static memref (all dimensions are known) - if yes93 // then we can generate an alloca operation.94 if (memrefTy.hasStaticShape()) {95 needsFree = false; // alloca doesn't need deallocation96 auto allocaOp = memref::AllocaOp::create(builder, loc, memrefTy);97 attachVarNameAttr(allocaOp, builder, varName);98 return allocaOp.getResult();99 }100 101 // For dynamic memrefs, extract sizes from the original variable if102 // provided. Otherwise they cannot be handled.103 if (originalVar && originalVar.getType() == memrefTy &&104 memrefTy.hasRank()) {105 SmallVector<Value> dynamicSizes;106 for (int64_t i = 0; i < memrefTy.getRank(); ++i) {107 if (memrefTy.isDynamicDim(i)) {108 // Extract the size of dimension i from the original variable109 auto indexValue = arith::ConstantIndexOp::create(builder, loc, i);110 auto dimSize =111 memref::DimOp::create(builder, loc, originalVar, indexValue);112 dynamicSizes.push_back(dimSize);113 }114 // Note: We only add dynamic sizes to the dynamicSizes array115 // Static dimensions are handled automatically by AllocOp116 }117 needsFree = true; // alloc needs deallocation118 auto allocOp =119 memref::AllocOp::create(builder, loc, memrefTy, dynamicSizes);120 attachVarNameAttr(allocOp, builder, varName);121 return allocOp.getResult();122 }123 124 // TODO: Unranked not yet supported.125 return {};126 }127 128 bool genFree(Type pointer, OpBuilder &builder, Location loc,129 TypedValue<PointerLikeType> varToFree, Value allocRes,130 Type varType) const {131 if (auto memrefValue = dyn_cast<TypedValue<MemRefType>>(varToFree)) {132 // Use allocRes if provided to determine the allocation type133 Value valueToInspect = allocRes ? allocRes : memrefValue;134 135 // Walk through casts to find the original allocation136 Value currentValue = valueToInspect;137 Operation *originalAlloc = nullptr;138 139 // Follow the chain of operations to find the original allocation140 // even if a casted result is provided.141 while (currentValue) {142 if (auto *definingOp = currentValue.getDefiningOp()) {143 // Check if this is an allocation operation144 if (isa<memref::AllocOp, memref::AllocaOp>(definingOp)) {145 originalAlloc = definingOp;146 break;147 }148 149 // Check if this is a cast operation we can look through150 if (auto castOp = dyn_cast<memref::CastOp>(definingOp)) {151 currentValue = castOp.getSource();152 continue;153 }154 155 // Check for other cast-like operations156 if (auto reinterpretCastOp =157 dyn_cast<memref::ReinterpretCastOp>(definingOp)) {158 currentValue = reinterpretCastOp.getSource();159 continue;160 }161 162 // If we can't look through this operation, stop163 break;164 }165 // This is a block argument or similar - can't trace further.166 break;167 }168 169 if (originalAlloc) {170 if (isa<memref::AllocaOp>(originalAlloc)) {171 // This is an alloca - no dealloc needed, but return true (success)172 return true;173 }174 if (isa<memref::AllocOp>(originalAlloc)) {175 // This is an alloc - generate dealloc on varToFree176 memref::DeallocOp::create(builder, loc, memrefValue);177 return true;178 }179 }180 }181 182 return false;183 }184 185 bool genCopy(Type pointer, OpBuilder &builder, Location loc,186 TypedValue<PointerLikeType> destination,187 TypedValue<PointerLikeType> source, Type varType) const {188 // Generate a copy operation between two memrefs189 auto destMemref = dyn_cast_if_present<TypedValue<MemRefType>>(destination);190 auto srcMemref = dyn_cast_if_present<TypedValue<MemRefType>>(source);191 192 // As per memref documentation, source and destination must have same193 // element type and shape in order to be compatible. We do not want to fail194 // with an IR verification error - thus check that before generating the195 // copy operation.196 if (destMemref && srcMemref &&197 destMemref.getType().getElementType() ==198 srcMemref.getType().getElementType() &&199 destMemref.getType().getShape() == srcMemref.getType().getShape()) {200 memref::CopyOp::create(builder, loc, srcMemref, destMemref);201 return true;202 }203 204 return false;205 }206};207 208struct LLVMPointerPointerLikeModel209 : public PointerLikeType::ExternalModel<LLVMPointerPointerLikeModel,210 LLVM::LLVMPointerType> {211 Type getElementType(Type pointer) const { return Type(); }212};213 214struct MemrefAddressOfGlobalModel215 : public AddressOfGlobalOpInterface::ExternalModel<216 MemrefAddressOfGlobalModel, memref::GetGlobalOp> {217 SymbolRefAttr getSymbol(Operation *op) const {218 auto getGlobalOp = cast<memref::GetGlobalOp>(op);219 return getGlobalOp.getNameAttr();220 }221};222 223struct MemrefGlobalVariableModel224 : public GlobalVariableOpInterface::ExternalModel<MemrefGlobalVariableModel,225 memref::GlobalOp> {226 bool isConstant(Operation *op) const {227 auto globalOp = cast<memref::GlobalOp>(op);228 return globalOp.getConstant();229 }230 231 Region *getInitRegion(Operation *op) const {232 // GlobalOp uses attributes for initialization, not regions233 return nullptr;234 }235};236 237/// Helper function for any of the times we need to modify an ArrayAttr based on238/// a device type list. Returns a new ArrayAttr with all of the239/// existingDeviceTypes, plus the effective new ones(or an added none if hte new240/// list is empty).241mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(242 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,243 llvm::ArrayRef<acc::DeviceType> newDeviceTypes) {244 llvm::SmallVector<mlir::Attribute> deviceTypes;245 if (existingDeviceTypes)246 llvm::copy(existingDeviceTypes, std::back_inserter(deviceTypes));247 248 if (newDeviceTypes.empty())249 deviceTypes.push_back(250 acc::DeviceTypeAttr::get(context, acc::DeviceType::None));251 252 for (DeviceType dt : newDeviceTypes)253 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));254 255 return mlir::ArrayAttr::get(context, deviceTypes);256}257 258/// Helper function for any of the times we need to add operands that are259/// affected by a device type list. Returns a new ArrayAttr with all of the260/// existingDeviceTypes, plus the effective new ones (or an added none, if the261/// new list is empty). Additionally, adds the arguments to the argCollection262/// the correct number of times. This will also update a 'segments' array, even263/// if it won't be used.264mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(265 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,266 llvm::ArrayRef<acc::DeviceType> newDeviceTypes, mlir::ValueRange arguments,267 mlir::MutableOperandRange argCollection,268 llvm::SmallVector<int32_t> &segments) {269 llvm::SmallVector<mlir::Attribute> deviceTypes;270 if (existingDeviceTypes)271 llvm::copy(existingDeviceTypes, std::back_inserter(deviceTypes));272 273 if (newDeviceTypes.empty()) {274 argCollection.append(arguments);275 segments.push_back(arguments.size());276 deviceTypes.push_back(277 acc::DeviceTypeAttr::get(context, acc::DeviceType::None));278 }279 280 for (DeviceType dt : newDeviceTypes) {281 argCollection.append(arguments);282 segments.push_back(arguments.size());283 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));284 }285 286 return mlir::ArrayAttr::get(context, deviceTypes);287}288 289/// Overload for when the 'segments' aren't needed.290mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(291 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,292 llvm::ArrayRef<acc::DeviceType> newDeviceTypes, mlir::ValueRange arguments,293 mlir::MutableOperandRange argCollection) {294 llvm::SmallVector<int32_t> segments;295 return addDeviceTypeAffectedOperandHelper(context, existingDeviceTypes,296 newDeviceTypes, arguments,297 argCollection, segments);298}299} // namespace300 301//===----------------------------------------------------------------------===//302// OpenACC operations303//===----------------------------------------------------------------------===//304 305void OpenACCDialect::initialize() {306 addOperations<307#define GET_OP_LIST308#include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"309 >();310 addAttributes<311#define GET_ATTRDEF_LIST312#include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"313 >();314 addTypes<315#define GET_TYPEDEF_LIST316#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"317 >();318 319 // By attaching interfaces here, we make the OpenACC dialect dependent on320 // the other dialects. This is probably better than having dialects like LLVM321 // and memref be dependent on OpenACC.322 MemRefType::attachInterface<MemRefPointerLikeModel<MemRefType>>(323 *getContext());324 UnrankedMemRefType::attachInterface<325 MemRefPointerLikeModel<UnrankedMemRefType>>(*getContext());326 LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(327 *getContext());328 329 // Attach operation interfaces330 memref::GetGlobalOp::attachInterface<MemrefAddressOfGlobalModel>(331 *getContext());332 memref::GlobalOp::attachInterface<MemrefGlobalVariableModel>(*getContext());333}334 335//===----------------------------------------------------------------------===//336// device_type support helpers337//===----------------------------------------------------------------------===//338 339static bool hasDeviceTypeValues(std::optional<mlir::ArrayAttr> arrayAttr) {340 return arrayAttr && *arrayAttr && arrayAttr->size() > 0;341}342 343static bool hasDeviceType(std::optional<mlir::ArrayAttr> arrayAttr,344 mlir::acc::DeviceType deviceType) {345 if (!hasDeviceTypeValues(arrayAttr))346 return false;347 348 for (auto attr : *arrayAttr) {349 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);350 if (deviceTypeAttr.getValue() == deviceType)351 return true;352 }353 354 return false;355}356 357static void printDeviceTypes(mlir::OpAsmPrinter &p,358 std::optional<mlir::ArrayAttr> deviceTypes) {359 if (!hasDeviceTypeValues(deviceTypes))360 return;361 362 p << "[";363 llvm::interleaveComma(*deviceTypes, p,364 [&](mlir::Attribute attr) { p << attr; });365 p << "]";366}367 368static std::optional<unsigned> findSegment(ArrayAttr segments,369 mlir::acc::DeviceType deviceType) {370 unsigned segmentIdx = 0;371 for (auto attr : segments) {372 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);373 if (deviceTypeAttr.getValue() == deviceType)374 return std::make_optional(segmentIdx);375 ++segmentIdx;376 }377 return std::nullopt;378}379 380static mlir::Operation::operand_range381getValuesFromSegments(std::optional<mlir::ArrayAttr> arrayAttr,382 mlir::Operation::operand_range range,383 std::optional<llvm::ArrayRef<int32_t>> segments,384 mlir::acc::DeviceType deviceType) {385 if (!arrayAttr)386 return range.take_front(0);387 if (auto pos = findSegment(*arrayAttr, deviceType)) {388 int32_t nbOperandsBefore = 0;389 for (unsigned i = 0; i < *pos; ++i)390 nbOperandsBefore += (*segments)[i];391 return range.drop_front(nbOperandsBefore).take_front((*segments)[*pos]);392 }393 return range.take_front(0);394}395 396static mlir::Value397getWaitDevnumValue(std::optional<mlir::ArrayAttr> deviceTypeAttr,398 mlir::Operation::operand_range operands,399 std::optional<llvm::ArrayRef<int32_t>> segments,400 std::optional<mlir::ArrayAttr> hasWaitDevnum,401 mlir::acc::DeviceType deviceType) {402 if (!hasDeviceTypeValues(deviceTypeAttr))403 return {};404 if (auto pos = findSegment(*deviceTypeAttr, deviceType))405 if (hasWaitDevnum->getValue()[*pos])406 return getValuesFromSegments(deviceTypeAttr, operands, segments,407 deviceType)408 .front();409 return {};410}411 412static mlir::Operation::operand_range413getWaitValuesWithoutDevnum(std::optional<mlir::ArrayAttr> deviceTypeAttr,414 mlir::Operation::operand_range operands,415 std::optional<llvm::ArrayRef<int32_t>> segments,416 std::optional<mlir::ArrayAttr> hasWaitDevnum,417 mlir::acc::DeviceType deviceType) {418 auto range =419 getValuesFromSegments(deviceTypeAttr, operands, segments, deviceType);420 if (range.empty())421 return range;422 if (auto pos = findSegment(*deviceTypeAttr, deviceType)) {423 if (hasWaitDevnum && *hasWaitDevnum) {424 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasWaitDevnum)[*pos]);425 if (boolAttr.getValue())426 return range.drop_front(1); // first value is devnum427 }428 }429 return range;430}431 432template <typename Op>433static LogicalResult checkWaitAndAsyncConflict(Op op) {434 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();435 ++dtypeInt) {436 auto dtype = static_cast<acc::DeviceType>(dtypeInt);437 438 // The asyncOnly attribute represent the async clause without value.439 // Therefore the attribute and operand cannot appear at the same time.440 if (hasDeviceType(op.getAsyncOperandsDeviceType(), dtype) &&441 op.hasAsyncOnly(dtype))442 return op.emitError(443 "asyncOnly attribute cannot appear with asyncOperand");444 445 // The wait attribute represent the wait clause without values. Therefore446 // the attribute and operands cannot appear at the same time.447 if (hasDeviceType(op.getWaitOperandsDeviceType(), dtype) &&448 op.hasWaitOnly(dtype))449 return op.emitError("wait attribute cannot appear with waitOperands");450 }451 return success();452}453 454template <typename Op>455static LogicalResult checkVarAndVarType(Op op) {456 if (!op.getVar())457 return op.emitError("must have var operand");458 459 // A variable must have a type that is either pointer-like or mappable.460 if (!mlir::isa<mlir::acc::PointerLikeType>(op.getVar().getType()) &&461 !mlir::isa<mlir::acc::MappableType>(op.getVar().getType()))462 return op.emitError("var must be mappable or pointer-like");463 464 // When it is a pointer-like type, the varType must capture the target type.465 if (mlir::isa<mlir::acc::PointerLikeType>(op.getVar().getType()) &&466 op.getVarType() == op.getVar().getType())467 return op.emitError("varType must capture the element type of var");468 469 return success();470}471 472template <typename Op>473static LogicalResult checkVarAndAccVar(Op op) {474 if (op.getVar().getType() != op.getAccVar().getType())475 return op.emitError("input and output types must match");476 477 return success();478}479 480template <typename Op>481static LogicalResult checkNoModifier(Op op) {482 if (op.getModifiers() != acc::DataClauseModifier::none)483 return op.emitError("no data clause modifiers are allowed");484 return success();485}486 487template <typename Op>488static LogicalResult489checkValidModifier(Op op, acc::DataClauseModifier validModifiers) {490 if (acc::bitEnumContainsAny(op.getModifiers(), ~validModifiers))491 return op.emitError(492 "invalid data clause modifiers: " +493 acc::stringifyDataClauseModifier(op.getModifiers() & ~validModifiers));494 495 return success();496}497 498template <typename OpT, typename RecipeOpT>499static LogicalResult checkRecipe(OpT op, llvm::StringRef operandName) {500 // Mappable types do not need a recipe because it is possible to generate one501 // from its API. Reject reductions though because no API is available for them502 // at this time.503 if (mlir::acc::isMappableType(op.getVar().getType()) &&504 !std::is_same_v<OpT, acc::ReductionOp>)505 return success();506 507 mlir::SymbolRefAttr operandRecipe = op.getRecipeAttr();508 if (!operandRecipe)509 return op->emitOpError() << "recipe expected for " << operandName;510 511 auto decl =512 SymbolTable::lookupNearestSymbolFrom<RecipeOpT>(op, operandRecipe);513 if (!decl)514 return op->emitOpError()515 << "expected symbol reference " << operandRecipe << " to point to a "516 << operandName << " declaration";517 return success();518}519 520static ParseResult parseVar(mlir::OpAsmParser &parser,521 OpAsmParser::UnresolvedOperand &var) {522 // Either `var` or `varPtr` keyword is required.523 if (failed(parser.parseOptionalKeyword("varPtr"))) {524 if (failed(parser.parseKeyword("var")))525 return failure();526 }527 if (failed(parser.parseLParen()))528 return failure();529 if (failed(parser.parseOperand(var)))530 return failure();531 532 return success();533}534 535static void printVar(mlir::OpAsmPrinter &p, mlir::Operation *op,536 mlir::Value var) {537 if (mlir::isa<mlir::acc::PointerLikeType>(var.getType()))538 p << "varPtr(";539 else540 p << "var(";541 p.printOperand(var);542}543 544static ParseResult parseAccVar(mlir::OpAsmParser &parser,545 OpAsmParser::UnresolvedOperand &var,546 mlir::Type &accVarType) {547 // Either `accVar` or `accPtr` keyword is required.548 if (failed(parser.parseOptionalKeyword("accPtr"))) {549 if (failed(parser.parseKeyword("accVar")))550 return failure();551 }552 if (failed(parser.parseLParen()))553 return failure();554 if (failed(parser.parseOperand(var)))555 return failure();556 if (failed(parser.parseColon()))557 return failure();558 if (failed(parser.parseType(accVarType)))559 return failure();560 if (failed(parser.parseRParen()))561 return failure();562 563 return success();564}565 566static void printAccVar(mlir::OpAsmPrinter &p, mlir::Operation *op,567 mlir::Value accVar, mlir::Type accVarType) {568 if (mlir::isa<mlir::acc::PointerLikeType>(accVar.getType()))569 p << "accPtr(";570 else571 p << "accVar(";572 p.printOperand(accVar);573 p << " : ";574 p.printType(accVarType);575 p << ")";576}577 578static ParseResult parseVarPtrType(mlir::OpAsmParser &parser,579 mlir::Type &varPtrType,580 mlir::TypeAttr &varTypeAttr) {581 if (failed(parser.parseType(varPtrType)))582 return failure();583 if (failed(parser.parseRParen()))584 return failure();585 586 if (succeeded(parser.parseOptionalKeyword("varType"))) {587 if (failed(parser.parseLParen()))588 return failure();589 mlir::Type varType;590 if (failed(parser.parseType(varType)))591 return failure();592 varTypeAttr = mlir::TypeAttr::get(varType);593 if (failed(parser.parseRParen()))594 return failure();595 } else {596 // Set `varType` from the element type of the type of `varPtr`.597 if (mlir::isa<mlir::acc::PointerLikeType>(varPtrType))598 varTypeAttr = mlir::TypeAttr::get(599 mlir::cast<mlir::acc::PointerLikeType>(varPtrType).getElementType());600 else601 varTypeAttr = mlir::TypeAttr::get(varPtrType);602 }603 604 return success();605}606 607static void printVarPtrType(mlir::OpAsmPrinter &p, mlir::Operation *op,608 mlir::Type varPtrType, mlir::TypeAttr varTypeAttr) {609 p.printType(varPtrType);610 p << ")";611 612 // Print the `varType` only if it differs from the element type of613 // `varPtr`'s type.614 mlir::Type varType = varTypeAttr.getValue();615 mlir::Type typeToCheckAgainst =616 mlir::isa<mlir::acc::PointerLikeType>(varPtrType)617 ? mlir::cast<mlir::acc::PointerLikeType>(varPtrType).getElementType()618 : varPtrType;619 if (typeToCheckAgainst != varType) {620 p << " varType(";621 p.printType(varType);622 p << ")";623 }624}625 626static ParseResult parseRecipeSym(mlir::OpAsmParser &parser,627 mlir::SymbolRefAttr &recipeAttr) {628 if (failed(parser.parseAttribute(recipeAttr)))629 return failure();630 return success();631}632 633static void printRecipeSym(mlir::OpAsmPrinter &p, mlir::Operation *op,634 mlir::SymbolRefAttr recipeAttr) {635 p << recipeAttr;636}637 638//===----------------------------------------------------------------------===//639// DataBoundsOp640//===----------------------------------------------------------------------===//641LogicalResult acc::DataBoundsOp::verify() {642 auto extent = getExtent();643 auto upperbound = getUpperbound();644 if (!extent && !upperbound)645 return emitError("expected extent or upperbound.");646 return success();647}648 649//===----------------------------------------------------------------------===//650// PrivateOp651//===----------------------------------------------------------------------===//652LogicalResult acc::PrivateOp::verify() {653 if (getDataClause() != acc::DataClause::acc_private)654 return emitError(655 "data clause associated with private operation must match its intent");656 if (failed(checkVarAndVarType(*this)))657 return failure();658 if (failed(checkNoModifier(*this)))659 return failure();660 if (failed(661 checkRecipe<acc::PrivateOp, acc::PrivateRecipeOp>(*this, "private")))662 return failure();663 return success();664}665 666//===----------------------------------------------------------------------===//667// FirstprivateOp668//===----------------------------------------------------------------------===//669LogicalResult acc::FirstprivateOp::verify() {670 if (getDataClause() != acc::DataClause::acc_firstprivate)671 return emitError("data clause associated with firstprivate operation must "672 "match its intent");673 if (failed(checkVarAndVarType(*this)))674 return failure();675 if (failed(checkNoModifier(*this)))676 return failure();677 if (failed(checkRecipe<acc::FirstprivateOp, acc::FirstprivateRecipeOp>(678 *this, "firstprivate")))679 return failure();680 return success();681}682 683//===----------------------------------------------------------------------===//684// FirstprivateMapInitialOp685//===----------------------------------------------------------------------===//686LogicalResult acc::FirstprivateMapInitialOp::verify() {687 if (getDataClause() != acc::DataClause::acc_firstprivate)688 return emitError("data clause associated with firstprivate operation must "689 "match its intent");690 if (failed(checkVarAndVarType(*this)))691 return failure();692 if (failed(checkNoModifier(*this)))693 return failure();694 return success();695}696 697//===----------------------------------------------------------------------===//698// ReductionOp699//===----------------------------------------------------------------------===//700LogicalResult acc::ReductionOp::verify() {701 if (getDataClause() != acc::DataClause::acc_reduction)702 return emitError("data clause associated with reduction operation must "703 "match its intent");704 if (failed(checkVarAndVarType(*this)))705 return failure();706 if (failed(checkNoModifier(*this)))707 return failure();708 if (failed(checkRecipe<acc::ReductionOp, acc::ReductionRecipeOp>(709 *this, "reduction")))710 return failure();711 return success();712}713 714//===----------------------------------------------------------------------===//715// DevicePtrOp716//===----------------------------------------------------------------------===//717LogicalResult acc::DevicePtrOp::verify() {718 if (getDataClause() != acc::DataClause::acc_deviceptr)719 return emitError("data clause associated with deviceptr operation must "720 "match its intent");721 if (failed(checkVarAndVarType(*this)))722 return failure();723 if (failed(checkVarAndAccVar(*this)))724 return failure();725 if (failed(checkNoModifier(*this)))726 return failure();727 return success();728}729 730//===----------------------------------------------------------------------===//731// PresentOp732//===----------------------------------------------------------------------===//733LogicalResult acc::PresentOp::verify() {734 if (getDataClause() != acc::DataClause::acc_present)735 return emitError(736 "data clause associated with present operation must match its intent");737 if (failed(checkVarAndVarType(*this)))738 return failure();739 if (failed(checkVarAndAccVar(*this)))740 return failure();741 if (failed(checkNoModifier(*this)))742 return failure();743 return success();744}745 746//===----------------------------------------------------------------------===//747// CopyinOp748//===----------------------------------------------------------------------===//749LogicalResult acc::CopyinOp::verify() {750 // Test for all clauses this operation can be decomposed from:751 if (!getImplicit() && getDataClause() != acc::DataClause::acc_copyin &&752 getDataClause() != acc::DataClause::acc_copyin_readonly &&753 getDataClause() != acc::DataClause::acc_copy &&754 getDataClause() != acc::DataClause::acc_reduction)755 return emitError(756 "data clause associated with copyin operation must match its intent"757 " or specify original clause this operation was decomposed from");758 if (failed(checkVarAndVarType(*this)))759 return failure();760 if (failed(checkVarAndAccVar(*this)))761 return failure();762 if (failed(checkValidModifier(*this, acc::DataClauseModifier::readonly |763 acc::DataClauseModifier::always |764 acc::DataClauseModifier::capture)))765 return failure();766 return success();767}768 769bool acc::CopyinOp::isCopyinReadonly() {770 return getDataClause() == acc::DataClause::acc_copyin_readonly ||771 acc::bitEnumContainsAny(getModifiers(),772 acc::DataClauseModifier::readonly);773}774 775//===----------------------------------------------------------------------===//776// CreateOp777//===----------------------------------------------------------------------===//778LogicalResult acc::CreateOp::verify() {779 // Test for all clauses this operation can be decomposed from:780 if (getDataClause() != acc::DataClause::acc_create &&781 getDataClause() != acc::DataClause::acc_create_zero &&782 getDataClause() != acc::DataClause::acc_copyout &&783 getDataClause() != acc::DataClause::acc_copyout_zero)784 return emitError(785 "data clause associated with create operation must match its intent"786 " or specify original clause this operation was decomposed from");787 if (failed(checkVarAndVarType(*this)))788 return failure();789 if (failed(checkVarAndAccVar(*this)))790 return failure();791 // this op is the entry part of copyout, so it also needs to allow all792 // modifiers allowed on copyout.793 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |794 acc::DataClauseModifier::always |795 acc::DataClauseModifier::capture)))796 return failure();797 return success();798}799 800bool acc::CreateOp::isCreateZero() {801 // The zero modifier is encoded in the data clause.802 return getDataClause() == acc::DataClause::acc_create_zero ||803 getDataClause() == acc::DataClause::acc_copyout_zero ||804 acc::bitEnumContainsAny(getModifiers(), acc::DataClauseModifier::zero);805}806 807//===----------------------------------------------------------------------===//808// NoCreateOp809//===----------------------------------------------------------------------===//810LogicalResult acc::NoCreateOp::verify() {811 if (getDataClause() != acc::DataClause::acc_no_create)812 return emitError("data clause associated with no_create operation must "813 "match its intent");814 if (failed(checkVarAndVarType(*this)))815 return failure();816 if (failed(checkVarAndAccVar(*this)))817 return failure();818 if (failed(checkNoModifier(*this)))819 return failure();820 return success();821}822 823//===----------------------------------------------------------------------===//824// AttachOp825//===----------------------------------------------------------------------===//826LogicalResult acc::AttachOp::verify() {827 if (getDataClause() != acc::DataClause::acc_attach)828 return emitError(829 "data clause associated with attach operation must match its intent");830 if (failed(checkVarAndVarType(*this)))831 return failure();832 if (failed(checkVarAndAccVar(*this)))833 return failure();834 if (failed(checkNoModifier(*this)))835 return failure();836 return success();837}838 839//===----------------------------------------------------------------------===//840// DeclareDeviceResidentOp841//===----------------------------------------------------------------------===//842 843LogicalResult acc::DeclareDeviceResidentOp::verify() {844 if (getDataClause() != acc::DataClause::acc_declare_device_resident)845 return emitError("data clause associated with device_resident operation "846 "must match its intent");847 if (failed(checkVarAndVarType(*this)))848 return failure();849 if (failed(checkVarAndAccVar(*this)))850 return failure();851 if (failed(checkNoModifier(*this)))852 return failure();853 return success();854}855 856//===----------------------------------------------------------------------===//857// DeclareLinkOp858//===----------------------------------------------------------------------===//859 860LogicalResult acc::DeclareLinkOp::verify() {861 if (getDataClause() != acc::DataClause::acc_declare_link)862 return emitError(863 "data clause associated with link operation must match its intent");864 if (failed(checkVarAndVarType(*this)))865 return failure();866 if (failed(checkVarAndAccVar(*this)))867 return failure();868 if (failed(checkNoModifier(*this)))869 return failure();870 return success();871}872 873//===----------------------------------------------------------------------===//874// CopyoutOp875//===----------------------------------------------------------------------===//876LogicalResult acc::CopyoutOp::verify() {877 // Test for all clauses this operation can be decomposed from:878 if (getDataClause() != acc::DataClause::acc_copyout &&879 getDataClause() != acc::DataClause::acc_copyout_zero &&880 getDataClause() != acc::DataClause::acc_copy &&881 getDataClause() != acc::DataClause::acc_reduction)882 return emitError(883 "data clause associated with copyout operation must match its intent"884 " or specify original clause this operation was decomposed from");885 if (!getVar() || !getAccVar())886 return emitError("must have both host and device pointers");887 if (failed(checkVarAndVarType(*this)))888 return failure();889 if (failed(checkVarAndAccVar(*this)))890 return failure();891 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |892 acc::DataClauseModifier::always |893 acc::DataClauseModifier::capture)))894 return failure();895 return success();896}897 898bool acc::CopyoutOp::isCopyoutZero() {899 return getDataClause() == acc::DataClause::acc_copyout_zero ||900 acc::bitEnumContainsAny(getModifiers(), acc::DataClauseModifier::zero);901}902 903//===----------------------------------------------------------------------===//904// DeleteOp905//===----------------------------------------------------------------------===//906LogicalResult acc::DeleteOp::verify() {907 // Test for all clauses this operation can be decomposed from:908 if (getDataClause() != acc::DataClause::acc_delete &&909 getDataClause() != acc::DataClause::acc_create &&910 getDataClause() != acc::DataClause::acc_create_zero &&911 getDataClause() != acc::DataClause::acc_copyin &&912 getDataClause() != acc::DataClause::acc_copyin_readonly &&913 getDataClause() != acc::DataClause::acc_present &&914 getDataClause() != acc::DataClause::acc_no_create &&915 getDataClause() != acc::DataClause::acc_declare_device_resident &&916 getDataClause() != acc::DataClause::acc_declare_link)917 return emitError(918 "data clause associated with delete operation must match its intent"919 " or specify original clause this operation was decomposed from");920 if (!getAccVar())921 return emitError("must have device pointer");922 // This op is the exit part of copyin and create - thus allow all modifiers923 // allowed on either case.924 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |925 acc::DataClauseModifier::readonly |926 acc::DataClauseModifier::always |927 acc::DataClauseModifier::capture)))928 return failure();929 return success();930}931 932//===----------------------------------------------------------------------===//933// DetachOp934//===----------------------------------------------------------------------===//935LogicalResult acc::DetachOp::verify() {936 // Test for all clauses this operation can be decomposed from:937 if (getDataClause() != acc::DataClause::acc_detach &&938 getDataClause() != acc::DataClause::acc_attach)939 return emitError(940 "data clause associated with detach operation must match its intent"941 " or specify original clause this operation was decomposed from");942 if (!getAccVar())943 return emitError("must have device pointer");944 if (failed(checkNoModifier(*this)))945 return failure();946 return success();947}948 949//===----------------------------------------------------------------------===//950// HostOp951//===----------------------------------------------------------------------===//952LogicalResult acc::UpdateHostOp::verify() {953 // Test for all clauses this operation can be decomposed from:954 if (getDataClause() != acc::DataClause::acc_update_host &&955 getDataClause() != acc::DataClause::acc_update_self)956 return emitError(957 "data clause associated with host operation must match its intent"958 " or specify original clause this operation was decomposed from");959 if (!getVar() || !getAccVar())960 return emitError("must have both host and device pointers");961 if (failed(checkVarAndVarType(*this)))962 return failure();963 if (failed(checkVarAndAccVar(*this)))964 return failure();965 if (failed(checkNoModifier(*this)))966 return failure();967 return success();968}969 970//===----------------------------------------------------------------------===//971// DeviceOp972//===----------------------------------------------------------------------===//973LogicalResult acc::UpdateDeviceOp::verify() {974 // Test for all clauses this operation can be decomposed from:975 if (getDataClause() != acc::DataClause::acc_update_device)976 return emitError(977 "data clause associated with device operation must match its intent"978 " or specify original clause this operation was decomposed from");979 if (failed(checkVarAndVarType(*this)))980 return failure();981 if (failed(checkVarAndAccVar(*this)))982 return failure();983 if (failed(checkNoModifier(*this)))984 return failure();985 return success();986}987 988//===----------------------------------------------------------------------===//989// UseDeviceOp990//===----------------------------------------------------------------------===//991LogicalResult acc::UseDeviceOp::verify() {992 // Test for all clauses this operation can be decomposed from:993 if (getDataClause() != acc::DataClause::acc_use_device)994 return emitError(995 "data clause associated with use_device operation must match its intent"996 " or specify original clause this operation was decomposed from");997 if (failed(checkVarAndVarType(*this)))998 return failure();999 if (failed(checkVarAndAccVar(*this)))1000 return failure();1001 if (failed(checkNoModifier(*this)))1002 return failure();1003 return success();1004}1005 1006//===----------------------------------------------------------------------===//1007// CacheOp1008//===----------------------------------------------------------------------===//1009LogicalResult acc::CacheOp::verify() {1010 // Test for all clauses this operation can be decomposed from:1011 if (getDataClause() != acc::DataClause::acc_cache &&1012 getDataClause() != acc::DataClause::acc_cache_readonly)1013 return emitError(1014 "data clause associated with cache operation must match its intent"1015 " or specify original clause this operation was decomposed from");1016 if (failed(checkVarAndVarType(*this)))1017 return failure();1018 if (failed(checkVarAndAccVar(*this)))1019 return failure();1020 if (failed(checkValidModifier(*this, acc::DataClauseModifier::readonly)))1021 return failure();1022 return success();1023}1024 1025bool acc::CacheOp::isCacheReadonly() {1026 return getDataClause() == acc::DataClause::acc_cache_readonly ||1027 acc::bitEnumContainsAny(getModifiers(),1028 acc::DataClauseModifier::readonly);1029}1030 1031template <typename StructureOp>1032static ParseResult parseRegions(OpAsmParser &parser, OperationState &state,1033 unsigned nRegions = 1) {1034 1035 SmallVector<Region *, 2> regions;1036 for (unsigned i = 0; i < nRegions; ++i)1037 regions.push_back(state.addRegion());1038 1039 for (Region *region : regions)1040 if (parser.parseRegion(*region, /*arguments=*/{}, /*argTypes=*/{}))1041 return failure();1042 1043 return success();1044}1045 1046static bool isComputeOperation(Operation *op) {1047 return isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(op);1048}1049 1050namespace {1051/// Pattern to remove operation without region that have constant false `ifCond`1052/// and remove the condition from the operation if the `ifCond` is a true1053/// constant.1054template <typename OpTy>1055struct RemoveConstantIfCondition : public OpRewritePattern<OpTy> {1056 using OpRewritePattern<OpTy>::OpRewritePattern;1057 1058 LogicalResult matchAndRewrite(OpTy op,1059 PatternRewriter &rewriter) const override {1060 // Early return if there is no condition.1061 Value ifCond = op.getIfCond();1062 if (!ifCond)1063 return failure();1064 1065 IntegerAttr constAttr;1066 if (!matchPattern(ifCond, m_Constant(&constAttr)))1067 return failure();1068 if (constAttr.getInt())1069 rewriter.modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });1070 else1071 rewriter.eraseOp(op);1072 1073 return success();1074 }1075};1076 1077/// Replaces the given op with the contents of the given single-block region,1078/// using the operands of the block terminator to replace operation results.1079static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op,1080 Region ®ion, ValueRange blockArgs = {}) {1081 assert(region.hasOneBlock() && "expected single-block region");1082 Block *block = ®ion.front();1083 Operation *terminator = block->getTerminator();1084 ValueRange results = terminator->getOperands();1085 rewriter.inlineBlockBefore(block, op, blockArgs);1086 rewriter.replaceOp(op, results);1087 rewriter.eraseOp(terminator);1088}1089 1090/// Pattern to remove operation with region that have constant false `ifCond`1091/// and remove the condition from the operation if the `ifCond` is constant1092/// true.1093template <typename OpTy>1094struct RemoveConstantIfConditionWithRegion : public OpRewritePattern<OpTy> {1095 using OpRewritePattern<OpTy>::OpRewritePattern;1096 1097 LogicalResult matchAndRewrite(OpTy op,1098 PatternRewriter &rewriter) const override {1099 // Early return if there is no condition.1100 Value ifCond = op.getIfCond();1101 if (!ifCond)1102 return failure();1103 1104 IntegerAttr constAttr;1105 if (!matchPattern(ifCond, m_Constant(&constAttr)))1106 return failure();1107 if (constAttr.getInt())1108 rewriter.modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });1109 else1110 replaceOpWithRegion(rewriter, op, op.getRegion());1111 1112 return success();1113 }1114};1115 1116/// Remove empty acc.kernel_environment operations. If the operation has wait1117/// operands, create a acc.wait operation to preserve synchronization.1118struct RemoveEmptyKernelEnvironment1119 : public OpRewritePattern<acc::KernelEnvironmentOp> {1120 using OpRewritePattern<acc::KernelEnvironmentOp>::OpRewritePattern;1121 1122 LogicalResult matchAndRewrite(acc::KernelEnvironmentOp op,1123 PatternRewriter &rewriter) const override {1124 assert(op->getNumRegions() == 1 && "expected op to have one region");1125 1126 Block &block = op.getRegion().front();1127 if (!block.empty())1128 return failure();1129 1130 // Conservatively disable canonicalization of empty acc.kernel_environment1131 // operations if the wait operands in the kernel_environment cannot be fully1132 // represented by acc.wait operation.1133 1134 // Disable canonicalization if device type is not the default1135 if (auto deviceTypeAttr = op.getWaitOperandsDeviceTypeAttr()) {1136 for (auto attr : deviceTypeAttr) {1137 if (auto dtAttr = mlir::dyn_cast<acc::DeviceTypeAttr>(attr)) {1138 if (dtAttr.getValue() != mlir::acc::DeviceType::None)1139 return failure();1140 }1141 }1142 }1143 1144 // Disable canonicalization if any wait segment has a devnum1145 if (auto hasDevnumAttr = op.getHasWaitDevnumAttr()) {1146 for (auto attr : hasDevnumAttr) {1147 if (auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>(attr)) {1148 if (boolAttr.getValue())1149 return failure();1150 }1151 }1152 }1153 1154 // Disable canonicalization if there are multiple wait segments1155 if (auto segmentsAttr = op.getWaitOperandsSegmentsAttr()) {1156 if (segmentsAttr.size() > 1)1157 return failure();1158 }1159 1160 // Remove empty kernel environment.1161 // Preserve synchronization by creating acc.wait operation if needed.1162 if (!op.getWaitOperands().empty() || op.getWaitOnlyAttr())1163 rewriter.replaceOpWithNewOp<acc::WaitOp>(op, op.getWaitOperands(),1164 /*asyncOperand=*/Value(),1165 /*waitDevnum=*/Value(),1166 /*async=*/nullptr,1167 /*ifCond=*/Value());1168 else1169 rewriter.eraseOp(op);1170 1171 return success();1172 }1173};1174 1175//===----------------------------------------------------------------------===//1176// Recipe Region Helpers1177//===----------------------------------------------------------------------===//1178 1179/// Create and populate an init region for privatization recipes.1180/// Returns success if the region is populated, failure otherwise.1181/// Sets needsFree to indicate if the allocated memory requires deallocation.1182static LogicalResult createInitRegion(OpBuilder &builder, Location loc,1183 Region &initRegion, Type varType,1184 StringRef varName, ValueRange bounds,1185 bool &needsFree) {1186 // Create init block with arguments: original value + bounds1187 SmallVector<Type> argTypes{varType};1188 SmallVector<Location> argLocs{loc};1189 for (Value bound : bounds) {1190 argTypes.push_back(bound.getType());1191 argLocs.push_back(loc);1192 }1193 1194 Block *initBlock = builder.createBlock(&initRegion);1195 initBlock->addArguments(argTypes, argLocs);1196 builder.setInsertionPointToStart(initBlock);1197 1198 Value privatizedValue;1199 1200 // Get the block argument that represents the original variable1201 Value blockArgVar = initBlock->getArgument(0);1202 1203 // Generate init region body based on variable type1204 if (isa<MappableType>(varType)) {1205 auto mappableTy = cast<MappableType>(varType);1206 auto typedVar = cast<TypedValue<MappableType>>(blockArgVar);1207 privatizedValue = mappableTy.generatePrivateInit(1208 builder, loc, typedVar, varName, bounds, {}, needsFree);1209 if (!privatizedValue)1210 return failure();1211 } else {1212 assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");1213 auto pointerLikeTy = cast<PointerLikeType>(varType);1214 // Use PointerLikeType's allocation API with the block argument1215 privatizedValue = pointerLikeTy.genAllocate(builder, loc, varName, varType,1216 blockArgVar, needsFree);1217 if (!privatizedValue)1218 return failure();1219 }1220 1221 // Add yield operation to init block1222 acc::YieldOp::create(builder, loc, privatizedValue);1223 1224 return success();1225}1226 1227/// Create and populate a copy region for firstprivate recipes.1228/// Returns success if the region is populated, failure otherwise.1229/// TODO: Handle MappableType - it does not yet have a copy API.1230static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,1231 Region ©Region, Type varType,1232 ValueRange bounds) {1233 // Create copy block with arguments: original value + privatized value +1234 // bounds1235 SmallVector<Type> copyArgTypes{varType, varType};1236 SmallVector<Location> copyArgLocs{loc, loc};1237 for (Value bound : bounds) {1238 copyArgTypes.push_back(bound.getType());1239 copyArgLocs.push_back(loc);1240 }1241 1242 Block *copyBlock = builder.createBlock(©Region);1243 copyBlock->addArguments(copyArgTypes, copyArgLocs);1244 builder.setInsertionPointToStart(copyBlock);1245 1246 bool isMappable = isa<MappableType>(varType);1247 bool isPointerLike = isa<PointerLikeType>(varType);1248 // TODO: Handle MappableType - it does not yet have a copy API.1249 // Otherwise, for now just fallback to pointer-like behavior.1250 if (isMappable && !isPointerLike)1251 return failure();1252 1253 // Generate copy region body based on variable type1254 if (isPointerLike) {1255 auto pointerLikeTy = cast<PointerLikeType>(varType);1256 Value originalArg = copyBlock->getArgument(0);1257 Value privatizedArg = copyBlock->getArgument(1);1258 1259 // Generate copy operation using PointerLikeType interface1260 if (!pointerLikeTy.genCopy(1261 builder, loc, cast<TypedValue<PointerLikeType>>(privatizedArg),1262 cast<TypedValue<PointerLikeType>>(originalArg), varType))1263 return failure();1264 }1265 1266 // Add terminator to copy block1267 acc::TerminatorOp::create(builder, loc);1268 1269 return success();1270}1271 1272/// Create and populate a destroy region for privatization recipes.1273/// Returns success if the region is populated, failure otherwise.1274static LogicalResult createDestroyRegion(OpBuilder &builder, Location loc,1275 Region &destroyRegion, Type varType,1276 Value allocRes, ValueRange bounds) {1277 // Create destroy block with arguments: original value + privatized value +1278 // bounds1279 SmallVector<Type> destroyArgTypes{varType, varType};1280 SmallVector<Location> destroyArgLocs{loc, loc};1281 for (Value bound : bounds) {1282 destroyArgTypes.push_back(bound.getType());1283 destroyArgLocs.push_back(loc);1284 }1285 1286 Block *destroyBlock = builder.createBlock(&destroyRegion);1287 destroyBlock->addArguments(destroyArgTypes, destroyArgLocs);1288 builder.setInsertionPointToStart(destroyBlock);1289 1290 auto varToFree =1291 cast<TypedValue<PointerLikeType>>(destroyBlock->getArgument(1));1292 if (isa<MappableType>(varType)) {1293 auto mappableTy = cast<MappableType>(varType);1294 if (!mappableTy.generatePrivateDestroy(builder, loc, varToFree))1295 return failure();1296 } else {1297 assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");1298 auto pointerLikeTy = cast<PointerLikeType>(varType);1299 if (!pointerLikeTy.genFree(builder, loc, varToFree, allocRes, varType))1300 return failure();1301 }1302 1303 acc::TerminatorOp::create(builder, loc);1304 return success();1305}1306 1307} // namespace1308 1309//===----------------------------------------------------------------------===//1310// PrivateRecipeOp1311//===----------------------------------------------------------------------===//1312 1313static LogicalResult verifyInitLikeSingleArgRegion(1314 Operation *op, Region ®ion, StringRef regionType, StringRef regionName,1315 Type type, bool verifyYield, bool optional = false) {1316 if (optional && region.empty())1317 return success();1318 1319 if (region.empty())1320 return op->emitOpError() << "expects non-empty " << regionName << " region";1321 Block &firstBlock = region.front();1322 if (firstBlock.getNumArguments() < 1 ||1323 firstBlock.getArgument(0).getType() != type)1324 return op->emitOpError() << "expects " << regionName1325 << " region first "1326 "argument of the "1327 << regionType << " type";1328 1329 if (verifyYield) {1330 for (YieldOp yieldOp : region.getOps<acc::YieldOp>()) {1331 if (yieldOp.getOperands().size() != 1 ||1332 yieldOp.getOperands().getTypes()[0] != type)1333 return op->emitOpError() << "expects " << regionName1334 << " region to "1335 "yield a value of the "1336 << regionType << " type";1337 }1338 }1339 return success();1340}1341 1342LogicalResult acc::PrivateRecipeOp::verifyRegions() {1343 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(),1344 "privatization", "init", getType(),1345 /*verifyYield=*/false)))1346 return failure();1347 if (failed(verifyInitLikeSingleArgRegion(1348 *this, getDestroyRegion(), "privatization", "destroy", getType(),1349 /*verifyYield=*/false, /*optional=*/true)))1350 return failure();1351 return success();1352}1353 1354std::optional<PrivateRecipeOp>1355PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,1356 StringRef recipeName, Type varType,1357 StringRef varName, ValueRange bounds) {1358 // First, validate that we can handle this variable type1359 bool isMappable = isa<MappableType>(varType);1360 bool isPointerLike = isa<PointerLikeType>(varType);1361 1362 // Unsupported type1363 if (!isMappable && !isPointerLike)1364 return std::nullopt;1365 1366 OpBuilder::InsertionGuard guard(builder);1367 1368 // Create the recipe operation first so regions have proper parent context1369 auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType);1370 1371 // Populate the init region1372 bool needsFree = false;1373 if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType,1374 varName, bounds, needsFree))) {1375 recipe.erase();1376 return std::nullopt;1377 }1378 1379 // Only create destroy region if the allocation needs deallocation1380 if (needsFree) {1381 // Extract the allocated value from the init block's yield operation1382 auto yieldOp =1383 cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator());1384 Value allocRes = yieldOp.getOperand(0);1385 1386 if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),1387 varType, allocRes, bounds))) {1388 recipe.erase();1389 return std::nullopt;1390 }1391 }1392 1393 return recipe;1394}1395 1396//===----------------------------------------------------------------------===//1397// FirstprivateRecipeOp1398//===----------------------------------------------------------------------===//1399 1400LogicalResult acc::FirstprivateRecipeOp::verifyRegions() {1401 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(),1402 "privatization", "init", getType(),1403 /*verifyYield=*/false)))1404 return failure();1405 1406 if (getCopyRegion().empty())1407 return emitOpError() << "expects non-empty copy region";1408 1409 Block &firstBlock = getCopyRegion().front();1410 if (firstBlock.getNumArguments() < 2 ||1411 firstBlock.getArgument(0).getType() != getType())1412 return emitOpError() << "expects copy region with two arguments of the "1413 "privatization type";1414 1415 if (getDestroyRegion().empty())1416 return success();1417 1418 if (failed(verifyInitLikeSingleArgRegion(*this, getDestroyRegion(),1419 "privatization", "destroy",1420 getType(), /*verifyYield=*/false)))1421 return failure();1422 1423 return success();1424}1425 1426std::optional<FirstprivateRecipeOp>1427FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,1428 StringRef recipeName, Type varType,1429 StringRef varName, ValueRange bounds) {1430 // First, validate that we can handle this variable type1431 bool isMappable = isa<MappableType>(varType);1432 bool isPointerLike = isa<PointerLikeType>(varType);1433 1434 // Unsupported type1435 if (!isMappable && !isPointerLike)1436 return std::nullopt;1437 1438 OpBuilder::InsertionGuard guard(builder);1439 1440 // Create the recipe operation first so regions have proper parent context1441 auto recipe = FirstprivateRecipeOp::create(builder, loc, recipeName, varType);1442 1443 // Populate the init region1444 bool needsFree = false;1445 if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType,1446 varName, bounds, needsFree))) {1447 recipe.erase();1448 return std::nullopt;1449 }1450 1451 // Populate the copy region1452 if (failed(createCopyRegion(builder, loc, recipe.getCopyRegion(), varType,1453 bounds))) {1454 recipe.erase();1455 return std::nullopt;1456 }1457 1458 // Only create destroy region if the allocation needs deallocation1459 if (needsFree) {1460 // Extract the allocated value from the init block's yield operation1461 auto yieldOp =1462 cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator());1463 Value allocRes = yieldOp.getOperand(0);1464 1465 if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),1466 varType, allocRes, bounds))) {1467 recipe.erase();1468 return std::nullopt;1469 }1470 }1471 1472 return recipe;1473}1474 1475//===----------------------------------------------------------------------===//1476// ReductionRecipeOp1477//===----------------------------------------------------------------------===//1478 1479LogicalResult acc::ReductionRecipeOp::verifyRegions() {1480 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(), "reduction",1481 "init", getType(),1482 /*verifyYield=*/false)))1483 return failure();1484 1485 if (getCombinerRegion().empty())1486 return emitOpError() << "expects non-empty combiner region";1487 1488 Block &reductionBlock = getCombinerRegion().front();1489 if (reductionBlock.getNumArguments() < 2 ||1490 reductionBlock.getArgument(0).getType() != getType() ||1491 reductionBlock.getArgument(1).getType() != getType())1492 return emitOpError() << "expects combiner region with the first two "1493 << "arguments of the reduction type";1494 1495 for (YieldOp yieldOp : getCombinerRegion().getOps<YieldOp>()) {1496 if (yieldOp.getOperands().size() != 1 ||1497 yieldOp.getOperands().getTypes()[0] != getType())1498 return emitOpError() << "expects combiner region to yield a value "1499 "of the reduction type";1500 }1501 1502 return success();1503}1504 1505//===----------------------------------------------------------------------===//1506// ParallelOp1507//===----------------------------------------------------------------------===//1508 1509/// Check dataOperands for acc.parallel, acc.serial and acc.kernels.1510template <typename Op>1511static LogicalResult checkDataOperands(Op op,1512 const mlir::ValueRange &operands) {1513 for (mlir::Value operand : operands)1514 if (!mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,1515 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,1516 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp>(1517 operand.getDefiningOp()))1518 return op.emitError(1519 "expect data entry/exit operation or acc.getdeviceptr "1520 "as defining op");1521 return success();1522}1523 1524template <typename OpT, typename RecipeOpT>1525static LogicalResult checkPrivateOperands(mlir::Operation *accConstructOp,1526 const mlir::ValueRange &operands,1527 llvm::StringRef operandName) {1528 llvm::DenseSet<Value> set;1529 for (mlir::Value operand : operands) {1530 if (!mlir::isa<OpT>(operand.getDefiningOp()))1531 return accConstructOp->emitOpError()1532 << "expected " << operandName << " as defining op";1533 if (!set.insert(operand).second)1534 return accConstructOp->emitOpError()1535 << operandName << " operand appears more than once";1536 }1537 return success();1538}1539 1540unsigned ParallelOp::getNumDataOperands() {1541 return getReductionOperands().size() + getPrivateOperands().size() +1542 getFirstprivateOperands().size() + getDataClauseOperands().size();1543}1544 1545Value ParallelOp::getDataOperand(unsigned i) {1546 unsigned numOptional = getAsyncOperands().size();1547 numOptional += getNumGangs().size();1548 numOptional += getNumWorkers().size();1549 numOptional += getVectorLength().size();1550 numOptional += getIfCond() ? 1 : 0;1551 numOptional += getSelfCond() ? 1 : 0;1552 return getOperand(getWaitOperands().size() + numOptional + i);1553}1554 1555template <typename Op>1556static LogicalResult verifyDeviceTypeCountMatch(Op op, OperandRange operands,1557 ArrayAttr deviceTypes,1558 llvm::StringRef keyword) {1559 if (!operands.empty() && deviceTypes.getValue().size() != operands.size())1560 return op.emitOpError() << keyword << " operands count must match "1561 << keyword << " device_type count";1562 return success();1563}1564 1565template <typename Op>1566static LogicalResult verifyDeviceTypeAndSegmentCountMatch(1567 Op op, OperandRange operands, DenseI32ArrayAttr segments,1568 ArrayAttr deviceTypes, llvm::StringRef keyword, int32_t maxInSegment = 0) {1569 std::size_t numOperandsInSegments = 0;1570 std::size_t nbOfSegments = 0;1571 1572 if (segments) {1573 for (auto segCount : segments.asArrayRef()) {1574 if (maxInSegment != 0 && segCount > maxInSegment)1575 return op.emitOpError() << keyword << " expects a maximum of "1576 << maxInSegment << " values per segment";1577 numOperandsInSegments += segCount;1578 ++nbOfSegments;1579 }1580 }1581 1582 if ((numOperandsInSegments != operands.size()) ||1583 (!deviceTypes && !operands.empty()))1584 return op.emitOpError()1585 << keyword << " operand count does not match count in segments";1586 if (deviceTypes && deviceTypes.getValue().size() != nbOfSegments)1587 return op.emitOpError()1588 << keyword << " segment count does not match device_type count";1589 return success();1590}1591 1592LogicalResult acc::ParallelOp::verify() {1593 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,1594 mlir::acc::PrivateRecipeOp>(1595 *this, getPrivateOperands(), "private")))1596 return failure();1597 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,1598 mlir::acc::FirstprivateRecipeOp>(1599 *this, getFirstprivateOperands(), "firstprivate")))1600 return failure();1601 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,1602 mlir::acc::ReductionRecipeOp>(1603 *this, getReductionOperands(), "reduction")))1604 return failure();1605 1606 if (failed(verifyDeviceTypeAndSegmentCountMatch(1607 *this, getNumGangs(), getNumGangsSegmentsAttr(),1608 getNumGangsDeviceTypeAttr(), "num_gangs", 3)))1609 return failure();1610 1611 if (failed(verifyDeviceTypeAndSegmentCountMatch(1612 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),1613 getWaitOperandsDeviceTypeAttr(), "wait")))1614 return failure();1615 1616 if (failed(verifyDeviceTypeCountMatch(*this, getNumWorkers(),1617 getNumWorkersDeviceTypeAttr(),1618 "num_workers")))1619 return failure();1620 1621 if (failed(verifyDeviceTypeCountMatch(*this, getVectorLength(),1622 getVectorLengthDeviceTypeAttr(),1623 "vector_length")))1624 return failure();1625 1626 if (failed(verifyDeviceTypeCountMatch(*this, getAsyncOperands(),1627 getAsyncOperandsDeviceTypeAttr(),1628 "async")))1629 return failure();1630 1631 if (failed(checkWaitAndAsyncConflict<acc::ParallelOp>(*this)))1632 return failure();1633 1634 return checkDataOperands<acc::ParallelOp>(*this, getDataClauseOperands());1635}1636 1637static mlir::Value1638getValueInDeviceTypeSegment(std::optional<mlir::ArrayAttr> arrayAttr,1639 mlir::Operation::operand_range range,1640 mlir::acc::DeviceType deviceType) {1641 if (!arrayAttr)1642 return {};1643 if (auto pos = findSegment(*arrayAttr, deviceType))1644 return range[*pos];1645 return {};1646}1647 1648bool acc::ParallelOp::hasAsyncOnly() {1649 return hasAsyncOnly(mlir::acc::DeviceType::None);1650}1651 1652bool acc::ParallelOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {1653 return hasDeviceType(getAsyncOnly(), deviceType);1654}1655 1656mlir::Value acc::ParallelOp::getAsyncValue() {1657 return getAsyncValue(mlir::acc::DeviceType::None);1658}1659 1660mlir::Value acc::ParallelOp::getAsyncValue(mlir::acc::DeviceType deviceType) {1661 return getValueInDeviceTypeSegment(getAsyncOperandsDeviceType(),1662 getAsyncOperands(), deviceType);1663}1664 1665mlir::Value acc::ParallelOp::getNumWorkersValue() {1666 return getNumWorkersValue(mlir::acc::DeviceType::None);1667}1668 1669mlir::Value1670acc::ParallelOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {1671 return getValueInDeviceTypeSegment(getNumWorkersDeviceType(), getNumWorkers(),1672 deviceType);1673}1674 1675mlir::Value acc::ParallelOp::getVectorLengthValue() {1676 return getVectorLengthValue(mlir::acc::DeviceType::None);1677}1678 1679mlir::Value1680acc::ParallelOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {1681 return getValueInDeviceTypeSegment(getVectorLengthDeviceType(),1682 getVectorLength(), deviceType);1683}1684 1685mlir::Operation::operand_range ParallelOp::getNumGangsValues() {1686 return getNumGangsValues(mlir::acc::DeviceType::None);1687}1688 1689mlir::Operation::operand_range1690ParallelOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {1691 return getValuesFromSegments(getNumGangsDeviceType(), getNumGangs(),1692 getNumGangsSegments(), deviceType);1693}1694 1695bool acc::ParallelOp::hasWaitOnly() {1696 return hasWaitOnly(mlir::acc::DeviceType::None);1697}1698 1699bool acc::ParallelOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {1700 return hasDeviceType(getWaitOnly(), deviceType);1701}1702 1703mlir::Operation::operand_range ParallelOp::getWaitValues() {1704 return getWaitValues(mlir::acc::DeviceType::None);1705}1706 1707mlir::Operation::operand_range1708ParallelOp::getWaitValues(mlir::acc::DeviceType deviceType) {1709 return getWaitValuesWithoutDevnum(1710 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),1711 getHasWaitDevnum(), deviceType);1712}1713 1714mlir::Value ParallelOp::getWaitDevnum() {1715 return getWaitDevnum(mlir::acc::DeviceType::None);1716}1717 1718mlir::Value ParallelOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {1719 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),1720 getWaitOperandsSegments(), getHasWaitDevnum(),1721 deviceType);1722}1723 1724void ParallelOp::build(mlir::OpBuilder &odsBuilder,1725 mlir::OperationState &odsState,1726 mlir::ValueRange numGangs, mlir::ValueRange numWorkers,1727 mlir::ValueRange vectorLength,1728 mlir::ValueRange asyncOperands,1729 mlir::ValueRange waitOperands, mlir::Value ifCond,1730 mlir::Value selfCond, mlir::ValueRange reductionOperands,1731 mlir::ValueRange gangPrivateOperands,1732 mlir::ValueRange gangFirstPrivateOperands,1733 mlir::ValueRange dataClauseOperands) {1734 ParallelOp::build(1735 odsBuilder, odsState, asyncOperands, /*asyncOperandsDeviceType=*/nullptr,1736 /*asyncOnly=*/nullptr, waitOperands, /*waitOperandsSegments=*/nullptr,1737 /*waitOperandsDeviceType=*/nullptr, /*hasWaitDevnum=*/nullptr,1738 /*waitOnly=*/nullptr, numGangs, /*numGangsSegments=*/nullptr,1739 /*numGangsDeviceType=*/nullptr, numWorkers,1740 /*numWorkersDeviceType=*/nullptr, vectorLength,1741 /*vectorLengthDeviceType=*/nullptr, ifCond, selfCond,1742 /*selfAttr=*/nullptr, reductionOperands, gangPrivateOperands,1743 gangFirstPrivateOperands, dataClauseOperands,1744 /*defaultAttr=*/nullptr, /*combined=*/nullptr);1745}1746 1747void acc::ParallelOp::addNumWorkersOperand(1748 MLIRContext *context, mlir::Value newValue,1749 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1750 setNumWorkersDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(1751 context, getNumWorkersDeviceTypeAttr(), effectiveDeviceTypes, newValue,1752 getNumWorkersMutable()));1753}1754void acc::ParallelOp::addVectorLengthOperand(1755 MLIRContext *context, mlir::Value newValue,1756 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1757 setVectorLengthDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(1758 context, getVectorLengthDeviceTypeAttr(), effectiveDeviceTypes, newValue,1759 getVectorLengthMutable()));1760}1761 1762void acc::ParallelOp::addAsyncOnly(1763 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1764 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(1765 context, getAsyncOnlyAttr(), effectiveDeviceTypes));1766}1767 1768void acc::ParallelOp::addAsyncOperand(1769 MLIRContext *context, mlir::Value newValue,1770 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1771 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(1772 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,1773 getAsyncOperandsMutable()));1774}1775 1776void acc::ParallelOp::addNumGangsOperands(1777 MLIRContext *context, mlir::ValueRange newValues,1778 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1779 llvm::SmallVector<int32_t> segments;1780 if (getNumGangsSegments())1781 llvm::copy(*getNumGangsSegments(), std::back_inserter(segments));1782 1783 setNumGangsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(1784 context, getNumGangsDeviceTypeAttr(), effectiveDeviceTypes, newValues,1785 getNumGangsMutable(), segments));1786 1787 setNumGangsSegments(segments);1788}1789void acc::ParallelOp::addWaitOnly(1790 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1791 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),1792 effectiveDeviceTypes));1793}1794void acc::ParallelOp::addWaitOperands(1795 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,1796 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {1797 1798 llvm::SmallVector<int32_t> segments;1799 if (getWaitOperandsSegments())1800 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));1801 1802 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(1803 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,1804 getWaitOperandsMutable(), segments));1805 setWaitOperandsSegments(segments);1806 1807 llvm::SmallVector<mlir::Attribute> hasDevnums;1808 if (getHasWaitDevnumAttr())1809 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));1810 hasDevnums.insert(1811 hasDevnums.end(),1812 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),1813 mlir::BoolAttr::get(context, hasDevnum));1814 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));1815}1816 1817void acc::ParallelOp::addPrivatization(MLIRContext *context,1818 mlir::acc::PrivateOp op,1819 mlir::acc::PrivateRecipeOp recipe) {1820 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));1821 getPrivateOperandsMutable().append(op.getResult());1822}1823 1824void acc::ParallelOp::addFirstPrivatization(1825 MLIRContext *context, mlir::acc::FirstprivateOp op,1826 mlir::acc::FirstprivateRecipeOp recipe) {1827 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));1828 getFirstprivateOperandsMutable().append(op.getResult());1829}1830 1831void acc::ParallelOp::addReduction(MLIRContext *context,1832 mlir::acc::ReductionOp op,1833 mlir::acc::ReductionRecipeOp recipe) {1834 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));1835 getReductionOperandsMutable().append(op.getResult());1836}1837 1838static ParseResult parseNumGangs(1839 mlir::OpAsmParser &parser,1840 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,1841 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,1842 mlir::DenseI32ArrayAttr &segments) {1843 llvm::SmallVector<DeviceTypeAttr> attributes;1844 llvm::SmallVector<int32_t> seg;1845 1846 do {1847 if (failed(parser.parseLBrace()))1848 return failure();1849 1850 int32_t crtOperandsSize = operands.size();1851 if (failed(parser.parseCommaSeparatedList(1852 mlir::AsmParser::Delimiter::None, [&]() {1853 if (parser.parseOperand(operands.emplace_back()) ||1854 parser.parseColonType(types.emplace_back()))1855 return failure();1856 return success();1857 })))1858 return failure();1859 seg.push_back(operands.size() - crtOperandsSize);1860 1861 if (failed(parser.parseRBrace()))1862 return failure();1863 1864 if (succeeded(parser.parseOptionalLSquare())) {1865 if (parser.parseAttribute(attributes.emplace_back()) ||1866 parser.parseRSquare())1867 return failure();1868 } else {1869 attributes.push_back(mlir::acc::DeviceTypeAttr::get(1870 parser.getContext(), mlir::acc::DeviceType::None));1871 }1872 } while (succeeded(parser.parseOptionalComma()));1873 1874 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),1875 attributes.end());1876 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);1877 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);1878 1879 return success();1880}1881 1882static void printSingleDeviceType(mlir::OpAsmPrinter &p, mlir::Attribute attr) {1883 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);1884 if (deviceTypeAttr.getValue() != mlir::acc::DeviceType::None)1885 p << " [" << attr << "]";1886}1887 1888static void printNumGangs(mlir::OpAsmPrinter &p, mlir::Operation *op,1889 mlir::OperandRange operands, mlir::TypeRange types,1890 std::optional<mlir::ArrayAttr> deviceTypes,1891 std::optional<mlir::DenseI32ArrayAttr> segments) {1892 unsigned opIdx = 0;1893 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {1894 p << "{";1895 llvm::interleaveComma(1896 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {1897 p << operands[opIdx] << " : " << operands[opIdx].getType();1898 ++opIdx;1899 });1900 p << "}";1901 printSingleDeviceType(p, it.value());1902 });1903}1904 1905static ParseResult parseDeviceTypeOperandsWithSegment(1906 mlir::OpAsmParser &parser,1907 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,1908 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,1909 mlir::DenseI32ArrayAttr &segments) {1910 llvm::SmallVector<DeviceTypeAttr> attributes;1911 llvm::SmallVector<int32_t> seg;1912 1913 do {1914 if (failed(parser.parseLBrace()))1915 return failure();1916 1917 int32_t crtOperandsSize = operands.size();1918 1919 if (failed(parser.parseCommaSeparatedList(1920 mlir::AsmParser::Delimiter::None, [&]() {1921 if (parser.parseOperand(operands.emplace_back()) ||1922 parser.parseColonType(types.emplace_back()))1923 return failure();1924 return success();1925 })))1926 return failure();1927 1928 seg.push_back(operands.size() - crtOperandsSize);1929 1930 if (failed(parser.parseRBrace()))1931 return failure();1932 1933 if (succeeded(parser.parseOptionalLSquare())) {1934 if (parser.parseAttribute(attributes.emplace_back()) ||1935 parser.parseRSquare())1936 return failure();1937 } else {1938 attributes.push_back(mlir::acc::DeviceTypeAttr::get(1939 parser.getContext(), mlir::acc::DeviceType::None));1940 }1941 } while (succeeded(parser.parseOptionalComma()));1942 1943 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),1944 attributes.end());1945 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);1946 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);1947 1948 return success();1949}1950 1951static void printDeviceTypeOperandsWithSegment(1952 mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands,1953 mlir::TypeRange types, std::optional<mlir::ArrayAttr> deviceTypes,1954 std::optional<mlir::DenseI32ArrayAttr> segments) {1955 unsigned opIdx = 0;1956 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {1957 p << "{";1958 llvm::interleaveComma(1959 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {1960 p << operands[opIdx] << " : " << operands[opIdx].getType();1961 ++opIdx;1962 });1963 p << "}";1964 printSingleDeviceType(p, it.value());1965 });1966}1967 1968static ParseResult parseWaitClause(1969 mlir::OpAsmParser &parser,1970 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,1971 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,1972 mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &hasDevNum,1973 mlir::ArrayAttr &keywordOnly) {1974 llvm::SmallVector<mlir::Attribute> deviceTypeAttrs, keywordAttrs, devnum;1975 llvm::SmallVector<int32_t> seg;1976 1977 bool needCommaBeforeOperands = false;1978 1979 // Keyword only1980 if (failed(parser.parseOptionalLParen())) {1981 keywordAttrs.push_back(mlir::acc::DeviceTypeAttr::get(1982 parser.getContext(), mlir::acc::DeviceType::None));1983 keywordOnly = ArrayAttr::get(parser.getContext(), keywordAttrs);1984 return success();1985 }1986 1987 // Parse keyword only attributes1988 if (succeeded(parser.parseOptionalLSquare())) {1989 if (failed(parser.parseCommaSeparatedList([&]() {1990 if (parser.parseAttribute(keywordAttrs.emplace_back()))1991 return failure();1992 return success();1993 })))1994 return failure();1995 if (parser.parseRSquare())1996 return failure();1997 needCommaBeforeOperands = true;1998 }1999 2000 if (needCommaBeforeOperands && failed(parser.parseComma()))2001 return failure();2002 2003 do {2004 if (failed(parser.parseLBrace()))2005 return failure();2006 2007 int32_t crtOperandsSize = operands.size();2008 2009 if (succeeded(parser.parseOptionalKeyword("devnum"))) {2010 if (failed(parser.parseColon()))2011 return failure();2012 devnum.push_back(BoolAttr::get(parser.getContext(), true));2013 } else {2014 devnum.push_back(BoolAttr::get(parser.getContext(), false));2015 }2016 2017 if (failed(parser.parseCommaSeparatedList(2018 mlir::AsmParser::Delimiter::None, [&]() {2019 if (parser.parseOperand(operands.emplace_back()) ||2020 parser.parseColonType(types.emplace_back()))2021 return failure();2022 return success();2023 })))2024 return failure();2025 2026 seg.push_back(operands.size() - crtOperandsSize);2027 2028 if (failed(parser.parseRBrace()))2029 return failure();2030 2031 if (succeeded(parser.parseOptionalLSquare())) {2032 if (parser.parseAttribute(deviceTypeAttrs.emplace_back()) ||2033 parser.parseRSquare())2034 return failure();2035 } else {2036 deviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(2037 parser.getContext(), mlir::acc::DeviceType::None));2038 }2039 } while (succeeded(parser.parseOptionalComma()));2040 2041 if (failed(parser.parseRParen()))2042 return failure();2043 2044 deviceTypes = ArrayAttr::get(parser.getContext(), deviceTypeAttrs);2045 keywordOnly = ArrayAttr::get(parser.getContext(), keywordAttrs);2046 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);2047 hasDevNum = ArrayAttr::get(parser.getContext(), devnum);2048 2049 return success();2050}2051 2052static bool hasOnlyDeviceTypeNone(std::optional<mlir::ArrayAttr> attrs) {2053 if (!hasDeviceTypeValues(attrs))2054 return false;2055 if (attrs->size() != 1)2056 return false;2057 if (auto deviceTypeAttr =2058 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*attrs)[0]))2059 return deviceTypeAttr.getValue() == mlir::acc::DeviceType::None;2060 return false;2061}2062 2063static void printWaitClause(mlir::OpAsmPrinter &p, mlir::Operation *op,2064 mlir::OperandRange operands, mlir::TypeRange types,2065 std::optional<mlir::ArrayAttr> deviceTypes,2066 std::optional<mlir::DenseI32ArrayAttr> segments,2067 std::optional<mlir::ArrayAttr> hasDevNum,2068 std::optional<mlir::ArrayAttr> keywordOnly) {2069 2070 if (operands.begin() == operands.end() && hasOnlyDeviceTypeNone(keywordOnly))2071 return;2072 2073 p << "(";2074 2075 printDeviceTypes(p, keywordOnly);2076 if (hasDeviceTypeValues(keywordOnly) && hasDeviceTypeValues(deviceTypes))2077 p << ", ";2078 2079 if (hasDeviceTypeValues(deviceTypes)) {2080 unsigned opIdx = 0;2081 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {2082 p << "{";2083 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasDevNum)[it.index()]);2084 if (boolAttr && boolAttr.getValue())2085 p << "devnum: ";2086 llvm::interleaveComma(2087 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {2088 p << operands[opIdx] << " : " << operands[opIdx].getType();2089 ++opIdx;2090 });2091 p << "}";2092 printSingleDeviceType(p, it.value());2093 });2094 }2095 2096 p << ")";2097}2098 2099static ParseResult parseDeviceTypeOperands(2100 mlir::OpAsmParser &parser,2101 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,2102 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes) {2103 llvm::SmallVector<DeviceTypeAttr> attributes;2104 if (failed(parser.parseCommaSeparatedList([&]() {2105 if (parser.parseOperand(operands.emplace_back()) ||2106 parser.parseColonType(types.emplace_back()))2107 return failure();2108 if (succeeded(parser.parseOptionalLSquare())) {2109 if (parser.parseAttribute(attributes.emplace_back()) ||2110 parser.parseRSquare())2111 return failure();2112 } else {2113 attributes.push_back(mlir::acc::DeviceTypeAttr::get(2114 parser.getContext(), mlir::acc::DeviceType::None));2115 }2116 return success();2117 })))2118 return failure();2119 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),2120 attributes.end());2121 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);2122 return success();2123}2124 2125static void2126printDeviceTypeOperands(mlir::OpAsmPrinter &p, mlir::Operation *op,2127 mlir::OperandRange operands, mlir::TypeRange types,2128 std::optional<mlir::ArrayAttr> deviceTypes) {2129 if (!hasDeviceTypeValues(deviceTypes))2130 return;2131 llvm::interleaveComma(llvm::zip(*deviceTypes, operands), p, [&](auto it) {2132 p << std::get<1>(it) << " : " << std::get<1>(it).getType();2133 printSingleDeviceType(p, std::get<0>(it));2134 });2135}2136 2137static ParseResult parseDeviceTypeOperandsWithKeywordOnly(2138 mlir::OpAsmParser &parser,2139 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,2140 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,2141 mlir::ArrayAttr &keywordOnlyDeviceType) {2142 2143 llvm::SmallVector<mlir::Attribute> keywordOnlyDeviceTypeAttributes;2144 bool needCommaBeforeOperands = false;2145 2146 if (failed(parser.parseOptionalLParen())) {2147 // Keyword only2148 keywordOnlyDeviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(2149 parser.getContext(), mlir::acc::DeviceType::None));2150 keywordOnlyDeviceType =2151 ArrayAttr::get(parser.getContext(), keywordOnlyDeviceTypeAttributes);2152 return success();2153 }2154 2155 // Parse keyword only attributes2156 if (succeeded(parser.parseOptionalLSquare())) {2157 // Parse keyword only attributes2158 if (failed(parser.parseCommaSeparatedList([&]() {2159 if (parser.parseAttribute(2160 keywordOnlyDeviceTypeAttributes.emplace_back()))2161 return failure();2162 return success();2163 })))2164 return failure();2165 if (parser.parseRSquare())2166 return failure();2167 needCommaBeforeOperands = true;2168 }2169 2170 if (needCommaBeforeOperands && failed(parser.parseComma()))2171 return failure();2172 2173 llvm::SmallVector<DeviceTypeAttr> attributes;2174 if (failed(parser.parseCommaSeparatedList([&]() {2175 if (parser.parseOperand(operands.emplace_back()) ||2176 parser.parseColonType(types.emplace_back()))2177 return failure();2178 if (succeeded(parser.parseOptionalLSquare())) {2179 if (parser.parseAttribute(attributes.emplace_back()) ||2180 parser.parseRSquare())2181 return failure();2182 } else {2183 attributes.push_back(mlir::acc::DeviceTypeAttr::get(2184 parser.getContext(), mlir::acc::DeviceType::None));2185 }2186 return success();2187 })))2188 return failure();2189 2190 if (failed(parser.parseRParen()))2191 return failure();2192 2193 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),2194 attributes.end());2195 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);2196 return success();2197}2198 2199static void printDeviceTypeOperandsWithKeywordOnly(2200 mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands,2201 mlir::TypeRange types, std::optional<mlir::ArrayAttr> deviceTypes,2202 std::optional<mlir::ArrayAttr> keywordOnlyDeviceTypes) {2203 2204 if (operands.begin() == operands.end() &&2205 hasOnlyDeviceTypeNone(keywordOnlyDeviceTypes)) {2206 return;2207 }2208 2209 p << "(";2210 printDeviceTypes(p, keywordOnlyDeviceTypes);2211 if (hasDeviceTypeValues(keywordOnlyDeviceTypes) &&2212 hasDeviceTypeValues(deviceTypes))2213 p << ", ";2214 printDeviceTypeOperands(p, op, operands, types, deviceTypes);2215 p << ")";2216}2217 2218static ParseResult parseOperandWithKeywordOnly(2219 mlir::OpAsmParser &parser,2220 std::optional<OpAsmParser::UnresolvedOperand> &operand,2221 mlir::Type &operandType, mlir::UnitAttr &attr) {2222 // Keyword only2223 if (failed(parser.parseOptionalLParen())) {2224 attr = mlir::UnitAttr::get(parser.getContext());2225 return success();2226 }2227 2228 OpAsmParser::UnresolvedOperand op;2229 if (failed(parser.parseOperand(op)))2230 return failure();2231 operand = op;2232 if (failed(parser.parseColon()))2233 return failure();2234 if (failed(parser.parseType(operandType)))2235 return failure();2236 if (failed(parser.parseRParen()))2237 return failure();2238 2239 return success();2240}2241 2242static void printOperandWithKeywordOnly(mlir::OpAsmPrinter &p,2243 mlir::Operation *op,2244 std::optional<mlir::Value> operand,2245 mlir::Type operandType,2246 mlir::UnitAttr attr) {2247 if (attr)2248 return;2249 2250 p << "(";2251 p.printOperand(*operand);2252 p << " : ";2253 p.printType(operandType);2254 p << ")";2255}2256 2257static ParseResult parseOperandsWithKeywordOnly(2258 mlir::OpAsmParser &parser,2259 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,2260 llvm::SmallVectorImpl<Type> &types, mlir::UnitAttr &attr) {2261 // Keyword only2262 if (failed(parser.parseOptionalLParen())) {2263 attr = mlir::UnitAttr::get(parser.getContext());2264 return success();2265 }2266 2267 if (failed(parser.parseCommaSeparatedList([&]() {2268 if (parser.parseOperand(operands.emplace_back()))2269 return failure();2270 return success();2271 })))2272 return failure();2273 if (failed(parser.parseColon()))2274 return failure();2275 if (failed(parser.parseCommaSeparatedList([&]() {2276 if (parser.parseType(types.emplace_back()))2277 return failure();2278 return success();2279 })))2280 return failure();2281 if (failed(parser.parseRParen()))2282 return failure();2283 2284 return success();2285}2286 2287static void printOperandsWithKeywordOnly(mlir::OpAsmPrinter &p,2288 mlir::Operation *op,2289 mlir::OperandRange operands,2290 mlir::TypeRange types,2291 mlir::UnitAttr attr) {2292 if (attr)2293 return;2294 2295 p << "(";2296 llvm::interleaveComma(operands, p, [&](auto it) { p << it; });2297 p << " : ";2298 llvm::interleaveComma(types, p, [&](auto it) { p << it; });2299 p << ")";2300}2301 2302static ParseResult2303parseCombinedConstructsLoop(mlir::OpAsmParser &parser,2304 mlir::acc::CombinedConstructsTypeAttr &attr) {2305 if (succeeded(parser.parseOptionalKeyword("kernels"))) {2306 attr = mlir::acc::CombinedConstructsTypeAttr::get(2307 parser.getContext(), mlir::acc::CombinedConstructsType::KernelsLoop);2308 } else if (succeeded(parser.parseOptionalKeyword("parallel"))) {2309 attr = mlir::acc::CombinedConstructsTypeAttr::get(2310 parser.getContext(), mlir::acc::CombinedConstructsType::ParallelLoop);2311 } else if (succeeded(parser.parseOptionalKeyword("serial"))) {2312 attr = mlir::acc::CombinedConstructsTypeAttr::get(2313 parser.getContext(), mlir::acc::CombinedConstructsType::SerialLoop);2314 } else {2315 parser.emitError(parser.getCurrentLocation(),2316 "expected compute construct name");2317 return failure();2318 }2319 return success();2320}2321 2322static void2323printCombinedConstructsLoop(mlir::OpAsmPrinter &p, mlir::Operation *op,2324 mlir::acc::CombinedConstructsTypeAttr attr) {2325 if (attr) {2326 switch (attr.getValue()) {2327 case mlir::acc::CombinedConstructsType::KernelsLoop:2328 p << "kernels";2329 break;2330 case mlir::acc::CombinedConstructsType::ParallelLoop:2331 p << "parallel";2332 break;2333 case mlir::acc::CombinedConstructsType::SerialLoop:2334 p << "serial";2335 break;2336 };2337 }2338}2339 2340//===----------------------------------------------------------------------===//2341// SerialOp2342//===----------------------------------------------------------------------===//2343 2344unsigned SerialOp::getNumDataOperands() {2345 return getReductionOperands().size() + getPrivateOperands().size() +2346 getFirstprivateOperands().size() + getDataClauseOperands().size();2347}2348 2349Value SerialOp::getDataOperand(unsigned i) {2350 unsigned numOptional = getAsyncOperands().size();2351 numOptional += getIfCond() ? 1 : 0;2352 numOptional += getSelfCond() ? 1 : 0;2353 return getOperand(getWaitOperands().size() + numOptional + i);2354}2355 2356bool acc::SerialOp::hasAsyncOnly() {2357 return hasAsyncOnly(mlir::acc::DeviceType::None);2358}2359 2360bool acc::SerialOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {2361 return hasDeviceType(getAsyncOnly(), deviceType);2362}2363 2364mlir::Value acc::SerialOp::getAsyncValue() {2365 return getAsyncValue(mlir::acc::DeviceType::None);2366}2367 2368mlir::Value acc::SerialOp::getAsyncValue(mlir::acc::DeviceType deviceType) {2369 return getValueInDeviceTypeSegment(getAsyncOperandsDeviceType(),2370 getAsyncOperands(), deviceType);2371}2372 2373bool acc::SerialOp::hasWaitOnly() {2374 return hasWaitOnly(mlir::acc::DeviceType::None);2375}2376 2377bool acc::SerialOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {2378 return hasDeviceType(getWaitOnly(), deviceType);2379}2380 2381mlir::Operation::operand_range SerialOp::getWaitValues() {2382 return getWaitValues(mlir::acc::DeviceType::None);2383}2384 2385mlir::Operation::operand_range2386SerialOp::getWaitValues(mlir::acc::DeviceType deviceType) {2387 return getWaitValuesWithoutDevnum(2388 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),2389 getHasWaitDevnum(), deviceType);2390}2391 2392mlir::Value SerialOp::getWaitDevnum() {2393 return getWaitDevnum(mlir::acc::DeviceType::None);2394}2395 2396mlir::Value SerialOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {2397 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),2398 getWaitOperandsSegments(), getHasWaitDevnum(),2399 deviceType);2400}2401 2402LogicalResult acc::SerialOp::verify() {2403 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,2404 mlir::acc::PrivateRecipeOp>(2405 *this, getPrivateOperands(), "private")))2406 return failure();2407 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,2408 mlir::acc::FirstprivateRecipeOp>(2409 *this, getFirstprivateOperands(), "firstprivate")))2410 return failure();2411 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,2412 mlir::acc::ReductionRecipeOp>(2413 *this, getReductionOperands(), "reduction")))2414 return failure();2415 2416 if (failed(verifyDeviceTypeAndSegmentCountMatch(2417 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),2418 getWaitOperandsDeviceTypeAttr(), "wait")))2419 return failure();2420 2421 if (failed(verifyDeviceTypeCountMatch(*this, getAsyncOperands(),2422 getAsyncOperandsDeviceTypeAttr(),2423 "async")))2424 return failure();2425 2426 if (failed(checkWaitAndAsyncConflict<acc::SerialOp>(*this)))2427 return failure();2428 2429 return checkDataOperands<acc::SerialOp>(*this, getDataClauseOperands());2430}2431 2432void acc::SerialOp::addAsyncOnly(2433 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2434 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(2435 context, getAsyncOnlyAttr(), effectiveDeviceTypes));2436}2437 2438void acc::SerialOp::addAsyncOperand(2439 MLIRContext *context, mlir::Value newValue,2440 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2441 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2442 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,2443 getAsyncOperandsMutable()));2444}2445 2446void acc::SerialOp::addWaitOnly(2447 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2448 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),2449 effectiveDeviceTypes));2450}2451void acc::SerialOp::addWaitOperands(2452 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,2453 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2454 2455 llvm::SmallVector<int32_t> segments;2456 if (getWaitOperandsSegments())2457 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));2458 2459 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2460 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,2461 getWaitOperandsMutable(), segments));2462 setWaitOperandsSegments(segments);2463 2464 llvm::SmallVector<mlir::Attribute> hasDevnums;2465 if (getHasWaitDevnumAttr())2466 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));2467 hasDevnums.insert(2468 hasDevnums.end(),2469 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),2470 mlir::BoolAttr::get(context, hasDevnum));2471 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));2472}2473 2474void acc::SerialOp::addPrivatization(MLIRContext *context,2475 mlir::acc::PrivateOp op,2476 mlir::acc::PrivateRecipeOp recipe) {2477 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));2478 getPrivateOperandsMutable().append(op.getResult());2479}2480 2481void acc::SerialOp::addFirstPrivatization(2482 MLIRContext *context, mlir::acc::FirstprivateOp op,2483 mlir::acc::FirstprivateRecipeOp recipe) {2484 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));2485 getFirstprivateOperandsMutable().append(op.getResult());2486}2487 2488void acc::SerialOp::addReduction(MLIRContext *context,2489 mlir::acc::ReductionOp op,2490 mlir::acc::ReductionRecipeOp recipe) {2491 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));2492 getReductionOperandsMutable().append(op.getResult());2493}2494 2495//===----------------------------------------------------------------------===//2496// KernelsOp2497//===----------------------------------------------------------------------===//2498 2499unsigned KernelsOp::getNumDataOperands() {2500 return getDataClauseOperands().size();2501}2502 2503Value KernelsOp::getDataOperand(unsigned i) {2504 unsigned numOptional = getAsyncOperands().size();2505 numOptional += getWaitOperands().size();2506 numOptional += getNumGangs().size();2507 numOptional += getNumWorkers().size();2508 numOptional += getVectorLength().size();2509 numOptional += getIfCond() ? 1 : 0;2510 numOptional += getSelfCond() ? 1 : 0;2511 return getOperand(numOptional + i);2512}2513 2514bool acc::KernelsOp::hasAsyncOnly() {2515 return hasAsyncOnly(mlir::acc::DeviceType::None);2516}2517 2518bool acc::KernelsOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {2519 return hasDeviceType(getAsyncOnly(), deviceType);2520}2521 2522mlir::Value acc::KernelsOp::getAsyncValue() {2523 return getAsyncValue(mlir::acc::DeviceType::None);2524}2525 2526mlir::Value acc::KernelsOp::getAsyncValue(mlir::acc::DeviceType deviceType) {2527 return getValueInDeviceTypeSegment(getAsyncOperandsDeviceType(),2528 getAsyncOperands(), deviceType);2529}2530 2531mlir::Value acc::KernelsOp::getNumWorkersValue() {2532 return getNumWorkersValue(mlir::acc::DeviceType::None);2533}2534 2535mlir::Value2536acc::KernelsOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {2537 return getValueInDeviceTypeSegment(getNumWorkersDeviceType(), getNumWorkers(),2538 deviceType);2539}2540 2541mlir::Value acc::KernelsOp::getVectorLengthValue() {2542 return getVectorLengthValue(mlir::acc::DeviceType::None);2543}2544 2545mlir::Value2546acc::KernelsOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {2547 return getValueInDeviceTypeSegment(getVectorLengthDeviceType(),2548 getVectorLength(), deviceType);2549}2550 2551mlir::Operation::operand_range KernelsOp::getNumGangsValues() {2552 return getNumGangsValues(mlir::acc::DeviceType::None);2553}2554 2555mlir::Operation::operand_range2556KernelsOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {2557 return getValuesFromSegments(getNumGangsDeviceType(), getNumGangs(),2558 getNumGangsSegments(), deviceType);2559}2560 2561bool acc::KernelsOp::hasWaitOnly() {2562 return hasWaitOnly(mlir::acc::DeviceType::None);2563}2564 2565bool acc::KernelsOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {2566 return hasDeviceType(getWaitOnly(), deviceType);2567}2568 2569mlir::Operation::operand_range KernelsOp::getWaitValues() {2570 return getWaitValues(mlir::acc::DeviceType::None);2571}2572 2573mlir::Operation::operand_range2574KernelsOp::getWaitValues(mlir::acc::DeviceType deviceType) {2575 return getWaitValuesWithoutDevnum(2576 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),2577 getHasWaitDevnum(), deviceType);2578}2579 2580mlir::Value KernelsOp::getWaitDevnum() {2581 return getWaitDevnum(mlir::acc::DeviceType::None);2582}2583 2584mlir::Value KernelsOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {2585 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),2586 getWaitOperandsSegments(), getHasWaitDevnum(),2587 deviceType);2588}2589 2590LogicalResult acc::KernelsOp::verify() {2591 if (failed(verifyDeviceTypeAndSegmentCountMatch(2592 *this, getNumGangs(), getNumGangsSegmentsAttr(),2593 getNumGangsDeviceTypeAttr(), "num_gangs", 3)))2594 return failure();2595 2596 if (failed(verifyDeviceTypeAndSegmentCountMatch(2597 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),2598 getWaitOperandsDeviceTypeAttr(), "wait")))2599 return failure();2600 2601 if (failed(verifyDeviceTypeCountMatch(*this, getNumWorkers(),2602 getNumWorkersDeviceTypeAttr(),2603 "num_workers")))2604 return failure();2605 2606 if (failed(verifyDeviceTypeCountMatch(*this, getVectorLength(),2607 getVectorLengthDeviceTypeAttr(),2608 "vector_length")))2609 return failure();2610 2611 if (failed(verifyDeviceTypeCountMatch(*this, getAsyncOperands(),2612 getAsyncOperandsDeviceTypeAttr(),2613 "async")))2614 return failure();2615 2616 if (failed(checkWaitAndAsyncConflict<acc::KernelsOp>(*this)))2617 return failure();2618 2619 return checkDataOperands<acc::KernelsOp>(*this, getDataClauseOperands());2620}2621 2622void acc::KernelsOp::addNumWorkersOperand(2623 MLIRContext *context, mlir::Value newValue,2624 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2625 setNumWorkersDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2626 context, getNumWorkersDeviceTypeAttr(), effectiveDeviceTypes, newValue,2627 getNumWorkersMutable()));2628}2629 2630void acc::KernelsOp::addVectorLengthOperand(2631 MLIRContext *context, mlir::Value newValue,2632 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2633 setVectorLengthDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2634 context, getVectorLengthDeviceTypeAttr(), effectiveDeviceTypes, newValue,2635 getVectorLengthMutable()));2636}2637void acc::KernelsOp::addAsyncOnly(2638 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2639 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(2640 context, getAsyncOnlyAttr(), effectiveDeviceTypes));2641}2642 2643void acc::KernelsOp::addAsyncOperand(2644 MLIRContext *context, mlir::Value newValue,2645 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2646 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2647 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,2648 getAsyncOperandsMutable()));2649}2650 2651void acc::KernelsOp::addNumGangsOperands(2652 MLIRContext *context, mlir::ValueRange newValues,2653 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2654 llvm::SmallVector<int32_t> segments;2655 if (getNumGangsSegmentsAttr())2656 llvm::copy(*getNumGangsSegments(), std::back_inserter(segments));2657 2658 setNumGangsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2659 context, getNumGangsDeviceTypeAttr(), effectiveDeviceTypes, newValues,2660 getNumGangsMutable(), segments));2661 2662 setNumGangsSegments(segments);2663}2664 2665void acc::KernelsOp::addWaitOnly(2666 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2667 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),2668 effectiveDeviceTypes));2669}2670void acc::KernelsOp::addWaitOperands(2671 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,2672 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {2673 2674 llvm::SmallVector<int32_t> segments;2675 if (getWaitOperandsSegments())2676 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));2677 2678 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(2679 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,2680 getWaitOperandsMutable(), segments));2681 setWaitOperandsSegments(segments);2682 2683 llvm::SmallVector<mlir::Attribute> hasDevnums;2684 if (getHasWaitDevnumAttr())2685 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));2686 hasDevnums.insert(2687 hasDevnums.end(),2688 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),2689 mlir::BoolAttr::get(context, hasDevnum));2690 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));2691}2692 2693//===----------------------------------------------------------------------===//2694// HostDataOp2695//===----------------------------------------------------------------------===//2696 2697LogicalResult acc::HostDataOp::verify() {2698 if (getDataClauseOperands().empty())2699 return emitError("at least one operand must appear on the host_data "2700 "operation");2701 2702 for (mlir::Value operand : getDataClauseOperands())2703 if (!mlir::isa<acc::UseDeviceOp>(operand.getDefiningOp()))2704 return emitError("expect data entry operation as defining op");2705 return success();2706}2707 2708void acc::HostDataOp::getCanonicalizationPatterns(RewritePatternSet &results,2709 MLIRContext *context) {2710 results.add<RemoveConstantIfConditionWithRegion<HostDataOp>>(context);2711}2712 2713//===----------------------------------------------------------------------===//2714// KernelEnvironmentOp2715//===----------------------------------------------------------------------===//2716 2717void acc::KernelEnvironmentOp::getCanonicalizationPatterns(2718 RewritePatternSet &results, MLIRContext *context) {2719 results.add<RemoveEmptyKernelEnvironment>(context);2720}2721 2722//===----------------------------------------------------------------------===//2723// LoopOp2724//===----------------------------------------------------------------------===//2725 2726static ParseResult parseGangValue(2727 OpAsmParser &parser, llvm::StringRef keyword,2728 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &operands,2729 llvm::SmallVectorImpl<Type> &types,2730 llvm::SmallVector<GangArgTypeAttr> &attributes, GangArgTypeAttr gangArgType,2731 bool &needCommaBetweenValues, bool &newValue) {2732 if (succeeded(parser.parseOptionalKeyword(keyword))) {2733 if (parser.parseEqual())2734 return failure();2735 if (parser.parseOperand(operands.emplace_back()) ||2736 parser.parseColonType(types.emplace_back()))2737 return failure();2738 attributes.push_back(gangArgType);2739 needCommaBetweenValues = true;2740 newValue = true;2741 }2742 return success();2743}2744 2745static ParseResult parseGangClause(2746 OpAsmParser &parser,2747 llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &gangOperands,2748 llvm::SmallVectorImpl<Type> &gangOperandsType, mlir::ArrayAttr &gangArgType,2749 mlir::ArrayAttr &deviceType, mlir::DenseI32ArrayAttr &segments,2750 mlir::ArrayAttr &gangOnlyDeviceType) {2751 llvm::SmallVector<GangArgTypeAttr> gangArgTypeAttributes;2752 llvm::SmallVector<mlir::Attribute> deviceTypeAttributes;2753 llvm::SmallVector<mlir::Attribute> gangOnlyDeviceTypeAttributes;2754 llvm::SmallVector<int32_t> seg;2755 bool needCommaBetweenValues = false;2756 bool needCommaBeforeOperands = false;2757 2758 if (failed(parser.parseOptionalLParen())) {2759 // Gang only keyword2760 gangOnlyDeviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(2761 parser.getContext(), mlir::acc::DeviceType::None));2762 gangOnlyDeviceType =2763 ArrayAttr::get(parser.getContext(), gangOnlyDeviceTypeAttributes);2764 return success();2765 }2766 2767 // Parse gang only attributes2768 if (succeeded(parser.parseOptionalLSquare())) {2769 // Parse gang only attributes2770 if (failed(parser.parseCommaSeparatedList([&]() {2771 if (parser.parseAttribute(2772 gangOnlyDeviceTypeAttributes.emplace_back()))2773 return failure();2774 return success();2775 })))2776 return failure();2777 if (parser.parseRSquare())2778 return failure();2779 needCommaBeforeOperands = true;2780 }2781 2782 auto argNum = mlir::acc::GangArgTypeAttr::get(parser.getContext(),2783 mlir::acc::GangArgType::Num);2784 auto argDim = mlir::acc::GangArgTypeAttr::get(parser.getContext(),2785 mlir::acc::GangArgType::Dim);2786 auto argStatic = mlir::acc::GangArgTypeAttr::get(2787 parser.getContext(), mlir::acc::GangArgType::Static);2788 2789 do {2790 if (needCommaBeforeOperands) {2791 needCommaBeforeOperands = false;2792 continue;2793 }2794 2795 if (failed(parser.parseLBrace()))2796 return failure();2797 2798 int32_t crtOperandsSize = gangOperands.size();2799 while (true) {2800 bool newValue = false;2801 bool needValue = false;2802 if (needCommaBetweenValues) {2803 if (succeeded(parser.parseOptionalComma()))2804 needValue = true; // expect a new value after comma.2805 else2806 break;2807 }2808 2809 if (failed(parseGangValue(parser, LoopOp::getGangNumKeyword(),2810 gangOperands, gangOperandsType,2811 gangArgTypeAttributes, argNum,2812 needCommaBetweenValues, newValue)))2813 return failure();2814 if (failed(parseGangValue(parser, LoopOp::getGangDimKeyword(),2815 gangOperands, gangOperandsType,2816 gangArgTypeAttributes, argDim,2817 needCommaBetweenValues, newValue)))2818 return failure();2819 if (failed(parseGangValue(parser, LoopOp::getGangStaticKeyword(),2820 gangOperands, gangOperandsType,2821 gangArgTypeAttributes, argStatic,2822 needCommaBetweenValues, newValue)))2823 return failure();2824 2825 if (!newValue && needValue) {2826 parser.emitError(parser.getCurrentLocation(),2827 "new value expected after comma");2828 return failure();2829 }2830 2831 if (!newValue)2832 break;2833 }2834 2835 if (gangOperands.empty())2836 return parser.emitError(2837 parser.getCurrentLocation(),2838 "expect at least one of num, dim or static values");2839 2840 if (failed(parser.parseRBrace()))2841 return failure();2842 2843 if (succeeded(parser.parseOptionalLSquare())) {2844 if (parser.parseAttribute(deviceTypeAttributes.emplace_back()) ||2845 parser.parseRSquare())2846 return failure();2847 } else {2848 deviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(2849 parser.getContext(), mlir::acc::DeviceType::None));2850 }2851 2852 seg.push_back(gangOperands.size() - crtOperandsSize);2853 2854 } while (succeeded(parser.parseOptionalComma()));2855 2856 if (failed(parser.parseRParen()))2857 return failure();2858 2859 llvm::SmallVector<mlir::Attribute> arrayAttr(gangArgTypeAttributes.begin(),2860 gangArgTypeAttributes.end());2861 gangArgType = ArrayAttr::get(parser.getContext(), arrayAttr);2862 deviceType = ArrayAttr::get(parser.getContext(), deviceTypeAttributes);2863 2864 llvm::SmallVector<mlir::Attribute> gangOnlyAttr(2865 gangOnlyDeviceTypeAttributes.begin(), gangOnlyDeviceTypeAttributes.end());2866 gangOnlyDeviceType = ArrayAttr::get(parser.getContext(), gangOnlyAttr);2867 2868 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);2869 return success();2870}2871 2872void printGangClause(OpAsmPrinter &p, Operation *op,2873 mlir::OperandRange operands, mlir::TypeRange types,2874 std::optional<mlir::ArrayAttr> gangArgTypes,2875 std::optional<mlir::ArrayAttr> deviceTypes,2876 std::optional<mlir::DenseI32ArrayAttr> segments,2877 std::optional<mlir::ArrayAttr> gangOnlyDeviceTypes) {2878 2879 if (operands.begin() == operands.end() &&2880 hasOnlyDeviceTypeNone(gangOnlyDeviceTypes)) {2881 return;2882 }2883 2884 p << "(";2885 2886 printDeviceTypes(p, gangOnlyDeviceTypes);2887 2888 if (hasDeviceTypeValues(gangOnlyDeviceTypes) &&2889 hasDeviceTypeValues(deviceTypes))2890 p << ", ";2891 2892 if (hasDeviceTypeValues(deviceTypes)) {2893 unsigned opIdx = 0;2894 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {2895 p << "{";2896 llvm::interleaveComma(2897 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {2898 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(2899 (*gangArgTypes)[opIdx]);2900 if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Num)2901 p << LoopOp::getGangNumKeyword();2902 else if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Dim)2903 p << LoopOp::getGangDimKeyword();2904 else if (gangArgTypeAttr.getValue() ==2905 mlir::acc::GangArgType::Static)2906 p << LoopOp::getGangStaticKeyword();2907 p << "=" << operands[opIdx] << " : " << operands[opIdx].getType();2908 ++opIdx;2909 });2910 p << "}";2911 printSingleDeviceType(p, it.value());2912 });2913 }2914 p << ")";2915}2916 2917bool hasDuplicateDeviceTypes(2918 std::optional<mlir::ArrayAttr> segments,2919 llvm::SmallSet<mlir::acc::DeviceType, 3> &deviceTypes) {2920 if (!segments)2921 return false;2922 for (auto attr : *segments) {2923 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);2924 if (!deviceTypes.insert(deviceTypeAttr.getValue()).second)2925 return true;2926 }2927 return false;2928}2929 2930/// Check for duplicates in the DeviceType array attribute.2931LogicalResult checkDeviceTypes(mlir::ArrayAttr deviceTypes) {2932 llvm::SmallSet<mlir::acc::DeviceType, 3> crtDeviceTypes;2933 if (!deviceTypes)2934 return success();2935 for (auto attr : deviceTypes) {2936 auto deviceTypeAttr =2937 mlir::dyn_cast_or_null<mlir::acc::DeviceTypeAttr>(attr);2938 if (!deviceTypeAttr)2939 return failure();2940 if (!crtDeviceTypes.insert(deviceTypeAttr.getValue()).second)2941 return failure();2942 }2943 return success();2944}2945 2946LogicalResult acc::LoopOp::verify() {2947 if (getUpperbound().size() != getStep().size())2948 return emitError() << "number of upperbounds expected to be the same as "2949 "number of steps";2950 2951 if (getUpperbound().size() != getLowerbound().size())2952 return emitError() << "number of upperbounds expected to be the same as "2953 "number of lowerbounds";2954 2955 if (!getUpperbound().empty() && getInclusiveUpperbound() &&2956 (getUpperbound().size() != getInclusiveUpperbound()->size()))2957 return emitError() << "inclusiveUpperbound size is expected to be the same"2958 << " as upperbound size";2959 2960 // Check collapse2961 if (getCollapseAttr() && !getCollapseDeviceTypeAttr())2962 return emitOpError() << "collapse device_type attr must be define when"2963 << " collapse attr is present";2964 2965 if (getCollapseAttr() && getCollapseDeviceTypeAttr() &&2966 getCollapseAttr().getValue().size() !=2967 getCollapseDeviceTypeAttr().getValue().size())2968 return emitOpError() << "collapse attribute count must match collapse"2969 << " device_type count";2970 if (failed(checkDeviceTypes(getCollapseDeviceTypeAttr())))2971 return emitOpError()2972 << "duplicate device_type found in collapseDeviceType attribute";2973 2974 // Check gang2975 if (!getGangOperands().empty()) {2976 if (!getGangOperandsArgType())2977 return emitOpError() << "gangOperandsArgType attribute must be defined"2978 << " when gang operands are present";2979 2980 if (getGangOperands().size() !=2981 getGangOperandsArgTypeAttr().getValue().size())2982 return emitOpError() << "gangOperandsArgType attribute count must match"2983 << " gangOperands count";2984 }2985 if (getGangAttr() && failed(checkDeviceTypes(getGangAttr())))2986 return emitOpError() << "duplicate device_type found in gang attribute";2987 2988 if (failed(verifyDeviceTypeAndSegmentCountMatch(2989 *this, getGangOperands(), getGangOperandsSegmentsAttr(),2990 getGangOperandsDeviceTypeAttr(), "gang")))2991 return failure();2992 2993 // Check worker2994 if (failed(checkDeviceTypes(getWorkerAttr())))2995 return emitOpError() << "duplicate device_type found in worker attribute";2996 if (failed(checkDeviceTypes(getWorkerNumOperandsDeviceTypeAttr())))2997 return emitOpError() << "duplicate device_type found in "2998 "workerNumOperandsDeviceType attribute";2999 if (failed(verifyDeviceTypeCountMatch(*this, getWorkerNumOperands(),3000 getWorkerNumOperandsDeviceTypeAttr(),3001 "worker")))3002 return failure();3003 3004 // Check vector3005 if (failed(checkDeviceTypes(getVectorAttr())))3006 return emitOpError() << "duplicate device_type found in vector attribute";3007 if (failed(checkDeviceTypes(getVectorOperandsDeviceTypeAttr())))3008 return emitOpError() << "duplicate device_type found in "3009 "vectorOperandsDeviceType attribute";3010 if (failed(verifyDeviceTypeCountMatch(*this, getVectorOperands(),3011 getVectorOperandsDeviceTypeAttr(),3012 "vector")))3013 return failure();3014 3015 if (failed(verifyDeviceTypeAndSegmentCountMatch(3016 *this, getTileOperands(), getTileOperandsSegmentsAttr(),3017 getTileOperandsDeviceTypeAttr(), "tile")))3018 return failure();3019 3020 // auto, independent and seq attribute are mutually exclusive.3021 llvm::SmallSet<mlir::acc::DeviceType, 3> deviceTypes;3022 if (hasDuplicateDeviceTypes(getAuto_(), deviceTypes) ||3023 hasDuplicateDeviceTypes(getIndependent(), deviceTypes) ||3024 hasDuplicateDeviceTypes(getSeq(), deviceTypes)) {3025 return emitError() << "only one of auto, independent, seq can be present "3026 "at the same time";3027 }3028 3029 // Check that at least one of auto, independent, or seq is present3030 // for the device-independent default clauses.3031 auto hasDeviceNone = [](mlir::acc::DeviceTypeAttr attr) -> bool {3032 return attr.getValue() == mlir::acc::DeviceType::None;3033 };3034 bool hasDefaultSeq =3035 getSeqAttr()3036 ? llvm::any_of(getSeqAttr().getAsRange<mlir::acc::DeviceTypeAttr>(),3037 hasDeviceNone)3038 : false;3039 bool hasDefaultIndependent =3040 getIndependentAttr()3041 ? llvm::any_of(3042 getIndependentAttr().getAsRange<mlir::acc::DeviceTypeAttr>(),3043 hasDeviceNone)3044 : false;3045 bool hasDefaultAuto =3046 getAuto_Attr()3047 ? llvm::any_of(getAuto_Attr().getAsRange<mlir::acc::DeviceTypeAttr>(),3048 hasDeviceNone)3049 : false;3050 if (!hasDefaultSeq && !hasDefaultIndependent && !hasDefaultAuto) {3051 return emitError()3052 << "at least one of auto, independent, seq must be present";3053 }3054 3055 // Gang, worker and vector are incompatible with seq.3056 if (getSeqAttr()) {3057 for (auto attr : getSeqAttr()) {3058 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);3059 if (hasVector(deviceTypeAttr.getValue()) ||3060 getVectorValue(deviceTypeAttr.getValue()) ||3061 hasWorker(deviceTypeAttr.getValue()) ||3062 getWorkerValue(deviceTypeAttr.getValue()) ||3063 hasGang(deviceTypeAttr.getValue()) ||3064 getGangValue(mlir::acc::GangArgType::Num,3065 deviceTypeAttr.getValue()) ||3066 getGangValue(mlir::acc::GangArgType::Dim,3067 deviceTypeAttr.getValue()) ||3068 getGangValue(mlir::acc::GangArgType::Static,3069 deviceTypeAttr.getValue()))3070 return emitError() << "gang, worker or vector cannot appear with seq";3071 }3072 }3073 3074 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,3075 mlir::acc::PrivateRecipeOp>(3076 *this, getPrivateOperands(), "private")))3077 return failure();3078 3079 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,3080 mlir::acc::FirstprivateRecipeOp>(3081 *this, getFirstprivateOperands(), "firstprivate")))3082 return failure();3083 3084 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,3085 mlir::acc::ReductionRecipeOp>(3086 *this, getReductionOperands(), "reduction")))3087 return failure();3088 3089 if (getCombined().has_value() &&3090 (getCombined().value() != acc::CombinedConstructsType::ParallelLoop &&3091 getCombined().value() != acc::CombinedConstructsType::KernelsLoop &&3092 getCombined().value() != acc::CombinedConstructsType::SerialLoop)) {3093 return emitError("unexpected combined constructs attribute");3094 }3095 3096 // Check non-empty body().3097 if (getRegion().empty())3098 return emitError("expected non-empty body.");3099 3100 if (getUnstructured()) {3101 if (!isContainerLike())3102 return emitError(3103 "unstructured acc.loop must not have induction variables");3104 } else if (isContainerLike()) {3105 // When it is container-like - it is expected to hold a loop-like operation.3106 // Obtain the maximum collapse count - we use this to check that there3107 // are enough loops contained.3108 uint64_t collapseCount = getCollapseValue().value_or(1);3109 if (getCollapseAttr()) {3110 for (auto collapseEntry : getCollapseAttr()) {3111 auto intAttr = mlir::dyn_cast<IntegerAttr>(collapseEntry);3112 if (intAttr.getValue().getZExtValue() > collapseCount)3113 collapseCount = intAttr.getValue().getZExtValue();3114 }3115 }3116 3117 // We want to check that we find enough loop-like operations inside.3118 // PreOrder walk allows us to walk in a breadth-first manner at each nesting3119 // level.3120 mlir::Operation *expectedParent = this->getOperation();3121 bool foundSibling = false;3122 getRegion().walk<WalkOrder::PreOrder>([&](mlir::Operation *op) {3123 if (mlir::isa<mlir::LoopLikeOpInterface>(op)) {3124 // This effectively checks that we are not looking at a sibling loop.3125 if (op->getParentOfType<mlir::LoopLikeOpInterface>() !=3126 expectedParent) {3127 foundSibling = true;3128 return mlir::WalkResult::interrupt();3129 }3130 3131 collapseCount--;3132 expectedParent = op;3133 }3134 // We found enough contained loops.3135 if (collapseCount == 0)3136 return mlir::WalkResult::interrupt();3137 return mlir::WalkResult::advance();3138 });3139 3140 if (foundSibling)3141 return emitError("found sibling loops inside container-like acc.loop");3142 if (collapseCount != 0)3143 return emitError("failed to find enough loop-like operations inside "3144 "container-like acc.loop");3145 }3146 3147 return success();3148}3149 3150unsigned LoopOp::getNumDataOperands() {3151 return getReductionOperands().size() + getPrivateOperands().size() +3152 getFirstprivateOperands().size();3153}3154 3155Value LoopOp::getDataOperand(unsigned i) {3156 unsigned numOptional =3157 getLowerbound().size() + getUpperbound().size() + getStep().size();3158 numOptional += getGangOperands().size();3159 numOptional += getVectorOperands().size();3160 numOptional += getWorkerNumOperands().size();3161 numOptional += getTileOperands().size();3162 numOptional += getCacheOperands().size();3163 return getOperand(numOptional + i);3164}3165 3166bool LoopOp::hasAuto() { return hasAuto(mlir::acc::DeviceType::None); }3167 3168bool LoopOp::hasAuto(mlir::acc::DeviceType deviceType) {3169 return hasDeviceType(getAuto_(), deviceType);3170}3171 3172bool LoopOp::hasIndependent() {3173 return hasIndependent(mlir::acc::DeviceType::None);3174}3175 3176bool LoopOp::hasIndependent(mlir::acc::DeviceType deviceType) {3177 return hasDeviceType(getIndependent(), deviceType);3178}3179 3180bool LoopOp::hasSeq() { return hasSeq(mlir::acc::DeviceType::None); }3181 3182bool LoopOp::hasSeq(mlir::acc::DeviceType deviceType) {3183 return hasDeviceType(getSeq(), deviceType);3184}3185 3186mlir::Value LoopOp::getVectorValue() {3187 return getVectorValue(mlir::acc::DeviceType::None);3188}3189 3190mlir::Value LoopOp::getVectorValue(mlir::acc::DeviceType deviceType) {3191 return getValueInDeviceTypeSegment(getVectorOperandsDeviceType(),3192 getVectorOperands(), deviceType);3193}3194 3195bool LoopOp::hasVector() { return hasVector(mlir::acc::DeviceType::None); }3196 3197bool LoopOp::hasVector(mlir::acc::DeviceType deviceType) {3198 return hasDeviceType(getVector(), deviceType);3199}3200 3201mlir::Value LoopOp::getWorkerValue() {3202 return getWorkerValue(mlir::acc::DeviceType::None);3203}3204 3205mlir::Value LoopOp::getWorkerValue(mlir::acc::DeviceType deviceType) {3206 return getValueInDeviceTypeSegment(getWorkerNumOperandsDeviceType(),3207 getWorkerNumOperands(), deviceType);3208}3209 3210bool LoopOp::hasWorker() { return hasWorker(mlir::acc::DeviceType::None); }3211 3212bool LoopOp::hasWorker(mlir::acc::DeviceType deviceType) {3213 return hasDeviceType(getWorker(), deviceType);3214}3215 3216mlir::Operation::operand_range LoopOp::getTileValues() {3217 return getTileValues(mlir::acc::DeviceType::None);3218}3219 3220mlir::Operation::operand_range3221LoopOp::getTileValues(mlir::acc::DeviceType deviceType) {3222 return getValuesFromSegments(getTileOperandsDeviceType(), getTileOperands(),3223 getTileOperandsSegments(), deviceType);3224}3225 3226std::optional<int64_t> LoopOp::getCollapseValue() {3227 return getCollapseValue(mlir::acc::DeviceType::None);3228}3229 3230std::optional<int64_t>3231LoopOp::getCollapseValue(mlir::acc::DeviceType deviceType) {3232 if (!getCollapseAttr())3233 return std::nullopt;3234 if (auto pos = findSegment(getCollapseDeviceTypeAttr(), deviceType)) {3235 auto intAttr =3236 mlir::dyn_cast<IntegerAttr>(getCollapseAttr().getValue()[*pos]);3237 return intAttr.getValue().getZExtValue();3238 }3239 return std::nullopt;3240}3241 3242mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType) {3243 return getGangValue(gangArgType, mlir::acc::DeviceType::None);3244}3245 3246mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType,3247 mlir::acc::DeviceType deviceType) {3248 if (getGangOperands().empty())3249 return {};3250 if (auto pos = findSegment(*getGangOperandsDeviceType(), deviceType)) {3251 int32_t nbOperandsBefore = 0;3252 for (unsigned i = 0; i < *pos; ++i)3253 nbOperandsBefore += (*getGangOperandsSegments())[i];3254 mlir::Operation::operand_range values =3255 getGangOperands()3256 .drop_front(nbOperandsBefore)3257 .take_front((*getGangOperandsSegments())[*pos]);3258 3259 int32_t argTypeIdx = nbOperandsBefore;3260 for (auto value : values) {3261 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(3262 (*getGangOperandsArgType())[argTypeIdx]);3263 if (gangArgTypeAttr.getValue() == gangArgType)3264 return value;3265 ++argTypeIdx;3266 }3267 }3268 return {};3269}3270 3271bool LoopOp::hasGang() { return hasGang(mlir::acc::DeviceType::None); }3272 3273bool LoopOp::hasGang(mlir::acc::DeviceType deviceType) {3274 return hasDeviceType(getGang(), deviceType);3275}3276 3277llvm::SmallVector<mlir::Region *> acc::LoopOp::getLoopRegions() {3278 return {&getRegion()};3279}3280 3281/// loop-control ::= `control` `(` ssa-id-and-type-list `)` `=`3282/// `(` ssa-id-and-type-list `)` `to` `(` ssa-id-and-type-list `)` `step`3283/// `(` ssa-id-and-type-list `)`3284/// region3285ParseResult3286parseLoopControl(OpAsmParser &parser, Region ®ion,3287 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &lowerbound,3288 SmallVectorImpl<Type> &lowerboundType,3289 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &upperbound,3290 SmallVectorImpl<Type> &upperboundType,3291 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &step,3292 SmallVectorImpl<Type> &stepType) {3293 3294 SmallVector<OpAsmParser::Argument> inductionVars;3295 if (succeeded(3296 parser.parseOptionalKeyword(acc::LoopOp::getControlKeyword()))) {3297 if (parser.parseLParen() ||3298 parser.parseArgumentList(inductionVars, OpAsmParser::Delimiter::None,3299 /*allowType=*/true) ||3300 parser.parseRParen() || parser.parseEqual() || parser.parseLParen() ||3301 parser.parseOperandList(lowerbound, inductionVars.size(),3302 OpAsmParser::Delimiter::None) ||3303 parser.parseColonTypeList(lowerboundType) || parser.parseRParen() ||3304 parser.parseKeyword("to") || parser.parseLParen() ||3305 parser.parseOperandList(upperbound, inductionVars.size(),3306 OpAsmParser::Delimiter::None) ||3307 parser.parseColonTypeList(upperboundType) || parser.parseRParen() ||3308 parser.parseKeyword("step") || parser.parseLParen() ||3309 parser.parseOperandList(step, inductionVars.size(),3310 OpAsmParser::Delimiter::None) ||3311 parser.parseColonTypeList(stepType) || parser.parseRParen())3312 return failure();3313 }3314 return parser.parseRegion(region, inductionVars);3315}3316 3317void printLoopControl(OpAsmPrinter &p, Operation *op, Region ®ion,3318 ValueRange lowerbound, TypeRange lowerboundType,3319 ValueRange upperbound, TypeRange upperboundType,3320 ValueRange steps, TypeRange stepType) {3321 ValueRange regionArgs = region.front().getArguments();3322 if (!regionArgs.empty()) {3323 p << acc::LoopOp::getControlKeyword() << "(";3324 llvm::interleaveComma(regionArgs, p,3325 [&p](Value v) { p << v << " : " << v.getType(); });3326 p << ") = (" << lowerbound << " : " << lowerboundType << ") to ("3327 << upperbound << " : " << upperboundType << ") " << " step (" << steps3328 << " : " << stepType << ") ";3329 }3330 p.printRegion(region, /*printEntryBlockArgs=*/false);3331}3332 3333void acc::LoopOp::addSeq(MLIRContext *context,3334 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3335 setSeqAttr(addDeviceTypeAffectedOperandHelper(context, getSeqAttr(),3336 effectiveDeviceTypes));3337}3338 3339void acc::LoopOp::addIndependent(3340 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3341 setIndependentAttr(addDeviceTypeAffectedOperandHelper(3342 context, getIndependentAttr(), effectiveDeviceTypes));3343}3344 3345void acc::LoopOp::addAuto(MLIRContext *context,3346 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3347 setAuto_Attr(addDeviceTypeAffectedOperandHelper(context, getAuto_Attr(),3348 effectiveDeviceTypes));3349}3350 3351void acc::LoopOp::setCollapseForDeviceTypes(3352 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,3353 llvm::APInt value) {3354 llvm::SmallVector<mlir::Attribute> newValues;3355 llvm::SmallVector<mlir::Attribute> newDeviceTypes;3356 3357 assert((getCollapseAttr() == nullptr) ==3358 (getCollapseDeviceTypeAttr() == nullptr));3359 assert(value.getBitWidth() == 64);3360 3361 if (getCollapseAttr()) {3362 for (const auto &existing :3363 llvm::zip_equal(getCollapseAttr(), getCollapseDeviceTypeAttr())) {3364 newValues.push_back(std::get<0>(existing));3365 newDeviceTypes.push_back(std::get<1>(existing));3366 }3367 }3368 3369 if (effectiveDeviceTypes.empty()) {3370 // If the effective device-types list is empty, this is before there are any3371 // being applied by device_type, so this should be added as a 'none'.3372 newValues.push_back(3373 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), value));3374 newDeviceTypes.push_back(3375 acc::DeviceTypeAttr::get(context, DeviceType::None));3376 } else {3377 for (DeviceType dt : effectiveDeviceTypes) {3378 newValues.push_back(3379 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), value));3380 newDeviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));3381 }3382 }3383 3384 setCollapseAttr(ArrayAttr::get(context, newValues));3385 setCollapseDeviceTypeAttr(ArrayAttr::get(context, newDeviceTypes));3386}3387 3388void acc::LoopOp::setTileForDeviceTypes(3389 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,3390 ValueRange values) {3391 llvm::SmallVector<int32_t> segments;3392 if (getTileOperandsSegments())3393 llvm::copy(*getTileOperandsSegments(), std::back_inserter(segments));3394 3395 setTileOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3396 context, getTileOperandsDeviceTypeAttr(), effectiveDeviceTypes, values,3397 getTileOperandsMutable(), segments));3398 3399 setTileOperandsSegments(segments);3400}3401 3402void acc::LoopOp::addVectorOperand(3403 MLIRContext *context, mlir::Value newValue,3404 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3405 setVectorOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3406 context, getVectorOperandsDeviceTypeAttr(), effectiveDeviceTypes,3407 newValue, getVectorOperandsMutable()));3408}3409 3410void acc::LoopOp::addEmptyVector(3411 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3412 setVectorAttr(addDeviceTypeAffectedOperandHelper(context, getVectorAttr(),3413 effectiveDeviceTypes));3414}3415 3416void acc::LoopOp::addWorkerNumOperand(3417 MLIRContext *context, mlir::Value newValue,3418 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3419 setWorkerNumOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3420 context, getWorkerNumOperandsDeviceTypeAttr(), effectiveDeviceTypes,3421 newValue, getWorkerNumOperandsMutable()));3422}3423 3424void acc::LoopOp::addEmptyWorker(3425 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3426 setWorkerAttr(addDeviceTypeAffectedOperandHelper(context, getWorkerAttr(),3427 effectiveDeviceTypes));3428}3429 3430void acc::LoopOp::addEmptyGang(3431 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3432 setGangAttr(addDeviceTypeAffectedOperandHelper(context, getGangAttr(),3433 effectiveDeviceTypes));3434}3435 3436bool acc::LoopOp::hasParallelismFlag(DeviceType dt) {3437 auto hasDevice = [=](DeviceTypeAttr attr) -> bool {3438 return attr.getValue() == dt;3439 };3440 auto testFromArr = [=](ArrayAttr arr) -> bool {3441 return llvm::any_of(arr.getAsRange<DeviceTypeAttr>(), hasDevice);3442 };3443 3444 if (ArrayAttr arr = getSeqAttr(); arr && testFromArr(arr))3445 return true;3446 if (ArrayAttr arr = getIndependentAttr(); arr && testFromArr(arr))3447 return true;3448 if (ArrayAttr arr = getAuto_Attr(); arr && testFromArr(arr))3449 return true;3450 3451 return false;3452}3453 3454bool acc::LoopOp::hasDefaultGangWorkerVector() {3455 return hasVector() || getVectorValue() || hasWorker() || getWorkerValue() ||3456 hasGang() || getGangValue(GangArgType::Num) ||3457 getGangValue(GangArgType::Dim) || getGangValue(GangArgType::Static);3458}3459 3460acc::LoopParMode3461acc::LoopOp::getDefaultOrDeviceTypeParallelism(DeviceType deviceType) {3462 if (hasSeq(deviceType))3463 return LoopParMode::loop_seq;3464 if (hasAuto(deviceType))3465 return LoopParMode::loop_auto;3466 if (hasIndependent(deviceType))3467 return LoopParMode::loop_independent;3468 if (hasSeq())3469 return LoopParMode::loop_seq;3470 if (hasAuto())3471 return LoopParMode::loop_auto;3472 assert(hasIndependent() &&3473 "loop must have default auto, seq, or independent");3474 return LoopParMode::loop_independent;3475}3476 3477void acc::LoopOp::addGangOperands(3478 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,3479 llvm::ArrayRef<GangArgType> argTypes, mlir::ValueRange values) {3480 llvm::SmallVector<int32_t> segments;3481 if (std::optional<ArrayRef<int32_t>> existingSegments =3482 getGangOperandsSegments())3483 llvm::copy(*existingSegments, std::back_inserter(segments));3484 3485 unsigned beforeCount = segments.size();3486 3487 setGangOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3488 context, getGangOperandsDeviceTypeAttr(), effectiveDeviceTypes, values,3489 getGangOperandsMutable(), segments));3490 3491 setGangOperandsSegments(segments);3492 3493 // This is a bit of extra work to make sure we update the 'types' correctly by3494 // adding to the types collection the correct number of times. We could3495 // potentially add something similar to the3496 // addDeviceTypeAffectedOperandHelper, but it seems that would be pretty3497 // excessive for a one-off case.3498 unsigned numAdded = segments.size() - beforeCount;3499 3500 if (numAdded > 0) {3501 llvm::SmallVector<mlir::Attribute> gangTypes;3502 if (getGangOperandsArgTypeAttr())3503 llvm::copy(getGangOperandsArgTypeAttr(), std::back_inserter(gangTypes));3504 3505 for (auto i : llvm::index_range(0u, numAdded)) {3506 llvm::transform(argTypes, std::back_inserter(gangTypes),3507 [=](mlir::acc::GangArgType gangTy) {3508 return mlir::acc::GangArgTypeAttr::get(context, gangTy);3509 });3510 (void)i;3511 }3512 3513 setGangOperandsArgTypeAttr(mlir::ArrayAttr::get(context, gangTypes));3514 }3515}3516 3517void acc::LoopOp::addPrivatization(MLIRContext *context,3518 mlir::acc::PrivateOp op,3519 mlir::acc::PrivateRecipeOp recipe) {3520 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));3521 getPrivateOperandsMutable().append(op.getResult());3522}3523 3524void acc::LoopOp::addFirstPrivatization(3525 MLIRContext *context, mlir::acc::FirstprivateOp op,3526 mlir::acc::FirstprivateRecipeOp recipe) {3527 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));3528 getFirstprivateOperandsMutable().append(op.getResult());3529}3530 3531void acc::LoopOp::addReduction(MLIRContext *context, mlir::acc::ReductionOp op,3532 mlir::acc::ReductionRecipeOp recipe) {3533 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));3534 getReductionOperandsMutable().append(op.getResult());3535}3536 3537//===----------------------------------------------------------------------===//3538// DataOp3539//===----------------------------------------------------------------------===//3540 3541LogicalResult acc::DataOp::verify() {3542 // 2.6.5. Data Construct restriction3543 // At least one copy, copyin, copyout, create, no_create, present, deviceptr,3544 // attach, or default clause must appear on a data construct.3545 if (getOperands().empty() && !getDefaultAttr())3546 return emitError("at least one operand or the default attribute "3547 "must appear on the data operation");3548 3549 for (mlir::Value operand : getDataClauseOperands())3550 if (isa<BlockArgument>(operand) ||3551 !mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,3552 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,3553 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp>(3554 operand.getDefiningOp()))3555 return emitError("expect data entry/exit operation or acc.getdeviceptr "3556 "as defining op");3557 3558 if (failed(checkWaitAndAsyncConflict<acc::DataOp>(*this)))3559 return failure();3560 3561 return success();3562}3563 3564unsigned DataOp::getNumDataOperands() { return getDataClauseOperands().size(); }3565 3566Value DataOp::getDataOperand(unsigned i) {3567 unsigned numOptional = getIfCond() ? 1 : 0;3568 numOptional += getAsyncOperands().size() ? 1 : 0;3569 numOptional += getWaitOperands().size();3570 return getOperand(numOptional + i);3571}3572 3573bool acc::DataOp::hasAsyncOnly() {3574 return hasAsyncOnly(mlir::acc::DeviceType::None);3575}3576 3577bool acc::DataOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {3578 return hasDeviceType(getAsyncOnly(), deviceType);3579}3580 3581mlir::Value DataOp::getAsyncValue() {3582 return getAsyncValue(mlir::acc::DeviceType::None);3583}3584 3585mlir::Value DataOp::getAsyncValue(mlir::acc::DeviceType deviceType) {3586 return getValueInDeviceTypeSegment(getAsyncOperandsDeviceType(),3587 getAsyncOperands(), deviceType);3588}3589 3590bool DataOp::hasWaitOnly() { return hasWaitOnly(mlir::acc::DeviceType::None); }3591 3592bool DataOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {3593 return hasDeviceType(getWaitOnly(), deviceType);3594}3595 3596mlir::Operation::operand_range DataOp::getWaitValues() {3597 return getWaitValues(mlir::acc::DeviceType::None);3598}3599 3600mlir::Operation::operand_range3601DataOp::getWaitValues(mlir::acc::DeviceType deviceType) {3602 return getWaitValuesWithoutDevnum(3603 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),3604 getHasWaitDevnum(), deviceType);3605}3606 3607mlir::Value DataOp::getWaitDevnum() {3608 return getWaitDevnum(mlir::acc::DeviceType::None);3609}3610 3611mlir::Value DataOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {3612 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),3613 getWaitOperandsSegments(), getHasWaitDevnum(),3614 deviceType);3615}3616 3617void acc::DataOp::addAsyncOnly(3618 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3619 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(3620 context, getAsyncOnlyAttr(), effectiveDeviceTypes));3621}3622 3623void acc::DataOp::addAsyncOperand(3624 MLIRContext *context, mlir::Value newValue,3625 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3626 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3627 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,3628 getAsyncOperandsMutable()));3629}3630 3631void acc::DataOp::addWaitOnly(MLIRContext *context,3632 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3633 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),3634 effectiveDeviceTypes));3635}3636 3637void acc::DataOp::addWaitOperands(3638 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,3639 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3640 3641 llvm::SmallVector<int32_t> segments;3642 if (getWaitOperandsSegments())3643 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));3644 3645 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(3646 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,3647 getWaitOperandsMutable(), segments));3648 setWaitOperandsSegments(segments);3649 3650 llvm::SmallVector<mlir::Attribute> hasDevnums;3651 if (getHasWaitDevnumAttr())3652 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));3653 hasDevnums.insert(3654 hasDevnums.end(),3655 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),3656 mlir::BoolAttr::get(context, hasDevnum));3657 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));3658}3659 3660//===----------------------------------------------------------------------===//3661// ExitDataOp3662//===----------------------------------------------------------------------===//3663 3664LogicalResult acc::ExitDataOp::verify() {3665 // 2.6.6. Data Exit Directive restriction3666 // At least one copyout, delete, or detach clause must appear on an exit data3667 // directive.3668 if (getDataClauseOperands().empty())3669 return emitError("at least one operand must be present in dataOperands on "3670 "the exit data operation");3671 3672 // The async attribute represent the async clause without value. Therefore the3673 // attribute and operand cannot appear at the same time.3674 if (getAsyncOperand() && getAsync())3675 return emitError("async attribute cannot appear with asyncOperand");3676 3677 // The wait attribute represent the wait clause without values. Therefore the3678 // attribute and operands cannot appear at the same time.3679 if (!getWaitOperands().empty() && getWait())3680 return emitError("wait attribute cannot appear with waitOperands");3681 3682 if (getWaitDevnum() && getWaitOperands().empty())3683 return emitError("wait_devnum cannot appear without waitOperands");3684 3685 return success();3686}3687 3688unsigned ExitDataOp::getNumDataOperands() {3689 return getDataClauseOperands().size();3690}3691 3692Value ExitDataOp::getDataOperand(unsigned i) {3693 unsigned numOptional = getIfCond() ? 1 : 0;3694 numOptional += getAsyncOperand() ? 1 : 0;3695 numOptional += getWaitDevnum() ? 1 : 0;3696 return getOperand(getWaitOperands().size() + numOptional + i);3697}3698 3699void ExitDataOp::getCanonicalizationPatterns(RewritePatternSet &results,3700 MLIRContext *context) {3701 results.add<RemoveConstantIfCondition<ExitDataOp>>(context);3702}3703 3704void ExitDataOp::addAsyncOnly(MLIRContext *context,3705 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3706 assert(effectiveDeviceTypes.empty());3707 assert(!getAsyncAttr());3708 assert(!getAsyncOperand());3709 3710 setAsyncAttr(mlir::UnitAttr::get(context));3711}3712 3713void ExitDataOp::addAsyncOperand(3714 MLIRContext *context, mlir::Value newValue,3715 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3716 assert(effectiveDeviceTypes.empty());3717 assert(!getAsyncAttr());3718 assert(!getAsyncOperand());3719 3720 getAsyncOperandMutable().append(newValue);3721}3722 3723void ExitDataOp::addWaitOnly(MLIRContext *context,3724 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3725 assert(effectiveDeviceTypes.empty());3726 assert(!getWaitAttr());3727 assert(getWaitOperands().empty());3728 assert(!getWaitDevnum());3729 3730 setWaitAttr(mlir::UnitAttr::get(context));3731}3732 3733void ExitDataOp::addWaitOperands(3734 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,3735 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3736 assert(effectiveDeviceTypes.empty());3737 assert(!getWaitAttr());3738 assert(getWaitOperands().empty());3739 assert(!getWaitDevnum());3740 3741 // if hasDevnum, the first value is the devnum. The 'rest' go into the3742 // operands list.3743 if (hasDevnum) {3744 getWaitDevnumMutable().append(newValues.front());3745 newValues = newValues.drop_front();3746 }3747 3748 getWaitOperandsMutable().append(newValues);3749}3750 3751//===----------------------------------------------------------------------===//3752// EnterDataOp3753//===----------------------------------------------------------------------===//3754 3755LogicalResult acc::EnterDataOp::verify() {3756 // 2.6.6. Data Enter Directive restriction3757 // At least one copyin, create, or attach clause must appear on an enter data3758 // directive.3759 if (getDataClauseOperands().empty())3760 return emitError("at least one operand must be present in dataOperands on "3761 "the enter data operation");3762 3763 // The async attribute represent the async clause without value. Therefore the3764 // attribute and operand cannot appear at the same time.3765 if (getAsyncOperand() && getAsync())3766 return emitError("async attribute cannot appear with asyncOperand");3767 3768 // The wait attribute represent the wait clause without values. Therefore the3769 // attribute and operands cannot appear at the same time.3770 if (!getWaitOperands().empty() && getWait())3771 return emitError("wait attribute cannot appear with waitOperands");3772 3773 if (getWaitDevnum() && getWaitOperands().empty())3774 return emitError("wait_devnum cannot appear without waitOperands");3775 3776 for (mlir::Value operand : getDataClauseOperands())3777 if (!mlir::isa<acc::AttachOp, acc::CreateOp, acc::CopyinOp>(3778 operand.getDefiningOp()))3779 return emitError("expect data entry operation as defining op");3780 3781 return success();3782}3783 3784unsigned EnterDataOp::getNumDataOperands() {3785 return getDataClauseOperands().size();3786}3787 3788Value EnterDataOp::getDataOperand(unsigned i) {3789 unsigned numOptional = getIfCond() ? 1 : 0;3790 numOptional += getAsyncOperand() ? 1 : 0;3791 numOptional += getWaitDevnum() ? 1 : 0;3792 return getOperand(getWaitOperands().size() + numOptional + i);3793}3794 3795void EnterDataOp::getCanonicalizationPatterns(RewritePatternSet &results,3796 MLIRContext *context) {3797 results.add<RemoveConstantIfCondition<EnterDataOp>>(context);3798}3799 3800void EnterDataOp::addAsyncOnly(3801 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3802 assert(effectiveDeviceTypes.empty());3803 assert(!getAsyncAttr());3804 assert(!getAsyncOperand());3805 3806 setAsyncAttr(mlir::UnitAttr::get(context));3807}3808 3809void EnterDataOp::addAsyncOperand(3810 MLIRContext *context, mlir::Value newValue,3811 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3812 assert(effectiveDeviceTypes.empty());3813 assert(!getAsyncAttr());3814 assert(!getAsyncOperand());3815 3816 getAsyncOperandMutable().append(newValue);3817}3818 3819void EnterDataOp::addWaitOnly(MLIRContext *context,3820 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3821 assert(effectiveDeviceTypes.empty());3822 assert(!getWaitAttr());3823 assert(getWaitOperands().empty());3824 assert(!getWaitDevnum());3825 3826 setWaitAttr(mlir::UnitAttr::get(context));3827}3828 3829void EnterDataOp::addWaitOperands(3830 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,3831 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {3832 assert(effectiveDeviceTypes.empty());3833 assert(!getWaitAttr());3834 assert(getWaitOperands().empty());3835 assert(!getWaitDevnum());3836 3837 // if hasDevnum, the first value is the devnum. The 'rest' go into the3838 // operands list.3839 if (hasDevnum) {3840 getWaitDevnumMutable().append(newValues.front());3841 newValues = newValues.drop_front();3842 }3843 3844 getWaitOperandsMutable().append(newValues);3845}3846 3847//===----------------------------------------------------------------------===//3848// AtomicReadOp3849//===----------------------------------------------------------------------===//3850 3851LogicalResult AtomicReadOp::verify() { return verifyCommon(); }3852 3853//===----------------------------------------------------------------------===//3854// AtomicWriteOp3855//===----------------------------------------------------------------------===//3856 3857LogicalResult AtomicWriteOp::verify() { return verifyCommon(); }3858 3859//===----------------------------------------------------------------------===//3860// AtomicUpdateOp3861//===----------------------------------------------------------------------===//3862 3863LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,3864 PatternRewriter &rewriter) {3865 if (op.isNoOp()) {3866 rewriter.eraseOp(op);3867 return success();3868 }3869 3870 if (Value writeVal = op.getWriteOpVal()) {3871 rewriter.replaceOpWithNewOp<AtomicWriteOp>(op, op.getX(), writeVal,3872 op.getIfCond());3873 return success();3874 }3875 3876 return failure();3877}3878 3879LogicalResult AtomicUpdateOp::verify() { return verifyCommon(); }3880 3881LogicalResult AtomicUpdateOp::verifyRegions() { return verifyRegionsCommon(); }3882 3883//===----------------------------------------------------------------------===//3884// AtomicCaptureOp3885//===----------------------------------------------------------------------===//3886 3887AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {3888 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))3889 return op;3890 return dyn_cast<AtomicReadOp>(getSecondOp());3891}3892 3893AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {3894 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))3895 return op;3896 return dyn_cast<AtomicWriteOp>(getSecondOp());3897}3898 3899AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {3900 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))3901 return op;3902 return dyn_cast<AtomicUpdateOp>(getSecondOp());3903}3904 3905LogicalResult AtomicCaptureOp::verifyRegions() { return verifyRegionsCommon(); }3906 3907//===----------------------------------------------------------------------===//3908// DeclareEnterOp3909//===----------------------------------------------------------------------===//3910 3911template <typename Op>3912static LogicalResult3913checkDeclareOperands(Op &op, const mlir::ValueRange &operands,3914 bool requireAtLeastOneOperand = true) {3915 if (operands.empty() && requireAtLeastOneOperand)3916 return emitError(3917 op->getLoc(),3918 "at least one operand must appear on the declare operation");3919 3920 for (mlir::Value operand : operands) {3921 if (isa<BlockArgument>(operand) ||3922 !mlir::isa<acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,3923 acc::DevicePtrOp, acc::GetDevicePtrOp, acc::PresentOp,3924 acc::DeclareDeviceResidentOp, acc::DeclareLinkOp>(3925 operand.getDefiningOp()))3926 return op.emitError(3927 "expect valid declare data entry operation or acc.getdeviceptr "3928 "as defining op");3929 3930 mlir::Value var{getVar(operand.getDefiningOp())};3931 assert(var && "declare operands can only be data entry operations which "3932 "must have var");3933 (void)var;3934 std::optional<mlir::acc::DataClause> dataClauseOptional{3935 getDataClause(operand.getDefiningOp())};3936 assert(dataClauseOptional.has_value() &&3937 "declare operands can only be data entry operations which must have "3938 "dataClause");3939 (void)dataClauseOptional;3940 }3941 3942 return success();3943}3944 3945LogicalResult acc::DeclareEnterOp::verify() {3946 return checkDeclareOperands(*this, this->getDataClauseOperands());3947}3948 3949//===----------------------------------------------------------------------===//3950// DeclareExitOp3951//===----------------------------------------------------------------------===//3952 3953LogicalResult acc::DeclareExitOp::verify() {3954 if (getToken())3955 return checkDeclareOperands(*this, this->getDataClauseOperands(),3956 /*requireAtLeastOneOperand=*/false);3957 return checkDeclareOperands(*this, this->getDataClauseOperands());3958}3959 3960//===----------------------------------------------------------------------===//3961// DeclareOp3962//===----------------------------------------------------------------------===//3963 3964LogicalResult acc::DeclareOp::verify() {3965 return checkDeclareOperands(*this, this->getDataClauseOperands());3966}3967 3968//===----------------------------------------------------------------------===//3969// RoutineOp3970//===----------------------------------------------------------------------===//3971 3972static unsigned getParallelismForDeviceType(acc::RoutineOp op,3973 acc::DeviceType dtype) {3974 unsigned parallelism = 0;3975 parallelism += (op.hasGang(dtype) || op.getGangDimValue(dtype)) ? 1 : 0;3976 parallelism += op.hasWorker(dtype) ? 1 : 0;3977 parallelism += op.hasVector(dtype) ? 1 : 0;3978 parallelism += op.hasSeq(dtype) ? 1 : 0;3979 return parallelism;3980}3981 3982LogicalResult acc::RoutineOp::verify() {3983 unsigned baseParallelism =3984 getParallelismForDeviceType(*this, acc::DeviceType::None);3985 3986 if (baseParallelism > 1)3987 return emitError() << "only one of `gang`, `worker`, `vector`, `seq` can "3988 "be present at the same time";3989 3990 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();3991 ++dtypeInt) {3992 auto dtype = static_cast<acc::DeviceType>(dtypeInt);3993 if (dtype == acc::DeviceType::None)3994 continue;3995 unsigned parallelism = getParallelismForDeviceType(*this, dtype);3996 3997 if (parallelism > 1 || (baseParallelism == 1 && parallelism == 1))3998 return emitError() << "only one of `gang`, `worker`, `vector`, `seq` can "3999 "be present at the same time";4000 }4001 4002 return success();4003}4004 4005static ParseResult parseBindName(OpAsmParser &parser,4006 mlir::ArrayAttr &bindIdName,4007 mlir::ArrayAttr &bindStrName,4008 mlir::ArrayAttr &deviceIdTypes,4009 mlir::ArrayAttr &deviceStrTypes) {4010 llvm::SmallVector<mlir::Attribute> bindIdNameAttrs;4011 llvm::SmallVector<mlir::Attribute> bindStrNameAttrs;4012 llvm::SmallVector<mlir::Attribute> deviceIdTypeAttrs;4013 llvm::SmallVector<mlir::Attribute> deviceStrTypeAttrs;4014 4015 if (failed(parser.parseCommaSeparatedList([&]() {4016 mlir::Attribute newAttr;4017 bool isSymbolRefAttr;4018 auto parseResult = parser.parseAttribute(newAttr);4019 if (auto symbolRefAttr = dyn_cast<mlir::SymbolRefAttr>(newAttr)) {4020 bindIdNameAttrs.push_back(symbolRefAttr);4021 isSymbolRefAttr = true;4022 } else if (auto stringAttr = dyn_cast<mlir::StringAttr>(newAttr)) {4023 bindStrNameAttrs.push_back(stringAttr);4024 isSymbolRefAttr = false;4025 }4026 if (parseResult)4027 return failure();4028 if (failed(parser.parseOptionalLSquare())) {4029 if (isSymbolRefAttr) {4030 deviceIdTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(4031 parser.getContext(), mlir::acc::DeviceType::None));4032 } else {4033 deviceStrTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(4034 parser.getContext(), mlir::acc::DeviceType::None));4035 }4036 } else {4037 if (isSymbolRefAttr) {4038 if (parser.parseAttribute(deviceIdTypeAttrs.emplace_back()) ||4039 parser.parseRSquare())4040 return failure();4041 } else {4042 if (parser.parseAttribute(deviceStrTypeAttrs.emplace_back()) ||4043 parser.parseRSquare())4044 return failure();4045 }4046 }4047 return success();4048 })))4049 return failure();4050 4051 bindIdName = ArrayAttr::get(parser.getContext(), bindIdNameAttrs);4052 bindStrName = ArrayAttr::get(parser.getContext(), bindStrNameAttrs);4053 deviceIdTypes = ArrayAttr::get(parser.getContext(), deviceIdTypeAttrs);4054 deviceStrTypes = ArrayAttr::get(parser.getContext(), deviceStrTypeAttrs);4055 4056 return success();4057}4058 4059static void printBindName(mlir::OpAsmPrinter &p, mlir::Operation *op,4060 std::optional<mlir::ArrayAttr> bindIdName,4061 std::optional<mlir::ArrayAttr> bindStrName,4062 std::optional<mlir::ArrayAttr> deviceIdTypes,4063 std::optional<mlir::ArrayAttr> deviceStrTypes) {4064 // Create combined vectors for all bind names and device types4065 llvm::SmallVector<mlir::Attribute> allBindNames;4066 llvm::SmallVector<mlir::Attribute> allDeviceTypes;4067 4068 // Append bindIdName and deviceIdTypes4069 if (hasDeviceTypeValues(deviceIdTypes)) {4070 allBindNames.append(bindIdName->begin(), bindIdName->end());4071 allDeviceTypes.append(deviceIdTypes->begin(), deviceIdTypes->end());4072 }4073 4074 // Append bindStrName and deviceStrTypes4075 if (hasDeviceTypeValues(deviceStrTypes)) {4076 allBindNames.append(bindStrName->begin(), bindStrName->end());4077 allDeviceTypes.append(deviceStrTypes->begin(), deviceStrTypes->end());4078 }4079 4080 // Print the combined sequence4081 if (!allBindNames.empty())4082 llvm::interleaveComma(llvm::zip(allBindNames, allDeviceTypes), p,4083 [&](const auto &pair) {4084 p << std::get<0>(pair);4085 printSingleDeviceType(p, std::get<1>(pair));4086 });4087}4088 4089static ParseResult parseRoutineGangClause(OpAsmParser &parser,4090 mlir::ArrayAttr &gang,4091 mlir::ArrayAttr &gangDim,4092 mlir::ArrayAttr &gangDimDeviceTypes) {4093 4094 llvm::SmallVector<mlir::Attribute> gangAttrs, gangDimAttrs,4095 gangDimDeviceTypeAttrs;4096 bool needCommaBeforeOperands = false;4097 4098 // Gang keyword only4099 if (failed(parser.parseOptionalLParen())) {4100 gangAttrs.push_back(mlir::acc::DeviceTypeAttr::get(4101 parser.getContext(), mlir::acc::DeviceType::None));4102 gang = ArrayAttr::get(parser.getContext(), gangAttrs);4103 return success();4104 }4105 4106 // Parse keyword only attributes4107 if (succeeded(parser.parseOptionalLSquare())) {4108 if (failed(parser.parseCommaSeparatedList([&]() {4109 if (parser.parseAttribute(gangAttrs.emplace_back()))4110 return failure();4111 return success();4112 })))4113 return failure();4114 if (parser.parseRSquare())4115 return failure();4116 needCommaBeforeOperands = true;4117 }4118 4119 if (needCommaBeforeOperands && failed(parser.parseComma()))4120 return failure();4121 4122 if (failed(parser.parseCommaSeparatedList([&]() {4123 if (parser.parseKeyword(acc::RoutineOp::getGangDimKeyword()) ||4124 parser.parseColon() ||4125 parser.parseAttribute(gangDimAttrs.emplace_back()))4126 return failure();4127 if (succeeded(parser.parseOptionalLSquare())) {4128 if (parser.parseAttribute(gangDimDeviceTypeAttrs.emplace_back()) ||4129 parser.parseRSquare())4130 return failure();4131 } else {4132 gangDimDeviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(4133 parser.getContext(), mlir::acc::DeviceType::None));4134 }4135 return success();4136 })))4137 return failure();4138 4139 if (failed(parser.parseRParen()))4140 return failure();4141 4142 gang = ArrayAttr::get(parser.getContext(), gangAttrs);4143 gangDim = ArrayAttr::get(parser.getContext(), gangDimAttrs);4144 gangDimDeviceTypes =4145 ArrayAttr::get(parser.getContext(), gangDimDeviceTypeAttrs);4146 4147 return success();4148}4149 4150void printRoutineGangClause(OpAsmPrinter &p, Operation *op,4151 std::optional<mlir::ArrayAttr> gang,4152 std::optional<mlir::ArrayAttr> gangDim,4153 std::optional<mlir::ArrayAttr> gangDimDeviceTypes) {4154 4155 if (!hasDeviceTypeValues(gangDimDeviceTypes) && hasDeviceTypeValues(gang) &&4156 gang->size() == 1) {4157 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*gang)[0]);4158 if (deviceTypeAttr.getValue() == mlir::acc::DeviceType::None)4159 return;4160 }4161 4162 p << "(";4163 4164 printDeviceTypes(p, gang);4165 4166 if (hasDeviceTypeValues(gang) && hasDeviceTypeValues(gangDimDeviceTypes))4167 p << ", ";4168 4169 if (hasDeviceTypeValues(gangDimDeviceTypes))4170 llvm::interleaveComma(llvm::zip(*gangDim, *gangDimDeviceTypes), p,4171 [&](const auto &pair) {4172 p << acc::RoutineOp::getGangDimKeyword() << ": ";4173 p << std::get<0>(pair);4174 printSingleDeviceType(p, std::get<1>(pair));4175 });4176 4177 p << ")";4178}4179 4180static ParseResult parseDeviceTypeArrayAttr(OpAsmParser &parser,4181 mlir::ArrayAttr &deviceTypes) {4182 llvm::SmallVector<mlir::Attribute> attributes;4183 // Keyword only4184 if (failed(parser.parseOptionalLParen())) {4185 attributes.push_back(mlir::acc::DeviceTypeAttr::get(4186 parser.getContext(), mlir::acc::DeviceType::None));4187 deviceTypes = ArrayAttr::get(parser.getContext(), attributes);4188 return success();4189 }4190 4191 // Parse device type attributes4192 if (succeeded(parser.parseOptionalLSquare())) {4193 if (failed(parser.parseCommaSeparatedList([&]() {4194 if (parser.parseAttribute(attributes.emplace_back()))4195 return failure();4196 return success();4197 })))4198 return failure();4199 if (parser.parseRSquare() || parser.parseRParen())4200 return failure();4201 }4202 deviceTypes = ArrayAttr::get(parser.getContext(), attributes);4203 return success();4204}4205 4206static void4207printDeviceTypeArrayAttr(mlir::OpAsmPrinter &p, mlir::Operation *op,4208 std::optional<mlir::ArrayAttr> deviceTypes) {4209 4210 if (hasDeviceTypeValues(deviceTypes) && deviceTypes->size() == 1) {4211 auto deviceTypeAttr =4212 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*deviceTypes)[0]);4213 if (deviceTypeAttr.getValue() == mlir::acc::DeviceType::None)4214 return;4215 }4216 4217 if (!hasDeviceTypeValues(deviceTypes))4218 return;4219 4220 p << "([";4221 llvm::interleaveComma(*deviceTypes, p, [&](mlir::Attribute attr) {4222 auto dTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);4223 p << dTypeAttr;4224 });4225 p << "])";4226}4227 4228bool RoutineOp::hasWorker() { return hasWorker(mlir::acc::DeviceType::None); }4229 4230bool RoutineOp::hasWorker(mlir::acc::DeviceType deviceType) {4231 return hasDeviceType(getWorker(), deviceType);4232}4233 4234bool RoutineOp::hasVector() { return hasVector(mlir::acc::DeviceType::None); }4235 4236bool RoutineOp::hasVector(mlir::acc::DeviceType deviceType) {4237 return hasDeviceType(getVector(), deviceType);4238}4239 4240bool RoutineOp::hasSeq() { return hasSeq(mlir::acc::DeviceType::None); }4241 4242bool RoutineOp::hasSeq(mlir::acc::DeviceType deviceType) {4243 return hasDeviceType(getSeq(), deviceType);4244}4245 4246std::optional<std::variant<mlir::SymbolRefAttr, mlir::StringAttr>>4247RoutineOp::getBindNameValue() {4248 return getBindNameValue(mlir::acc::DeviceType::None);4249}4250 4251std::optional<std::variant<mlir::SymbolRefAttr, mlir::StringAttr>>4252RoutineOp::getBindNameValue(mlir::acc::DeviceType deviceType) {4253 if (!hasDeviceTypeValues(getBindIdNameDeviceType()) &&4254 !hasDeviceTypeValues(getBindStrNameDeviceType())) {4255 return std::nullopt;4256 }4257 4258 if (auto pos = findSegment(*getBindIdNameDeviceType(), deviceType)) {4259 auto attr = (*getBindIdName())[*pos];4260 auto symbolRefAttr = dyn_cast<mlir::SymbolRefAttr>(attr);4261 assert(symbolRefAttr && "expected SymbolRef");4262 return symbolRefAttr;4263 }4264 4265 if (auto pos = findSegment(*getBindStrNameDeviceType(), deviceType)) {4266 auto attr = (*getBindStrName())[*pos];4267 auto stringAttr = dyn_cast<mlir::StringAttr>(attr);4268 assert(stringAttr && "expected String");4269 return stringAttr;4270 }4271 4272 return std::nullopt;4273}4274 4275bool RoutineOp::hasGang() { return hasGang(mlir::acc::DeviceType::None); }4276 4277bool RoutineOp::hasGang(mlir::acc::DeviceType deviceType) {4278 return hasDeviceType(getGang(), deviceType);4279}4280 4281std::optional<int64_t> RoutineOp::getGangDimValue() {4282 return getGangDimValue(mlir::acc::DeviceType::None);4283}4284 4285std::optional<int64_t>4286RoutineOp::getGangDimValue(mlir::acc::DeviceType deviceType) {4287 if (!hasDeviceTypeValues(getGangDimDeviceType()))4288 return std::nullopt;4289 if (auto pos = findSegment(*getGangDimDeviceType(), deviceType)) {4290 auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>((*getGangDim())[*pos]);4291 return intAttr.getInt();4292 }4293 return std::nullopt;4294}4295 4296//===----------------------------------------------------------------------===//4297// InitOp4298//===----------------------------------------------------------------------===//4299 4300LogicalResult acc::InitOp::verify() {4301 Operation *currOp = *this;4302 while ((currOp = currOp->getParentOp()))4303 if (isComputeOperation(currOp))4304 return emitOpError("cannot be nested in a compute operation");4305 return success();4306}4307 4308void acc::InitOp::addDeviceType(MLIRContext *context,4309 mlir::acc::DeviceType deviceType) {4310 llvm::SmallVector<mlir::Attribute> deviceTypes;4311 if (getDeviceTypesAttr())4312 llvm::copy(getDeviceTypesAttr(), std::back_inserter(deviceTypes));4313 4314 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, deviceType));4315 setDeviceTypesAttr(mlir::ArrayAttr::get(context, deviceTypes));4316}4317 4318//===----------------------------------------------------------------------===//4319// ShutdownOp4320//===----------------------------------------------------------------------===//4321 4322LogicalResult acc::ShutdownOp::verify() {4323 Operation *currOp = *this;4324 while ((currOp = currOp->getParentOp()))4325 if (isComputeOperation(currOp))4326 return emitOpError("cannot be nested in a compute operation");4327 return success();4328}4329 4330void acc::ShutdownOp::addDeviceType(MLIRContext *context,4331 mlir::acc::DeviceType deviceType) {4332 llvm::SmallVector<mlir::Attribute> deviceTypes;4333 if (getDeviceTypesAttr())4334 llvm::copy(getDeviceTypesAttr(), std::back_inserter(deviceTypes));4335 4336 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, deviceType));4337 setDeviceTypesAttr(mlir::ArrayAttr::get(context, deviceTypes));4338}4339 4340//===----------------------------------------------------------------------===//4341// SetOp4342//===----------------------------------------------------------------------===//4343 4344LogicalResult acc::SetOp::verify() {4345 Operation *currOp = *this;4346 while ((currOp = currOp->getParentOp()))4347 if (isComputeOperation(currOp))4348 return emitOpError("cannot be nested in a compute operation");4349 if (!getDeviceTypeAttr() && !getDefaultAsync() && !getDeviceNum())4350 return emitOpError("at least one default_async, device_num, or device_type "4351 "operand must appear");4352 return success();4353}4354 4355//===----------------------------------------------------------------------===//4356// UpdateOp4357//===----------------------------------------------------------------------===//4358 4359LogicalResult acc::UpdateOp::verify() {4360 // At least one of host or device should have a value.4361 if (getDataClauseOperands().empty())4362 return emitError("at least one value must be present in dataOperands");4363 4364 if (failed(verifyDeviceTypeCountMatch(*this, getAsyncOperands(),4365 getAsyncOperandsDeviceTypeAttr(),4366 "async")))4367 return failure();4368 4369 if (failed(verifyDeviceTypeAndSegmentCountMatch(4370 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),4371 getWaitOperandsDeviceTypeAttr(), "wait")))4372 return failure();4373 4374 if (failed(checkWaitAndAsyncConflict<acc::UpdateOp>(*this)))4375 return failure();4376 4377 for (mlir::Value operand : getDataClauseOperands())4378 if (!mlir::isa<acc::UpdateDeviceOp, acc::UpdateHostOp, acc::GetDevicePtrOp>(4379 operand.getDefiningOp()))4380 return emitError("expect data entry/exit operation or acc.getdeviceptr "4381 "as defining op");4382 4383 return success();4384}4385 4386unsigned UpdateOp::getNumDataOperands() {4387 return getDataClauseOperands().size();4388}4389 4390Value UpdateOp::getDataOperand(unsigned i) {4391 unsigned numOptional = getAsyncOperands().size();4392 numOptional += getIfCond() ? 1 : 0;4393 return getOperand(getWaitOperands().size() + numOptional + i);4394}4395 4396void UpdateOp::getCanonicalizationPatterns(RewritePatternSet &results,4397 MLIRContext *context) {4398 results.add<RemoveConstantIfCondition<UpdateOp>>(context);4399}4400 4401bool UpdateOp::hasAsyncOnly() {4402 return hasAsyncOnly(mlir::acc::DeviceType::None);4403}4404 4405bool UpdateOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {4406 return hasDeviceType(getAsyncOnly(), deviceType);4407}4408 4409mlir::Value UpdateOp::getAsyncValue() {4410 return getAsyncValue(mlir::acc::DeviceType::None);4411}4412 4413mlir::Value UpdateOp::getAsyncValue(mlir::acc::DeviceType deviceType) {4414 if (!hasDeviceTypeValues(getAsyncOperandsDeviceType()))4415 return {};4416 4417 if (auto pos = findSegment(*getAsyncOperandsDeviceType(), deviceType))4418 return getAsyncOperands()[*pos];4419 4420 return {};4421}4422 4423bool UpdateOp::hasWaitOnly() {4424 return hasWaitOnly(mlir::acc::DeviceType::None);4425}4426 4427bool UpdateOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {4428 return hasDeviceType(getWaitOnly(), deviceType);4429}4430 4431mlir::Operation::operand_range UpdateOp::getWaitValues() {4432 return getWaitValues(mlir::acc::DeviceType::None);4433}4434 4435mlir::Operation::operand_range4436UpdateOp::getWaitValues(mlir::acc::DeviceType deviceType) {4437 return getWaitValuesWithoutDevnum(4438 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),4439 getHasWaitDevnum(), deviceType);4440}4441 4442mlir::Value UpdateOp::getWaitDevnum() {4443 return getWaitDevnum(mlir::acc::DeviceType::None);4444}4445 4446mlir::Value UpdateOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {4447 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),4448 getWaitOperandsSegments(), getHasWaitDevnum(),4449 deviceType);4450}4451 4452void UpdateOp::addAsyncOnly(MLIRContext *context,4453 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {4454 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(4455 context, getAsyncOnlyAttr(), effectiveDeviceTypes));4456}4457 4458void UpdateOp::addAsyncOperand(4459 MLIRContext *context, mlir::Value newValue,4460 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {4461 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(4462 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,4463 getAsyncOperandsMutable()));4464}4465 4466void UpdateOp::addWaitOnly(MLIRContext *context,4467 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {4468 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),4469 effectiveDeviceTypes));4470}4471 4472void UpdateOp::addWaitOperands(4473 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,4474 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {4475 4476 llvm::SmallVector<int32_t> segments;4477 if (getWaitOperandsSegments())4478 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));4479 4480 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(4481 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,4482 getWaitOperandsMutable(), segments));4483 setWaitOperandsSegments(segments);4484 4485 llvm::SmallVector<mlir::Attribute> hasDevnums;4486 if (getHasWaitDevnumAttr())4487 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));4488 hasDevnums.insert(4489 hasDevnums.end(),4490 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),4491 mlir::BoolAttr::get(context, hasDevnum));4492 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));4493}4494 4495//===----------------------------------------------------------------------===//4496// WaitOp4497//===----------------------------------------------------------------------===//4498 4499LogicalResult acc::WaitOp::verify() {4500 // The async attribute represent the async clause without value. Therefore the4501 // attribute and operand cannot appear at the same time.4502 if (getAsyncOperand() && getAsync())4503 return emitError("async attribute cannot appear with asyncOperand");4504 4505 if (getWaitDevnum() && getWaitOperands().empty())4506 return emitError("wait_devnum cannot appear without waitOperands");4507 4508 return success();4509}4510 4511#define GET_OP_CLASSES4512#include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"4513 4514#define GET_ATTRDEF_CLASSES4515#include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"4516 4517#define GET_TYPEDEF_CLASSES4518#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"4519 4520//===----------------------------------------------------------------------===//4521// acc dialect utilities4522//===----------------------------------------------------------------------===//4523 4524mlir::TypedValue<mlir::acc::PointerLikeType>4525mlir::acc::getVarPtr(mlir::Operation *accDataClauseOp) {4526 auto varPtr{llvm::TypeSwitch<mlir::Operation *,4527 mlir::TypedValue<mlir::acc::PointerLikeType>>(4528 accDataClauseOp)4529 .Case<ACC_DATA_ENTRY_OPS>(4530 [&](auto entry) { return entry.getVarPtr(); })4531 .Case<mlir::acc::CopyoutOp, mlir::acc::UpdateHostOp>(4532 [&](auto exit) { return exit.getVarPtr(); })4533 .Default([&](mlir::Operation *) {4534 return mlir::TypedValue<mlir::acc::PointerLikeType>();4535 })};4536 return varPtr;4537}4538 4539mlir::Value mlir::acc::getVar(mlir::Operation *accDataClauseOp) {4540 auto varPtr{4541 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(accDataClauseOp)4542 .Case<ACC_DATA_ENTRY_OPS>([&](auto entry) { return entry.getVar(); })4543 .Default([&](mlir::Operation *) { return mlir::Value(); })};4544 return varPtr;4545}4546 4547mlir::Type mlir::acc::getVarType(mlir::Operation *accDataClauseOp) {4548 auto varType{llvm::TypeSwitch<mlir::Operation *, mlir::Type>(accDataClauseOp)4549 .Case<ACC_DATA_ENTRY_OPS>(4550 [&](auto entry) { return entry.getVarType(); })4551 .Case<mlir::acc::CopyoutOp, mlir::acc::UpdateHostOp>(4552 [&](auto exit) { return exit.getVarType(); })4553 .Default([&](mlir::Operation *) { return mlir::Type(); })};4554 return varType;4555}4556 4557mlir::TypedValue<mlir::acc::PointerLikeType>4558mlir::acc::getAccPtr(mlir::Operation *accDataClauseOp) {4559 auto accPtr{llvm::TypeSwitch<mlir::Operation *,4560 mlir::TypedValue<mlir::acc::PointerLikeType>>(4561 accDataClauseOp)4562 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>(4563 [&](auto dataClause) { return dataClause.getAccPtr(); })4564 .Default([&](mlir::Operation *) {4565 return mlir::TypedValue<mlir::acc::PointerLikeType>();4566 })};4567 return accPtr;4568}4569 4570mlir::Value mlir::acc::getAccVar(mlir::Operation *accDataClauseOp) {4571 auto accPtr{llvm::TypeSwitch<mlir::Operation *, mlir::Value>(accDataClauseOp)4572 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>(4573 [&](auto dataClause) { return dataClause.getAccVar(); })4574 .Default([&](mlir::Operation *) { return mlir::Value(); })};4575 return accPtr;4576}4577 4578mlir::Value mlir::acc::getVarPtrPtr(mlir::Operation *accDataClauseOp) {4579 auto varPtrPtr{4580 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(accDataClauseOp)4581 .Case<ACC_DATA_ENTRY_OPS>(4582 [&](auto dataClause) { return dataClause.getVarPtrPtr(); })4583 .Default([&](mlir::Operation *) { return mlir::Value(); })};4584 return varPtrPtr;4585}4586 4587mlir::SmallVector<mlir::Value>4588mlir::acc::getBounds(mlir::Operation *accDataClauseOp) {4589 mlir::SmallVector<mlir::Value> bounds{4590 llvm::TypeSwitch<mlir::Operation *, mlir::SmallVector<mlir::Value>>(4591 accDataClauseOp)4592 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClause) {4593 return mlir::SmallVector<mlir::Value>(4594 dataClause.getBounds().begin(), dataClause.getBounds().end());4595 })4596 .Default([&](mlir::Operation *) {4597 return mlir::SmallVector<mlir::Value, 0>();4598 })};4599 return bounds;4600}4601 4602mlir::SmallVector<mlir::Value>4603mlir::acc::getAsyncOperands(mlir::Operation *accDataClauseOp) {4604 return llvm::TypeSwitch<mlir::Operation *, mlir::SmallVector<mlir::Value>>(4605 accDataClauseOp)4606 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClause) {4607 return mlir::SmallVector<mlir::Value>(4608 dataClause.getAsyncOperands().begin(),4609 dataClause.getAsyncOperands().end());4610 })4611 .Default([&](mlir::Operation *) {4612 return mlir::SmallVector<mlir::Value, 0>();4613 });4614}4615 4616mlir::ArrayAttr4617mlir::acc::getAsyncOperandsDeviceType(mlir::Operation *accDataClauseOp) {4618 return llvm::TypeSwitch<mlir::Operation *, mlir::ArrayAttr>(accDataClauseOp)4619 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClause) {4620 return dataClause.getAsyncOperandsDeviceTypeAttr();4621 })4622 .Default([&](mlir::Operation *) { return mlir::ArrayAttr{}; });4623}4624 4625mlir::ArrayAttr mlir::acc::getAsyncOnly(mlir::Operation *accDataClauseOp) {4626 return llvm::TypeSwitch<mlir::Operation *, mlir::ArrayAttr>(accDataClauseOp)4627 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>(4628 [&](auto dataClause) { return dataClause.getAsyncOnlyAttr(); })4629 .Default([&](mlir::Operation *) { return mlir::ArrayAttr{}; });4630}4631 4632std::optional<llvm::StringRef> mlir::acc::getVarName(mlir::Operation *accOp) {4633 auto name{4634 llvm::TypeSwitch<mlir::Operation *, std::optional<llvm::StringRef>>(accOp)4635 .Case<ACC_DATA_ENTRY_OPS>([&](auto entry) { return entry.getName(); })4636 .Default([&](mlir::Operation *) -> std::optional<llvm::StringRef> {4637 return {};4638 })};4639 return name;4640}4641 4642std::optional<mlir::acc::DataClause>4643mlir::acc::getDataClause(mlir::Operation *accDataEntryOp) {4644 auto dataClause{4645 llvm::TypeSwitch<mlir::Operation *, std::optional<mlir::acc::DataClause>>(4646 accDataEntryOp)4647 .Case<ACC_DATA_ENTRY_OPS>(4648 [&](auto entry) { return entry.getDataClause(); })4649 .Default([&](mlir::Operation *) { return std::nullopt; })};4650 return dataClause;4651}4652 4653bool mlir::acc::getImplicitFlag(mlir::Operation *accDataEntryOp) {4654 auto implicit{llvm::TypeSwitch<mlir::Operation *, bool>(accDataEntryOp)4655 .Case<ACC_DATA_ENTRY_OPS>(4656 [&](auto entry) { return entry.getImplicit(); })4657 .Default([&](mlir::Operation *) { return false; })};4658 return implicit;4659}4660 4661mlir::ValueRange mlir::acc::getDataOperands(mlir::Operation *accOp) {4662 auto dataOperands{4663 llvm::TypeSwitch<mlir::Operation *, mlir::ValueRange>(accOp)4664 .Case<ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS>(4665 [&](auto entry) { return entry.getDataClauseOperands(); })4666 .Default([&](mlir::Operation *) { return mlir::ValueRange(); })};4667 return dataOperands;4668}4669 4670mlir::MutableOperandRange4671mlir::acc::getMutableDataOperands(mlir::Operation *accOp) {4672 auto dataOperands{4673 llvm::TypeSwitch<mlir::Operation *, mlir::MutableOperandRange>(accOp)4674 .Case<ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS>(4675 [&](auto entry) { return entry.getDataClauseOperandsMutable(); })4676 .Default([&](mlir::Operation *) { return nullptr; })};4677 return dataOperands;4678}4679 4680mlir::SymbolRefAttr mlir::acc::getRecipe(mlir::Operation *accOp) {4681 auto recipe{4682 llvm::TypeSwitch<mlir::Operation *, mlir::SymbolRefAttr>(accOp)4683 .Case<ACC_DATA_ENTRY_OPS>(4684 [&](auto entry) { return entry.getRecipeAttr(); })4685 .Default([&](mlir::Operation *) { return mlir::SymbolRefAttr{}; })};4686 return recipe;4687}4688