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