4464 lines · cpp
1//===- OpenMPDialect.cpp - MLIR Dialect for OpenMP implementation ---------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the OpenMP dialect and its operations.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/Dialect/OpenMP/OpenMPDialect.h"14#include "mlir/Conversion/ConvertToLLVM/ToLLVMInterface.h"15#include "mlir/Dialect/Func/IR/FuncOps.h"16#include "mlir/Dialect/LLVMIR/LLVMTypes.h"17#include "mlir/Dialect/OpenMP/OpenMPClauseOperands.h"18#include "mlir/IR/Attributes.h"19#include "mlir/IR/BuiltinAttributes.h"20#include "mlir/IR/DialectImplementation.h"21#include "mlir/IR/OpImplementation.h"22#include "mlir/IR/OperationSupport.h"23#include "mlir/IR/SymbolTable.h"24#include "mlir/Interfaces/FoldInterfaces.h"25 26#include "llvm/ADT/ArrayRef.h"27#include "llvm/ADT/PostOrderIterator.h"28#include "llvm/ADT/STLExtras.h"29#include "llvm/ADT/STLForwardCompat.h"30#include "llvm/ADT/SmallString.h"31#include "llvm/ADT/StringExtras.h"32#include "llvm/ADT/StringRef.h"33#include "llvm/ADT/TypeSwitch.h"34#include "llvm/ADT/bit.h"35#include "llvm/Support/InterleavedRange.h"36#include <cstddef>37#include <iterator>38#include <optional>39#include <variant>40 41#include "mlir/Dialect/OpenMP/OpenMPOpsDialect.cpp.inc"42#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc"43#include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.cpp.inc"44#include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.cpp.inc"45 46using namespace mlir;47using namespace mlir::omp;48 49static ArrayAttr makeArrayAttr(MLIRContext *context,50 llvm::ArrayRef<Attribute> attrs) {51 return attrs.empty() ? nullptr : ArrayAttr::get(context, attrs);52}53 54static DenseBoolArrayAttr55makeDenseBoolArrayAttr(MLIRContext *ctx, const ArrayRef<bool> boolArray) {56 return boolArray.empty() ? nullptr : DenseBoolArrayAttr::get(ctx, boolArray);57}58 59static DenseI64ArrayAttr60makeDenseI64ArrayAttr(MLIRContext *ctx, const ArrayRef<int64_t> intArray) {61 return intArray.empty() ? nullptr : DenseI64ArrayAttr::get(ctx, intArray);62}63 64namespace {65struct MemRefPointerLikeModel66 : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,67 MemRefType> {68 Type getElementType(Type pointer) const {69 return llvm::cast<MemRefType>(pointer).getElementType();70 }71};72 73struct LLVMPointerPointerLikeModel74 : public PointerLikeType::ExternalModel<LLVMPointerPointerLikeModel,75 LLVM::LLVMPointerType> {76 Type getElementType(Type pointer) const { return Type(); }77};78} // namespace79 80/// Generate a name of a canonical loop nest of the format81/// `<prefix>(_r<idx>_s<idx>)*`. Hereby, `_r<idx>` identifies the region82/// argument index of an operation that has multiple regions, if the operation83/// has multiple regions.84/// `_s<idx>` identifies the position of an operation within a region, where85/// only operations that may potentially contain loops ("container operations"86/// i.e. have region arguments) are counted. Again, it is omitted if there is87/// only one such operation in a region. If there are canonical loops nested88/// inside each other, also may also use the format `_d<num>` where <num> is the89/// nesting depth of the loop.90///91/// The generated name is a best-effort to make canonical loop unique within an92/// SSA namespace. This also means that regions with IsolatedFromAbove property93/// do not consider any parents or siblings.94static std::string generateLoopNestingName(StringRef prefix,95 CanonicalLoopOp op) {96 struct Component {97 /// If true, this component describes a region operand of an operation (the98 /// operand's owner) If false, this component describes an operation located99 /// in a parent region100 bool isRegionArgOfOp;101 bool skip = false;102 bool isUnique = false;103 104 size_t idx;105 Operation *op;106 Region *parentRegion;107 size_t loopDepth;108 109 Operation *&getOwnerOp() {110 assert(isRegionArgOfOp && "Must describe a region operand");111 return op;112 }113 size_t &getArgIdx() {114 assert(isRegionArgOfOp && "Must describe a region operand");115 return idx;116 }117 118 Operation *&getContainerOp() {119 assert(!isRegionArgOfOp && "Must describe a operation of a region");120 return op;121 }122 size_t &getOpPos() {123 assert(!isRegionArgOfOp && "Must describe a operation of a region");124 return idx;125 }126 bool isLoopOp() const {127 assert(!isRegionArgOfOp && "Must describe a operation of a region");128 return isa<CanonicalLoopOp>(op);129 }130 Region *&getParentRegion() {131 assert(!isRegionArgOfOp && "Must describe a operation of a region");132 return parentRegion;133 }134 size_t &getLoopDepth() {135 assert(!isRegionArgOfOp && "Must describe a operation of a region");136 return loopDepth;137 }138 139 void skipIf(bool v = true) { skip = skip || v; }140 };141 142 // List of ancestors, from inner to outer.143 // Alternates between144 // * region argument of an operation145 // * operation within a region146 SmallVector<Component> components;147 148 // Gather a list of parent regions and operations, and the position within149 // their parent150 Operation *o = op.getOperation();151 while (o) {152 // Operation within a region153 Region *r = o->getParentRegion();154 if (!r)155 break;156 157 llvm::ReversePostOrderTraversal<Block *> traversal(&r->getBlocks().front());158 size_t idx = 0;159 bool found = false;160 size_t sequentialIdx = -1;161 bool isOnlyContainerOp = true;162 for (Block *b : traversal) {163 for (Operation &op : *b) {164 if (&op == o && !found) {165 sequentialIdx = idx;166 found = true;167 }168 if (op.getNumRegions()) {169 idx += 1;170 if (idx > 1)171 isOnlyContainerOp = false;172 }173 if (found && !isOnlyContainerOp)174 break;175 }176 }177 178 Component &containerOpInRegion = components.emplace_back();179 containerOpInRegion.isRegionArgOfOp = false;180 containerOpInRegion.isUnique = isOnlyContainerOp;181 containerOpInRegion.getContainerOp() = o;182 containerOpInRegion.getOpPos() = sequentialIdx;183 containerOpInRegion.getParentRegion() = r;184 185 Operation *parent = r->getParentOp();186 187 // Region argument of an operation188 Component ®ionArgOfOperation = components.emplace_back();189 regionArgOfOperation.isRegionArgOfOp = true;190 regionArgOfOperation.isUnique = true;191 regionArgOfOperation.getArgIdx() = 0;192 regionArgOfOperation.getOwnerOp() = parent;193 194 // The IsolatedFromAbove trait of the parent operation implies that each195 // individual region argument has its own separate namespace, so no196 // ambiguity.197 if (!parent || parent->hasTrait<mlir::OpTrait::IsIsolatedFromAbove>())198 break;199 200 // Component only needed if operation has multiple region operands. Region201 // arguments may be optional, but we currently do not consider this.202 if (parent->getRegions().size() > 1) {203 auto getRegionIndex = [](Operation *o, Region *r) {204 for (auto [idx, region] : llvm::enumerate(o->getRegions())) {205 if (®ion == r)206 return idx;207 }208 llvm_unreachable("Region not child of its parent operation");209 };210 regionArgOfOperation.isUnique = false;211 regionArgOfOperation.getArgIdx() = getRegionIndex(parent, r);212 }213 214 // next parent215 o = parent;216 }217 218 // Determine whether a region-argument component is not needed219 for (Component &c : components)220 c.skipIf(c.isRegionArgOfOp && c.isUnique);221 222 // Find runs of nested loops and determine each loop's depth in the loop nest223 size_t numSurroundingLoops = 0;224 for (Component &c : llvm::reverse(components)) {225 if (c.skip)226 continue;227 228 // non-skipped multi-argument operands interrupt the loop nest229 if (c.isRegionArgOfOp) {230 numSurroundingLoops = 0;231 continue;232 }233 234 // Multiple loops in a region means each of them is the outermost loop of a235 // new loop nest236 if (!c.isUnique)237 numSurroundingLoops = 0;238 239 c.getLoopDepth() = numSurroundingLoops;240 241 // Next loop is surrounded by one more loop242 if (isa<CanonicalLoopOp>(c.getContainerOp()))243 numSurroundingLoops += 1;244 }245 246 // In loop nests, skip all but the innermost loop that contains the depth247 // number248 bool isLoopNest = false;249 for (Component &c : components) {250 if (c.skip || c.isRegionArgOfOp)251 continue;252 253 if (!isLoopNest && c.getLoopDepth() >= 1) {254 // Innermost loop of a loop nest of at least two loops255 isLoopNest = true;256 } else if (isLoopNest) {257 // Non-innermost loop of a loop nest258 c.skipIf(c.isUnique);259 260 // If there is no surrounding loop left, this must have been the outermost261 // loop; leave loop-nest mode for the next iteration262 if (c.getLoopDepth() == 0)263 isLoopNest = false;264 }265 }266 267 // Skip non-loop unambiguous regions (but they should interrupt loop nests, so268 // we mark them as skipped only after computing loop nests)269 for (Component &c : components)270 c.skipIf(!c.isRegionArgOfOp && c.isUnique &&271 !isa<CanonicalLoopOp>(c.getContainerOp()));272 273 // Components can be skipped if they are already disambiguated by their parent274 // (or does not have a parent)275 bool newRegion = true;276 for (Component &c : llvm::reverse(components)) {277 c.skipIf(newRegion && c.isUnique);278 279 // non-skipped components disambiguate unique children280 if (!c.skip)281 newRegion = true;282 283 // ...except canonical loops that need a suffix for each nest284 if (!c.isRegionArgOfOp && c.getContainerOp())285 newRegion = false;286 }287 288 // Compile the nesting name string289 SmallString<64> Name{prefix};290 llvm::raw_svector_ostream NameOS(Name);291 for (auto &c : llvm::reverse(components)) {292 if (c.skip)293 continue;294 295 if (c.isRegionArgOfOp)296 NameOS << "_r" << c.getArgIdx();297 else if (c.getLoopDepth() >= 1)298 NameOS << "_d" << c.getLoopDepth();299 else300 NameOS << "_s" << c.getOpPos();301 }302 303 return NameOS.str().str();304}305 306void OpenMPDialect::initialize() {307 addOperations<308#define GET_OP_LIST309#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"310 >();311 addAttributes<312#define GET_ATTRDEF_LIST313#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"314 >();315 addTypes<316#define GET_TYPEDEF_LIST317#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"318 >();319 320 declarePromisedInterface<ConvertToLLVMPatternInterface, OpenMPDialect>();321 322 MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());323 LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(324 *getContext());325 326 // Attach default offload module interface to module op to access327 // offload functionality through328 mlir::ModuleOp::attachInterface<mlir::omp::OffloadModuleDefaultModel>(329 *getContext());330 331 // Attach default declare target interfaces to operations which can be marked332 // as declare target (Global Operations and Functions/Subroutines in dialects333 // that Fortran (or other languages that lower to MLIR) translates too334 mlir::LLVM::GlobalOp::attachInterface<335 mlir::omp::DeclareTargetDefaultModel<mlir::LLVM::GlobalOp>>(336 *getContext());337 mlir::LLVM::LLVMFuncOp::attachInterface<338 mlir::omp::DeclareTargetDefaultModel<mlir::LLVM::LLVMFuncOp>>(339 *getContext());340 mlir::func::FuncOp::attachInterface<341 mlir::omp::DeclareTargetDefaultModel<mlir::func::FuncOp>>(*getContext());342}343 344//===----------------------------------------------------------------------===//345// Parser and printer for Allocate Clause346//===----------------------------------------------------------------------===//347 348/// Parse an allocate clause with allocators and a list of operands with types.349///350/// allocate-operand-list :: = allocate-operand |351/// allocator-operand `,` allocate-operand-list352/// allocate-operand :: = ssa-id-and-type -> ssa-id-and-type353/// ssa-id-and-type ::= ssa-id `:` type354static ParseResult parseAllocateAndAllocator(355 OpAsmParser &parser,356 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &allocateVars,357 SmallVectorImpl<Type> &allocateTypes,358 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &allocatorVars,359 SmallVectorImpl<Type> &allocatorTypes) {360 361 return parser.parseCommaSeparatedList([&]() {362 OpAsmParser::UnresolvedOperand operand;363 Type type;364 if (parser.parseOperand(operand) || parser.parseColonType(type))365 return failure();366 allocatorVars.push_back(operand);367 allocatorTypes.push_back(type);368 if (parser.parseArrow())369 return failure();370 if (parser.parseOperand(operand) || parser.parseColonType(type))371 return failure();372 373 allocateVars.push_back(operand);374 allocateTypes.push_back(type);375 return success();376 });377}378 379/// Print allocate clause380static void printAllocateAndAllocator(OpAsmPrinter &p, Operation *op,381 OperandRange allocateVars,382 TypeRange allocateTypes,383 OperandRange allocatorVars,384 TypeRange allocatorTypes) {385 for (unsigned i = 0; i < allocateVars.size(); ++i) {386 std::string separator = i == allocateVars.size() - 1 ? "" : ", ";387 p << allocatorVars[i] << " : " << allocatorTypes[i] << " -> ";388 p << allocateVars[i] << " : " << allocateTypes[i] << separator;389 }390}391 392//===----------------------------------------------------------------------===//393// Parser and printer for a clause attribute (StringEnumAttr)394//===----------------------------------------------------------------------===//395 396template <typename ClauseAttr>397static ParseResult parseClauseAttr(AsmParser &parser, ClauseAttr &attr) {398 using ClauseT = decltype(std::declval<ClauseAttr>().getValue());399 StringRef enumStr;400 SMLoc loc = parser.getCurrentLocation();401 if (parser.parseKeyword(&enumStr))402 return failure();403 if (std::optional<ClauseT> enumValue = symbolizeEnum<ClauseT>(enumStr)) {404 attr = ClauseAttr::get(parser.getContext(), *enumValue);405 return success();406 }407 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'";408}409 410template <typename ClauseAttr>411static void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr) {412 p << stringifyEnum(attr.getValue());413}414 415//===----------------------------------------------------------------------===//416// Parser and printer for Linear Clause417//===----------------------------------------------------------------------===//418 419/// linear ::= `linear` `(` linear-list `)`420/// linear-list := linear-val | linear-val linear-list421/// linear-val := ssa-id-and-type `=` ssa-id-and-type422static ParseResult parseLinearClause(423 OpAsmParser &parser,424 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &linearVars,425 SmallVectorImpl<Type> &linearTypes,426 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &linearStepVars) {427 return parser.parseCommaSeparatedList([&]() {428 OpAsmParser::UnresolvedOperand var;429 Type type;430 OpAsmParser::UnresolvedOperand stepVar;431 if (parser.parseOperand(var) || parser.parseEqual() ||432 parser.parseOperand(stepVar) || parser.parseColonType(type))433 return failure();434 435 linearVars.push_back(var);436 linearTypes.push_back(type);437 linearStepVars.push_back(stepVar);438 return success();439 });440}441 442/// Print Linear Clause443static void printLinearClause(OpAsmPrinter &p, Operation *op,444 ValueRange linearVars, TypeRange linearTypes,445 ValueRange linearStepVars) {446 size_t linearVarsSize = linearVars.size();447 for (unsigned i = 0; i < linearVarsSize; ++i) {448 std::string separator = i == linearVarsSize - 1 ? "" : ", ";449 p << linearVars[i];450 if (linearStepVars.size() > i)451 p << " = " << linearStepVars[i];452 p << " : " << linearVars[i].getType() << separator;453 }454}455 456//===----------------------------------------------------------------------===//457// Verifier for Nontemporal Clause458//===----------------------------------------------------------------------===//459 460static LogicalResult verifyNontemporalClause(Operation *op,461 OperandRange nontemporalVars) {462 463 // Check if each var is unique - OpenMP 5.0 -> 2.9.3.1 section464 DenseSet<Value> nontemporalItems;465 for (const auto &it : nontemporalVars)466 if (!nontemporalItems.insert(it).second)467 return op->emitOpError() << "nontemporal variable used more than once";468 469 return success();470}471 472//===----------------------------------------------------------------------===//473// Parser, verifier and printer for Aligned Clause474//===----------------------------------------------------------------------===//475static LogicalResult verifyAlignedClause(Operation *op,476 std::optional<ArrayAttr> alignments,477 OperandRange alignedVars) {478 // Check if number of alignment values equals to number of aligned variables479 if (!alignedVars.empty()) {480 if (!alignments || alignments->size() != alignedVars.size())481 return op->emitOpError()482 << "expected as many alignment values as aligned variables";483 } else {484 if (alignments)485 return op->emitOpError() << "unexpected alignment values attribute";486 return success();487 }488 489 // Check if each var is aligned only once - OpenMP 4.5 -> 2.8.1 section490 DenseSet<Value> alignedItems;491 for (auto it : alignedVars)492 if (!alignedItems.insert(it).second)493 return op->emitOpError() << "aligned variable used more than once";494 495 if (!alignments)496 return success();497 498 // Check if all alignment values are positive - OpenMP 4.5 -> 2.8.1 section499 for (unsigned i = 0; i < (*alignments).size(); ++i) {500 if (auto intAttr = llvm::dyn_cast<IntegerAttr>((*alignments)[i])) {501 if (intAttr.getValue().sle(0))502 return op->emitOpError() << "alignment should be greater than 0";503 } else {504 return op->emitOpError() << "expected integer alignment";505 }506 }507 508 return success();509}510 511/// aligned ::= `aligned` `(` aligned-list `)`512/// aligned-list := aligned-val | aligned-val aligned-list513/// aligned-val := ssa-id-and-type `->` alignment514static ParseResult515parseAlignedClause(OpAsmParser &parser,516 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &alignedVars,517 SmallVectorImpl<Type> &alignedTypes,518 ArrayAttr &alignmentsAttr) {519 SmallVector<Attribute> alignmentVec;520 if (failed(parser.parseCommaSeparatedList([&]() {521 if (parser.parseOperand(alignedVars.emplace_back()) ||522 parser.parseColonType(alignedTypes.emplace_back()) ||523 parser.parseArrow() ||524 parser.parseAttribute(alignmentVec.emplace_back())) {525 return failure();526 }527 return success();528 })))529 return failure();530 SmallVector<Attribute> alignments(alignmentVec.begin(), alignmentVec.end());531 alignmentsAttr = ArrayAttr::get(parser.getContext(), alignments);532 return success();533}534 535/// Print Aligned Clause536static void printAlignedClause(OpAsmPrinter &p, Operation *op,537 ValueRange alignedVars, TypeRange alignedTypes,538 std::optional<ArrayAttr> alignments) {539 for (unsigned i = 0; i < alignedVars.size(); ++i) {540 if (i != 0)541 p << ", ";542 p << alignedVars[i] << " : " << alignedVars[i].getType();543 p << " -> " << (*alignments)[i];544 }545}546 547//===----------------------------------------------------------------------===//548// Parser, printer and verifier for Schedule Clause549//===----------------------------------------------------------------------===//550 551static ParseResult552verifyScheduleModifiers(OpAsmParser &parser,553 SmallVectorImpl<SmallString<12>> &modifiers) {554 if (modifiers.size() > 2)555 return parser.emitError(parser.getNameLoc()) << " unexpected modifier(s)";556 for (const auto &mod : modifiers) {557 // Translate the string. If it has no value, then it was not a valid558 // modifier!559 auto symbol = symbolizeScheduleModifier(mod);560 if (!symbol)561 return parser.emitError(parser.getNameLoc())562 << " unknown modifier type: " << mod;563 }564 565 // If we have one modifier that is "simd", then stick a "none" modiifer in566 // index 0.567 if (modifiers.size() == 1) {568 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd) {569 modifiers.push_back(modifiers[0]);570 modifiers[0] = stringifyScheduleModifier(ScheduleModifier::none);571 }572 } else if (modifiers.size() == 2) {573 // If there are two modifier:574 // First modifier should not be simd, second one should be simd575 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd ||576 symbolizeScheduleModifier(modifiers[1]) != ScheduleModifier::simd)577 return parser.emitError(parser.getNameLoc())578 << " incorrect modifier order";579 }580 return success();581}582 583/// schedule ::= `schedule` `(` sched-list `)`584/// sched-list ::= sched-val | sched-val sched-list |585/// sched-val `,` sched-modifier586/// sched-val ::= sched-with-chunk | sched-wo-chunk587/// sched-with-chunk ::= sched-with-chunk-types (`=` ssa-id-and-type)?588/// sched-with-chunk-types ::= `static` | `dynamic` | `guided`589/// sched-wo-chunk ::= `auto` | `runtime`590/// sched-modifier ::= sched-mod-val | sched-mod-val `,` sched-mod-val591/// sched-mod-val ::= `monotonic` | `nonmonotonic` | `simd` | `none`592static ParseResult593parseScheduleClause(OpAsmParser &parser, ClauseScheduleKindAttr &scheduleAttr,594 ScheduleModifierAttr &scheduleMod, UnitAttr &scheduleSimd,595 std::optional<OpAsmParser::UnresolvedOperand> &chunkSize,596 Type &chunkType) {597 StringRef keyword;598 if (parser.parseKeyword(&keyword))599 return failure();600 std::optional<mlir::omp::ClauseScheduleKind> schedule =601 symbolizeClauseScheduleKind(keyword);602 if (!schedule)603 return parser.emitError(parser.getNameLoc()) << " expected schedule kind";604 605 scheduleAttr = ClauseScheduleKindAttr::get(parser.getContext(), *schedule);606 switch (*schedule) {607 case ClauseScheduleKind::Static:608 case ClauseScheduleKind::Dynamic:609 case ClauseScheduleKind::Guided:610 if (succeeded(parser.parseOptionalEqual())) {611 chunkSize = OpAsmParser::UnresolvedOperand{};612 if (parser.parseOperand(*chunkSize) || parser.parseColonType(chunkType))613 return failure();614 } else {615 chunkSize = std::nullopt;616 }617 break;618 case ClauseScheduleKind::Auto:619 case ClauseScheduleKind::Runtime:620 case ClauseScheduleKind::Distribute:621 chunkSize = std::nullopt;622 }623 624 // If there is a comma, we have one or more modifiers..625 SmallVector<SmallString<12>> modifiers;626 while (succeeded(parser.parseOptionalComma())) {627 StringRef mod;628 if (parser.parseKeyword(&mod))629 return failure();630 modifiers.push_back(mod);631 }632 633 if (verifyScheduleModifiers(parser, modifiers))634 return failure();635 636 if (!modifiers.empty()) {637 SMLoc loc = parser.getCurrentLocation();638 if (std::optional<ScheduleModifier> mod =639 symbolizeScheduleModifier(modifiers[0])) {640 scheduleMod = ScheduleModifierAttr::get(parser.getContext(), *mod);641 } else {642 return parser.emitError(loc, "invalid schedule modifier");643 }644 // Only SIMD attribute is allowed here!645 if (modifiers.size() > 1) {646 assert(symbolizeScheduleModifier(modifiers[1]) == ScheduleModifier::simd);647 scheduleSimd = UnitAttr::get(parser.getBuilder().getContext());648 }649 }650 651 return success();652}653 654/// Print schedule clause655static void printScheduleClause(OpAsmPrinter &p, Operation *op,656 ClauseScheduleKindAttr scheduleKind,657 ScheduleModifierAttr scheduleMod,658 UnitAttr scheduleSimd, Value scheduleChunk,659 Type scheduleChunkType) {660 p << stringifyClauseScheduleKind(scheduleKind.getValue());661 if (scheduleChunk)662 p << " = " << scheduleChunk << " : " << scheduleChunk.getType();663 if (scheduleMod)664 p << ", " << stringifyScheduleModifier(scheduleMod.getValue());665 if (scheduleSimd)666 p << ", simd";667}668 669//===----------------------------------------------------------------------===//670// Parser and printer for Order Clause671//===----------------------------------------------------------------------===//672 673// order ::= `order` `(` [order-modifier ':'] concurrent `)`674// order-modifier ::= reproducible | unconstrained675static ParseResult parseOrderClause(OpAsmParser &parser,676 ClauseOrderKindAttr &order,677 OrderModifierAttr &orderMod) {678 StringRef enumStr;679 SMLoc loc = parser.getCurrentLocation();680 if (parser.parseKeyword(&enumStr))681 return failure();682 if (std::optional<OrderModifier> enumValue =683 symbolizeOrderModifier(enumStr)) {684 orderMod = OrderModifierAttr::get(parser.getContext(), *enumValue);685 if (parser.parseOptionalColon())686 return failure();687 loc = parser.getCurrentLocation();688 if (parser.parseKeyword(&enumStr))689 return failure();690 }691 if (std::optional<ClauseOrderKind> enumValue =692 symbolizeClauseOrderKind(enumStr)) {693 order = ClauseOrderKindAttr::get(parser.getContext(), *enumValue);694 return success();695 }696 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'";697}698 699static void printOrderClause(OpAsmPrinter &p, Operation *op,700 ClauseOrderKindAttr order,701 OrderModifierAttr orderMod) {702 if (orderMod)703 p << stringifyOrderModifier(orderMod.getValue()) << ":";704 if (order)705 p << stringifyClauseOrderKind(order.getValue());706}707 708template <typename ClauseTypeAttr, typename ClauseType>709static ParseResult710parseGranularityClause(OpAsmParser &parser, ClauseTypeAttr &prescriptiveness,711 std::optional<OpAsmParser::UnresolvedOperand> &operand,712 Type &operandType,713 std::optional<ClauseType> (*symbolizeClause)(StringRef),714 StringRef clauseName) {715 StringRef enumStr;716 if (succeeded(parser.parseOptionalKeyword(&enumStr))) {717 if (std::optional<ClauseType> enumValue = symbolizeClause(enumStr)) {718 prescriptiveness = ClauseTypeAttr::get(parser.getContext(), *enumValue);719 if (parser.parseComma())720 return failure();721 } else {722 return parser.emitError(parser.getCurrentLocation())723 << "invalid " << clauseName << " modifier : '" << enumStr << "'";724 ;725 }726 }727 728 OpAsmParser::UnresolvedOperand var;729 if (succeeded(parser.parseOperand(var))) {730 operand = var;731 } else {732 return parser.emitError(parser.getCurrentLocation())733 << "expected " << clauseName << " operand";734 }735 736 if (operand.has_value()) {737 if (parser.parseColonType(operandType))738 return failure();739 }740 741 return success();742}743 744template <typename ClauseTypeAttr, typename ClauseType>745static void746printGranularityClause(OpAsmPrinter &p, Operation *op,747 ClauseTypeAttr prescriptiveness, Value operand,748 mlir::Type operandType,749 StringRef (*stringifyClauseType)(ClauseType)) {750 751 if (prescriptiveness)752 p << stringifyClauseType(prescriptiveness.getValue()) << ", ";753 754 if (operand)755 p << operand << ": " << operandType;756}757 758//===----------------------------------------------------------------------===//759// Parser and printer for grainsize Clause760//===----------------------------------------------------------------------===//761 762// grainsize ::= `grainsize` `(` [strict ':'] grain-size `)`763static ParseResult764parseGrainsizeClause(OpAsmParser &parser, ClauseGrainsizeTypeAttr &grainsizeMod,765 std::optional<OpAsmParser::UnresolvedOperand> &grainsize,766 Type &grainsizeType) {767 return parseGranularityClause<ClauseGrainsizeTypeAttr, ClauseGrainsizeType>(768 parser, grainsizeMod, grainsize, grainsizeType,769 &symbolizeClauseGrainsizeType, "grainsize");770}771 772static void printGrainsizeClause(OpAsmPrinter &p, Operation *op,773 ClauseGrainsizeTypeAttr grainsizeMod,774 Value grainsize, mlir::Type grainsizeType) {775 printGranularityClause<ClauseGrainsizeTypeAttr, ClauseGrainsizeType>(776 p, op, grainsizeMod, grainsize, grainsizeType,777 &stringifyClauseGrainsizeType);778}779 780//===----------------------------------------------------------------------===//781// Parser and printer for num_tasks Clause782//===----------------------------------------------------------------------===//783 784// numtask ::= `num_tasks` `(` [strict ':'] num-tasks `)`785static ParseResult786parseNumTasksClause(OpAsmParser &parser, ClauseNumTasksTypeAttr &numTasksMod,787 std::optional<OpAsmParser::UnresolvedOperand> &numTasks,788 Type &numTasksType) {789 return parseGranularityClause<ClauseNumTasksTypeAttr, ClauseNumTasksType>(790 parser, numTasksMod, numTasks, numTasksType, &symbolizeClauseNumTasksType,791 "num_tasks");792}793 794static void printNumTasksClause(OpAsmPrinter &p, Operation *op,795 ClauseNumTasksTypeAttr numTasksMod,796 Value numTasks, mlir::Type numTasksType) {797 printGranularityClause<ClauseNumTasksTypeAttr, ClauseNumTasksType>(798 p, op, numTasksMod, numTasks, numTasksType, &stringifyClauseNumTasksType);799}800 801//===----------------------------------------------------------------------===//802// Parsers for operations including clauses that define entry block arguments.803//===----------------------------------------------------------------------===//804 805namespace {806struct MapParseArgs {807 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;808 SmallVectorImpl<Type> &types;809 MapParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,810 SmallVectorImpl<Type> &types)811 : vars(vars), types(types) {}812};813struct PrivateParseArgs {814 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;815 llvm::SmallVectorImpl<Type> &types;816 ArrayAttr &syms;817 UnitAttr &needsBarrier;818 DenseI64ArrayAttr *mapIndices;819 PrivateParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,820 SmallVectorImpl<Type> &types, ArrayAttr &syms,821 UnitAttr &needsBarrier,822 DenseI64ArrayAttr *mapIndices = nullptr)823 : vars(vars), types(types), syms(syms), needsBarrier(needsBarrier),824 mapIndices(mapIndices) {}825};826 827struct ReductionParseArgs {828 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;829 SmallVectorImpl<Type> &types;830 DenseBoolArrayAttr &byref;831 ArrayAttr &syms;832 ReductionModifierAttr *modifier;833 ReductionParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,834 SmallVectorImpl<Type> &types, DenseBoolArrayAttr &byref,835 ArrayAttr &syms, ReductionModifierAttr *mod = nullptr)836 : vars(vars), types(types), byref(byref), syms(syms), modifier(mod) {}837};838 839struct AllRegionParseArgs {840 std::optional<MapParseArgs> hasDeviceAddrArgs;841 std::optional<MapParseArgs> hostEvalArgs;842 std::optional<ReductionParseArgs> inReductionArgs;843 std::optional<MapParseArgs> mapArgs;844 std::optional<PrivateParseArgs> privateArgs;845 std::optional<ReductionParseArgs> reductionArgs;846 std::optional<ReductionParseArgs> taskReductionArgs;847 std::optional<MapParseArgs> useDeviceAddrArgs;848 std::optional<MapParseArgs> useDevicePtrArgs;849};850} // namespace851 852static inline constexpr StringRef getPrivateNeedsBarrierSpelling() {853 return "private_barrier";854}855 856static ParseResult parseClauseWithRegionArgs(857 OpAsmParser &parser,858 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operands,859 SmallVectorImpl<Type> &types,860 SmallVectorImpl<OpAsmParser::Argument> ®ionPrivateArgs,861 ArrayAttr *symbols = nullptr, DenseI64ArrayAttr *mapIndices = nullptr,862 DenseBoolArrayAttr *byref = nullptr,863 ReductionModifierAttr *modifier = nullptr,864 UnitAttr *needsBarrier = nullptr) {865 SmallVector<SymbolRefAttr> symbolVec;866 SmallVector<int64_t> mapIndicesVec;867 SmallVector<bool> isByRefVec;868 unsigned regionArgOffset = regionPrivateArgs.size();869 870 if (parser.parseLParen())871 return failure();872 873 if (modifier && succeeded(parser.parseOptionalKeyword("mod"))) {874 StringRef enumStr;875 if (parser.parseColon() || parser.parseKeyword(&enumStr) ||876 parser.parseComma())877 return failure();878 std::optional<ReductionModifier> enumValue =879 symbolizeReductionModifier(enumStr);880 if (!enumValue.has_value())881 return failure();882 *modifier = ReductionModifierAttr::get(parser.getContext(), *enumValue);883 if (!*modifier)884 return failure();885 }886 887 if (parser.parseCommaSeparatedList([&]() {888 if (byref)889 isByRefVec.push_back(890 parser.parseOptionalKeyword("byref").succeeded());891 892 if (symbols && parser.parseAttribute(symbolVec.emplace_back()))893 return failure();894 895 if (parser.parseOperand(operands.emplace_back()) ||896 parser.parseArrow() ||897 parser.parseArgument(regionPrivateArgs.emplace_back()))898 return failure();899 900 if (mapIndices) {901 if (parser.parseOptionalLSquare().succeeded()) {902 if (parser.parseKeyword("map_idx") || parser.parseEqual() ||903 parser.parseInteger(mapIndicesVec.emplace_back()) ||904 parser.parseRSquare())905 return failure();906 } else {907 mapIndicesVec.push_back(-1);908 }909 }910 911 return success();912 }))913 return failure();914 915 if (parser.parseColon())916 return failure();917 918 if (parser.parseCommaSeparatedList([&]() {919 if (parser.parseType(types.emplace_back()))920 return failure();921 922 return success();923 }))924 return failure();925 926 if (operands.size() != types.size())927 return failure();928 929 if (parser.parseRParen())930 return failure();931 932 if (needsBarrier) {933 if (parser.parseOptionalKeyword(getPrivateNeedsBarrierSpelling())934 .succeeded())935 *needsBarrier = mlir::UnitAttr::get(parser.getContext());936 }937 938 auto *argsBegin = regionPrivateArgs.begin();939 MutableArrayRef argsSubrange(argsBegin + regionArgOffset,940 argsBegin + regionArgOffset + types.size());941 for (auto [prv, type] : llvm::zip_equal(argsSubrange, types)) {942 prv.type = type;943 }944 945 if (symbols) {946 SmallVector<Attribute> symbolAttrs(symbolVec.begin(), symbolVec.end());947 *symbols = ArrayAttr::get(parser.getContext(), symbolAttrs);948 }949 950 if (!mapIndicesVec.empty())951 *mapIndices =952 mlir::DenseI64ArrayAttr::get(parser.getContext(), mapIndicesVec);953 954 if (byref)955 *byref = makeDenseBoolArrayAttr(parser.getContext(), isByRefVec);956 957 return success();958}959 960static ParseResult parseBlockArgClause(961 OpAsmParser &parser,962 llvm::SmallVectorImpl<OpAsmParser::Argument> &entryBlockArgs,963 StringRef keyword, std::optional<MapParseArgs> mapArgs) {964 if (succeeded(parser.parseOptionalKeyword(keyword))) {965 if (!mapArgs)966 return failure();967 968 if (failed(parseClauseWithRegionArgs(parser, mapArgs->vars, mapArgs->types,969 entryBlockArgs)))970 return failure();971 }972 return success();973}974 975static ParseResult parseBlockArgClause(976 OpAsmParser &parser,977 llvm::SmallVectorImpl<OpAsmParser::Argument> &entryBlockArgs,978 StringRef keyword, std::optional<PrivateParseArgs> privateArgs) {979 if (succeeded(parser.parseOptionalKeyword(keyword))) {980 if (!privateArgs)981 return failure();982 983 if (failed(parseClauseWithRegionArgs(984 parser, privateArgs->vars, privateArgs->types, entryBlockArgs,985 &privateArgs->syms, privateArgs->mapIndices, /*byref=*/nullptr,986 /*modifier=*/nullptr, &privateArgs->needsBarrier)))987 return failure();988 }989 return success();990}991 992static ParseResult parseBlockArgClause(993 OpAsmParser &parser,994 llvm::SmallVectorImpl<OpAsmParser::Argument> &entryBlockArgs,995 StringRef keyword, std::optional<ReductionParseArgs> reductionArgs) {996 if (succeeded(parser.parseOptionalKeyword(keyword))) {997 if (!reductionArgs)998 return failure();999 if (failed(parseClauseWithRegionArgs(1000 parser, reductionArgs->vars, reductionArgs->types, entryBlockArgs,1001 &reductionArgs->syms, /*mapIndices=*/nullptr, &reductionArgs->byref,1002 reductionArgs->modifier)))1003 return failure();1004 }1005 return success();1006}1007 1008static ParseResult parseBlockArgRegion(OpAsmParser &parser, Region ®ion,1009 AllRegionParseArgs args) {1010 llvm::SmallVector<OpAsmParser::Argument> entryBlockArgs;1011 1012 if (failed(parseBlockArgClause(parser, entryBlockArgs, "has_device_addr",1013 args.hasDeviceAddrArgs)))1014 return parser.emitError(parser.getCurrentLocation())1015 << "invalid `has_device_addr` format";1016 1017 if (failed(parseBlockArgClause(parser, entryBlockArgs, "host_eval",1018 args.hostEvalArgs)))1019 return parser.emitError(parser.getCurrentLocation())1020 << "invalid `host_eval` format";1021 1022 if (failed(parseBlockArgClause(parser, entryBlockArgs, "in_reduction",1023 args.inReductionArgs)))1024 return parser.emitError(parser.getCurrentLocation())1025 << "invalid `in_reduction` format";1026 1027 if (failed(parseBlockArgClause(parser, entryBlockArgs, "map_entries",1028 args.mapArgs)))1029 return parser.emitError(parser.getCurrentLocation())1030 << "invalid `map_entries` format";1031 1032 if (failed(parseBlockArgClause(parser, entryBlockArgs, "private",1033 args.privateArgs)))1034 return parser.emitError(parser.getCurrentLocation())1035 << "invalid `private` format";1036 1037 if (failed(parseBlockArgClause(parser, entryBlockArgs, "reduction",1038 args.reductionArgs)))1039 return parser.emitError(parser.getCurrentLocation())1040 << "invalid `reduction` format";1041 1042 if (failed(parseBlockArgClause(parser, entryBlockArgs, "task_reduction",1043 args.taskReductionArgs)))1044 return parser.emitError(parser.getCurrentLocation())1045 << "invalid `task_reduction` format";1046 1047 if (failed(parseBlockArgClause(parser, entryBlockArgs, "use_device_addr",1048 args.useDeviceAddrArgs)))1049 return parser.emitError(parser.getCurrentLocation())1050 << "invalid `use_device_addr` format";1051 1052 if (failed(parseBlockArgClause(parser, entryBlockArgs, "use_device_ptr",1053 args.useDevicePtrArgs)))1054 return parser.emitError(parser.getCurrentLocation())1055 << "invalid `use_device_addr` format";1056 1057 return parser.parseRegion(region, entryBlockArgs);1058}1059 1060// These parseXyz functions correspond to the custom<Xyz> definitions1061// in the .td file(s).1062static ParseResult parseTargetOpRegion(1063 OpAsmParser &parser, Region ®ion,1064 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &hasDeviceAddrVars,1065 SmallVectorImpl<Type> &hasDeviceAddrTypes,1066 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &hostEvalVars,1067 SmallVectorImpl<Type> &hostEvalTypes,1068 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inReductionVars,1069 SmallVectorImpl<Type> &inReductionTypes,1070 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,1071 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &mapVars,1072 SmallVectorImpl<Type> &mapTypes,1073 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,1074 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,1075 UnitAttr &privateNeedsBarrier, DenseI64ArrayAttr &privateMaps) {1076 AllRegionParseArgs args;1077 args.hasDeviceAddrArgs.emplace(hasDeviceAddrVars, hasDeviceAddrTypes);1078 args.hostEvalArgs.emplace(hostEvalVars, hostEvalTypes);1079 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1080 inReductionByref, inReductionSyms);1081 args.mapArgs.emplace(mapVars, mapTypes);1082 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1083 privateNeedsBarrier, &privateMaps);1084 return parseBlockArgRegion(parser, region, args);1085}1086 1087static ParseResult parseInReductionPrivateRegion(1088 OpAsmParser &parser, Region ®ion,1089 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inReductionVars,1090 SmallVectorImpl<Type> &inReductionTypes,1091 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,1092 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,1093 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,1094 UnitAttr &privateNeedsBarrier) {1095 AllRegionParseArgs args;1096 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1097 inReductionByref, inReductionSyms);1098 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1099 privateNeedsBarrier);1100 return parseBlockArgRegion(parser, region, args);1101}1102 1103static ParseResult parseInReductionPrivateReductionRegion(1104 OpAsmParser &parser, Region ®ion,1105 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inReductionVars,1106 SmallVectorImpl<Type> &inReductionTypes,1107 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,1108 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,1109 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,1110 UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod,1111 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &reductionVars,1112 SmallVectorImpl<Type> &reductionTypes, DenseBoolArrayAttr &reductionByref,1113 ArrayAttr &reductionSyms) {1114 AllRegionParseArgs args;1115 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1116 inReductionByref, inReductionSyms);1117 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1118 privateNeedsBarrier);1119 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,1120 reductionSyms, &reductionMod);1121 return parseBlockArgRegion(parser, region, args);1122}1123 1124static ParseResult parsePrivateRegion(1125 OpAsmParser &parser, Region ®ion,1126 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,1127 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,1128 UnitAttr &privateNeedsBarrier) {1129 AllRegionParseArgs args;1130 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1131 privateNeedsBarrier);1132 return parseBlockArgRegion(parser, region, args);1133}1134 1135static ParseResult parsePrivateReductionRegion(1136 OpAsmParser &parser, Region ®ion,1137 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,1138 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,1139 UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod,1140 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &reductionVars,1141 SmallVectorImpl<Type> &reductionTypes, DenseBoolArrayAttr &reductionByref,1142 ArrayAttr &reductionSyms) {1143 AllRegionParseArgs args;1144 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1145 privateNeedsBarrier);1146 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,1147 reductionSyms, &reductionMod);1148 return parseBlockArgRegion(parser, region, args);1149}1150 1151static ParseResult parseTaskReductionRegion(1152 OpAsmParser &parser, Region ®ion,1153 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &taskReductionVars,1154 SmallVectorImpl<Type> &taskReductionTypes,1155 DenseBoolArrayAttr &taskReductionByref, ArrayAttr &taskReductionSyms) {1156 AllRegionParseArgs args;1157 args.taskReductionArgs.emplace(taskReductionVars, taskReductionTypes,1158 taskReductionByref, taskReductionSyms);1159 return parseBlockArgRegion(parser, region, args);1160}1161 1162static ParseResult parseUseDeviceAddrUseDevicePtrRegion(1163 OpAsmParser &parser, Region ®ion,1164 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &useDeviceAddrVars,1165 SmallVectorImpl<Type> &useDeviceAddrTypes,1166 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &useDevicePtrVars,1167 SmallVectorImpl<Type> &useDevicePtrTypes) {1168 AllRegionParseArgs args;1169 args.useDeviceAddrArgs.emplace(useDeviceAddrVars, useDeviceAddrTypes);1170 args.useDevicePtrArgs.emplace(useDevicePtrVars, useDevicePtrTypes);1171 return parseBlockArgRegion(parser, region, args);1172}1173 1174//===----------------------------------------------------------------------===//1175// Printers for operations including clauses that define entry block arguments.1176//===----------------------------------------------------------------------===//1177 1178namespace {1179struct MapPrintArgs {1180 ValueRange vars;1181 TypeRange types;1182 MapPrintArgs(ValueRange vars, TypeRange types) : vars(vars), types(types) {}1183};1184struct PrivatePrintArgs {1185 ValueRange vars;1186 TypeRange types;1187 ArrayAttr syms;1188 UnitAttr needsBarrier;1189 DenseI64ArrayAttr mapIndices;1190 PrivatePrintArgs(ValueRange vars, TypeRange types, ArrayAttr syms,1191 UnitAttr needsBarrier, DenseI64ArrayAttr mapIndices)1192 : vars(vars), types(types), syms(syms), needsBarrier(needsBarrier),1193 mapIndices(mapIndices) {}1194};1195struct ReductionPrintArgs {1196 ValueRange vars;1197 TypeRange types;1198 DenseBoolArrayAttr byref;1199 ArrayAttr syms;1200 ReductionModifierAttr modifier;1201 ReductionPrintArgs(ValueRange vars, TypeRange types, DenseBoolArrayAttr byref,1202 ArrayAttr syms, ReductionModifierAttr mod = nullptr)1203 : vars(vars), types(types), byref(byref), syms(syms), modifier(mod) {}1204};1205struct AllRegionPrintArgs {1206 std::optional<MapPrintArgs> hasDeviceAddrArgs;1207 std::optional<MapPrintArgs> hostEvalArgs;1208 std::optional<ReductionPrintArgs> inReductionArgs;1209 std::optional<MapPrintArgs> mapArgs;1210 std::optional<PrivatePrintArgs> privateArgs;1211 std::optional<ReductionPrintArgs> reductionArgs;1212 std::optional<ReductionPrintArgs> taskReductionArgs;1213 std::optional<MapPrintArgs> useDeviceAddrArgs;1214 std::optional<MapPrintArgs> useDevicePtrArgs;1215};1216} // namespace1217 1218static void printClauseWithRegionArgs(1219 OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName,1220 ValueRange argsSubrange, ValueRange operands, TypeRange types,1221 ArrayAttr symbols = nullptr, DenseI64ArrayAttr mapIndices = nullptr,1222 DenseBoolArrayAttr byref = nullptr,1223 ReductionModifierAttr modifier = nullptr, UnitAttr needsBarrier = nullptr) {1224 if (argsSubrange.empty())1225 return;1226 1227 p << clauseName << "(";1228 1229 if (modifier)1230 p << "mod: " << stringifyReductionModifier(modifier.getValue()) << ", ";1231 1232 if (!symbols) {1233 llvm::SmallVector<Attribute> values(operands.size(), nullptr);1234 symbols = ArrayAttr::get(ctx, values);1235 }1236 1237 if (!mapIndices) {1238 llvm::SmallVector<int64_t> values(operands.size(), -1);1239 mapIndices = DenseI64ArrayAttr::get(ctx, values);1240 }1241 1242 if (!byref) {1243 mlir::SmallVector<bool> values(operands.size(), false);1244 byref = DenseBoolArrayAttr::get(ctx, values);1245 }1246 1247 llvm::interleaveComma(llvm::zip_equal(operands, argsSubrange, symbols,1248 mapIndices.asArrayRef(),1249 byref.asArrayRef()),1250 p, [&p](auto t) {1251 auto [op, arg, sym, map, isByRef] = t;1252 if (isByRef)1253 p << "byref ";1254 if (sym)1255 p << sym << " ";1256 1257 p << op << " -> " << arg;1258 1259 if (map != -1)1260 p << " [map_idx=" << map << "]";1261 });1262 p << " : ";1263 llvm::interleaveComma(types, p);1264 p << ") ";1265 1266 if (needsBarrier)1267 p << getPrivateNeedsBarrierSpelling() << " ";1268}1269 1270static void printBlockArgClause(OpAsmPrinter &p, MLIRContext *ctx,1271 StringRef clauseName, ValueRange argsSubrange,1272 std::optional<MapPrintArgs> mapArgs) {1273 if (mapArgs)1274 printClauseWithRegionArgs(p, ctx, clauseName, argsSubrange, mapArgs->vars,1275 mapArgs->types);1276}1277 1278static void printBlockArgClause(OpAsmPrinter &p, MLIRContext *ctx,1279 StringRef clauseName, ValueRange argsSubrange,1280 std::optional<PrivatePrintArgs> privateArgs) {1281 if (privateArgs)1282 printClauseWithRegionArgs(1283 p, ctx, clauseName, argsSubrange, privateArgs->vars, privateArgs->types,1284 privateArgs->syms, privateArgs->mapIndices, /*byref=*/nullptr,1285 /*modifier=*/nullptr, privateArgs->needsBarrier);1286}1287 1288static void1289printBlockArgClause(OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName,1290 ValueRange argsSubrange,1291 std::optional<ReductionPrintArgs> reductionArgs) {1292 if (reductionArgs)1293 printClauseWithRegionArgs(p, ctx, clauseName, argsSubrange,1294 reductionArgs->vars, reductionArgs->types,1295 reductionArgs->syms, /*mapIndices=*/nullptr,1296 reductionArgs->byref, reductionArgs->modifier);1297}1298 1299static void printBlockArgRegion(OpAsmPrinter &p, Operation *op, Region ®ion,1300 const AllRegionPrintArgs &args) {1301 auto iface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op);1302 MLIRContext *ctx = op->getContext();1303 1304 printBlockArgClause(p, ctx, "has_device_addr",1305 iface.getHasDeviceAddrBlockArgs(),1306 args.hasDeviceAddrArgs);1307 printBlockArgClause(p, ctx, "host_eval", iface.getHostEvalBlockArgs(),1308 args.hostEvalArgs);1309 printBlockArgClause(p, ctx, "in_reduction", iface.getInReductionBlockArgs(),1310 args.inReductionArgs);1311 printBlockArgClause(p, ctx, "map_entries", iface.getMapBlockArgs(),1312 args.mapArgs);1313 printBlockArgClause(p, ctx, "private", iface.getPrivateBlockArgs(),1314 args.privateArgs);1315 printBlockArgClause(p, ctx, "reduction", iface.getReductionBlockArgs(),1316 args.reductionArgs);1317 printBlockArgClause(p, ctx, "task_reduction",1318 iface.getTaskReductionBlockArgs(),1319 args.taskReductionArgs);1320 printBlockArgClause(p, ctx, "use_device_addr",1321 iface.getUseDeviceAddrBlockArgs(),1322 args.useDeviceAddrArgs);1323 printBlockArgClause(p, ctx, "use_device_ptr",1324 iface.getUseDevicePtrBlockArgs(), args.useDevicePtrArgs);1325 1326 p.printRegion(region, /*printEntryBlockArgs=*/false);1327}1328 1329// These parseXyz functions correspond to the custom<Xyz> definitions1330// in the .td file(s).1331static void printTargetOpRegion(1332 OpAsmPrinter &p, Operation *op, Region ®ion,1333 ValueRange hasDeviceAddrVars, TypeRange hasDeviceAddrTypes,1334 ValueRange hostEvalVars, TypeRange hostEvalTypes,1335 ValueRange inReductionVars, TypeRange inReductionTypes,1336 DenseBoolArrayAttr inReductionByref, ArrayAttr inReductionSyms,1337 ValueRange mapVars, TypeRange mapTypes, ValueRange privateVars,1338 TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,1339 DenseI64ArrayAttr privateMaps) {1340 AllRegionPrintArgs args;1341 args.hasDeviceAddrArgs.emplace(hasDeviceAddrVars, hasDeviceAddrTypes);1342 args.hostEvalArgs.emplace(hostEvalVars, hostEvalTypes);1343 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1344 inReductionByref, inReductionSyms);1345 args.mapArgs.emplace(mapVars, mapTypes);1346 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1347 privateNeedsBarrier, privateMaps);1348 printBlockArgRegion(p, op, region, args);1349}1350 1351static void printInReductionPrivateRegion(1352 OpAsmPrinter &p, Operation *op, Region ®ion, ValueRange inReductionVars,1353 TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref,1354 ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes,1355 ArrayAttr privateSyms, UnitAttr privateNeedsBarrier) {1356 AllRegionPrintArgs args;1357 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1358 inReductionByref, inReductionSyms);1359 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1360 privateNeedsBarrier,1361 /*mapIndices=*/nullptr);1362 printBlockArgRegion(p, op, region, args);1363}1364 1365static void printInReductionPrivateReductionRegion(1366 OpAsmPrinter &p, Operation *op, Region ®ion, ValueRange inReductionVars,1367 TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref,1368 ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes,1369 ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,1370 ReductionModifierAttr reductionMod, ValueRange reductionVars,1371 TypeRange reductionTypes, DenseBoolArrayAttr reductionByref,1372 ArrayAttr reductionSyms) {1373 AllRegionPrintArgs args;1374 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,1375 inReductionByref, inReductionSyms);1376 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1377 privateNeedsBarrier,1378 /*mapIndices=*/nullptr);1379 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,1380 reductionSyms, reductionMod);1381 printBlockArgRegion(p, op, region, args);1382}1383 1384static void printPrivateRegion(OpAsmPrinter &p, Operation *op, Region ®ion,1385 ValueRange privateVars, TypeRange privateTypes,1386 ArrayAttr privateSyms,1387 UnitAttr privateNeedsBarrier) {1388 AllRegionPrintArgs args;1389 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1390 privateNeedsBarrier,1391 /*mapIndices=*/nullptr);1392 printBlockArgRegion(p, op, region, args);1393}1394 1395static void printPrivateReductionRegion(1396 OpAsmPrinter &p, Operation *op, Region ®ion, ValueRange privateVars,1397 TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,1398 ReductionModifierAttr reductionMod, ValueRange reductionVars,1399 TypeRange reductionTypes, DenseBoolArrayAttr reductionByref,1400 ArrayAttr reductionSyms) {1401 AllRegionPrintArgs args;1402 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,1403 privateNeedsBarrier,1404 /*mapIndices=*/nullptr);1405 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,1406 reductionSyms, reductionMod);1407 printBlockArgRegion(p, op, region, args);1408}1409 1410static void printTaskReductionRegion(OpAsmPrinter &p, Operation *op,1411 Region ®ion,1412 ValueRange taskReductionVars,1413 TypeRange taskReductionTypes,1414 DenseBoolArrayAttr taskReductionByref,1415 ArrayAttr taskReductionSyms) {1416 AllRegionPrintArgs args;1417 args.taskReductionArgs.emplace(taskReductionVars, taskReductionTypes,1418 taskReductionByref, taskReductionSyms);1419 printBlockArgRegion(p, op, region, args);1420}1421 1422static void printUseDeviceAddrUseDevicePtrRegion(OpAsmPrinter &p, Operation *op,1423 Region ®ion,1424 ValueRange useDeviceAddrVars,1425 TypeRange useDeviceAddrTypes,1426 ValueRange useDevicePtrVars,1427 TypeRange useDevicePtrTypes) {1428 AllRegionPrintArgs args;1429 args.useDeviceAddrArgs.emplace(useDeviceAddrVars, useDeviceAddrTypes);1430 args.useDevicePtrArgs.emplace(useDevicePtrVars, useDevicePtrTypes);1431 printBlockArgRegion(p, op, region, args);1432}1433 1434/// Verifies Reduction Clause1435static LogicalResult1436verifyReductionVarList(Operation *op, std::optional<ArrayAttr> reductionSyms,1437 OperandRange reductionVars,1438 std::optional<ArrayRef<bool>> reductionByref) {1439 if (!reductionVars.empty()) {1440 if (!reductionSyms || reductionSyms->size() != reductionVars.size())1441 return op->emitOpError()1442 << "expected as many reduction symbol references "1443 "as reduction variables";1444 if (reductionByref && reductionByref->size() != reductionVars.size())1445 return op->emitError() << "expected as many reduction variable by "1446 "reference attributes as reduction variables";1447 } else {1448 if (reductionSyms)1449 return op->emitOpError() << "unexpected reduction symbol references";1450 return success();1451 }1452 1453 // TODO: The followings should be done in1454 // SymbolUserOpInterface::verifySymbolUses.1455 DenseSet<Value> accumulators;1456 for (auto args : llvm::zip(reductionVars, *reductionSyms)) {1457 Value accum = std::get<0>(args);1458 1459 if (!accumulators.insert(accum).second)1460 return op->emitOpError() << "accumulator variable used more than once";1461 1462 Type varType = accum.getType();1463 auto symbolRef = llvm::cast<SymbolRefAttr>(std::get<1>(args));1464 auto decl =1465 SymbolTable::lookupNearestSymbolFrom<DeclareReductionOp>(op, symbolRef);1466 if (!decl)1467 return op->emitOpError() << "expected symbol reference " << symbolRef1468 << " to point to a reduction declaration";1469 1470 if (decl.getAccumulatorType() && decl.getAccumulatorType() != varType)1471 return op->emitOpError()1472 << "expected accumulator (" << varType1473 << ") to be the same type as reduction declaration ("1474 << decl.getAccumulatorType() << ")";1475 }1476 1477 return success();1478}1479 1480//===----------------------------------------------------------------------===//1481// Parser, printer and verifier for Copyprivate1482//===----------------------------------------------------------------------===//1483 1484/// copyprivate-entry-list ::= copyprivate-entry1485/// | copyprivate-entry-list `,` copyprivate-entry1486/// copyprivate-entry ::= ssa-id `->` symbol-ref `:` type1487static ParseResult parseCopyprivate(1488 OpAsmParser &parser,1489 SmallVectorImpl<OpAsmParser::UnresolvedOperand> ©privateVars,1490 SmallVectorImpl<Type> ©privateTypes, ArrayAttr ©privateSyms) {1491 SmallVector<SymbolRefAttr> symsVec;1492 if (failed(parser.parseCommaSeparatedList([&]() {1493 if (parser.parseOperand(copyprivateVars.emplace_back()) ||1494 parser.parseArrow() ||1495 parser.parseAttribute(symsVec.emplace_back()) ||1496 parser.parseColonType(copyprivateTypes.emplace_back()))1497 return failure();1498 return success();1499 })))1500 return failure();1501 SmallVector<Attribute> syms(symsVec.begin(), symsVec.end());1502 copyprivateSyms = ArrayAttr::get(parser.getContext(), syms);1503 return success();1504}1505 1506/// Print Copyprivate clause1507static void printCopyprivate(OpAsmPrinter &p, Operation *op,1508 OperandRange copyprivateVars,1509 TypeRange copyprivateTypes,1510 std::optional<ArrayAttr> copyprivateSyms) {1511 if (!copyprivateSyms.has_value())1512 return;1513 llvm::interleaveComma(1514 llvm::zip(copyprivateVars, *copyprivateSyms, copyprivateTypes), p,1515 [&](const auto &args) {1516 p << std::get<0>(args) << " -> " << std::get<1>(args) << " : "1517 << std::get<2>(args);1518 });1519}1520 1521/// Verifies CopyPrivate Clause1522static LogicalResult1523verifyCopyprivateVarList(Operation *op, OperandRange copyprivateVars,1524 std::optional<ArrayAttr> copyprivateSyms) {1525 size_t copyprivateSymsSize =1526 copyprivateSyms.has_value() ? copyprivateSyms->size() : 0;1527 if (copyprivateSymsSize != copyprivateVars.size())1528 return op->emitOpError() << "inconsistent number of copyprivate vars (= "1529 << copyprivateVars.size()1530 << ") and functions (= " << copyprivateSymsSize1531 << "), both must be equal";1532 if (!copyprivateSyms.has_value())1533 return success();1534 1535 for (auto copyprivateVarAndSym :1536 llvm::zip(copyprivateVars, *copyprivateSyms)) {1537 auto symbolRef =1538 llvm::cast<SymbolRefAttr>(std::get<1>(copyprivateVarAndSym));1539 std::optional<std::variant<mlir::func::FuncOp, mlir::LLVM::LLVMFuncOp>>1540 funcOp;1541 if (mlir::func::FuncOp mlirFuncOp =1542 SymbolTable::lookupNearestSymbolFrom<mlir::func::FuncOp>(op,1543 symbolRef))1544 funcOp = mlirFuncOp;1545 else if (mlir::LLVM::LLVMFuncOp llvmFuncOp =1546 SymbolTable::lookupNearestSymbolFrom<mlir::LLVM::LLVMFuncOp>(1547 op, symbolRef))1548 funcOp = llvmFuncOp;1549 1550 auto getNumArguments = [&] {1551 return std::visit([](auto &f) { return f.getNumArguments(); }, *funcOp);1552 };1553 1554 auto getArgumentType = [&](unsigned i) {1555 return std::visit([i](auto &f) { return f.getArgumentTypes()[i]; },1556 *funcOp);1557 };1558 1559 if (!funcOp)1560 return op->emitOpError() << "expected symbol reference " << symbolRef1561 << " to point to a copy function";1562 1563 if (getNumArguments() != 2)1564 return op->emitOpError()1565 << "expected copy function " << symbolRef << " to have 2 operands";1566 1567 Type argTy = getArgumentType(0);1568 if (argTy != getArgumentType(1))1569 return op->emitOpError() << "expected copy function " << symbolRef1570 << " arguments to have the same type";1571 1572 Type varType = std::get<0>(copyprivateVarAndSym).getType();1573 if (argTy != varType)1574 return op->emitOpError()1575 << "expected copy function arguments' type (" << argTy1576 << ") to be the same as copyprivate variable's type (" << varType1577 << ")";1578 }1579 1580 return success();1581}1582 1583//===----------------------------------------------------------------------===//1584// Parser, printer and verifier for DependVarList1585//===----------------------------------------------------------------------===//1586 1587/// depend-entry-list ::= depend-entry1588/// | depend-entry-list `,` depend-entry1589/// depend-entry ::= depend-kind `->` ssa-id `:` type1590static ParseResult1591parseDependVarList(OpAsmParser &parser,1592 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &dependVars,1593 SmallVectorImpl<Type> &dependTypes, ArrayAttr &dependKinds) {1594 SmallVector<ClauseTaskDependAttr> kindsVec;1595 if (failed(parser.parseCommaSeparatedList([&]() {1596 StringRef keyword;1597 if (parser.parseKeyword(&keyword) || parser.parseArrow() ||1598 parser.parseOperand(dependVars.emplace_back()) ||1599 parser.parseColonType(dependTypes.emplace_back()))1600 return failure();1601 if (std::optional<ClauseTaskDepend> keywordDepend =1602 (symbolizeClauseTaskDepend(keyword)))1603 kindsVec.emplace_back(1604 ClauseTaskDependAttr::get(parser.getContext(), *keywordDepend));1605 else1606 return failure();1607 return success();1608 })))1609 return failure();1610 SmallVector<Attribute> kinds(kindsVec.begin(), kindsVec.end());1611 dependKinds = ArrayAttr::get(parser.getContext(), kinds);1612 return success();1613}1614 1615/// Print Depend clause1616static void printDependVarList(OpAsmPrinter &p, Operation *op,1617 OperandRange dependVars, TypeRange dependTypes,1618 std::optional<ArrayAttr> dependKinds) {1619 1620 for (unsigned i = 0, e = dependKinds->size(); i < e; ++i) {1621 if (i != 0)1622 p << ", ";1623 p << stringifyClauseTaskDepend(1624 llvm::cast<mlir::omp::ClauseTaskDependAttr>((*dependKinds)[i])1625 .getValue())1626 << " -> " << dependVars[i] << " : " << dependTypes[i];1627 }1628}1629 1630/// Verifies Depend clause1631static LogicalResult verifyDependVarList(Operation *op,1632 std::optional<ArrayAttr> dependKinds,1633 OperandRange dependVars) {1634 if (!dependVars.empty()) {1635 if (!dependKinds || dependKinds->size() != dependVars.size())1636 return op->emitOpError() << "expected as many depend values"1637 " as depend variables";1638 } else {1639 if (dependKinds && !dependKinds->empty())1640 return op->emitOpError() << "unexpected depend values";1641 return success();1642 }1643 1644 return success();1645}1646 1647//===----------------------------------------------------------------------===//1648// Parser, printer and verifier for Synchronization Hint (2.17.12)1649//===----------------------------------------------------------------------===//1650 1651/// Parses a Synchronization Hint clause. The value of hint is an integer1652/// which is a combination of different hints from `omp_sync_hint_t`.1653///1654/// hint-clause = `hint` `(` hint-value `)`1655static ParseResult parseSynchronizationHint(OpAsmParser &parser,1656 IntegerAttr &hintAttr) {1657 StringRef hintKeyword;1658 int64_t hint = 0;1659 if (succeeded(parser.parseOptionalKeyword("none"))) {1660 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0);1661 return success();1662 }1663 auto parseKeyword = [&]() -> ParseResult {1664 if (failed(parser.parseKeyword(&hintKeyword)))1665 return failure();1666 if (hintKeyword == "uncontended")1667 hint |= 1;1668 else if (hintKeyword == "contended")1669 hint |= 2;1670 else if (hintKeyword == "nonspeculative")1671 hint |= 4;1672 else if (hintKeyword == "speculative")1673 hint |= 8;1674 else1675 return parser.emitError(parser.getCurrentLocation())1676 << hintKeyword << " is not a valid hint";1677 return success();1678 };1679 if (parser.parseCommaSeparatedList(parseKeyword))1680 return failure();1681 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint);1682 return success();1683}1684 1685/// Prints a Synchronization Hint clause1686static void printSynchronizationHint(OpAsmPrinter &p, Operation *op,1687 IntegerAttr hintAttr) {1688 int64_t hint = hintAttr.getInt();1689 1690 if (hint == 0) {1691 p << "none";1692 return;1693 }1694 1695 // Helper function to get n-th bit from the right end of `value`1696 auto bitn = [](int value, int n) -> bool { return value & (1 << n); };1697 1698 bool uncontended = bitn(hint, 0);1699 bool contended = bitn(hint, 1);1700 bool nonspeculative = bitn(hint, 2);1701 bool speculative = bitn(hint, 3);1702 1703 SmallVector<StringRef> hints;1704 if (uncontended)1705 hints.push_back("uncontended");1706 if (contended)1707 hints.push_back("contended");1708 if (nonspeculative)1709 hints.push_back("nonspeculative");1710 if (speculative)1711 hints.push_back("speculative");1712 1713 llvm::interleaveComma(hints, p);1714}1715 1716/// Verifies a synchronization hint clause1717static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) {1718 1719 // Helper function to get n-th bit from the right end of `value`1720 auto bitn = [](int value, int n) -> bool { return value & (1 << n); };1721 1722 bool uncontended = bitn(hint, 0);1723 bool contended = bitn(hint, 1);1724 bool nonspeculative = bitn(hint, 2);1725 bool speculative = bitn(hint, 3);1726 1727 if (uncontended && contended)1728 return op->emitOpError() << "the hints omp_sync_hint_uncontended and "1729 "omp_sync_hint_contended cannot be combined";1730 if (nonspeculative && speculative)1731 return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and "1732 "omp_sync_hint_speculative cannot be combined.";1733 return success();1734}1735 1736//===----------------------------------------------------------------------===//1737// Parser, printer and verifier for Target1738//===----------------------------------------------------------------------===//1739 1740// Helper function to get bitwise AND of `value` and 'flag' then return it as a1741// boolean1742static bool mapTypeToBool(ClauseMapFlags value, ClauseMapFlags flag) {1743 return (value & flag) == flag;1744}1745 1746/// Parses a map_entries map type from a string format back into its numeric1747/// value.1748///1749/// map-clause = `map_clauses ( ( `(` `always, `? `implicit, `? `ompx_hold, `?1750/// `close, `? `present, `? ( `to` | `from` | `delete` `)` )+ `)` )1751static ParseResult parseMapClause(OpAsmParser &parser,1752 ClauseMapFlagsAttr &mapType) {1753 ClauseMapFlags mapTypeBits = ClauseMapFlags::none;1754 // This simply verifies the correct keyword is read in, the1755 // keyword itself is stored inside of the operation1756 auto parseTypeAndMod = [&]() -> ParseResult {1757 StringRef mapTypeMod;1758 if (parser.parseKeyword(&mapTypeMod))1759 return failure();1760 1761 if (mapTypeMod == "always")1762 mapTypeBits |= ClauseMapFlags::always;1763 1764 if (mapTypeMod == "implicit")1765 mapTypeBits |= ClauseMapFlags::implicit;1766 1767 if (mapTypeMod == "ompx_hold")1768 mapTypeBits |= ClauseMapFlags::ompx_hold;1769 1770 if (mapTypeMod == "close")1771 mapTypeBits |= ClauseMapFlags::close;1772 1773 if (mapTypeMod == "present")1774 mapTypeBits |= ClauseMapFlags::present;1775 1776 if (mapTypeMod == "to")1777 mapTypeBits |= ClauseMapFlags::to;1778 1779 if (mapTypeMod == "from")1780 mapTypeBits |= ClauseMapFlags::from;1781 1782 if (mapTypeMod == "tofrom")1783 mapTypeBits |= ClauseMapFlags::to | ClauseMapFlags::from;1784 1785 if (mapTypeMod == "delete")1786 mapTypeBits |= ClauseMapFlags::del;1787 1788 if (mapTypeMod == "storage")1789 mapTypeBits |= ClauseMapFlags::storage;1790 1791 if (mapTypeMod == "return_param")1792 mapTypeBits |= ClauseMapFlags::return_param;1793 1794 if (mapTypeMod == "private")1795 mapTypeBits |= ClauseMapFlags::priv;1796 1797 if (mapTypeMod == "literal")1798 mapTypeBits |= ClauseMapFlags::literal;1799 1800 if (mapTypeMod == "attach")1801 mapTypeBits |= ClauseMapFlags::attach;1802 1803 if (mapTypeMod == "attach_always")1804 mapTypeBits |= ClauseMapFlags::attach_always;1805 1806 if (mapTypeMod == "attach_none")1807 mapTypeBits |= ClauseMapFlags::attach_none;1808 1809 if (mapTypeMod == "attach_auto")1810 mapTypeBits |= ClauseMapFlags::attach_auto;1811 1812 if (mapTypeMod == "ref_ptr")1813 mapTypeBits |= ClauseMapFlags::ref_ptr;1814 1815 if (mapTypeMod == "ref_ptee")1816 mapTypeBits |= ClauseMapFlags::ref_ptee;1817 1818 if (mapTypeMod == "ref_ptr_ptee")1819 mapTypeBits |= ClauseMapFlags::ref_ptr_ptee;1820 1821 return success();1822 };1823 1824 if (parser.parseCommaSeparatedList(parseTypeAndMod))1825 return failure();1826 1827 mapType =1828 parser.getBuilder().getAttr<mlir::omp::ClauseMapFlagsAttr>(mapTypeBits);1829 1830 return success();1831}1832 1833/// Prints a map_entries map type from its numeric value out into its string1834/// format.1835static void printMapClause(OpAsmPrinter &p, Operation *op,1836 ClauseMapFlagsAttr mapType) {1837 llvm::SmallVector<std::string, 4> mapTypeStrs;1838 ClauseMapFlags mapFlags = mapType.getValue();1839 1840 // handling of always, close, present placed at the beginning of the string1841 // to aid readability1842 if (mapTypeToBool(mapFlags, ClauseMapFlags::always))1843 mapTypeStrs.push_back("always");1844 if (mapTypeToBool(mapFlags, ClauseMapFlags::implicit))1845 mapTypeStrs.push_back("implicit");1846 if (mapTypeToBool(mapFlags, ClauseMapFlags::ompx_hold))1847 mapTypeStrs.push_back("ompx_hold");1848 if (mapTypeToBool(mapFlags, ClauseMapFlags::close))1849 mapTypeStrs.push_back("close");1850 if (mapTypeToBool(mapFlags, ClauseMapFlags::present))1851 mapTypeStrs.push_back("present");1852 1853 // special handling of to/from/tofrom/delete and release/alloc, release +1854 // alloc are the abscense of one of the other flags, whereas tofrom requires1855 // both the to and from flag to be set.1856 bool to = mapTypeToBool(mapFlags, ClauseMapFlags::to);1857 bool from = mapTypeToBool(mapFlags, ClauseMapFlags::from);1858 1859 if (to && from)1860 mapTypeStrs.push_back("tofrom");1861 else if (from)1862 mapTypeStrs.push_back("from");1863 else if (to)1864 mapTypeStrs.push_back("to");1865 1866 if (mapTypeToBool(mapFlags, ClauseMapFlags::del))1867 mapTypeStrs.push_back("delete");1868 if (mapTypeToBool(mapFlags, ClauseMapFlags::return_param))1869 mapTypeStrs.push_back("return_param");1870 if (mapTypeToBool(mapFlags, ClauseMapFlags::storage))1871 mapTypeStrs.push_back("storage");1872 if (mapTypeToBool(mapFlags, ClauseMapFlags::priv))1873 mapTypeStrs.push_back("private");1874 if (mapTypeToBool(mapFlags, ClauseMapFlags::literal))1875 mapTypeStrs.push_back("literal");1876 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach))1877 mapTypeStrs.push_back("attach");1878 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_always))1879 mapTypeStrs.push_back("attach_always");1880 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_none))1881 mapTypeStrs.push_back("attach_none");1882 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_auto))1883 mapTypeStrs.push_back("attach_auto");1884 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptr))1885 mapTypeStrs.push_back("ref_ptr");1886 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptee))1887 mapTypeStrs.push_back("ref_ptee");1888 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptr_ptee))1889 mapTypeStrs.push_back("ref_ptr_ptee");1890 if (mapFlags == ClauseMapFlags::none)1891 mapTypeStrs.push_back("none");1892 1893 for (unsigned int i = 0; i < mapTypeStrs.size(); ++i) {1894 p << mapTypeStrs[i];1895 if (i + 1 < mapTypeStrs.size()) {1896 p << ", ";1897 }1898 }1899}1900 1901static ParseResult parseMembersIndex(OpAsmParser &parser,1902 ArrayAttr &membersIdx) {1903 SmallVector<Attribute> values, memberIdxs;1904 1905 auto parseIndices = [&]() -> ParseResult {1906 int64_t value;1907 if (parser.parseInteger(value))1908 return failure();1909 values.push_back(IntegerAttr::get(parser.getBuilder().getIntegerType(64),1910 APInt(64, value, /*isSigned=*/false)));1911 return success();1912 };1913 1914 do {1915 if (failed(parser.parseLSquare()))1916 return failure();1917 1918 if (parser.parseCommaSeparatedList(parseIndices))1919 return failure();1920 1921 if (failed(parser.parseRSquare()))1922 return failure();1923 1924 memberIdxs.push_back(ArrayAttr::get(parser.getContext(), values));1925 values.clear();1926 } while (succeeded(parser.parseOptionalComma()));1927 1928 if (!memberIdxs.empty())1929 membersIdx = ArrayAttr::get(parser.getContext(), memberIdxs);1930 1931 return success();1932}1933 1934static void printMembersIndex(OpAsmPrinter &p, MapInfoOp op,1935 ArrayAttr membersIdx) {1936 if (!membersIdx)1937 return;1938 1939 llvm::interleaveComma(membersIdx, p, [&p](Attribute v) {1940 p << "[";1941 auto memberIdx = cast<ArrayAttr>(v);1942 llvm::interleaveComma(memberIdx.getValue(), p, [&p](Attribute v2) {1943 p << cast<IntegerAttr>(v2).getInt();1944 });1945 p << "]";1946 });1947}1948 1949static void printCaptureType(OpAsmPrinter &p, Operation *op,1950 VariableCaptureKindAttr mapCaptureType) {1951 std::string typeCapStr;1952 llvm::raw_string_ostream typeCap(typeCapStr);1953 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByRef)1954 typeCap << "ByRef";1955 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByCopy)1956 typeCap << "ByCopy";1957 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::VLAType)1958 typeCap << "VLAType";1959 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::This)1960 typeCap << "This";1961 p << typeCapStr;1962}1963 1964static ParseResult parseCaptureType(OpAsmParser &parser,1965 VariableCaptureKindAttr &mapCaptureType) {1966 StringRef mapCaptureKey;1967 if (parser.parseKeyword(&mapCaptureKey))1968 return failure();1969 1970 if (mapCaptureKey == "This")1971 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(1972 parser.getContext(), mlir::omp::VariableCaptureKind::This);1973 if (mapCaptureKey == "ByRef")1974 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(1975 parser.getContext(), mlir::omp::VariableCaptureKind::ByRef);1976 if (mapCaptureKey == "ByCopy")1977 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(1978 parser.getContext(), mlir::omp::VariableCaptureKind::ByCopy);1979 if (mapCaptureKey == "VLAType")1980 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(1981 parser.getContext(), mlir::omp::VariableCaptureKind::VLAType);1982 1983 return success();1984}1985 1986static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars) {1987 llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> updateToVars;1988 llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> updateFromVars;1989 1990 for (auto mapOp : mapVars) {1991 if (!mapOp.getDefiningOp())1992 return emitError(op->getLoc(), "missing map operation");1993 1994 if (auto mapInfoOp = mapOp.getDefiningOp<mlir::omp::MapInfoOp>()) {1995 mlir::omp::ClauseMapFlags mapTypeBits = mapInfoOp.getMapType();1996 1997 bool to = mapTypeToBool(mapTypeBits, ClauseMapFlags::to);1998 bool from = mapTypeToBool(mapTypeBits, ClauseMapFlags::from);1999 bool del = mapTypeToBool(mapTypeBits, ClauseMapFlags::del);2000 2001 bool always = mapTypeToBool(mapTypeBits, ClauseMapFlags::always);2002 bool close = mapTypeToBool(mapTypeBits, ClauseMapFlags::close);2003 bool implicit = mapTypeToBool(mapTypeBits, ClauseMapFlags::implicit);2004 2005 if ((isa<TargetDataOp>(op) || isa<TargetOp>(op)) && del)2006 return emitError(op->getLoc(),2007 "to, from, tofrom and alloc map types are permitted");2008 2009 if (isa<TargetEnterDataOp>(op) && (from || del))2010 return emitError(op->getLoc(), "to and alloc map types are permitted");2011 2012 if (isa<TargetExitDataOp>(op) && to)2013 return emitError(op->getLoc(),2014 "from, release and delete map types are permitted");2015 2016 if (isa<TargetUpdateOp>(op)) {2017 if (del) {2018 return emitError(op->getLoc(),2019 "at least one of to or from map types must be "2020 "specified, other map types are not permitted");2021 }2022 2023 if (!to && !from) {2024 return emitError(op->getLoc(),2025 "at least one of to or from map types must be "2026 "specified, other map types are not permitted");2027 }2028 2029 auto updateVar = mapInfoOp.getVarPtr();2030 2031 if ((to && from) || (to && updateFromVars.contains(updateVar)) ||2032 (from && updateToVars.contains(updateVar))) {2033 return emitError(2034 op->getLoc(),2035 "either to or from map types can be specified, not both");2036 }2037 2038 if (always || close || implicit) {2039 return emitError(2040 op->getLoc(),2041 "present, mapper and iterator map type modifiers are permitted");2042 }2043 2044 to ? updateToVars.insert(updateVar) : updateFromVars.insert(updateVar);2045 }2046 } else if (!isa<DeclareMapperInfoOp>(op)) {2047 return emitError(op->getLoc(),2048 "map argument is not a map entry operation");2049 }2050 }2051 2052 return success();2053}2054 2055static LogicalResult verifyPrivateVarsMapping(TargetOp targetOp) {2056 std::optional<DenseI64ArrayAttr> privateMapIndices =2057 targetOp.getPrivateMapsAttr();2058 2059 // None of the private operands are mapped.2060 if (!privateMapIndices.has_value() || !privateMapIndices.value())2061 return success();2062 2063 OperandRange privateVars = targetOp.getPrivateVars();2064 2065 if (privateMapIndices.value().size() !=2066 static_cast<int64_t>(privateVars.size()))2067 return emitError(targetOp.getLoc(), "sizes of `private` operand range and "2068 "`private_maps` attribute mismatch");2069 2070 return success();2071}2072 2073//===----------------------------------------------------------------------===//2074// MapInfoOp2075//===----------------------------------------------------------------------===//2076 2077static LogicalResult verifyMapInfoDefinedArgs(Operation *op,2078 StringRef clauseName,2079 OperandRange vars) {2080 for (Value var : vars)2081 if (!llvm::isa_and_present<MapInfoOp>(var.getDefiningOp()))2082 return op->emitOpError()2083 << "'" << clauseName2084 << "' arguments must be defined by 'omp.map.info' ops";2085 return success();2086}2087 2088LogicalResult MapInfoOp::verify() {2089 if (getMapperId() &&2090 !SymbolTable::lookupNearestSymbolFrom<omp::DeclareMapperOp>(2091 *this, getMapperIdAttr())) {2092 return emitError("invalid mapper id");2093 }2094 2095 if (failed(verifyMapInfoDefinedArgs(*this, "members", getMembers())))2096 return failure();2097 2098 return success();2099}2100 2101//===----------------------------------------------------------------------===//2102// TargetDataOp2103//===----------------------------------------------------------------------===//2104 2105void TargetDataOp::build(OpBuilder &builder, OperationState &state,2106 const TargetDataOperands &clauses) {2107 TargetDataOp::build(builder, state, clauses.device, clauses.ifExpr,2108 clauses.mapVars, clauses.useDeviceAddrVars,2109 clauses.useDevicePtrVars);2110}2111 2112LogicalResult TargetDataOp::verify() {2113 if (getMapVars().empty() && getUseDevicePtrVars().empty() &&2114 getUseDeviceAddrVars().empty()) {2115 return ::emitError(this->getLoc(),2116 "At least one of map, use_device_ptr_vars, or "2117 "use_device_addr_vars operand must be present");2118 }2119 2120 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_ptr",2121 getUseDevicePtrVars())))2122 return failure();2123 2124 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_addr",2125 getUseDeviceAddrVars())))2126 return failure();2127 2128 return verifyMapClause(*this, getMapVars());2129}2130 2131//===----------------------------------------------------------------------===//2132// TargetEnterDataOp2133//===----------------------------------------------------------------------===//2134 2135void TargetEnterDataOp::build(2136 OpBuilder &builder, OperationState &state,2137 const TargetEnterExitUpdateDataOperands &clauses) {2138 MLIRContext *ctx = builder.getContext();2139 TargetEnterDataOp::build(builder, state,2140 makeArrayAttr(ctx, clauses.dependKinds),2141 clauses.dependVars, clauses.device, clauses.ifExpr,2142 clauses.mapVars, clauses.nowait);2143}2144 2145LogicalResult TargetEnterDataOp::verify() {2146 LogicalResult verifyDependVars =2147 verifyDependVarList(*this, getDependKinds(), getDependVars());2148 return failed(verifyDependVars) ? verifyDependVars2149 : verifyMapClause(*this, getMapVars());2150}2151 2152//===----------------------------------------------------------------------===//2153// TargetExitDataOp2154//===----------------------------------------------------------------------===//2155 2156void TargetExitDataOp::build(OpBuilder &builder, OperationState &state,2157 const TargetEnterExitUpdateDataOperands &clauses) {2158 MLIRContext *ctx = builder.getContext();2159 TargetExitDataOp::build(builder, state,2160 makeArrayAttr(ctx, clauses.dependKinds),2161 clauses.dependVars, clauses.device, clauses.ifExpr,2162 clauses.mapVars, clauses.nowait);2163}2164 2165LogicalResult TargetExitDataOp::verify() {2166 LogicalResult verifyDependVars =2167 verifyDependVarList(*this, getDependKinds(), getDependVars());2168 return failed(verifyDependVars) ? verifyDependVars2169 : verifyMapClause(*this, getMapVars());2170}2171 2172//===----------------------------------------------------------------------===//2173// TargetUpdateOp2174//===----------------------------------------------------------------------===//2175 2176void TargetUpdateOp::build(OpBuilder &builder, OperationState &state,2177 const TargetEnterExitUpdateDataOperands &clauses) {2178 MLIRContext *ctx = builder.getContext();2179 TargetUpdateOp::build(builder, state, makeArrayAttr(ctx, clauses.dependKinds),2180 clauses.dependVars, clauses.device, clauses.ifExpr,2181 clauses.mapVars, clauses.nowait);2182}2183 2184LogicalResult TargetUpdateOp::verify() {2185 LogicalResult verifyDependVars =2186 verifyDependVarList(*this, getDependKinds(), getDependVars());2187 return failed(verifyDependVars) ? verifyDependVars2188 : verifyMapClause(*this, getMapVars());2189}2190 2191//===----------------------------------------------------------------------===//2192// TargetOp2193//===----------------------------------------------------------------------===//2194 2195void TargetOp::build(OpBuilder &builder, OperationState &state,2196 const TargetOperands &clauses) {2197 MLIRContext *ctx = builder.getContext();2198 // TODO Store clauses in op: allocateVars, allocatorVars, inReductionVars,2199 // inReductionByref, inReductionSyms.2200 TargetOp::build(builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{},2201 clauses.bare, makeArrayAttr(ctx, clauses.dependKinds),2202 clauses.dependVars, clauses.device, clauses.hasDeviceAddrVars,2203 clauses.hostEvalVars, clauses.ifExpr,2204 /*in_reduction_vars=*/{}, /*in_reduction_byref=*/nullptr,2205 /*in_reduction_syms=*/nullptr, clauses.isDevicePtrVars,2206 clauses.mapVars, clauses.nowait, clauses.privateVars,2207 makeArrayAttr(ctx, clauses.privateSyms),2208 clauses.privateNeedsBarrier, clauses.threadLimit,2209 /*private_maps=*/nullptr);2210}2211 2212LogicalResult TargetOp::verify() {2213 if (failed(verifyDependVarList(*this, getDependKinds(), getDependVars())))2214 return failure();2215 2216 if (failed(verifyMapInfoDefinedArgs(*this, "has_device_addr",2217 getHasDeviceAddrVars())))2218 return failure();2219 2220 if (failed(verifyMapClause(*this, getMapVars())))2221 return failure();2222 2223 return verifyPrivateVarsMapping(*this);2224}2225 2226LogicalResult TargetOp::verifyRegions() {2227 auto teamsOps = getOps<TeamsOp>();2228 if (std::distance(teamsOps.begin(), teamsOps.end()) > 1)2229 return emitError("target containing multiple 'omp.teams' nested ops");2230 2231 // Check that host_eval values are only used in legal ways.2232 Operation *capturedOp = getInnermostCapturedOmpOp();2233 TargetRegionFlags execFlags = getKernelExecFlags(capturedOp);2234 for (Value hostEvalArg :2235 cast<BlockArgOpenMPOpInterface>(getOperation()).getHostEvalBlockArgs()) {2236 for (Operation *user : hostEvalArg.getUsers()) {2237 if (auto teamsOp = dyn_cast<TeamsOp>(user)) {2238 if (llvm::is_contained({teamsOp.getNumTeamsLower(),2239 teamsOp.getNumTeamsUpper(),2240 teamsOp.getThreadLimit()},2241 hostEvalArg))2242 continue;2243 2244 return emitOpError() << "host_eval argument only legal as 'num_teams' "2245 "and 'thread_limit' in 'omp.teams'";2246 }2247 if (auto parallelOp = dyn_cast<ParallelOp>(user)) {2248 if (bitEnumContainsAny(execFlags, TargetRegionFlags::spmd) &&2249 parallelOp->isAncestor(capturedOp) &&2250 hostEvalArg == parallelOp.getNumThreads())2251 continue;2252 2253 return emitOpError()2254 << "host_eval argument only legal as 'num_threads' in "2255 "'omp.parallel' when representing target SPMD";2256 }2257 if (auto loopNestOp = dyn_cast<LoopNestOp>(user)) {2258 if (bitEnumContainsAny(execFlags, TargetRegionFlags::trip_count) &&2259 loopNestOp.getOperation() == capturedOp &&2260 (llvm::is_contained(loopNestOp.getLoopLowerBounds(), hostEvalArg) ||2261 llvm::is_contained(loopNestOp.getLoopUpperBounds(), hostEvalArg) ||2262 llvm::is_contained(loopNestOp.getLoopSteps(), hostEvalArg)))2263 continue;2264 2265 return emitOpError() << "host_eval argument only legal as loop bounds "2266 "and steps in 'omp.loop_nest' when trip count "2267 "must be evaluated in the host";2268 }2269 2270 return emitOpError() << "host_eval argument illegal use in '"2271 << user->getName() << "' operation";2272 }2273 }2274 return success();2275}2276 2277static Operation *2278findCapturedOmpOp(Operation *rootOp, bool checkSingleMandatoryExec,2279 llvm::function_ref<bool(Operation *)> siblingAllowedFn) {2280 assert(rootOp && "expected valid operation");2281 2282 Dialect *ompDialect = rootOp->getDialect();2283 Operation *capturedOp = nullptr;2284 DominanceInfo domInfo;2285 2286 // Process in pre-order to check operations from outermost to innermost,2287 // ensuring we only enter the region of an operation if it meets the criteria2288 // for being captured. We stop the exploration of nested operations as soon as2289 // we process a region holding no operations to be captured.2290 rootOp->walk<WalkOrder::PreOrder>([&](Operation *op) {2291 if (op == rootOp)2292 return WalkResult::advance();2293 2294 // Ignore operations of other dialects or omp operations with no regions,2295 // because these will only be checked if they are siblings of an omp2296 // operation that can potentially be captured.2297 bool isOmpDialect = op->getDialect() == ompDialect;2298 bool hasRegions = op->getNumRegions() > 0;2299 if (!isOmpDialect || !hasRegions)2300 return WalkResult::skip();2301 2302 // This operation cannot be captured if it can be executed more than once2303 // (i.e. its block's successors can reach it) or if it's not guaranteed to2304 // be executed before all exits of the region (i.e. it doesn't dominate all2305 // blocks with no successors reachable from the entry block).2306 if (checkSingleMandatoryExec) {2307 Region *parentRegion = op->getParentRegion();2308 Block *parentBlock = op->getBlock();2309 2310 for (Block *successor : parentBlock->getSuccessors())2311 if (successor->isReachable(parentBlock))2312 return WalkResult::interrupt();2313 2314 for (Block &block : *parentRegion)2315 if (domInfo.isReachableFromEntry(&block) && block.hasNoSuccessors() &&2316 !domInfo.dominates(parentBlock, &block))2317 return WalkResult::interrupt();2318 }2319 2320 // Don't capture this op if it has a not-allowed sibling, and stop recursing2321 // into nested operations.2322 for (Operation &sibling : op->getParentRegion()->getOps())2323 if (&sibling != op && !siblingAllowedFn(&sibling))2324 return WalkResult::interrupt();2325 2326 // Don't continue capturing nested operations if we reach an omp.loop_nest.2327 // Otherwise, process the contents of this operation.2328 capturedOp = op;2329 return llvm::isa<LoopNestOp>(op) ? WalkResult::interrupt()2330 : WalkResult::advance();2331 });2332 2333 return capturedOp;2334}2335 2336Operation *TargetOp::getInnermostCapturedOmpOp() {2337 auto *ompDialect = getContext()->getLoadedDialect<omp::OpenMPDialect>();2338 2339 // Only allow OpenMP terminators and non-OpenMP ops that have known memory2340 // effects, but don't include a memory write effect.2341 return findCapturedOmpOp(2342 *this, /*checkSingleMandatoryExec=*/true, [&](Operation *sibling) {2343 if (!sibling)2344 return false;2345 2346 if (ompDialect == sibling->getDialect())2347 return sibling->hasTrait<OpTrait::IsTerminator>();2348 2349 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(sibling)) {2350 SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>, 4>2351 effects;2352 memOp.getEffects(effects);2353 return !llvm::any_of(2354 effects, [&](MemoryEffects::EffectInstance &effect) {2355 return isa<MemoryEffects::Write>(effect.getEffect()) &&2356 isa<SideEffects::AutomaticAllocationScopeResource>(2357 effect.getResource());2358 });2359 }2360 return true;2361 });2362}2363 2364/// Check if we can promote SPMD kernel to No-Loop kernel.2365static bool canPromoteToNoLoop(Operation *capturedOp, TeamsOp teamsOp,2366 WsloopOp *wsLoopOp) {2367 // num_teams clause can break no-loop teams/threads assumption.2368 if (teamsOp.getNumTeamsUpper())2369 return false;2370 2371 // Reduction kernels are slower in no-loop mode.2372 if (teamsOp.getNumReductionVars())2373 return false;2374 if (wsLoopOp->getNumReductionVars())2375 return false;2376 2377 // Check if the user allows the promotion of kernels to no-loop mode.2378 OffloadModuleInterface offloadMod =2379 capturedOp->getParentOfType<omp::OffloadModuleInterface>();2380 if (!offloadMod)2381 return false;2382 auto ompFlags = offloadMod.getFlags();2383 if (!ompFlags)2384 return false;2385 return ompFlags.getAssumeTeamsOversubscription() &&2386 ompFlags.getAssumeThreadsOversubscription();2387}2388 2389TargetRegionFlags TargetOp::getKernelExecFlags(Operation *capturedOp) {2390 // A non-null captured op is only valid if it resides inside of a TargetOp2391 // and is the result of calling getInnermostCapturedOmpOp() on it.2392 TargetOp targetOp =2393 capturedOp ? capturedOp->getParentOfType<TargetOp>() : nullptr;2394 assert((!capturedOp ||2395 (targetOp && targetOp.getInnermostCapturedOmpOp() == capturedOp)) &&2396 "unexpected captured op");2397 2398 // If it's not capturing a loop, it's a default target region.2399 if (!isa_and_present<LoopNestOp>(capturedOp))2400 return TargetRegionFlags::generic;2401 2402 // Get the innermost non-simd loop wrapper.2403 SmallVector<LoopWrapperInterface> loopWrappers;2404 cast<LoopNestOp>(capturedOp).gatherWrappers(loopWrappers);2405 assert(!loopWrappers.empty());2406 2407 LoopWrapperInterface *innermostWrapper = loopWrappers.begin();2408 if (isa<SimdOp>(innermostWrapper))2409 innermostWrapper = std::next(innermostWrapper);2410 2411 auto numWrappers = std::distance(innermostWrapper, loopWrappers.end());2412 if (numWrappers != 1 && numWrappers != 2)2413 return TargetRegionFlags::generic;2414 2415 // Detect target-teams-distribute-parallel-wsloop[-simd].2416 if (numWrappers == 2) {2417 WsloopOp *wsloopOp = dyn_cast<WsloopOp>(innermostWrapper);2418 if (!wsloopOp)2419 return TargetRegionFlags::generic;2420 2421 innermostWrapper = std::next(innermostWrapper);2422 if (!isa<DistributeOp>(innermostWrapper))2423 return TargetRegionFlags::generic;2424 2425 Operation *parallelOp = (*innermostWrapper)->getParentOp();2426 if (!isa_and_present<ParallelOp>(parallelOp))2427 return TargetRegionFlags::generic;2428 2429 TeamsOp teamsOp = dyn_cast<TeamsOp>(parallelOp->getParentOp());2430 if (!teamsOp)2431 return TargetRegionFlags::generic;2432 2433 if (teamsOp->getParentOp() == targetOp.getOperation()) {2434 TargetRegionFlags result =2435 TargetRegionFlags::spmd | TargetRegionFlags::trip_count;2436 if (canPromoteToNoLoop(capturedOp, teamsOp, wsloopOp))2437 result = result | TargetRegionFlags::no_loop;2438 return result;2439 }2440 }2441 // Detect target-teams-distribute[-simd] and target-teams-loop.2442 else if (isa<DistributeOp, LoopOp>(innermostWrapper)) {2443 Operation *teamsOp = (*innermostWrapper)->getParentOp();2444 if (!isa_and_present<TeamsOp>(teamsOp))2445 return TargetRegionFlags::generic;2446 2447 if (teamsOp->getParentOp() != targetOp.getOperation())2448 return TargetRegionFlags::generic;2449 2450 if (isa<LoopOp>(innermostWrapper))2451 return TargetRegionFlags::spmd | TargetRegionFlags::trip_count;2452 2453 // Find single immediately nested captured omp.parallel and add spmd flag2454 // (generic-spmd case).2455 //2456 // TODO: This shouldn't have to be done here, as it is too easy to break.2457 // The openmp-opt pass should be updated to be able to promote kernels like2458 // this from "Generic" to "Generic-SPMD". However, the use of the2459 // `kmpc_distribute_static_loop` family of functions produced by the2460 // OMPIRBuilder for these kernels prevents that from working.2461 Dialect *ompDialect = targetOp->getDialect();2462 Operation *nestedCapture = findCapturedOmpOp(2463 capturedOp, /*checkSingleMandatoryExec=*/false,2464 [&](Operation *sibling) {2465 return sibling && (ompDialect != sibling->getDialect() ||2466 sibling->hasTrait<OpTrait::IsTerminator>());2467 });2468 2469 TargetRegionFlags result =2470 TargetRegionFlags::generic | TargetRegionFlags::trip_count;2471 2472 if (!nestedCapture)2473 return result;2474 2475 while (nestedCapture->getParentOp() != capturedOp)2476 nestedCapture = nestedCapture->getParentOp();2477 2478 return isa<ParallelOp>(nestedCapture) ? result | TargetRegionFlags::spmd2479 : result;2480 }2481 // Detect target-parallel-wsloop[-simd].2482 else if (isa<WsloopOp>(innermostWrapper)) {2483 Operation *parallelOp = (*innermostWrapper)->getParentOp();2484 if (!isa_and_present<ParallelOp>(parallelOp))2485 return TargetRegionFlags::generic;2486 2487 if (parallelOp->getParentOp() == targetOp.getOperation())2488 return TargetRegionFlags::spmd;2489 }2490 2491 return TargetRegionFlags::generic;2492}2493 2494//===----------------------------------------------------------------------===//2495// ParallelOp2496//===----------------------------------------------------------------------===//2497 2498void ParallelOp::build(OpBuilder &builder, OperationState &state,2499 ArrayRef<NamedAttribute> attributes) {2500 ParallelOp::build(builder, state, /*allocate_vars=*/ValueRange(),2501 /*allocator_vars=*/ValueRange(), /*if_expr=*/nullptr,2502 /*num_threads=*/nullptr, /*private_vars=*/ValueRange(),2503 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,2504 /*proc_bind_kind=*/nullptr,2505 /*reduction_mod =*/nullptr, /*reduction_vars=*/ValueRange(),2506 /*reduction_byref=*/nullptr, /*reduction_syms=*/nullptr);2507 state.addAttributes(attributes);2508}2509 2510void ParallelOp::build(OpBuilder &builder, OperationState &state,2511 const ParallelOperands &clauses) {2512 MLIRContext *ctx = builder.getContext();2513 ParallelOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,2514 clauses.ifExpr, clauses.numThreads, clauses.privateVars,2515 makeArrayAttr(ctx, clauses.privateSyms),2516 clauses.privateNeedsBarrier, clauses.procBindKind,2517 clauses.reductionMod, clauses.reductionVars,2518 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2519 makeArrayAttr(ctx, clauses.reductionSyms));2520}2521 2522template <typename OpType>2523static LogicalResult verifyPrivateVarList(OpType &op) {2524 auto privateVars = op.getPrivateVars();2525 auto privateSyms = op.getPrivateSymsAttr();2526 2527 if (privateVars.empty() && (privateSyms == nullptr || privateSyms.empty()))2528 return success();2529 2530 auto numPrivateVars = privateVars.size();2531 auto numPrivateSyms = (privateSyms == nullptr) ? 0 : privateSyms.size();2532 2533 if (numPrivateVars != numPrivateSyms)2534 return op.emitError() << "inconsistent number of private variables and "2535 "privatizer op symbols, private vars: "2536 << numPrivateVars2537 << " vs. privatizer op symbols: " << numPrivateSyms;2538 2539 for (auto privateVarInfo : llvm::zip_equal(privateVars, privateSyms)) {2540 Type varType = std::get<0>(privateVarInfo).getType();2541 SymbolRefAttr privateSym = cast<SymbolRefAttr>(std::get<1>(privateVarInfo));2542 PrivateClauseOp privatizerOp =2543 SymbolTable::lookupNearestSymbolFrom<PrivateClauseOp>(op, privateSym);2544 2545 if (privatizerOp == nullptr)2546 return op.emitError() << "failed to lookup privatizer op with symbol: '"2547 << privateSym << "'";2548 2549 Type privatizerType = privatizerOp.getArgType();2550 2551 if (privatizerType && (varType != privatizerType))2552 return op.emitError()2553 << "type mismatch between a "2554 << (privatizerOp.getDataSharingType() ==2555 DataSharingClauseType::Private2556 ? "private"2557 : "firstprivate")2558 << " variable and its privatizer op, var type: " << varType2559 << " vs. privatizer op type: " << privatizerType;2560 }2561 2562 return success();2563}2564 2565LogicalResult ParallelOp::verify() {2566 if (getAllocateVars().size() != getAllocatorVars().size())2567 return emitError(2568 "expected equal sizes for allocate and allocator variables");2569 2570 if (failed(verifyPrivateVarList(*this)))2571 return failure();2572 2573 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),2574 getReductionByref());2575}2576 2577LogicalResult ParallelOp::verifyRegions() {2578 auto distChildOps = getOps<DistributeOp>();2579 int numDistChildOps = std::distance(distChildOps.begin(), distChildOps.end());2580 if (numDistChildOps > 1)2581 return emitError()2582 << "multiple 'omp.distribute' nested inside of 'omp.parallel'";2583 2584 if (numDistChildOps == 1) {2585 if (!isComposite())2586 return emitError()2587 << "'omp.composite' attribute missing from composite operation";2588 2589 auto *ompDialect = getContext()->getLoadedDialect<OpenMPDialect>();2590 Operation &distributeOp = **distChildOps.begin();2591 for (Operation &childOp : getOps()) {2592 if (&childOp == &distributeOp || ompDialect != childOp.getDialect())2593 continue;2594 2595 if (!childOp.hasTrait<OpTrait::IsTerminator>())2596 return emitError() << "unexpected OpenMP operation inside of composite "2597 "'omp.parallel': "2598 << childOp.getName();2599 }2600 } else if (isComposite()) {2601 return emitError()2602 << "'omp.composite' attribute present in non-composite operation";2603 }2604 return success();2605}2606 2607//===----------------------------------------------------------------------===//2608// TeamsOp2609//===----------------------------------------------------------------------===//2610 2611static bool opInGlobalImplicitParallelRegion(Operation *op) {2612 while ((op = op->getParentOp()))2613 if (isa<OpenMPDialect>(op->getDialect()))2614 return false;2615 return true;2616}2617 2618void TeamsOp::build(OpBuilder &builder, OperationState &state,2619 const TeamsOperands &clauses) {2620 MLIRContext *ctx = builder.getContext();2621 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier2622 TeamsOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,2623 clauses.ifExpr, clauses.numTeamsLower, clauses.numTeamsUpper,2624 /*private_vars=*/{}, /*private_syms=*/nullptr,2625 /*private_needs_barrier=*/nullptr, clauses.reductionMod,2626 clauses.reductionVars,2627 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2628 makeArrayAttr(ctx, clauses.reductionSyms),2629 clauses.threadLimit);2630}2631 2632LogicalResult TeamsOp::verify() {2633 // Check parent region2634 // TODO If nested inside of a target region, also check that it does not2635 // contain any statements, declarations or directives other than this2636 // omp.teams construct. The issue is how to support the initialization of2637 // this operation's own arguments (allow SSA values across omp.target?).2638 Operation *op = getOperation();2639 if (!isa<TargetOp>(op->getParentOp()) &&2640 !opInGlobalImplicitParallelRegion(op))2641 return emitError("expected to be nested inside of omp.target or not nested "2642 "in any OpenMP dialect operations");2643 2644 // Check for num_teams clause restrictions2645 if (auto numTeamsLowerBound = getNumTeamsLower()) {2646 auto numTeamsUpperBound = getNumTeamsUpper();2647 if (!numTeamsUpperBound)2648 return emitError("expected num_teams upper bound to be defined if the "2649 "lower bound is defined");2650 if (numTeamsLowerBound.getType() != numTeamsUpperBound.getType())2651 return emitError(2652 "expected num_teams upper bound and lower bound to be the same type");2653 }2654 2655 // Check for allocate clause restrictions2656 if (getAllocateVars().size() != getAllocatorVars().size())2657 return emitError(2658 "expected equal sizes for allocate and allocator variables");2659 2660 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),2661 getReductionByref());2662}2663 2664//===----------------------------------------------------------------------===//2665// SectionOp2666//===----------------------------------------------------------------------===//2667 2668OperandRange SectionOp::getPrivateVars() {2669 return getParentOp().getPrivateVars();2670}2671 2672OperandRange SectionOp::getReductionVars() {2673 return getParentOp().getReductionVars();2674}2675 2676//===----------------------------------------------------------------------===//2677// SectionsOp2678//===----------------------------------------------------------------------===//2679 2680void SectionsOp::build(OpBuilder &builder, OperationState &state,2681 const SectionsOperands &clauses) {2682 MLIRContext *ctx = builder.getContext();2683 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier2684 SectionsOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,2685 clauses.nowait, /*private_vars=*/{},2686 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,2687 clauses.reductionMod, clauses.reductionVars,2688 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2689 makeArrayAttr(ctx, clauses.reductionSyms));2690}2691 2692LogicalResult SectionsOp::verify() {2693 if (getAllocateVars().size() != getAllocatorVars().size())2694 return emitError(2695 "expected equal sizes for allocate and allocator variables");2696 2697 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),2698 getReductionByref());2699}2700 2701LogicalResult SectionsOp::verifyRegions() {2702 for (auto &inst : *getRegion().begin()) {2703 if (!(isa<SectionOp>(inst) || isa<TerminatorOp>(inst))) {2704 return emitOpError()2705 << "expected omp.section op or terminator op inside region";2706 }2707 }2708 2709 return success();2710}2711 2712//===----------------------------------------------------------------------===//2713// SingleOp2714//===----------------------------------------------------------------------===//2715 2716void SingleOp::build(OpBuilder &builder, OperationState &state,2717 const SingleOperands &clauses) {2718 MLIRContext *ctx = builder.getContext();2719 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier2720 SingleOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,2721 clauses.copyprivateVars,2722 makeArrayAttr(ctx, clauses.copyprivateSyms), clauses.nowait,2723 /*private_vars=*/{}, /*private_syms=*/nullptr,2724 /*private_needs_barrier=*/nullptr);2725}2726 2727LogicalResult SingleOp::verify() {2728 // Check for allocate clause restrictions2729 if (getAllocateVars().size() != getAllocatorVars().size())2730 return emitError(2731 "expected equal sizes for allocate and allocator variables");2732 2733 return verifyCopyprivateVarList(*this, getCopyprivateVars(),2734 getCopyprivateSyms());2735}2736 2737//===----------------------------------------------------------------------===//2738// WorkshareOp2739//===----------------------------------------------------------------------===//2740 2741void WorkshareOp::build(OpBuilder &builder, OperationState &state,2742 const WorkshareOperands &clauses) {2743 WorkshareOp::build(builder, state, clauses.nowait);2744}2745 2746//===----------------------------------------------------------------------===//2747// WorkshareLoopWrapperOp2748//===----------------------------------------------------------------------===//2749 2750LogicalResult WorkshareLoopWrapperOp::verify() {2751 if (!(*this)->getParentOfType<WorkshareOp>())2752 return emitOpError() << "must be nested in an omp.workshare";2753 return success();2754}2755 2756LogicalResult WorkshareLoopWrapperOp::verifyRegions() {2757 if (isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||2758 getNestedWrapper())2759 return emitOpError() << "expected to be a standalone loop wrapper";2760 2761 return success();2762}2763 2764//===----------------------------------------------------------------------===//2765// LoopWrapperInterface2766//===----------------------------------------------------------------------===//2767 2768LogicalResult LoopWrapperInterface::verifyImpl() {2769 Operation *op = this->getOperation();2770 if (!op->hasTrait<OpTrait::NoTerminator>() ||2771 !op->hasTrait<OpTrait::SingleBlock>())2772 return emitOpError() << "loop wrapper must also have the `NoTerminator` "2773 "and `SingleBlock` traits";2774 2775 if (op->getNumRegions() != 1)2776 return emitOpError() << "loop wrapper does not contain exactly one region";2777 2778 Region ®ion = op->getRegion(0);2779 if (range_size(region.getOps()) != 1)2780 return emitOpError()2781 << "loop wrapper does not contain exactly one nested op";2782 2783 Operation &firstOp = *region.op_begin();2784 if (!isa<LoopNestOp, LoopWrapperInterface>(firstOp))2785 return emitOpError() << "nested in loop wrapper is not another loop "2786 "wrapper or `omp.loop_nest`";2787 2788 return success();2789}2790 2791//===----------------------------------------------------------------------===//2792// LoopOp2793//===----------------------------------------------------------------------===//2794 2795void LoopOp::build(OpBuilder &builder, OperationState &state,2796 const LoopOperands &clauses) {2797 MLIRContext *ctx = builder.getContext();2798 2799 LoopOp::build(builder, state, clauses.bindKind, clauses.privateVars,2800 makeArrayAttr(ctx, clauses.privateSyms),2801 clauses.privateNeedsBarrier, clauses.order, clauses.orderMod,2802 clauses.reductionMod, clauses.reductionVars,2803 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2804 makeArrayAttr(ctx, clauses.reductionSyms));2805}2806 2807LogicalResult LoopOp::verify() {2808 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),2809 getReductionByref());2810}2811 2812LogicalResult LoopOp::verifyRegions() {2813 if (llvm::isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||2814 getNestedWrapper())2815 return emitOpError() << "expected to be a standalone loop wrapper";2816 2817 return success();2818}2819 2820//===----------------------------------------------------------------------===//2821// WsloopOp2822//===----------------------------------------------------------------------===//2823 2824void WsloopOp::build(OpBuilder &builder, OperationState &state,2825 ArrayRef<NamedAttribute> attributes) {2826 build(builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{},2827 /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(),2828 /*nowait=*/false, /*order=*/nullptr, /*order_mod=*/nullptr,2829 /*ordered=*/nullptr, /*private_vars=*/{}, /*private_syms=*/nullptr,2830 /*private_needs_barrier=*/false,2831 /*reduction_mod=*/nullptr, /*reduction_vars=*/ValueRange(),2832 /*reduction_byref=*/nullptr,2833 /*reduction_syms=*/nullptr, /*schedule_kind=*/nullptr,2834 /*schedule_chunk=*/nullptr, /*schedule_mod=*/nullptr,2835 /*schedule_simd=*/false);2836 state.addAttributes(attributes);2837}2838 2839void WsloopOp::build(OpBuilder &builder, OperationState &state,2840 const WsloopOperands &clauses) {2841 MLIRContext *ctx = builder.getContext();2842 // TODO: Store clauses in op: allocateVars, allocatorVars2843 WsloopOp::build(2844 builder, state,2845 /*allocate_vars=*/{}, /*allocator_vars=*/{}, clauses.linearVars,2846 clauses.linearStepVars, clauses.nowait, clauses.order, clauses.orderMod,2847 clauses.ordered, clauses.privateVars,2848 makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier,2849 clauses.reductionMod, clauses.reductionVars,2850 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2851 makeArrayAttr(ctx, clauses.reductionSyms), clauses.scheduleKind,2852 clauses.scheduleChunk, clauses.scheduleMod, clauses.scheduleSimd);2853}2854 2855LogicalResult WsloopOp::verify() {2856 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),2857 getReductionByref());2858}2859 2860LogicalResult WsloopOp::verifyRegions() {2861 bool isCompositeChildLeaf =2862 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());2863 2864 if (LoopWrapperInterface nested = getNestedWrapper()) {2865 if (!isComposite())2866 return emitError()2867 << "'omp.composite' attribute missing from composite wrapper";2868 2869 // Check for the allowed leaf constructs that may appear in a composite2870 // construct directly after DO/FOR.2871 if (!isa<SimdOp>(nested))2872 return emitError() << "only supported nested wrapper is 'omp.simd'";2873 2874 } else if (isComposite() && !isCompositeChildLeaf) {2875 return emitError()2876 << "'omp.composite' attribute present in non-composite wrapper";2877 } else if (!isComposite() && isCompositeChildLeaf) {2878 return emitError()2879 << "'omp.composite' attribute missing from composite wrapper";2880 }2881 2882 return success();2883}2884 2885//===----------------------------------------------------------------------===//2886// Simd construct [2.9.3.1]2887//===----------------------------------------------------------------------===//2888 2889void SimdOp::build(OpBuilder &builder, OperationState &state,2890 const SimdOperands &clauses) {2891 MLIRContext *ctx = builder.getContext();2892 // TODO Store clauses in op: linearVars, linearStepVars2893 SimdOp::build(builder, state, clauses.alignedVars,2894 makeArrayAttr(ctx, clauses.alignments), clauses.ifExpr,2895 /*linear_vars=*/{}, /*linear_step_vars=*/{},2896 clauses.nontemporalVars, clauses.order, clauses.orderMod,2897 clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms),2898 clauses.privateNeedsBarrier, clauses.reductionMod,2899 clauses.reductionVars,2900 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),2901 makeArrayAttr(ctx, clauses.reductionSyms), clauses.safelen,2902 clauses.simdlen);2903}2904 2905LogicalResult SimdOp::verify() {2906 if (getSimdlen().has_value() && getSafelen().has_value() &&2907 getSimdlen().value() > getSafelen().value())2908 return emitOpError()2909 << "simdlen clause and safelen clause are both present, but the "2910 "simdlen value is not less than or equal to safelen value";2911 2912 if (verifyAlignedClause(*this, getAlignments(), getAlignedVars()).failed())2913 return failure();2914 2915 if (verifyNontemporalClause(*this, getNontemporalVars()).failed())2916 return failure();2917 2918 bool isCompositeChildLeaf =2919 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());2920 2921 if (!isComposite() && isCompositeChildLeaf)2922 return emitError()2923 << "'omp.composite' attribute missing from composite wrapper";2924 2925 if (isComposite() && !isCompositeChildLeaf)2926 return emitError()2927 << "'omp.composite' attribute present in non-composite wrapper";2928 2929 // Firstprivate is not allowed for SIMD in the standard. Check that none of2930 // the private decls are for firstprivate.2931 std::optional<ArrayAttr> privateSyms = getPrivateSyms();2932 if (privateSyms) {2933 for (const Attribute &sym : *privateSyms) {2934 auto symRef = cast<SymbolRefAttr>(sym);2935 omp::PrivateClauseOp privatizer =2936 SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(2937 getOperation(), symRef);2938 if (!privatizer)2939 return emitError() << "Cannot find privatizer '" << symRef << "'";2940 if (privatizer.getDataSharingType() ==2941 DataSharingClauseType::FirstPrivate)2942 return emitError() << "FIRSTPRIVATE cannot be used with SIMD";2943 }2944 }2945 2946 return success();2947}2948 2949LogicalResult SimdOp::verifyRegions() {2950 if (getNestedWrapper())2951 return emitOpError() << "must wrap an 'omp.loop_nest' directly";2952 2953 return success();2954}2955 2956//===----------------------------------------------------------------------===//2957// Distribute construct [2.9.4.1]2958//===----------------------------------------------------------------------===//2959 2960void DistributeOp::build(OpBuilder &builder, OperationState &state,2961 const DistributeOperands &clauses) {2962 DistributeOp::build(builder, state, clauses.allocateVars,2963 clauses.allocatorVars, clauses.distScheduleStatic,2964 clauses.distScheduleChunkSize, clauses.order,2965 clauses.orderMod, clauses.privateVars,2966 makeArrayAttr(builder.getContext(), clauses.privateSyms),2967 clauses.privateNeedsBarrier);2968}2969 2970LogicalResult DistributeOp::verify() {2971 if (this->getDistScheduleChunkSize() && !this->getDistScheduleStatic())2972 return emitOpError() << "chunk size set without "2973 "dist_schedule_static being present";2974 2975 if (getAllocateVars().size() != getAllocatorVars().size())2976 return emitError(2977 "expected equal sizes for allocate and allocator variables");2978 2979 return success();2980}2981 2982LogicalResult DistributeOp::verifyRegions() {2983 if (LoopWrapperInterface nested = getNestedWrapper()) {2984 if (!isComposite())2985 return emitError()2986 << "'omp.composite' attribute missing from composite wrapper";2987 // Check for the allowed leaf constructs that may appear in a composite2988 // construct directly after DISTRIBUTE.2989 if (isa<WsloopOp>(nested)) {2990 Operation *parentOp = (*this)->getParentOp();2991 if (!llvm::dyn_cast_if_present<ParallelOp>(parentOp) ||2992 !cast<ComposableOpInterface>(parentOp).isComposite()) {2993 return emitError() << "an 'omp.wsloop' nested wrapper is only allowed "2994 "when a composite 'omp.parallel' is the direct "2995 "parent";2996 }2997 } else if (!isa<SimdOp>(nested))2998 return emitError() << "only supported nested wrappers are 'omp.simd' and "2999 "'omp.wsloop'";3000 } else if (isComposite()) {3001 return emitError()3002 << "'omp.composite' attribute present in non-composite wrapper";3003 }3004 3005 return success();3006}3007 3008//===----------------------------------------------------------------------===//3009// DeclareMapperOp / DeclareMapperInfoOp3010//===----------------------------------------------------------------------===//3011 3012LogicalResult DeclareMapperInfoOp::verify() {3013 return verifyMapClause(*this, getMapVars());3014}3015 3016LogicalResult DeclareMapperOp::verifyRegions() {3017 if (!llvm::isa_and_present<DeclareMapperInfoOp>(3018 getRegion().getBlocks().front().getTerminator()))3019 return emitOpError() << "expected terminator to be a DeclareMapperInfoOp";3020 3021 return success();3022}3023 3024//===----------------------------------------------------------------------===//3025// DeclareReductionOp3026//===----------------------------------------------------------------------===//3027 3028LogicalResult DeclareReductionOp::verifyRegions() {3029 if (!getAllocRegion().empty()) {3030 for (YieldOp yieldOp : getAllocRegion().getOps<YieldOp>()) {3031 if (yieldOp.getResults().size() != 1 ||3032 yieldOp.getResults().getTypes()[0] != getType())3033 return emitOpError() << "expects alloc region to yield a value "3034 "of the reduction type";3035 }3036 }3037 3038 if (getInitializerRegion().empty())3039 return emitOpError() << "expects non-empty initializer region";3040 Block &initializerEntryBlock = getInitializerRegion().front();3041 3042 if (initializerEntryBlock.getNumArguments() == 1) {3043 if (!getAllocRegion().empty())3044 return emitOpError() << "expects two arguments to the initializer region "3045 "when an allocation region is used";3046 } else if (initializerEntryBlock.getNumArguments() == 2) {3047 if (getAllocRegion().empty())3048 return emitOpError() << "expects one argument to the initializer region "3049 "when no allocation region is used";3050 } else {3051 return emitOpError()3052 << "expects one or two arguments to the initializer region";3053 }3054 3055 for (mlir::Value arg : initializerEntryBlock.getArguments())3056 if (arg.getType() != getType())3057 return emitOpError() << "expects initializer region argument to match "3058 "the reduction type";3059 3060 for (YieldOp yieldOp : getInitializerRegion().getOps<YieldOp>()) {3061 if (yieldOp.getResults().size() != 1 ||3062 yieldOp.getResults().getTypes()[0] != getType())3063 return emitOpError() << "expects initializer region to yield a value "3064 "of the reduction type";3065 }3066 3067 if (getReductionRegion().empty())3068 return emitOpError() << "expects non-empty reduction region";3069 Block &reductionEntryBlock = getReductionRegion().front();3070 if (reductionEntryBlock.getNumArguments() != 2 ||3071 reductionEntryBlock.getArgumentTypes()[0] !=3072 reductionEntryBlock.getArgumentTypes()[1] ||3073 reductionEntryBlock.getArgumentTypes()[0] != getType())3074 return emitOpError() << "expects reduction region with two arguments of "3075 "the reduction type";3076 for (YieldOp yieldOp : getReductionRegion().getOps<YieldOp>()) {3077 if (yieldOp.getResults().size() != 1 ||3078 yieldOp.getResults().getTypes()[0] != getType())3079 return emitOpError() << "expects reduction region to yield a value "3080 "of the reduction type";3081 }3082 3083 if (!getAtomicReductionRegion().empty()) {3084 Block &atomicReductionEntryBlock = getAtomicReductionRegion().front();3085 if (atomicReductionEntryBlock.getNumArguments() != 2 ||3086 atomicReductionEntryBlock.getArgumentTypes()[0] !=3087 atomicReductionEntryBlock.getArgumentTypes()[1])3088 return emitOpError() << "expects atomic reduction region with two "3089 "arguments of the same type";3090 auto ptrType = llvm::dyn_cast<PointerLikeType>(3091 atomicReductionEntryBlock.getArgumentTypes()[0]);3092 if (!ptrType ||3093 (ptrType.getElementType() && ptrType.getElementType() != getType()))3094 return emitOpError() << "expects atomic reduction region arguments to "3095 "be accumulators containing the reduction type";3096 }3097 3098 if (getCleanupRegion().empty())3099 return success();3100 Block &cleanupEntryBlock = getCleanupRegion().front();3101 if (cleanupEntryBlock.getNumArguments() != 1 ||3102 cleanupEntryBlock.getArgument(0).getType() != getType())3103 return emitOpError() << "expects cleanup region with one argument "3104 "of the reduction type";3105 3106 return success();3107}3108 3109//===----------------------------------------------------------------------===//3110// TaskOp3111//===----------------------------------------------------------------------===//3112 3113void TaskOp::build(OpBuilder &builder, OperationState &state,3114 const TaskOperands &clauses) {3115 MLIRContext *ctx = builder.getContext();3116 TaskOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,3117 makeArrayAttr(ctx, clauses.dependKinds), clauses.dependVars,3118 clauses.final, clauses.ifExpr, clauses.inReductionVars,3119 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),3120 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,3121 clauses.priority, /*private_vars=*/clauses.privateVars,3122 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),3123 clauses.privateNeedsBarrier, clauses.untied,3124 clauses.eventHandle);3125}3126 3127LogicalResult TaskOp::verify() {3128 LogicalResult verifyDependVars =3129 verifyDependVarList(*this, getDependKinds(), getDependVars());3130 return failed(verifyDependVars)3131 ? verifyDependVars3132 : verifyReductionVarList(*this, getInReductionSyms(),3133 getInReductionVars(),3134 getInReductionByref());3135}3136 3137//===----------------------------------------------------------------------===//3138// TaskgroupOp3139//===----------------------------------------------------------------------===//3140 3141void TaskgroupOp::build(OpBuilder &builder, OperationState &state,3142 const TaskgroupOperands &clauses) {3143 MLIRContext *ctx = builder.getContext();3144 TaskgroupOp::build(builder, state, clauses.allocateVars,3145 clauses.allocatorVars, clauses.taskReductionVars,3146 makeDenseBoolArrayAttr(ctx, clauses.taskReductionByref),3147 makeArrayAttr(ctx, clauses.taskReductionSyms));3148}3149 3150LogicalResult TaskgroupOp::verify() {3151 return verifyReductionVarList(*this, getTaskReductionSyms(),3152 getTaskReductionVars(),3153 getTaskReductionByref());3154}3155 3156//===----------------------------------------------------------------------===//3157// TaskloopOp3158//===----------------------------------------------------------------------===//3159 3160void TaskloopOp::build(OpBuilder &builder, OperationState &state,3161 const TaskloopOperands &clauses) {3162 MLIRContext *ctx = builder.getContext();3163 TaskloopOp::build(3164 builder, state, clauses.allocateVars, clauses.allocatorVars,3165 clauses.final, clauses.grainsizeMod, clauses.grainsize, clauses.ifExpr,3166 clauses.inReductionVars,3167 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),3168 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,3169 clauses.nogroup, clauses.numTasksMod, clauses.numTasks, clauses.priority,3170 /*private_vars=*/clauses.privateVars,3171 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),3172 clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars,3173 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),3174 makeArrayAttr(ctx, clauses.reductionSyms), clauses.untied);3175}3176 3177LogicalResult TaskloopOp::verify() {3178 if (getAllocateVars().size() != getAllocatorVars().size())3179 return emitError(3180 "expected equal sizes for allocate and allocator variables");3181 if (failed(verifyReductionVarList(*this, getReductionSyms(),3182 getReductionVars(), getReductionByref())) ||3183 failed(verifyReductionVarList(*this, getInReductionSyms(),3184 getInReductionVars(),3185 getInReductionByref())))3186 return failure();3187 3188 if (!getReductionVars().empty() && getNogroup())3189 return emitError("if a reduction clause is present on the taskloop "3190 "directive, the nogroup clause must not be specified");3191 for (auto var : getReductionVars()) {3192 if (llvm::is_contained(getInReductionVars(), var))3193 return emitError("the same list item cannot appear in both a reduction "3194 "and an in_reduction clause");3195 }3196 3197 if (getGrainsize() && getNumTasks()) {3198 return emitError(3199 "the grainsize clause and num_tasks clause are mutually exclusive and "3200 "may not appear on the same taskloop directive");3201 }3202 3203 return success();3204}3205 3206LogicalResult TaskloopOp::verifyRegions() {3207 if (LoopWrapperInterface nested = getNestedWrapper()) {3208 if (!isComposite())3209 return emitError()3210 << "'omp.composite' attribute missing from composite wrapper";3211 3212 // Check for the allowed leaf constructs that may appear in a composite3213 // construct directly after TASKLOOP.3214 if (!isa<SimdOp>(nested))3215 return emitError() << "only supported nested wrapper is 'omp.simd'";3216 } else if (isComposite()) {3217 return emitError()3218 << "'omp.composite' attribute present in non-composite wrapper";3219 }3220 3221 return success();3222}3223 3224//===----------------------------------------------------------------------===//3225// LoopNestOp3226//===----------------------------------------------------------------------===//3227 3228ParseResult LoopNestOp::parse(OpAsmParser &parser, OperationState &result) {3229 // Parse an opening `(` followed by induction variables followed by `)`3230 SmallVector<OpAsmParser::Argument> ivs;3231 SmallVector<OpAsmParser::UnresolvedOperand> lbs, ubs;3232 Type loopVarType;3233 if (parser.parseArgumentList(ivs, OpAsmParser::Delimiter::Paren) ||3234 parser.parseColonType(loopVarType) ||3235 // Parse loop bounds.3236 parser.parseEqual() ||3237 parser.parseOperandList(lbs, ivs.size(), OpAsmParser::Delimiter::Paren) ||3238 parser.parseKeyword("to") ||3239 parser.parseOperandList(ubs, ivs.size(), OpAsmParser::Delimiter::Paren))3240 return failure();3241 3242 for (auto &iv : ivs)3243 iv.type = loopVarType;3244 3245 auto *ctx = parser.getBuilder().getContext();3246 // Parse "inclusive" flag.3247 if (succeeded(parser.parseOptionalKeyword("inclusive")))3248 result.addAttribute("loop_inclusive", UnitAttr::get(ctx));3249 3250 // Parse step values.3251 SmallVector<OpAsmParser::UnresolvedOperand> steps;3252 if (parser.parseKeyword("step") ||3253 parser.parseOperandList(steps, ivs.size(), OpAsmParser::Delimiter::Paren))3254 return failure();3255 3256 // Parse collapse3257 int64_t value = 0;3258 if (!parser.parseOptionalKeyword("collapse") &&3259 (parser.parseLParen() || parser.parseInteger(value) ||3260 parser.parseRParen()))3261 return failure();3262 if (value > 1)3263 result.addAttribute(3264 "collapse_num_loops",3265 IntegerAttr::get(parser.getBuilder().getI64Type(), value));3266 3267 // Parse tiles3268 SmallVector<int64_t> tiles;3269 auto parseTiles = [&]() -> ParseResult {3270 int64_t tile;3271 if (parser.parseInteger(tile))3272 return failure();3273 tiles.push_back(tile);3274 return success();3275 };3276 3277 if (!parser.parseOptionalKeyword("tiles") &&3278 (parser.parseLParen() || parser.parseCommaSeparatedList(parseTiles) ||3279 parser.parseRParen()))3280 return failure();3281 3282 if (tiles.size() > 0)3283 result.addAttribute("tile_sizes", DenseI64ArrayAttr::get(ctx, tiles));3284 3285 // Parse the body.3286 Region *region = result.addRegion();3287 if (parser.parseRegion(*region, ivs))3288 return failure();3289 3290 // Resolve operands.3291 if (parser.resolveOperands(lbs, loopVarType, result.operands) ||3292 parser.resolveOperands(ubs, loopVarType, result.operands) ||3293 parser.resolveOperands(steps, loopVarType, result.operands))3294 return failure();3295 3296 // Parse the optional attribute list.3297 return parser.parseOptionalAttrDict(result.attributes);3298}3299 3300void LoopNestOp::print(OpAsmPrinter &p) {3301 Region ®ion = getRegion();3302 auto args = region.getArguments();3303 p << " (" << args << ") : " << args[0].getType() << " = ("3304 << getLoopLowerBounds() << ") to (" << getLoopUpperBounds() << ") ";3305 if (getLoopInclusive())3306 p << "inclusive ";3307 p << "step (" << getLoopSteps() << ") ";3308 if (int64_t numCollapse = getCollapseNumLoops())3309 if (numCollapse > 1)3310 p << "collapse(" << numCollapse << ") ";3311 3312 if (const auto tiles = getTileSizes())3313 p << "tiles(" << tiles.value() << ") ";3314 3315 p.printRegion(region, /*printEntryBlockArgs=*/false);3316}3317 3318void LoopNestOp::build(OpBuilder &builder, OperationState &state,3319 const LoopNestOperands &clauses) {3320 MLIRContext *ctx = builder.getContext();3321 LoopNestOp::build(builder, state, clauses.collapseNumLoops,3322 clauses.loopLowerBounds, clauses.loopUpperBounds,3323 clauses.loopSteps, clauses.loopInclusive,3324 makeDenseI64ArrayAttr(ctx, clauses.tileSizes));3325}3326 3327LogicalResult LoopNestOp::verify() {3328 if (getLoopLowerBounds().empty())3329 return emitOpError() << "must represent at least one loop";3330 3331 if (getLoopLowerBounds().size() != getIVs().size())3332 return emitOpError() << "number of range arguments and IVs do not match";3333 3334 for (auto [lb, iv] : llvm::zip_equal(getLoopLowerBounds(), getIVs())) {3335 if (lb.getType() != iv.getType())3336 return emitOpError()3337 << "range argument type does not match corresponding IV type";3338 }3339 3340 uint64_t numIVs = getIVs().size();3341 3342 if (const auto &numCollapse = getCollapseNumLoops())3343 if (numCollapse > numIVs)3344 return emitOpError()3345 << "collapse value is larger than the number of loops";3346 3347 if (const auto &tiles = getTileSizes())3348 if (tiles.value().size() > numIVs)3349 return emitOpError() << "too few canonical loops for tile dimensions";3350 3351 if (!llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp()))3352 return emitOpError() << "expects parent op to be a loop wrapper";3353 3354 return success();3355}3356 3357void LoopNestOp::gatherWrappers(3358 SmallVectorImpl<LoopWrapperInterface> &wrappers) {3359 Operation *parent = (*this)->getParentOp();3360 while (auto wrapper =3361 llvm::dyn_cast_if_present<LoopWrapperInterface>(parent)) {3362 wrappers.push_back(wrapper);3363 parent = parent->getParentOp();3364 }3365}3366 3367//===----------------------------------------------------------------------===//3368// OpenMP canonical loop handling3369//===----------------------------------------------------------------------===//3370 3371std::tuple<NewCliOp, OpOperand *, OpOperand *>3372mlir::omp ::decodeCli(Value cli) {3373 3374 // Defining a CLI for a generated loop is optional; if there is none then3375 // there is no followup-tranformation3376 if (!cli)3377 return {{}, nullptr, nullptr};3378 3379 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&3380 "Unexpected type of cli");3381 3382 NewCliOp create = cast<NewCliOp>(cli.getDefiningOp());3383 OpOperand *gen = nullptr;3384 OpOperand *cons = nullptr;3385 for (OpOperand &use : cli.getUses()) {3386 auto op = cast<LoopTransformationInterface>(use.getOwner());3387 3388 unsigned opnum = use.getOperandNumber();3389 if (op.isGeneratee(opnum)) {3390 assert(!gen && "Each CLI may have at most one def");3391 gen = &use;3392 } else if (op.isApplyee(opnum)) {3393 assert(!cons && "Each CLI may have at most one consumer");3394 cons = &use;3395 } else {3396 llvm_unreachable("Unexpected operand for a CLI");3397 }3398 }3399 3400 return {create, gen, cons};3401}3402 3403void NewCliOp::build(::mlir::OpBuilder &odsBuilder,3404 ::mlir::OperationState &odsState) {3405 odsState.addTypes(CanonicalLoopInfoType::get(odsBuilder.getContext()));3406}3407 3408void NewCliOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {3409 Value result = getResult();3410 auto [newCli, gen, cons] = decodeCli(result);3411 3412 // Structured binding `gen` cannot be captured in lambdas before C++203413 OpOperand *generator = gen;3414 3415 // Derive the CLI variable name from its generator:3416 // * "canonloop" for omp.canonical_loop3417 // * custom name for loop transformation generatees3418 // * "cli" as fallback if no generator3419 // * "_r<idx>" suffix for nested loops, where <idx> is the sequential order3420 // at that level3421 // * "_s<idx>" suffix for operations with multiple regions, where <idx> is3422 // the index of that region3423 std::string cliName{"cli"};3424 if (gen) {3425 cliName =3426 TypeSwitch<Operation *, std::string>(gen->getOwner())3427 .Case([&](CanonicalLoopOp op) {3428 return generateLoopNestingName("canonloop", op);3429 })3430 .Case([&](UnrollHeuristicOp op) -> std::string {3431 llvm_unreachable("heuristic unrolling does not generate a loop");3432 })3433 .Case([&](TileOp op) -> std::string {3434 auto [generateesFirst, generateesCount] =3435 op.getGenerateesODSOperandIndexAndLength();3436 unsigned firstGrid = generateesFirst;3437 unsigned firstIntratile = generateesFirst + generateesCount / 2;3438 unsigned end = generateesFirst + generateesCount;3439 unsigned opnum = generator->getOperandNumber();3440 // In the OpenMP apply and looprange clauses, indices are 1-based3441 if (firstGrid <= opnum && opnum < firstIntratile) {3442 unsigned gridnum = opnum - firstGrid + 1;3443 return ("grid" + Twine(gridnum)).str();3444 }3445 if (firstIntratile <= opnum && opnum < end) {3446 unsigned intratilenum = opnum - firstIntratile + 1;3447 return ("intratile" + Twine(intratilenum)).str();3448 }3449 llvm_unreachable("Unexpected generatee argument");3450 })3451 .DefaultUnreachable("TODO: Custom name for this operation");3452 }3453 3454 setNameFn(result, cliName);3455}3456 3457LogicalResult NewCliOp::verify() {3458 Value cli = getResult();3459 3460 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&3461 "Unexpected type of cli");3462 3463 // Check that the CLI is used in at most generator and one consumer3464 OpOperand *gen = nullptr;3465 OpOperand *cons = nullptr;3466 for (mlir::OpOperand &use : cli.getUses()) {3467 auto op = cast<mlir::omp::LoopTransformationInterface>(use.getOwner());3468 3469 unsigned opnum = use.getOperandNumber();3470 if (op.isGeneratee(opnum)) {3471 if (gen) {3472 InFlightDiagnostic error =3473 emitOpError("CLI must have at most one generator");3474 error.attachNote(gen->getOwner()->getLoc())3475 .append("first generator here:");3476 error.attachNote(use.getOwner()->getLoc())3477 .append("second generator here:");3478 return error;3479 }3480 3481 gen = &use;3482 } else if (op.isApplyee(opnum)) {3483 if (cons) {3484 InFlightDiagnostic error =3485 emitOpError("CLI must have at most one consumer");3486 error.attachNote(cons->getOwner()->getLoc())3487 .append("first consumer here:")3488 .appendOp(*cons->getOwner(),3489 OpPrintingFlags().printGenericOpForm());3490 error.attachNote(use.getOwner()->getLoc())3491 .append("second consumer here:")3492 .appendOp(*use.getOwner(), OpPrintingFlags().printGenericOpForm());3493 return error;3494 }3495 3496 cons = &use;3497 } else {3498 llvm_unreachable("Unexpected operand for a CLI");3499 }3500 }3501 3502 // If the CLI is source of a transformation, it must have a generator3503 if (cons && !gen) {3504 InFlightDiagnostic error = emitOpError("CLI has no generator");3505 error.attachNote(cons->getOwner()->getLoc())3506 .append("see consumer here: ")3507 .appendOp(*cons->getOwner(), OpPrintingFlags().printGenericOpForm());3508 return error;3509 }3510 3511 return success();3512}3513 3514void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,3515 Value tripCount) {3516 odsState.addOperands(tripCount);3517 odsState.addOperands(Value());3518 (void)odsState.addRegion();3519}3520 3521void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,3522 Value tripCount, ::mlir::Value cli) {3523 odsState.addOperands(tripCount);3524 odsState.addOperands(cli);3525 (void)odsState.addRegion();3526}3527 3528void CanonicalLoopOp::getAsmBlockNames(OpAsmSetBlockNameFn setNameFn) {3529 setNameFn(&getRegion().front(), "body_entry");3530}3531 3532void CanonicalLoopOp::getAsmBlockArgumentNames(Region ®ion,3533 OpAsmSetValueNameFn setNameFn) {3534 std::string ivName = generateLoopNestingName("iv", *this);3535 setNameFn(region.getArgument(0), ivName);3536}3537 3538void CanonicalLoopOp::print(OpAsmPrinter &p) {3539 if (getCli())3540 p << '(' << getCli() << ')';3541 p << ' ' << getInductionVar() << " : " << getInductionVar().getType()3542 << " in range(" << getTripCount() << ") ";3543 3544 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,3545 /*printBlockTerminators=*/true);3546 3547 p.printOptionalAttrDict((*this)->getAttrs());3548}3549 3550mlir::ParseResult CanonicalLoopOp::parse(::mlir::OpAsmParser &parser,3551 ::mlir::OperationState &result) {3552 CanonicalLoopInfoType cliType =3553 CanonicalLoopInfoType::get(parser.getContext());3554 3555 // Parse (optional) omp.cli identifier3556 OpAsmParser::UnresolvedOperand cli;3557 SmallVector<mlir::Value, 1> cliOperand;3558 if (!parser.parseOptionalLParen()) {3559 if (parser.parseOperand(cli) ||3560 parser.resolveOperand(cli, cliType, cliOperand) || parser.parseRParen())3561 return failure();3562 }3563 3564 // We derive the type of tripCount from inductionVariable. MLIR requires the3565 // type of tripCount to be known when calling resolveOperand so we have parse3566 // the type before processing the inductionVariable.3567 OpAsmParser::Argument inductionVariable;3568 OpAsmParser::UnresolvedOperand tripcount;3569 if (parser.parseArgument(inductionVariable, /*allowType*/ true) ||3570 parser.parseKeyword("in") || parser.parseKeyword("range") ||3571 parser.parseLParen() || parser.parseOperand(tripcount) ||3572 parser.parseRParen() ||3573 parser.resolveOperand(tripcount, inductionVariable.type, result.operands))3574 return failure();3575 3576 // Parse the loop body.3577 Region *region = result.addRegion();3578 if (parser.parseRegion(*region, {inductionVariable}))3579 return failure();3580 3581 // We parsed the cli operand forst, but because it is optional, it must be3582 // last in the operand list.3583 result.operands.append(cliOperand);3584 3585 // Parse the optional attribute list.3586 if (parser.parseOptionalAttrDict(result.attributes))3587 return failure();3588 3589 return mlir::success();3590}3591 3592LogicalResult CanonicalLoopOp::verify() {3593 // The region's entry must accept the induction variable3594 // It can also be empty if just created3595 if (!getRegion().empty()) {3596 Region ®ion = getRegion();3597 if (region.getNumArguments() != 1)3598 return emitOpError(3599 "Canonical loop region must have exactly one argument");3600 3601 if (getInductionVar().getType() != getTripCount().getType())3602 return emitOpError(3603 "Region argument must be the same type as the trip count");3604 }3605 3606 return success();3607}3608 3609Value CanonicalLoopOp::getInductionVar() { return getRegion().getArgument(0); }3610 3611std::pair<unsigned, unsigned>3612CanonicalLoopOp::getApplyeesODSOperandIndexAndLength() {3613 // No applyees3614 return {0, 0};3615}3616 3617std::pair<unsigned, unsigned>3618CanonicalLoopOp::getGenerateesODSOperandIndexAndLength() {3619 return getODSOperandIndexAndLength(odsIndex_cli);3620}3621 3622//===----------------------------------------------------------------------===//3623// UnrollHeuristicOp3624//===----------------------------------------------------------------------===//3625 3626void UnrollHeuristicOp::build(::mlir::OpBuilder &odsBuilder,3627 ::mlir::OperationState &odsState,3628 ::mlir::Value cli) {3629 odsState.addOperands(cli);3630}3631 3632void UnrollHeuristicOp::print(OpAsmPrinter &p) {3633 p << '(' << getApplyee() << ')';3634 3635 p.printOptionalAttrDict((*this)->getAttrs());3636}3637 3638mlir::ParseResult UnrollHeuristicOp::parse(::mlir::OpAsmParser &parser,3639 ::mlir::OperationState &result) {3640 auto cliType = CanonicalLoopInfoType::get(parser.getContext());3641 3642 if (parser.parseLParen())3643 return failure();3644 3645 OpAsmParser::UnresolvedOperand applyee;3646 if (parser.parseOperand(applyee) ||3647 parser.resolveOperand(applyee, cliType, result.operands))3648 return failure();3649 3650 if (parser.parseRParen())3651 return failure();3652 3653 // Optional output loop (full unrolling has none)3654 if (!parser.parseOptionalArrow()) {3655 if (parser.parseLParen() || parser.parseRParen())3656 return failure();3657 }3658 3659 // Parse the optional attribute list.3660 if (parser.parseOptionalAttrDict(result.attributes))3661 return failure();3662 3663 return mlir::success();3664}3665 3666std::pair<unsigned, unsigned>3667UnrollHeuristicOp ::getApplyeesODSOperandIndexAndLength() {3668 return getODSOperandIndexAndLength(odsIndex_applyee);3669}3670 3671std::pair<unsigned, unsigned>3672UnrollHeuristicOp::getGenerateesODSOperandIndexAndLength() {3673 return {0, 0};3674}3675 3676//===----------------------------------------------------------------------===//3677// TileOp3678//===----------------------------------------------------------------------===//3679 3680static void printLoopTransformClis(OpAsmPrinter &p, TileOp op,3681 OperandRange generatees,3682 OperandRange applyees) {3683 if (!generatees.empty())3684 p << '(' << llvm::interleaved(generatees) << ')';3685 3686 if (!applyees.empty())3687 p << " <- (" << llvm::interleaved(applyees) << ')';3688}3689 3690static ParseResult parseLoopTransformClis(3691 OpAsmParser &parser,3692 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &generateesOperands,3693 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &applyeesOperands) {3694 if (parser.parseOptionalLess()) {3695 // Syntax 1: generatees present3696 3697 if (parser.parseOperandList(generateesOperands,3698 mlir::OpAsmParser::Delimiter::Paren))3699 return failure();3700 3701 if (parser.parseLess())3702 return failure();3703 } else {3704 // Syntax 2: generatees omitted3705 }3706 3707 // Parse `<-` (`<` has already been parsed)3708 if (parser.parseMinus())3709 return failure();3710 3711 if (parser.parseOperandList(applyeesOperands,3712 mlir::OpAsmParser::Delimiter::Paren))3713 return failure();3714 3715 return success();3716}3717 3718LogicalResult TileOp::verify() {3719 if (getApplyees().empty())3720 return emitOpError() << "must apply to at least one loop";3721 3722 if (getSizes().size() != getApplyees().size())3723 return emitOpError() << "there must be one tile size for each applyee";3724 3725 if (!getGeneratees().empty() &&3726 2 * getSizes().size() != getGeneratees().size())3727 return emitOpError()3728 << "expecting two times the number of generatees than applyees";3729 3730 DenseSet<Value> parentIVs;3731 3732 Value parent = getApplyees().front();3733 for (auto &&applyee : llvm::drop_begin(getApplyees())) {3734 auto [parentCreate, parentGen, parentCons] = decodeCli(parent);3735 auto [create, gen, cons] = decodeCli(applyee);3736 3737 if (!parentGen)3738 return emitOpError() << "applyee CLI has no generator";3739 3740 auto parentLoop = dyn_cast_or_null<CanonicalLoopOp>(parentGen->getOwner());3741 if (!parentGen)3742 return emitOpError()3743 << "currently only supports omp.canonical_loop as applyee";3744 3745 parentIVs.insert(parentLoop.getInductionVar());3746 3747 if (!gen)3748 return emitOpError() << "applyee CLI has no generator";3749 auto loop = dyn_cast_or_null<CanonicalLoopOp>(gen->getOwner());3750 if (!loop)3751 return emitOpError()3752 << "currently only supports omp.canonical_loop as applyee";3753 3754 // Canonical loop must be perfectly nested, i.e. the body of the parent must3755 // only contain the omp.canonical_loop of the nested loops, and3756 // omp.terminator3757 bool isPerfectlyNested = [&]() {3758 auto &parentBody = parentLoop.getRegion();3759 if (!parentBody.hasOneBlock())3760 return false;3761 auto &parentBlock = parentBody.getBlocks().front();3762 3763 auto nestedLoopIt = parentBlock.begin();3764 if (nestedLoopIt == parentBlock.end() ||3765 (&*nestedLoopIt != loop.getOperation()))3766 return false;3767 3768 auto termIt = std::next(nestedLoopIt);3769 if (termIt == parentBlock.end() || !isa<TerminatorOp>(termIt))3770 return false;3771 3772 if (std::next(termIt) != parentBlock.end())3773 return false;3774 3775 return true;3776 }();3777 if (!isPerfectlyNested)3778 return emitOpError() << "tiled loop nest must be perfectly nested";3779 3780 if (parentIVs.contains(loop.getTripCount()))3781 return emitOpError() << "tiled loop nest must be rectangular";3782 3783 parent = applyee;3784 }3785 3786 // TODO: The tile sizes must be computed before the loop, but checking this3787 // requires dominance analysis. For instance:3788 //3789 // %canonloop = omp.new_cli3790 // omp.canonical_loop(%canonloop) %iv : i32 in range(%tc) {3791 // // write to %x3792 // omp.terminator3793 // }3794 // %ts = llvm.load %x3795 // omp.tile <- (%canonloop) sizes(%ts : i32)3796 3797 return success();3798}3799 3800std::pair<unsigned, unsigned> TileOp ::getApplyeesODSOperandIndexAndLength() {3801 return getODSOperandIndexAndLength(odsIndex_applyees);3802}3803 3804std::pair<unsigned, unsigned> TileOp::getGenerateesODSOperandIndexAndLength() {3805 return getODSOperandIndexAndLength(odsIndex_generatees);3806}3807 3808//===----------------------------------------------------------------------===//3809// Critical construct (2.17.1)3810//===----------------------------------------------------------------------===//3811 3812void CriticalDeclareOp::build(OpBuilder &builder, OperationState &state,3813 const CriticalDeclareOperands &clauses) {3814 CriticalDeclareOp::build(builder, state, clauses.symName, clauses.hint);3815}3816 3817LogicalResult CriticalDeclareOp::verify() {3818 return verifySynchronizationHint(*this, getHint());3819}3820 3821LogicalResult CriticalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {3822 if (getNameAttr()) {3823 SymbolRefAttr symbolRef = getNameAttr();3824 auto decl = symbolTable.lookupNearestSymbolFrom<CriticalDeclareOp>(3825 *this, symbolRef);3826 if (!decl) {3827 return emitOpError() << "expected symbol reference " << symbolRef3828 << " to point to a critical declaration";3829 }3830 }3831 3832 return success();3833}3834 3835//===----------------------------------------------------------------------===//3836// Ordered construct3837//===----------------------------------------------------------------------===//3838 3839static LogicalResult verifyOrderedParent(Operation &op) {3840 bool hasRegion = op.getNumRegions() > 0;3841 auto loopOp = op.getParentOfType<LoopNestOp>();3842 if (!loopOp) {3843 if (hasRegion)3844 return success();3845 3846 // TODO: Consider if this needs to be the case only for the standalone3847 // variant of the ordered construct.3848 return op.emitOpError() << "must be nested inside of a loop";3849 }3850 3851 Operation *wrapper = loopOp->getParentOp();3852 if (auto wsloopOp = dyn_cast<WsloopOp>(wrapper)) {3853 IntegerAttr orderedAttr = wsloopOp.getOrderedAttr();3854 if (!orderedAttr)3855 return op.emitOpError() << "the enclosing worksharing-loop region must "3856 "have an ordered clause";3857 3858 if (hasRegion && orderedAttr.getInt() != 0)3859 return op.emitOpError() << "the enclosing loop's ordered clause must not "3860 "have a parameter present";3861 3862 if (!hasRegion && orderedAttr.getInt() == 0)3863 return op.emitOpError() << "the enclosing loop's ordered clause must "3864 "have a parameter present";3865 } else if (!isa<SimdOp>(wrapper)) {3866 return op.emitOpError() << "must be nested inside of a worksharing, simd "3867 "or worksharing simd loop";3868 }3869 return success();3870}3871 3872void OrderedOp::build(OpBuilder &builder, OperationState &state,3873 const OrderedOperands &clauses) {3874 OrderedOp::build(builder, state, clauses.doacrossDependType,3875 clauses.doacrossNumLoops, clauses.doacrossDependVars);3876}3877 3878LogicalResult OrderedOp::verify() {3879 if (failed(verifyOrderedParent(**this)))3880 return failure();3881 3882 auto wrapper = (*this)->getParentOfType<WsloopOp>();3883 if (!wrapper || *wrapper.getOrdered() != *getDoacrossNumLoops())3884 return emitOpError() << "number of variables in depend clause does not "3885 << "match number of iteration variables in the "3886 << "doacross loop";3887 3888 return success();3889}3890 3891void OrderedRegionOp::build(OpBuilder &builder, OperationState &state,3892 const OrderedRegionOperands &clauses) {3893 OrderedRegionOp::build(builder, state, clauses.parLevelSimd);3894}3895 3896LogicalResult OrderedRegionOp::verify() { return verifyOrderedParent(**this); }3897 3898//===----------------------------------------------------------------------===//3899// TaskwaitOp3900//===----------------------------------------------------------------------===//3901 3902void TaskwaitOp::build(OpBuilder &builder, OperationState &state,3903 const TaskwaitOperands &clauses) {3904 // TODO Store clauses in op: dependKinds, dependVars, nowait.3905 TaskwaitOp::build(builder, state, /*depend_kinds=*/nullptr,3906 /*depend_vars=*/{}, /*nowait=*/nullptr);3907}3908 3909//===----------------------------------------------------------------------===//3910// Verifier for AtomicReadOp3911//===----------------------------------------------------------------------===//3912 3913LogicalResult AtomicReadOp::verify() {3914 if (verifyCommon().failed())3915 return mlir::failure();3916 3917 if (auto mo = getMemoryOrder()) {3918 if (*mo == ClauseMemoryOrderKind::Acq_rel ||3919 *mo == ClauseMemoryOrderKind::Release) {3920 return emitError(3921 "memory-order must not be acq_rel or release for atomic reads");3922 }3923 }3924 return verifySynchronizationHint(*this, getHint());3925}3926 3927//===----------------------------------------------------------------------===//3928// Verifier for AtomicWriteOp3929//===----------------------------------------------------------------------===//3930 3931LogicalResult AtomicWriteOp::verify() {3932 if (verifyCommon().failed())3933 return mlir::failure();3934 3935 if (auto mo = getMemoryOrder()) {3936 if (*mo == ClauseMemoryOrderKind::Acq_rel ||3937 *mo == ClauseMemoryOrderKind::Acquire) {3938 return emitError(3939 "memory-order must not be acq_rel or acquire for atomic writes");3940 }3941 }3942 return verifySynchronizationHint(*this, getHint());3943}3944 3945//===----------------------------------------------------------------------===//3946// Verifier for AtomicUpdateOp3947//===----------------------------------------------------------------------===//3948 3949LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,3950 PatternRewriter &rewriter) {3951 if (op.isNoOp()) {3952 rewriter.eraseOp(op);3953 return success();3954 }3955 if (Value writeVal = op.getWriteOpVal()) {3956 rewriter.replaceOpWithNewOp<AtomicWriteOp>(3957 op, op.getX(), writeVal, op.getHintAttr(), op.getMemoryOrderAttr());3958 return success();3959 }3960 return failure();3961}3962 3963LogicalResult AtomicUpdateOp::verify() {3964 if (verifyCommon().failed())3965 return mlir::failure();3966 3967 if (auto mo = getMemoryOrder()) {3968 if (*mo == ClauseMemoryOrderKind::Acq_rel ||3969 *mo == ClauseMemoryOrderKind::Acquire) {3970 return emitError(3971 "memory-order must not be acq_rel or acquire for atomic updates");3972 }3973 }3974 3975 return verifySynchronizationHint(*this, getHint());3976}3977 3978LogicalResult AtomicUpdateOp::verifyRegions() { return verifyRegionsCommon(); }3979 3980//===----------------------------------------------------------------------===//3981// Verifier for AtomicCaptureOp3982//===----------------------------------------------------------------------===//3983 3984AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {3985 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))3986 return op;3987 return dyn_cast<AtomicReadOp>(getSecondOp());3988}3989 3990AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {3991 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))3992 return op;3993 return dyn_cast<AtomicWriteOp>(getSecondOp());3994}3995 3996AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {3997 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))3998 return op;3999 return dyn_cast<AtomicUpdateOp>(getSecondOp());4000}4001 4002LogicalResult AtomicCaptureOp::verify() {4003 return verifySynchronizationHint(*this, getHint());4004}4005 4006LogicalResult AtomicCaptureOp::verifyRegions() {4007 if (verifyRegionsCommon().failed())4008 return mlir::failure();4009 4010 if (getFirstOp()->getAttr("hint") || getSecondOp()->getAttr("hint"))4011 return emitOpError(4012 "operations inside capture region must not have hint clause");4013 4014 if (getFirstOp()->getAttr("memory_order") ||4015 getSecondOp()->getAttr("memory_order"))4016 return emitOpError(4017 "operations inside capture region must not have memory_order clause");4018 return success();4019}4020 4021//===----------------------------------------------------------------------===//4022// CancelOp4023//===----------------------------------------------------------------------===//4024 4025void CancelOp::build(OpBuilder &builder, OperationState &state,4026 const CancelOperands &clauses) {4027 CancelOp::build(builder, state, clauses.cancelDirective, clauses.ifExpr);4028}4029 4030static Operation *getParentInSameDialect(Operation *thisOp) {4031 Operation *parent = thisOp->getParentOp();4032 while (parent) {4033 if (parent->getDialect() == thisOp->getDialect())4034 return parent;4035 parent = parent->getParentOp();4036 }4037 return nullptr;4038}4039 4040LogicalResult CancelOp::verify() {4041 ClauseCancellationConstructType cct = getCancelDirective();4042 // The next OpenMP operation in the chain of parents4043 Operation *structuralParent = getParentInSameDialect((*this).getOperation());4044 if (!structuralParent)4045 return emitOpError() << "Orphaned cancel construct";4046 4047 if ((cct == ClauseCancellationConstructType::Parallel) &&4048 !mlir::isa<ParallelOp>(structuralParent)) {4049 return emitOpError() << "cancel parallel must appear "4050 << "inside a parallel region";4051 }4052 if (cct == ClauseCancellationConstructType::Loop) {4053 // structural parent will be omp.loop_nest, directly nested inside4054 // omp.wsloop4055 auto wsloopOp = mlir::dyn_cast<WsloopOp>(structuralParent->getParentOp());4056 4057 if (!wsloopOp) {4058 return emitOpError()4059 << "cancel loop must appear inside a worksharing-loop region";4060 }4061 if (wsloopOp.getNowaitAttr()) {4062 return emitError() << "A worksharing construct that is canceled "4063 << "must not have a nowait clause";4064 }4065 if (wsloopOp.getOrderedAttr()) {4066 return emitError() << "A worksharing construct that is canceled "4067 << "must not have an ordered clause";4068 }4069 4070 } else if (cct == ClauseCancellationConstructType::Sections) {4071 // structural parent will be an omp.section, directly nested inside4072 // omp.sections4073 auto sectionsOp =4074 mlir::dyn_cast<SectionsOp>(structuralParent->getParentOp());4075 if (!sectionsOp) {4076 return emitOpError() << "cancel sections must appear "4077 << "inside a sections region";4078 }4079 if (sectionsOp.getNowait()) {4080 return emitError() << "A sections construct that is canceled "4081 << "must not have a nowait clause";4082 }4083 }4084 if ((cct == ClauseCancellationConstructType::Taskgroup) &&4085 (!mlir::isa<omp::TaskOp>(structuralParent) &&4086 !mlir::isa<omp::TaskloopOp>(structuralParent->getParentOp()))) {4087 return emitOpError() << "cancel taskgroup must appear "4088 << "inside a task region";4089 }4090 return success();4091}4092 4093//===----------------------------------------------------------------------===//4094// CancellationPointOp4095//===----------------------------------------------------------------------===//4096 4097void CancellationPointOp::build(OpBuilder &builder, OperationState &state,4098 const CancellationPointOperands &clauses) {4099 CancellationPointOp::build(builder, state, clauses.cancelDirective);4100}4101 4102LogicalResult CancellationPointOp::verify() {4103 ClauseCancellationConstructType cct = getCancelDirective();4104 // The next OpenMP operation in the chain of parents4105 Operation *structuralParent = getParentInSameDialect((*this).getOperation());4106 if (!structuralParent)4107 return emitOpError() << "Orphaned cancellation point";4108 4109 if ((cct == ClauseCancellationConstructType::Parallel) &&4110 !mlir::isa<ParallelOp>(structuralParent)) {4111 return emitOpError() << "cancellation point parallel must appear "4112 << "inside a parallel region";4113 }4114 // Strucutal parent here will be an omp.loop_nest. Get the parent of that to4115 // find the wsloop4116 if ((cct == ClauseCancellationConstructType::Loop) &&4117 !mlir::isa<WsloopOp>(structuralParent->getParentOp())) {4118 return emitOpError() << "cancellation point loop must appear "4119 << "inside a worksharing-loop region";4120 }4121 if ((cct == ClauseCancellationConstructType::Sections) &&4122 !mlir::isa<omp::SectionOp>(structuralParent)) {4123 return emitOpError() << "cancellation point sections must appear "4124 << "inside a sections region";4125 }4126 if ((cct == ClauseCancellationConstructType::Taskgroup) &&4127 !mlir::isa<omp::TaskOp>(structuralParent)) {4128 return emitOpError() << "cancellation point taskgroup must appear "4129 << "inside a task region";4130 }4131 return success();4132}4133 4134//===----------------------------------------------------------------------===//4135// MapBoundsOp4136//===----------------------------------------------------------------------===//4137 4138LogicalResult MapBoundsOp::verify() {4139 auto extent = getExtent();4140 auto upperbound = getUpperBound();4141 if (!extent && !upperbound)4142 return emitError("expected extent or upperbound.");4143 return success();4144}4145 4146void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState,4147 TypeRange /*result_types*/, StringAttr symName,4148 TypeAttr type) {4149 PrivateClauseOp::build(4150 odsBuilder, odsState, symName, type,4151 DataSharingClauseTypeAttr::get(odsBuilder.getContext(),4152 DataSharingClauseType::Private));4153}4154 4155LogicalResult PrivateClauseOp::verifyRegions() {4156 Type argType = getArgType();4157 auto verifyTerminator = [&](Operation *terminator,4158 bool yieldsValue) -> LogicalResult {4159 if (!terminator->getBlock()->getSuccessors().empty())4160 return success();4161 4162 if (!llvm::isa<YieldOp>(terminator))4163 return mlir::emitError(terminator->getLoc())4164 << "expected exit block terminator to be an `omp.yield` op.";4165 4166 YieldOp yieldOp = llvm::cast<YieldOp>(terminator);4167 TypeRange yieldedTypes = yieldOp.getResults().getTypes();4168 4169 if (!yieldsValue) {4170 if (yieldedTypes.empty())4171 return success();4172 4173 return mlir::emitError(terminator->getLoc())4174 << "Did not expect any values to be yielded.";4175 }4176 4177 if (yieldedTypes.size() == 1 && yieldedTypes.front() == argType)4178 return success();4179 4180 auto error = mlir::emitError(yieldOp.getLoc())4181 << "Invalid yielded value. Expected type: " << argType4182 << ", got: ";4183 4184 if (yieldedTypes.empty())4185 error << "None";4186 else4187 error << yieldedTypes;4188 4189 return error;4190 };4191 4192 auto verifyRegion = [&](Region ®ion, unsigned expectedNumArgs,4193 StringRef regionName,4194 bool yieldsValue) -> LogicalResult {4195 assert(!region.empty());4196 4197 if (region.getNumArguments() != expectedNumArgs)4198 return mlir::emitError(region.getLoc())4199 << "`" << regionName << "`: "4200 << "expected " << expectedNumArgs4201 << " region arguments, got: " << region.getNumArguments();4202 4203 for (Block &block : region) {4204 // MLIR will verify the absence of the terminator for us.4205 if (!block.mightHaveTerminator())4206 continue;4207 4208 if (failed(verifyTerminator(block.getTerminator(), yieldsValue)))4209 return failure();4210 }4211 4212 return success();4213 };4214 4215 // Ensure all of the region arguments have the same type4216 for (Region *region : getRegions())4217 for (Type ty : region->getArgumentTypes())4218 if (ty != argType)4219 return emitError() << "Region argument type mismatch: got " << ty4220 << " expected " << argType << ".";4221 4222 mlir::Region &initRegion = getInitRegion();4223 if (!initRegion.empty() &&4224 failed(verifyRegion(getInitRegion(), /*expectedNumArgs=*/2, "init",4225 /*yieldsValue=*/true)))4226 return failure();4227 4228 DataSharingClauseType dsType = getDataSharingType();4229 4230 if (dsType == DataSharingClauseType::Private && !getCopyRegion().empty())4231 return emitError("`private` clauses do not require a `copy` region.");4232 4233 if (dsType == DataSharingClauseType::FirstPrivate && getCopyRegion().empty())4234 return emitError(4235 "`firstprivate` clauses require at least a `copy` region.");4236 4237 if (dsType == DataSharingClauseType::FirstPrivate &&4238 failed(verifyRegion(getCopyRegion(), /*expectedNumArgs=*/2, "copy",4239 /*yieldsValue=*/true)))4240 return failure();4241 4242 if (!getDeallocRegion().empty() &&4243 failed(verifyRegion(getDeallocRegion(), /*expectedNumArgs=*/1, "dealloc",4244 /*yieldsValue=*/false)))4245 return failure();4246 4247 return success();4248}4249 4250//===----------------------------------------------------------------------===//4251// Spec 5.2: Masked construct (10.5)4252//===----------------------------------------------------------------------===//4253 4254void MaskedOp::build(OpBuilder &builder, OperationState &state,4255 const MaskedOperands &clauses) {4256 MaskedOp::build(builder, state, clauses.filteredThreadId);4257}4258 4259//===----------------------------------------------------------------------===//4260// Spec 5.2: Scan construct (5.6)4261//===----------------------------------------------------------------------===//4262 4263void ScanOp::build(OpBuilder &builder, OperationState &state,4264 const ScanOperands &clauses) {4265 ScanOp::build(builder, state, clauses.inclusiveVars, clauses.exclusiveVars);4266}4267 4268LogicalResult ScanOp::verify() {4269 if (hasExclusiveVars() == hasInclusiveVars())4270 return emitError(4271 "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected");4272 if (WsloopOp parentWsLoopOp = (*this)->getParentOfType<WsloopOp>()) {4273 if (parentWsLoopOp.getReductionModAttr() &&4274 parentWsLoopOp.getReductionModAttr().getValue() ==4275 ReductionModifier::inscan)4276 return success();4277 }4278 if (SimdOp parentSimdOp = (*this)->getParentOfType<SimdOp>()) {4279 if (parentSimdOp.getReductionModAttr() &&4280 parentSimdOp.getReductionModAttr().getValue() ==4281 ReductionModifier::inscan)4282 return success();4283 }4284 return emitError("SCAN directive needs to be enclosed within a parent "4285 "worksharing loop construct or SIMD construct with INSCAN "4286 "reduction modifier");4287}4288 4289/// Verifies align clause in allocate directive4290 4291LogicalResult AllocateDirOp::verify() {4292 std::optional<uint64_t> align = this->getAlign();4293 4294 if (align.has_value()) {4295 if ((align.value() > 0) && !llvm::has_single_bit(align.value()))4296 return emitError() << "ALIGN value : " << align.value()4297 << " must be power of 2";4298 }4299 4300 return success();4301}4302 4303//===----------------------------------------------------------------------===//4304// TargetAllocMemOp4305//===----------------------------------------------------------------------===//4306 4307mlir::Type omp::TargetAllocMemOp::getAllocatedType() {4308 return getInTypeAttr().getValue();4309}4310 4311/// operation ::= %res = (`omp.target_alloc_mem`) $device : devicetype,4312/// $in_type ( `(` $typeparams `)` )? ( `,` $shape )?4313/// attr-dict-without-keyword4314static mlir::ParseResult parseTargetAllocMemOp(mlir::OpAsmParser &parser,4315 mlir::OperationState &result) {4316 auto &builder = parser.getBuilder();4317 bool hasOperands = false;4318 std::int32_t typeparamsSize = 0;4319 4320 // Parse device number as a new operand4321 mlir::OpAsmParser::UnresolvedOperand deviceOperand;4322 mlir::Type deviceType;4323 if (parser.parseOperand(deviceOperand) || parser.parseColonType(deviceType))4324 return mlir::failure();4325 if (parser.resolveOperand(deviceOperand, deviceType, result.operands))4326 return mlir::failure();4327 if (parser.parseComma())4328 return mlir::failure();4329 4330 mlir::Type intype;4331 if (parser.parseType(intype))4332 return mlir::failure();4333 result.addAttribute("in_type", mlir::TypeAttr::get(intype));4334 llvm::SmallVector<mlir::OpAsmParser::UnresolvedOperand> operands;4335 llvm::SmallVector<mlir::Type> typeVec;4336 if (!parser.parseOptionalLParen()) {4337 // parse the LEN params of the derived type. (<params> : <types>)4338 if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None) ||4339 parser.parseColonTypeList(typeVec) || parser.parseRParen())4340 return mlir::failure();4341 typeparamsSize = operands.size();4342 hasOperands = true;4343 }4344 std::int32_t shapeSize = 0;4345 if (!parser.parseOptionalComma()) {4346 // parse size to scale by, vector of n dimensions of type index4347 if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None))4348 return mlir::failure();4349 shapeSize = operands.size() - typeparamsSize;4350 auto idxTy = builder.getIndexType();4351 for (std::int32_t i = typeparamsSize, end = operands.size(); i != end; ++i)4352 typeVec.push_back(idxTy);4353 hasOperands = true;4354 }4355 if (hasOperands &&4356 parser.resolveOperands(operands, typeVec, parser.getNameLoc(),4357 result.operands))4358 return mlir::failure();4359 4360 mlir::Type restype = builder.getIntegerType(64);4361 if (!restype) {4362 parser.emitError(parser.getNameLoc(), "invalid allocate type: ") << intype;4363 return mlir::failure();4364 }4365 llvm::SmallVector<std::int32_t> segmentSizes{1, typeparamsSize, shapeSize};4366 result.addAttribute("operandSegmentSizes",4367 builder.getDenseI32ArrayAttr(segmentSizes));4368 if (parser.parseOptionalAttrDict(result.attributes) ||4369 parser.addTypeToList(restype, result.types))4370 return mlir::failure();4371 return mlir::success();4372}4373 4374mlir::ParseResult omp::TargetAllocMemOp::parse(mlir::OpAsmParser &parser,4375 mlir::OperationState &result) {4376 return parseTargetAllocMemOp(parser, result);4377}4378 4379void omp::TargetAllocMemOp::print(mlir::OpAsmPrinter &p) {4380 p << " ";4381 p.printOperand(getDevice());4382 p << " : ";4383 p << getDevice().getType();4384 p << ", ";4385 p << getInType();4386 if (!getTypeparams().empty()) {4387 p << '(' << getTypeparams() << " : " << getTypeparams().getTypes() << ')';4388 }4389 for (auto sh : getShape()) {4390 p << ", ";4391 p.printOperand(sh);4392 }4393 p.printOptionalAttrDict((*this)->getAttrs(),4394 {"in_type", "operandSegmentSizes"});4395}4396 4397llvm::LogicalResult omp::TargetAllocMemOp::verify() {4398 mlir::Type outType = getType();4399 if (!mlir::dyn_cast<IntegerType>(outType))4400 return emitOpError("must be a integer type");4401 return mlir::success();4402}4403 4404//===----------------------------------------------------------------------===//4405// WorkdistributeOp4406//===----------------------------------------------------------------------===//4407 4408LogicalResult WorkdistributeOp::verify() {4409 // Check that region exists and is not empty4410 Region ®ion = getRegion();4411 if (region.empty())4412 return emitOpError("region cannot be empty");4413 // Verify single entry point.4414 Block &entryBlock = region.front();4415 if (entryBlock.empty())4416 return emitOpError("region must contain a structured block");4417 // Verify single exit point.4418 bool hasTerminator = false;4419 for (Block &block : region) {4420 if (isa<TerminatorOp>(block.back())) {4421 if (hasTerminator) {4422 return emitOpError("region must have exactly one terminator");4423 }4424 hasTerminator = true;4425 }4426 }4427 if (!hasTerminator) {4428 return emitOpError("region must be terminated with omp.terminator");4429 }4430 auto walkResult = region.walk([&](Operation *op) -> WalkResult {4431 // No implicit barrier at end4432 if (isa<BarrierOp>(op)) {4433 return emitOpError(4434 "explicit barriers are not allowed in workdistribute region");4435 }4436 // Check for invalid nested constructs4437 if (isa<ParallelOp>(op)) {4438 return emitOpError(4439 "nested parallel constructs not allowed in workdistribute");4440 }4441 if (isa<TeamsOp>(op)) {4442 return emitOpError(4443 "nested teams constructs not allowed in workdistribute");4444 }4445 return WalkResult::advance();4446 });4447 if (walkResult.wasInterrupted())4448 return failure();4449 4450 Operation *parentOp = (*this)->getParentOp();4451 if (!llvm::dyn_cast<TeamsOp>(parentOp))4452 return emitOpError("workdistribute must be nested under teams");4453 return success();4454}4455 4456#define GET_ATTRDEF_CLASSES4457#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"4458 4459#define GET_OP_CLASSES4460#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"4461 4462#define GET_TYPEDEF_CLASSES4463#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"4464