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-exception 6 // 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/Dialect/LLVMIR/LLVMTypes.h" 15 #include "mlir/IR/Attributes.h" 16 #include "mlir/IR/DialectImplementation.h" 17 #include "mlir/IR/OpImplementation.h" 18 #include "mlir/IR/OperationSupport.h" 19 20 #include "llvm/ADT/BitVector.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/ADT/StringExtras.h" 23 #include "llvm/ADT/StringRef.h" 24 #include "llvm/ADT/StringSwitch.h" 25 #include "llvm/ADT/TypeSwitch.h" 26 #include <cstddef> 27 28 #include "mlir/Dialect/OpenMP/OpenMPOpsDialect.cpp.inc" 29 #include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc" 30 #include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.cpp.inc" 31 32 using namespace mlir; 33 using namespace mlir::omp; 34 35 namespace { 36 /// Model for pointer-like types that already provide a `getElementType` method. 37 template <typename T> 38 struct PointerLikeModel 39 : public PointerLikeType::ExternalModel<PointerLikeModel<T>, T> { 40 Type getElementType(Type pointer) const { 41 return pointer.cast<T>().getElementType(); 42 } 43 }; 44 } // namespace 45 46 void OpenMPDialect::initialize() { 47 addOperations< 48 #define GET_OP_LIST 49 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc" 50 >(); 51 addAttributes< 52 #define GET_ATTRDEF_LIST 53 #include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc" 54 >(); 55 56 LLVM::LLVMPointerType::attachInterface< 57 PointerLikeModel<LLVM::LLVMPointerType>>(*getContext()); 58 MemRefType::attachInterface<PointerLikeModel<MemRefType>>(*getContext()); 59 } 60 61 //===----------------------------------------------------------------------===// 62 // ParallelOp 63 //===----------------------------------------------------------------------===// 64 65 void ParallelOp::build(OpBuilder &builder, OperationState &state, 66 ArrayRef<NamedAttribute> attributes) { 67 ParallelOp::build( 68 builder, state, /*if_expr_var=*/nullptr, /*num_threads_var=*/nullptr, 69 /*allocate_vars=*/ValueRange(), /*allocators_vars=*/ValueRange(), 70 /*proc_bind_val=*/nullptr); 71 state.addAttributes(attributes); 72 } 73 74 //===----------------------------------------------------------------------===// 75 // Parser and printer for Allocate Clause 76 //===----------------------------------------------------------------------===// 77 78 /// Parse an allocate clause with allocators and a list of operands with types. 79 /// 80 /// allocate-operand-list :: = allocate-operand | 81 /// allocator-operand `,` allocate-operand-list 82 /// allocate-operand :: = ssa-id-and-type -> ssa-id-and-type 83 /// ssa-id-and-type ::= ssa-id `:` type 84 static ParseResult parseAllocateAndAllocator( 85 OpAsmParser &parser, 86 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operandsAllocate, 87 SmallVectorImpl<Type> &typesAllocate, 88 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operandsAllocator, 89 SmallVectorImpl<Type> &typesAllocator) { 90 91 return parser.parseCommaSeparatedList([&]() -> ParseResult { 92 OpAsmParser::UnresolvedOperand operand; 93 Type type; 94 if (parser.parseOperand(operand) || parser.parseColonType(type)) 95 return failure(); 96 operandsAllocator.push_back(operand); 97 typesAllocator.push_back(type); 98 if (parser.parseArrow()) 99 return failure(); 100 if (parser.parseOperand(operand) || parser.parseColonType(type)) 101 return failure(); 102 103 operandsAllocate.push_back(operand); 104 typesAllocate.push_back(type); 105 return success(); 106 }); 107 } 108 109 /// Print allocate clause 110 static void printAllocateAndAllocator(OpAsmPrinter &p, Operation *op, 111 OperandRange varsAllocate, 112 TypeRange typesAllocate, 113 OperandRange varsAllocator, 114 TypeRange typesAllocator) { 115 for (unsigned i = 0; i < varsAllocate.size(); ++i) { 116 std::string separator = i == varsAllocate.size() - 1 ? "" : ", "; 117 p << varsAllocator[i] << " : " << typesAllocator[i] << " -> "; 118 p << varsAllocate[i] << " : " << typesAllocate[i] << separator; 119 } 120 } 121 122 /// Parse a clause attribute (StringEnumAttr) 123 template <typename ClauseAttr> 124 static ParseResult parseClauseAttr(AsmParser &parser, ClauseAttr &attr) { 125 using ClauseT = decltype(std::declval<ClauseAttr>().getValue()); 126 StringRef enumStr; 127 SMLoc loc = parser.getCurrentLocation(); 128 if (parser.parseKeyword(&enumStr)) 129 return failure(); 130 if (Optional<ClauseT> enumValue = symbolizeEnum<ClauseT>(enumStr)) { 131 attr = ClauseAttr::get(parser.getContext(), *enumValue); 132 return success(); 133 } 134 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'"; 135 } 136 137 template <typename ClauseAttr> 138 void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr) { 139 p << stringifyEnum(attr.getValue()); 140 } 141 142 //===----------------------------------------------------------------------===// 143 // Parser and printer for Procbind Clause 144 //===----------------------------------------------------------------------===// 145 146 ParseResult parseProcBindKind(OpAsmParser &parser, 147 omp::ClauseProcBindKindAttr &procBindAttr) { 148 StringRef procBindStr; 149 if (parser.parseKeyword(&procBindStr)) 150 return failure(); 151 if (auto procBindVal = symbolizeClauseProcBindKind(procBindStr)) { 152 procBindAttr = 153 ClauseProcBindKindAttr::get(parser.getContext(), *procBindVal); 154 return success(); 155 } 156 return failure(); 157 } 158 159 void printProcBindKind(OpAsmPrinter &p, Operation *op, 160 omp::ClauseProcBindKindAttr procBindAttr) { 161 p << stringifyClauseProcBindKind(procBindAttr.getValue()); 162 } 163 164 LogicalResult ParallelOp::verify() { 165 if (allocate_vars().size() != allocators_vars().size()) 166 return emitError( 167 "expected equal sizes for allocate and allocator variables"); 168 return success(); 169 } 170 171 //===----------------------------------------------------------------------===// 172 // Parser and printer for Linear Clause 173 //===----------------------------------------------------------------------===// 174 175 /// linear ::= `linear` `(` linear-list `)` 176 /// linear-list := linear-val | linear-val linear-list 177 /// linear-val := ssa-id-and-type `=` ssa-id-and-type 178 static ParseResult 179 parseLinearClause(OpAsmParser &parser, 180 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars, 181 SmallVectorImpl<Type> &types, 182 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &stepVars) { 183 if (parser.parseLParen()) 184 return failure(); 185 186 do { 187 OpAsmParser::UnresolvedOperand var; 188 Type type; 189 OpAsmParser::UnresolvedOperand stepVar; 190 if (parser.parseOperand(var) || parser.parseEqual() || 191 parser.parseOperand(stepVar) || parser.parseColonType(type)) 192 return failure(); 193 194 vars.push_back(var); 195 types.push_back(type); 196 stepVars.push_back(stepVar); 197 } while (succeeded(parser.parseOptionalComma())); 198 199 if (parser.parseRParen()) 200 return failure(); 201 202 return success(); 203 } 204 205 /// Print Linear Clause 206 static void printLinearClause(OpAsmPrinter &p, OperandRange linearVars, 207 OperandRange linearStepVars) { 208 size_t linearVarsSize = linearVars.size(); 209 p << "linear("; 210 for (unsigned i = 0; i < linearVarsSize; ++i) { 211 std::string separator = i == linearVarsSize - 1 ? ") " : ", "; 212 p << linearVars[i]; 213 if (linearStepVars.size() > i) 214 p << " = " << linearStepVars[i]; 215 p << " : " << linearVars[i].getType() << separator; 216 } 217 } 218 219 //===----------------------------------------------------------------------===// 220 // Parser, printer and verifier for Schedule Clause 221 //===----------------------------------------------------------------------===// 222 223 static ParseResult 224 verifyScheduleModifiers(OpAsmParser &parser, 225 SmallVectorImpl<SmallString<12>> &modifiers) { 226 if (modifiers.size() > 2) 227 return parser.emitError(parser.getNameLoc()) << " unexpected modifier(s)"; 228 for (const auto &mod : modifiers) { 229 // Translate the string. If it has no value, then it was not a valid 230 // modifier! 231 auto symbol = symbolizeScheduleModifier(mod); 232 if (!symbol.hasValue()) 233 return parser.emitError(parser.getNameLoc()) 234 << " unknown modifier type: " << mod; 235 } 236 237 // If we have one modifier that is "simd", then stick a "none" modiifer in 238 // index 0. 239 if (modifiers.size() == 1) { 240 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd) { 241 modifiers.push_back(modifiers[0]); 242 modifiers[0] = stringifyScheduleModifier(ScheduleModifier::none); 243 } 244 } else if (modifiers.size() == 2) { 245 // If there are two modifier: 246 // First modifier should not be simd, second one should be simd 247 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd || 248 symbolizeScheduleModifier(modifiers[1]) != ScheduleModifier::simd) 249 return parser.emitError(parser.getNameLoc()) 250 << " incorrect modifier order"; 251 } 252 return success(); 253 } 254 255 /// schedule ::= `schedule` `(` sched-list `)` 256 /// sched-list ::= sched-val | sched-val sched-list | 257 /// sched-val `,` sched-modifier 258 /// sched-val ::= sched-with-chunk | sched-wo-chunk 259 /// sched-with-chunk ::= sched-with-chunk-types (`=` ssa-id-and-type)? 260 /// sched-with-chunk-types ::= `static` | `dynamic` | `guided` 261 /// sched-wo-chunk ::= `auto` | `runtime` 262 /// sched-modifier ::= sched-mod-val | sched-mod-val `,` sched-mod-val 263 /// sched-mod-val ::= `monotonic` | `nonmonotonic` | `simd` | `none` 264 static ParseResult 265 parseScheduleClause(OpAsmParser &parser, SmallString<8> &schedule, 266 SmallVectorImpl<SmallString<12>> &modifiers, 267 Optional<OpAsmParser::UnresolvedOperand> &chunkSize, 268 Type &chunkType) { 269 if (parser.parseLParen()) 270 return failure(); 271 272 StringRef keyword; 273 if (parser.parseKeyword(&keyword)) 274 return failure(); 275 276 schedule = keyword; 277 if (keyword == "static" || keyword == "dynamic" || keyword == "guided") { 278 if (succeeded(parser.parseOptionalEqual())) { 279 chunkSize = OpAsmParser::UnresolvedOperand{}; 280 if (parser.parseOperand(*chunkSize) || parser.parseColonType(chunkType)) 281 return failure(); 282 } else { 283 chunkSize = llvm::NoneType::None; 284 } 285 } else if (keyword == "auto" || keyword == "runtime") { 286 chunkSize = llvm::NoneType::None; 287 } else { 288 return parser.emitError(parser.getNameLoc()) << " expected schedule kind"; 289 } 290 291 // If there is a comma, we have one or more modifiers.. 292 while (succeeded(parser.parseOptionalComma())) { 293 StringRef mod; 294 if (parser.parseKeyword(&mod)) 295 return failure(); 296 modifiers.push_back(mod); 297 } 298 299 if (parser.parseRParen()) 300 return failure(); 301 302 if (verifyScheduleModifiers(parser, modifiers)) 303 return failure(); 304 305 return success(); 306 } 307 308 /// Print schedule clause 309 static void printScheduleClause(OpAsmPrinter &p, ClauseScheduleKind sched, 310 Optional<ScheduleModifier> modifier, bool simd, 311 Value scheduleChunkVar) { 312 p << "schedule(" << stringifyClauseScheduleKind(sched).lower(); 313 if (scheduleChunkVar) 314 p << " = " << scheduleChunkVar << " : " << scheduleChunkVar.getType(); 315 if (modifier) 316 p << ", " << stringifyScheduleModifier(*modifier); 317 if (simd) 318 p << ", simd"; 319 p << ") "; 320 } 321 322 //===----------------------------------------------------------------------===// 323 // Parser, printer and verifier for ReductionVarList 324 //===----------------------------------------------------------------------===// 325 326 /// reduction-entry-list ::= reduction-entry 327 /// | reduction-entry-list `,` reduction-entry 328 /// reduction-entry ::= symbol-ref `->` ssa-id `:` type 329 static ParseResult 330 parseReductionVarList(OpAsmParser &parser, 331 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operands, 332 SmallVectorImpl<Type> &types, 333 ArrayAttr &redcuctionSymbols) { 334 SmallVector<SymbolRefAttr> reductionVec; 335 do { 336 if (parser.parseAttribute(reductionVec.emplace_back()) || 337 parser.parseArrow() || parser.parseOperand(operands.emplace_back()) || 338 parser.parseColonType(types.emplace_back())) 339 return failure(); 340 } while (succeeded(parser.parseOptionalComma())); 341 SmallVector<Attribute> reductions(reductionVec.begin(), reductionVec.end()); 342 redcuctionSymbols = ArrayAttr::get(parser.getContext(), reductions); 343 return success(); 344 } 345 346 /// Print Reduction clause 347 static void printReductionVarList(OpAsmPrinter &p, Operation *op, 348 OperandRange reductionVars, 349 TypeRange reductionTypes, 350 Optional<ArrayAttr> reductions) { 351 for (unsigned i = 0, e = reductions->size(); i < e; ++i) { 352 if (i != 0) 353 p << ", "; 354 p << (*reductions)[i] << " -> " << reductionVars[i] << " : " 355 << reductionVars[i].getType(); 356 } 357 } 358 359 /// Verifies Reduction Clause 360 static LogicalResult verifyReductionVarList(Operation *op, 361 Optional<ArrayAttr> reductions, 362 OperandRange reductionVars) { 363 if (!reductionVars.empty()) { 364 if (!reductions || reductions->size() != reductionVars.size()) 365 return op->emitOpError() 366 << "expected as many reduction symbol references " 367 "as reduction variables"; 368 } else { 369 if (reductions) 370 return op->emitOpError() << "unexpected reduction symbol references"; 371 return success(); 372 } 373 374 // TODO: The followings should be done in 375 // SymbolUserOpInterface::verifySymbolUses. 376 DenseSet<Value> accumulators; 377 for (auto args : llvm::zip(reductionVars, *reductions)) { 378 Value accum = std::get<0>(args); 379 380 if (!accumulators.insert(accum).second) 381 return op->emitOpError() << "accumulator variable used more than once"; 382 383 Type varType = accum.getType().cast<PointerLikeType>(); 384 auto symbolRef = std::get<1>(args).cast<SymbolRefAttr>(); 385 auto decl = 386 SymbolTable::lookupNearestSymbolFrom<ReductionDeclareOp>(op, symbolRef); 387 if (!decl) 388 return op->emitOpError() << "expected symbol reference " << symbolRef 389 << " to point to a reduction declaration"; 390 391 if (decl.getAccumulatorType() && decl.getAccumulatorType() != varType) 392 return op->emitOpError() 393 << "expected accumulator (" << varType 394 << ") to be the same type as reduction declaration (" 395 << decl.getAccumulatorType() << ")"; 396 } 397 398 return success(); 399 } 400 401 //===----------------------------------------------------------------------===// 402 // Parser, printer and verifier for Synchronization Hint (2.17.12) 403 //===----------------------------------------------------------------------===// 404 405 /// Parses a Synchronization Hint clause. The value of hint is an integer 406 /// which is a combination of different hints from `omp_sync_hint_t`. 407 /// 408 /// hint-clause = `hint` `(` hint-value `)` 409 static ParseResult parseSynchronizationHint(OpAsmParser &parser, 410 IntegerAttr &hintAttr) { 411 StringRef hintKeyword; 412 int64_t hint = 0; 413 do { 414 if (failed(parser.parseKeyword(&hintKeyword))) 415 return failure(); 416 if (hintKeyword == "uncontended") 417 hint |= 1; 418 else if (hintKeyword == "contended") 419 hint |= 2; 420 else if (hintKeyword == "nonspeculative") 421 hint |= 4; 422 else if (hintKeyword == "speculative") 423 hint |= 8; 424 else 425 return parser.emitError(parser.getCurrentLocation()) 426 << hintKeyword << " is not a valid hint"; 427 } while (succeeded(parser.parseOptionalComma())); 428 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint); 429 return success(); 430 } 431 432 /// Prints a Synchronization Hint clause 433 static void printSynchronizationHint(OpAsmPrinter &p, Operation *op, 434 IntegerAttr hintAttr) { 435 int64_t hint = hintAttr.getInt(); 436 437 if (hint == 0) 438 return; 439 440 // Helper function to get n-th bit from the right end of `value` 441 auto bitn = [](int value, int n) -> bool { return value & (1 << n); }; 442 443 bool uncontended = bitn(hint, 0); 444 bool contended = bitn(hint, 1); 445 bool nonspeculative = bitn(hint, 2); 446 bool speculative = bitn(hint, 3); 447 448 SmallVector<StringRef> hints; 449 if (uncontended) 450 hints.push_back("uncontended"); 451 if (contended) 452 hints.push_back("contended"); 453 if (nonspeculative) 454 hints.push_back("nonspeculative"); 455 if (speculative) 456 hints.push_back("speculative"); 457 458 llvm::interleaveComma(hints, p); 459 } 460 461 /// Verifies a synchronization hint clause 462 static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) { 463 464 // Helper function to get n-th bit from the right end of `value` 465 auto bitn = [](int value, int n) -> bool { return value & (1 << n); }; 466 467 bool uncontended = bitn(hint, 0); 468 bool contended = bitn(hint, 1); 469 bool nonspeculative = bitn(hint, 2); 470 bool speculative = bitn(hint, 3); 471 472 if (uncontended && contended) 473 return op->emitOpError() << "the hints omp_sync_hint_uncontended and " 474 "omp_sync_hint_contended cannot be combined"; 475 if (nonspeculative && speculative) 476 return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and " 477 "omp_sync_hint_speculative cannot be combined."; 478 return success(); 479 } 480 481 enum ClauseType { 482 allocateClause, 483 reductionClause, 484 nowaitClause, 485 linearClause, 486 scheduleClause, 487 collapseClause, 488 orderClause, 489 orderedClause, 490 COUNT 491 }; 492 493 //===----------------------------------------------------------------------===// 494 // Parser for Clause List 495 //===----------------------------------------------------------------------===// 496 497 /// Parse a list of clauses. The clauses can appear in any order, but their 498 /// operand segment indices are in the same order that they are passed in the 499 /// `clauses` list. The operand segments are added over the prevSegments 500 501 /// clause-list ::= clause clause-list | empty 502 /// clause ::= allocate | reduction | nowait | linear | schedule | collapse 503 /// | order | ordered 504 /// allocate ::= `allocate` `(` allocate-operand-list `)` 505 /// reduction ::= `reduction` `(` reduction-entry-list `)` 506 /// nowait ::= `nowait` 507 /// linear ::= `linear` `(` linear-list `)` 508 /// schedule ::= `schedule` `(` sched-list `)` 509 /// collapse ::= `collapse` `(` ssa-id-and-type `)` 510 /// order ::= `order` `(` `concurrent` `)` 511 /// ordered ::= `ordered` `(` ssa-id-and-type `)` 512 /// 513 /// Note that each clause can only appear once in the clase-list. 514 static ParseResult parseClauses(OpAsmParser &parser, OperationState &result, 515 SmallVectorImpl<ClauseType> &clauses, 516 SmallVectorImpl<int> &segments) { 517 518 // Check done[clause] to see if it has been parsed already 519 BitVector done(ClauseType::COUNT, false); 520 521 // See pos[clause] to get position of clause in operand segments 522 SmallVector<int> pos(ClauseType::COUNT, -1); 523 524 // Stores the last parsed clause keyword 525 StringRef clauseKeyword; 526 StringRef opName = result.name.getStringRef(); 527 528 // Containers for storing operands, types and attributes for various clauses 529 SmallVector<OpAsmParser::UnresolvedOperand> allocates, allocators; 530 SmallVector<Type> allocateTypes, allocatorTypes; 531 532 ArrayAttr reductions; 533 SmallVector<OpAsmParser::UnresolvedOperand> reductionVars; 534 SmallVector<Type> reductionVarTypes; 535 536 SmallVector<OpAsmParser::UnresolvedOperand> linears; 537 SmallVector<Type> linearTypes; 538 SmallVector<OpAsmParser::UnresolvedOperand> linearSteps; 539 540 SmallString<8> schedule; 541 SmallVector<SmallString<12>> modifiers; 542 Optional<OpAsmParser::UnresolvedOperand> scheduleChunkSize; 543 Type scheduleChunkType; 544 545 // Compute the position of clauses in operand segments 546 int currPos = 0; 547 for (ClauseType clause : clauses) { 548 549 // Skip the following clauses - they do not take any position in operand 550 // segments 551 if (clause == nowaitClause || clause == collapseClause || 552 clause == orderClause || clause == orderedClause) 553 continue; 554 555 pos[clause] = currPos++; 556 557 // For the following clauses, two positions are reserved in the operand 558 // segments 559 if (clause == allocateClause || clause == linearClause) 560 currPos++; 561 } 562 563 SmallVector<int> clauseSegments(currPos); 564 565 // Helper function to check if a clause is allowed/repeated or not 566 auto checkAllowed = [&](ClauseType clause) -> ParseResult { 567 if (!llvm::is_contained(clauses, clause)) 568 return parser.emitError(parser.getCurrentLocation()) 569 << clauseKeyword << " is not a valid clause for the " << opName 570 << " operation"; 571 if (done[clause]) 572 return parser.emitError(parser.getCurrentLocation()) 573 << "at most one " << clauseKeyword << " clause can appear on the " 574 << opName << " operation"; 575 done[clause] = true; 576 return success(); 577 }; 578 579 while (succeeded(parser.parseOptionalKeyword(&clauseKeyword))) { 580 if (clauseKeyword == "allocate") { 581 if (checkAllowed(allocateClause) || parser.parseLParen() || 582 parseAllocateAndAllocator(parser, allocates, allocateTypes, 583 allocators, allocatorTypes) || 584 parser.parseRParen()) 585 return failure(); 586 clauseSegments[pos[allocateClause]] = allocates.size(); 587 clauseSegments[pos[allocateClause] + 1] = allocators.size(); 588 } else if (clauseKeyword == "reduction") { 589 if (checkAllowed(reductionClause) || parser.parseLParen() || 590 parseReductionVarList(parser, reductionVars, reductionVarTypes, 591 reductions) || 592 parser.parseRParen()) 593 return failure(); 594 clauseSegments[pos[reductionClause]] = reductionVars.size(); 595 } else if (clauseKeyword == "nowait") { 596 if (checkAllowed(nowaitClause)) 597 return failure(); 598 auto attr = UnitAttr::get(parser.getBuilder().getContext()); 599 result.addAttribute("nowait", attr); 600 } else if (clauseKeyword == "linear") { 601 if (checkAllowed(linearClause) || 602 parseLinearClause(parser, linears, linearTypes, linearSteps)) 603 return failure(); 604 clauseSegments[pos[linearClause]] = linears.size(); 605 clauseSegments[pos[linearClause] + 1] = linearSteps.size(); 606 } else if (clauseKeyword == "schedule") { 607 if (checkAllowed(scheduleClause) || 608 parseScheduleClause(parser, schedule, modifiers, scheduleChunkSize, 609 scheduleChunkType)) 610 return failure(); 611 if (scheduleChunkSize) { 612 clauseSegments[pos[scheduleClause]] = 1; 613 } 614 } else if (clauseKeyword == "collapse") { 615 auto type = parser.getBuilder().getI64Type(); 616 mlir::IntegerAttr attr; 617 if (checkAllowed(collapseClause) || parser.parseLParen() || 618 parser.parseAttribute(attr, type) || parser.parseRParen()) 619 return failure(); 620 result.addAttribute("collapse_val", attr); 621 } else if (clauseKeyword == "ordered") { 622 mlir::IntegerAttr attr; 623 if (checkAllowed(orderedClause)) 624 return failure(); 625 if (succeeded(parser.parseOptionalLParen())) { 626 auto type = parser.getBuilder().getI64Type(); 627 if (parser.parseAttribute(attr, type) || parser.parseRParen()) 628 return failure(); 629 } else { 630 // Use 0 to represent no ordered parameter was specified 631 attr = parser.getBuilder().getI64IntegerAttr(0); 632 } 633 result.addAttribute("ordered_val", attr); 634 } else if (clauseKeyword == "order") { 635 ClauseOrderKindAttr order; 636 if (checkAllowed(orderClause) || parser.parseLParen() || 637 parseClauseAttr<ClauseOrderKindAttr>(parser, order) || 638 parser.parseRParen()) 639 return failure(); 640 result.addAttribute("order_val", order); 641 } else { 642 return parser.emitError(parser.getNameLoc()) 643 << clauseKeyword << " is not a valid clause"; 644 } 645 } 646 647 // Add allocate parameters. 648 if (done[allocateClause] && clauseSegments[pos[allocateClause]] && 649 failed(parser.resolveOperands(allocates, allocateTypes, 650 allocates[0].location, result.operands))) 651 return failure(); 652 653 // Add allocator parameters. 654 if (done[allocateClause] && clauseSegments[pos[allocateClause] + 1] && 655 failed(parser.resolveOperands(allocators, allocatorTypes, 656 allocators[0].location, result.operands))) 657 return failure(); 658 659 // Add reduction parameters and symbols 660 if (done[reductionClause] && clauseSegments[pos[reductionClause]]) { 661 if (failed(parser.resolveOperands(reductionVars, reductionVarTypes, 662 parser.getNameLoc(), result.operands))) 663 return failure(); 664 result.addAttribute("reductions", reductions); 665 } 666 667 // Add linear parameters 668 if (done[linearClause] && clauseSegments[pos[linearClause]]) { 669 auto linearStepType = parser.getBuilder().getI32Type(); 670 SmallVector<Type> linearStepTypes(linearSteps.size(), linearStepType); 671 if (failed(parser.resolveOperands(linears, linearTypes, linears[0].location, 672 result.operands)) || 673 failed(parser.resolveOperands(linearSteps, linearStepTypes, 674 linearSteps[0].location, 675 result.operands))) 676 return failure(); 677 } 678 679 // Add schedule parameters 680 if (done[scheduleClause] && !schedule.empty()) { 681 if (Optional<ClauseScheduleKind> sched = 682 symbolizeClauseScheduleKind(schedule)) { 683 auto attr = ClauseScheduleKindAttr::get(parser.getContext(), *sched); 684 result.addAttribute("schedule_val", attr); 685 } else { 686 return parser.emitError(parser.getCurrentLocation(), 687 "invalid schedule kind"); 688 } 689 if (!modifiers.empty()) { 690 SMLoc loc = parser.getCurrentLocation(); 691 if (Optional<ScheduleModifier> mod = 692 symbolizeScheduleModifier(modifiers[0])) { 693 result.addAttribute( 694 "schedule_modifier", 695 ScheduleModifierAttr::get(parser.getContext(), *mod)); 696 } else { 697 return parser.emitError(loc, "invalid schedule modifier"); 698 } 699 // Only SIMD attribute is allowed here! 700 if (modifiers.size() > 1) { 701 assert(symbolizeScheduleModifier(modifiers[1]) == 702 ScheduleModifier::simd); 703 auto attr = UnitAttr::get(parser.getBuilder().getContext()); 704 result.addAttribute("simd_modifier", attr); 705 } 706 } 707 if (scheduleChunkSize) 708 parser.resolveOperand(*scheduleChunkSize, scheduleChunkType, 709 result.operands); 710 } 711 712 segments.insert(segments.end(), clauseSegments.begin(), clauseSegments.end()); 713 714 return success(); 715 } 716 717 //===----------------------------------------------------------------------===// 718 // Verifier for SectionsOp 719 //===----------------------------------------------------------------------===// 720 721 LogicalResult SectionsOp::verify() { 722 if (allocate_vars().size() != allocators_vars().size()) 723 return emitError( 724 "expected equal sizes for allocate and allocator variables"); 725 726 return verifyReductionVarList(*this, reductions(), reduction_vars()); 727 } 728 729 LogicalResult SectionsOp::verifyRegions() { 730 for (auto &inst : *region().begin()) { 731 if (!(isa<SectionOp>(inst) || isa<TerminatorOp>(inst))) { 732 return emitOpError() 733 << "expected omp.section op or terminator op inside region"; 734 } 735 } 736 737 return success(); 738 } 739 740 /// Parses an OpenMP Workshare Loop operation 741 /// 742 /// wsloop ::= `omp.wsloop` loop-control clause-list 743 /// loop-control ::= `(` ssa-id-list `)` `:` type `=` loop-bounds 744 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` inclusive? steps 745 /// steps := `step` `(`ssa-id-list`)` 746 /// clause-list ::= clause clause-list | empty 747 /// clause ::= linear | schedule | collapse | nowait | ordered | order 748 /// | reduction 749 ParseResult WsLoopOp::parse(OpAsmParser &parser, OperationState &result) { 750 // Parse an opening `(` followed by induction variables followed by `)` 751 SmallVector<OpAsmParser::UnresolvedOperand> ivs; 752 if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1, 753 OpAsmParser::Delimiter::Paren)) 754 return failure(); 755 756 int numIVs = static_cast<int>(ivs.size()); 757 Type loopVarType; 758 if (parser.parseColonType(loopVarType)) 759 return failure(); 760 761 // Parse loop bounds. 762 SmallVector<OpAsmParser::UnresolvedOperand> lower; 763 if (parser.parseEqual() || 764 parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) || 765 parser.resolveOperands(lower, loopVarType, result.operands)) 766 return failure(); 767 768 SmallVector<OpAsmParser::UnresolvedOperand> upper; 769 if (parser.parseKeyword("to") || 770 parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) || 771 parser.resolveOperands(upper, loopVarType, result.operands)) 772 return failure(); 773 774 if (succeeded(parser.parseOptionalKeyword("inclusive"))) { 775 auto attr = UnitAttr::get(parser.getBuilder().getContext()); 776 result.addAttribute("inclusive", attr); 777 } 778 779 // Parse step values. 780 SmallVector<OpAsmParser::UnresolvedOperand> steps; 781 if (parser.parseKeyword("step") || 782 parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) || 783 parser.resolveOperands(steps, loopVarType, result.operands)) 784 return failure(); 785 786 SmallVector<ClauseType> clauses = { 787 linearClause, reductionClause, collapseClause, orderClause, 788 orderedClause, nowaitClause, scheduleClause}; 789 SmallVector<int> segments{numIVs, numIVs, numIVs}; 790 if (failed(parseClauses(parser, result, clauses, segments))) 791 return failure(); 792 793 result.addAttribute("operand_segment_sizes", 794 parser.getBuilder().getI32VectorAttr(segments)); 795 796 // Now parse the body. 797 Region *body = result.addRegion(); 798 SmallVector<Type> ivTypes(numIVs, loopVarType); 799 SmallVector<OpAsmParser::UnresolvedOperand> blockArgs(ivs); 800 if (parser.parseRegion(*body, blockArgs, ivTypes)) 801 return failure(); 802 return success(); 803 } 804 805 void WsLoopOp::print(OpAsmPrinter &p) { 806 auto args = getRegion().front().getArguments(); 807 p << " (" << args << ") : " << args[0].getType() << " = (" << lowerBound() 808 << ") to (" << upperBound() << ") "; 809 if (inclusive()) { 810 p << "inclusive "; 811 } 812 p << "step (" << step() << ") "; 813 814 if (!linear_vars().empty()) 815 printLinearClause(p, linear_vars(), linear_step_vars()); 816 817 if (auto sched = schedule_val()) 818 printScheduleClause(p, sched.getValue(), schedule_modifier(), 819 simd_modifier(), schedule_chunk_var()); 820 821 if (auto collapse = collapse_val()) 822 p << "collapse(" << collapse << ") "; 823 824 if (nowait()) 825 p << "nowait "; 826 827 if (auto ordered = ordered_val()) 828 p << "ordered(" << ordered << ") "; 829 830 if (auto order = order_val()) 831 p << "order(" << stringifyClauseOrderKind(*order) << ") "; 832 833 if (!reduction_vars().empty()) { 834 printReductionVarList(p << "reduction(", *this, reduction_vars(), 835 reduction_vars().getTypes(), reductions()); 836 p << ")"; 837 } 838 839 p << ' '; 840 p.printRegion(region(), /*printEntryBlockArgs=*/false); 841 } 842 843 //===----------------------------------------------------------------------===// 844 // SimdLoopOp 845 //===----------------------------------------------------------------------===// 846 /// Parses an OpenMP Simd construct [2.9.3.1] 847 /// 848 /// simdloop ::= `omp.simdloop` loop-control clause-list 849 /// loop-control ::= `(` ssa-id-list `)` `:` type `=` loop-bounds 850 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` steps 851 /// steps := `step` `(`ssa-id-list`)` 852 /// clause-list ::= clause clause-list | empty 853 /// clause ::= TODO 854 ParseResult SimdLoopOp::parse(OpAsmParser &parser, OperationState &result) { 855 // Parse an opening `(` followed by induction variables followed by `)` 856 SmallVector<OpAsmParser::UnresolvedOperand> ivs; 857 if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1, 858 OpAsmParser::Delimiter::Paren)) 859 return failure(); 860 int numIVs = static_cast<int>(ivs.size()); 861 Type loopVarType; 862 if (parser.parseColonType(loopVarType)) 863 return failure(); 864 // Parse loop bounds. 865 SmallVector<OpAsmParser::UnresolvedOperand> lower; 866 if (parser.parseEqual() || 867 parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) || 868 parser.resolveOperands(lower, loopVarType, result.operands)) 869 return failure(); 870 SmallVector<OpAsmParser::UnresolvedOperand> upper; 871 if (parser.parseKeyword("to") || 872 parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) || 873 parser.resolveOperands(upper, loopVarType, result.operands)) 874 return failure(); 875 876 // Parse step values. 877 SmallVector<OpAsmParser::UnresolvedOperand> steps; 878 if (parser.parseKeyword("step") || 879 parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) || 880 parser.resolveOperands(steps, loopVarType, result.operands)) 881 return failure(); 882 883 SmallVector<int> segments{numIVs, numIVs, numIVs}; 884 // TODO: Add parseClauses() when we support clauses 885 result.addAttribute("operand_segment_sizes", 886 parser.getBuilder().getI32VectorAttr(segments)); 887 888 // Now parse the body. 889 Region *body = result.addRegion(); 890 SmallVector<Type> ivTypes(numIVs, loopVarType); 891 SmallVector<OpAsmParser::UnresolvedOperand> blockArgs(ivs); 892 if (parser.parseRegion(*body, blockArgs, ivTypes)) 893 return failure(); 894 return success(); 895 } 896 897 void SimdLoopOp::print(OpAsmPrinter &p) { 898 auto args = getRegion().front().getArguments(); 899 p << " (" << args << ") : " << args[0].getType() << " = (" << lowerBound() 900 << ") to (" << upperBound() << ") "; 901 p << "step (" << step() << ") "; 902 903 p.printRegion(region(), /*printEntryBlockArgs=*/false); 904 } 905 906 //===----------------------------------------------------------------------===// 907 // Verifier for Simd construct [2.9.3.1] 908 //===----------------------------------------------------------------------===// 909 910 LogicalResult SimdLoopOp::verify() { 911 if (this->lowerBound().empty()) { 912 return emitOpError() << "empty lowerbound for simd loop operation"; 913 } 914 return success(); 915 } 916 917 //===----------------------------------------------------------------------===// 918 // ReductionOp 919 //===----------------------------------------------------------------------===// 920 921 static ParseResult parseAtomicReductionRegion(OpAsmParser &parser, 922 Region ®ion) { 923 if (parser.parseOptionalKeyword("atomic")) 924 return success(); 925 return parser.parseRegion(region); 926 } 927 928 static void printAtomicReductionRegion(OpAsmPrinter &printer, 929 ReductionDeclareOp op, Region ®ion) { 930 if (region.empty()) 931 return; 932 printer << "atomic "; 933 printer.printRegion(region); 934 } 935 936 LogicalResult ReductionDeclareOp::verifyRegions() { 937 if (initializerRegion().empty()) 938 return emitOpError() << "expects non-empty initializer region"; 939 Block &initializerEntryBlock = initializerRegion().front(); 940 if (initializerEntryBlock.getNumArguments() != 1 || 941 initializerEntryBlock.getArgument(0).getType() != type()) { 942 return emitOpError() << "expects initializer region with one argument " 943 "of the reduction type"; 944 } 945 946 for (YieldOp yieldOp : initializerRegion().getOps<YieldOp>()) { 947 if (yieldOp.results().size() != 1 || 948 yieldOp.results().getTypes()[0] != type()) 949 return emitOpError() << "expects initializer region to yield a value " 950 "of the reduction type"; 951 } 952 953 if (reductionRegion().empty()) 954 return emitOpError() << "expects non-empty reduction region"; 955 Block &reductionEntryBlock = reductionRegion().front(); 956 if (reductionEntryBlock.getNumArguments() != 2 || 957 reductionEntryBlock.getArgumentTypes()[0] != 958 reductionEntryBlock.getArgumentTypes()[1] || 959 reductionEntryBlock.getArgumentTypes()[0] != type()) 960 return emitOpError() << "expects reduction region with two arguments of " 961 "the reduction type"; 962 for (YieldOp yieldOp : reductionRegion().getOps<YieldOp>()) { 963 if (yieldOp.results().size() != 1 || 964 yieldOp.results().getTypes()[0] != type()) 965 return emitOpError() << "expects reduction region to yield a value " 966 "of the reduction type"; 967 } 968 969 if (atomicReductionRegion().empty()) 970 return success(); 971 972 Block &atomicReductionEntryBlock = atomicReductionRegion().front(); 973 if (atomicReductionEntryBlock.getNumArguments() != 2 || 974 atomicReductionEntryBlock.getArgumentTypes()[0] != 975 atomicReductionEntryBlock.getArgumentTypes()[1]) 976 return emitOpError() << "expects atomic reduction region with two " 977 "arguments of the same type"; 978 auto ptrType = atomicReductionEntryBlock.getArgumentTypes()[0] 979 .dyn_cast<PointerLikeType>(); 980 if (!ptrType || ptrType.getElementType() != type()) 981 return emitOpError() << "expects atomic reduction region arguments to " 982 "be accumulators containing the reduction type"; 983 return success(); 984 } 985 986 LogicalResult ReductionOp::verify() { 987 // TODO: generalize this to an op interface when there is more than one op 988 // that supports reductions. 989 auto container = (*this)->getParentOfType<WsLoopOp>(); 990 for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i) 991 if (container.reduction_vars()[i] == accumulator()) 992 return success(); 993 994 return emitOpError() << "the accumulator is not used by the parent"; 995 } 996 997 //===----------------------------------------------------------------------===// 998 // WsLoopOp 999 //===----------------------------------------------------------------------===// 1000 1001 void WsLoopOp::build(OpBuilder &builder, OperationState &state, 1002 ValueRange lowerBound, ValueRange upperBound, 1003 ValueRange step, ArrayRef<NamedAttribute> attributes) { 1004 build(builder, state, lowerBound, upperBound, step, 1005 /*linear_vars=*/ValueRange(), 1006 /*linear_step_vars=*/ValueRange(), /*reduction_vars=*/ValueRange(), 1007 /*reductions=*/nullptr, /*schedule_val=*/nullptr, 1008 /*schedule_chunk_var=*/nullptr, /*schedule_modifier=*/nullptr, 1009 /*simd_modifier=*/false, /*collapse_val=*/nullptr, /*nowait=*/false, 1010 /*ordered_val=*/nullptr, /*order_val=*/nullptr, /*inclusive=*/false); 1011 state.addAttributes(attributes); 1012 } 1013 1014 LogicalResult WsLoopOp::verify() { 1015 return verifyReductionVarList(*this, reductions(), reduction_vars()); 1016 } 1017 1018 //===----------------------------------------------------------------------===// 1019 // Verifier for critical construct (2.17.1) 1020 //===----------------------------------------------------------------------===// 1021 1022 LogicalResult CriticalDeclareOp::verify() { 1023 return verifySynchronizationHint(*this, hint_val()); 1024 } 1025 1026 LogicalResult 1027 CriticalOp::verifySymbolUses(SymbolTableCollection &symbol_table) { 1028 if (nameAttr()) { 1029 SymbolRefAttr symbolRef = nameAttr(); 1030 auto decl = symbol_table.lookupNearestSymbolFrom<CriticalDeclareOp>( 1031 *this, symbolRef); 1032 if (!decl) { 1033 return emitOpError() << "expected symbol reference " << symbolRef 1034 << " to point to a critical declaration"; 1035 } 1036 } 1037 1038 return success(); 1039 } 1040 1041 //===----------------------------------------------------------------------===// 1042 // Verifier for ordered construct 1043 //===----------------------------------------------------------------------===// 1044 1045 LogicalResult OrderedOp::verify() { 1046 auto container = (*this)->getParentOfType<WsLoopOp>(); 1047 if (!container || !container.ordered_valAttr() || 1048 container.ordered_valAttr().getInt() == 0) 1049 return emitOpError() << "ordered depend directive must be closely " 1050 << "nested inside a worksharing-loop with ordered " 1051 << "clause with parameter present"; 1052 1053 if (container.ordered_valAttr().getInt() != 1054 (int64_t)num_loops_val().getValue()) 1055 return emitOpError() << "number of variables in depend clause does not " 1056 << "match number of iteration variables in the " 1057 << "doacross loop"; 1058 1059 return success(); 1060 } 1061 1062 LogicalResult OrderedRegionOp::verify() { 1063 // TODO: The code generation for ordered simd directive is not supported yet. 1064 if (simd()) 1065 return failure(); 1066 1067 if (auto container = (*this)->getParentOfType<WsLoopOp>()) { 1068 if (!container.ordered_valAttr() || 1069 container.ordered_valAttr().getInt() != 0) 1070 return emitOpError() << "ordered region must be closely nested inside " 1071 << "a worksharing-loop region with an ordered " 1072 << "clause without parameter present"; 1073 } 1074 1075 return success(); 1076 } 1077 1078 //===----------------------------------------------------------------------===// 1079 // Verifier for AtomicReadOp 1080 //===----------------------------------------------------------------------===// 1081 1082 LogicalResult AtomicReadOp::verify() { 1083 if (auto mo = memory_order_val()) { 1084 if (*mo == ClauseMemoryOrderKind::Acq_rel || 1085 *mo == ClauseMemoryOrderKind::Release) { 1086 return emitError( 1087 "memory-order must not be acq_rel or release for atomic reads"); 1088 } 1089 } 1090 if (x() == v()) 1091 return emitError( 1092 "read and write must not be to the same location for atomic reads"); 1093 return verifySynchronizationHint(*this, hint_val()); 1094 } 1095 1096 //===----------------------------------------------------------------------===// 1097 // Verifier for AtomicWriteOp 1098 //===----------------------------------------------------------------------===// 1099 1100 LogicalResult AtomicWriteOp::verify() { 1101 if (auto mo = memory_order_val()) { 1102 if (*mo == ClauseMemoryOrderKind::Acq_rel || 1103 *mo == ClauseMemoryOrderKind::Acquire) { 1104 return emitError( 1105 "memory-order must not be acq_rel or acquire for atomic writes"); 1106 } 1107 } 1108 return verifySynchronizationHint(*this, hint_val()); 1109 } 1110 1111 //===----------------------------------------------------------------------===// 1112 // Verifier for AtomicUpdateOp 1113 //===----------------------------------------------------------------------===// 1114 1115 LogicalResult AtomicUpdateOp::verify() { 1116 if (auto mo = memory_order_val()) { 1117 if (*mo == ClauseMemoryOrderKind::Acq_rel || 1118 *mo == ClauseMemoryOrderKind::Acquire) { 1119 return emitError( 1120 "memory-order must not be acq_rel or acquire for atomic updates"); 1121 } 1122 } 1123 1124 if (x().getType().cast<PointerLikeType>().getElementType() != 1125 region().getArgument(0).getType()) { 1126 return emitError("the type of the operand must be a pointer type whose " 1127 "element type is the same as that of the region argument"); 1128 } 1129 1130 return success(); 1131 } 1132 1133 LogicalResult AtomicUpdateOp::verifyRegions() { 1134 if (region().getNumArguments() != 1) 1135 return emitError("the region must accept exactly one argument"); 1136 1137 if (region().front().getOperations().size() < 2) 1138 return emitError() << "the update region must have at least two operations " 1139 "(binop and terminator)"; 1140 1141 YieldOp yieldOp = *region().getOps<YieldOp>().begin(); 1142 1143 if (yieldOp.results().size() != 1) 1144 return emitError("only updated value must be returned"); 1145 if (yieldOp.results().front().getType() != region().getArgument(0).getType()) 1146 return emitError("input and yielded value must have the same type"); 1147 return success(); 1148 } 1149 1150 //===----------------------------------------------------------------------===// 1151 // Verifier for AtomicCaptureOp 1152 //===----------------------------------------------------------------------===// 1153 1154 Operation *AtomicCaptureOp::getFirstOp() { 1155 return &getRegion().front().getOperations().front(); 1156 } 1157 1158 Operation *AtomicCaptureOp::getSecondOp() { 1159 auto &ops = getRegion().front().getOperations(); 1160 return ops.getNextNode(ops.front()); 1161 } 1162 1163 AtomicReadOp AtomicCaptureOp::getAtomicReadOp() { 1164 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp())) 1165 return op; 1166 return dyn_cast<AtomicReadOp>(getSecondOp()); 1167 } 1168 1169 AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() { 1170 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp())) 1171 return op; 1172 return dyn_cast<AtomicWriteOp>(getSecondOp()); 1173 } 1174 1175 AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() { 1176 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp())) 1177 return op; 1178 return dyn_cast<AtomicUpdateOp>(getSecondOp()); 1179 } 1180 1181 LogicalResult AtomicCaptureOp::verifyRegions() { 1182 Block::OpListType &ops = region().front().getOperations(); 1183 if (ops.size() != 3) 1184 return emitError() 1185 << "expected three operations in omp.atomic.capture region (one " 1186 "terminator, and two atomic ops)"; 1187 auto &firstOp = ops.front(); 1188 auto &secondOp = *ops.getNextNode(firstOp); 1189 auto firstReadStmt = dyn_cast<AtomicReadOp>(firstOp); 1190 auto firstUpdateStmt = dyn_cast<AtomicUpdateOp>(firstOp); 1191 auto secondReadStmt = dyn_cast<AtomicReadOp>(secondOp); 1192 auto secondUpdateStmt = dyn_cast<AtomicUpdateOp>(secondOp); 1193 auto secondWriteStmt = dyn_cast<AtomicWriteOp>(secondOp); 1194 1195 if (!((firstUpdateStmt && secondReadStmt) || 1196 (firstReadStmt && secondUpdateStmt) || 1197 (firstReadStmt && secondWriteStmt))) 1198 return ops.front().emitError() 1199 << "invalid sequence of operations in the capture region"; 1200 if (firstUpdateStmt && secondReadStmt && 1201 firstUpdateStmt.x() != secondReadStmt.x()) 1202 return firstUpdateStmt.emitError() 1203 << "updated variable in omp.atomic.update must be captured in " 1204 "second operation"; 1205 if (firstReadStmt && secondUpdateStmt && 1206 firstReadStmt.x() != secondUpdateStmt.x()) 1207 return firstReadStmt.emitError() 1208 << "captured variable in omp.atomic.read must be updated in second " 1209 "operation"; 1210 if (firstReadStmt && secondWriteStmt && 1211 firstReadStmt.x() != secondWriteStmt.address()) 1212 return firstReadStmt.emitError() 1213 << "captured variable in omp.atomic.read must be updated in " 1214 "second operation"; 1215 return success(); 1216 } 1217 1218 #define GET_ATTRDEF_CLASSES 1219 #include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc" 1220 1221 #define GET_OP_CLASSES 1222 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc" 1223