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 if (failed(parser.parseOptionalKeyword("hint"))) { 396 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0); 397 return success(); 398 } 399 400 if (failed(parser.parseLParen())) 401 return failure(); 402 StringRef hintKeyword; 403 int64_t hint = 0; 404 do { 405 if (failed(parser.parseKeyword(&hintKeyword))) 406 return failure(); 407 if (hintKeyword == "uncontended") 408 hint |= 1; 409 else if (hintKeyword == "contended") 410 hint |= 2; 411 else if (hintKeyword == "nonspeculative") 412 hint |= 4; 413 else if (hintKeyword == "speculative") 414 hint |= 8; 415 else 416 return parser.emitError(parser.getCurrentLocation()) 417 << hintKeyword << " is not a valid hint"; 418 } while (succeeded(parser.parseOptionalComma())); 419 if (failed(parser.parseRParen())) 420 return failure(); 421 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint); 422 return success(); 423 } 424 425 /// Prints a Synchronization Hint clause 426 static void printSynchronizationHint(OpAsmPrinter &p, Operation *op, 427 IntegerAttr hintAttr) { 428 int64_t hint = hintAttr.getInt(); 429 430 if (hint == 0) 431 return; 432 433 // Helper function to get n-th bit from the right end of `value` 434 auto bitn = [](int value, int n) -> bool { return value & (1 << n); }; 435 436 bool uncontended = bitn(hint, 0); 437 bool contended = bitn(hint, 1); 438 bool nonspeculative = bitn(hint, 2); 439 bool speculative = bitn(hint, 3); 440 441 SmallVector<StringRef> hints; 442 if (uncontended) 443 hints.push_back("uncontended"); 444 if (contended) 445 hints.push_back("contended"); 446 if (nonspeculative) 447 hints.push_back("nonspeculative"); 448 if (speculative) 449 hints.push_back("speculative"); 450 451 p << "hint("; 452 llvm::interleaveComma(hints, p); 453 p << ")"; 454 } 455 456 /// Verifies a synchronization hint clause 457 static LogicalResult verifySynchronizationHint(Operation *op, int32_t hint) { 458 459 // Helper function to get n-th bit from the right end of `value` 460 auto bitn = [](int value, int n) -> bool { return value & (1 << n); }; 461 462 bool uncontended = bitn(hint, 0); 463 bool contended = bitn(hint, 1); 464 bool nonspeculative = bitn(hint, 2); 465 bool speculative = bitn(hint, 3); 466 467 if (uncontended && contended) 468 return op->emitOpError() << "the hints omp_sync_hint_uncontended and " 469 "omp_sync_hint_contended cannot be combined"; 470 if (nonspeculative && speculative) 471 return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and " 472 "omp_sync_hint_speculative cannot be combined."; 473 return success(); 474 } 475 476 enum ClauseType { 477 ifClause, 478 numThreadsClause, 479 privateClause, 480 firstprivateClause, 481 lastprivateClause, 482 sharedClause, 483 copyinClause, 484 allocateClause, 485 defaultClause, 486 procBindClause, 487 reductionClause, 488 nowaitClause, 489 linearClause, 490 scheduleClause, 491 collapseClause, 492 orderClause, 493 orderedClause, 494 inclusiveClause, 495 COUNT 496 }; 497 498 //===----------------------------------------------------------------------===// 499 // Parser for Clause List 500 //===----------------------------------------------------------------------===// 501 502 /// Parse a list of clauses. The clauses can appear in any order, but their 503 /// operand segment indices are in the same order that they are passed in the 504 /// `clauses` list. The operand segments are added over the prevSegments 505 506 /// clause-list ::= clause clause-list | empty 507 /// clause ::= if | num-threads | private | firstprivate | lastprivate | 508 /// shared | copyin | allocate | default | proc-bind | reduction | 509 /// nowait | linear | schedule | collapse | order | ordered | 510 /// inclusive 511 /// if ::= `if` `(` ssa-id-and-type `)` 512 /// num-threads ::= `num_threads` `(` ssa-id-and-type `)` 513 /// private ::= `private` operand-and-type-list 514 /// firstprivate ::= `firstprivate` operand-and-type-list 515 /// lastprivate ::= `lastprivate` operand-and-type-list 516 /// shared ::= `shared` operand-and-type-list 517 /// copyin ::= `copyin` operand-and-type-list 518 /// allocate ::= `allocate` `(` allocate-operand-list `)` 519 /// default ::= `default` `(` (`private` | `firstprivate` | `shared` | `none`) 520 /// proc-bind ::= `proc_bind` `(` (`master` | `close` | `spread`) `)` 521 /// reduction ::= `reduction` `(` reduction-entry-list `)` 522 /// nowait ::= `nowait` 523 /// linear ::= `linear` `(` linear-list `)` 524 /// schedule ::= `schedule` `(` sched-list `)` 525 /// collapse ::= `collapse` `(` ssa-id-and-type `)` 526 /// order ::= `order` `(` `concurrent` `)` 527 /// ordered ::= `ordered` `(` ssa-id-and-type `)` 528 /// inclusive ::= `inclusive` 529 /// 530 /// Note that each clause can only appear once in the clase-list. 531 static ParseResult parseClauses(OpAsmParser &parser, OperationState &result, 532 SmallVectorImpl<ClauseType> &clauses, 533 SmallVectorImpl<int> &segments) { 534 535 // Check done[clause] to see if it has been parsed already 536 llvm::BitVector done(ClauseType::COUNT, false); 537 538 // See pos[clause] to get position of clause in operand segments 539 SmallVector<int> pos(ClauseType::COUNT, -1); 540 541 // Stores the last parsed clause keyword 542 StringRef clauseKeyword; 543 StringRef opName = result.name.getStringRef(); 544 545 // Containers for storing operands, types and attributes for various clauses 546 std::pair<OpAsmParser::OperandType, Type> ifCond; 547 std::pair<OpAsmParser::OperandType, Type> numThreads; 548 549 SmallVector<OpAsmParser::OperandType> privates, firstprivates, lastprivates, 550 shareds, copyins; 551 SmallVector<Type> privateTypes, firstprivateTypes, lastprivateTypes, 552 sharedTypes, copyinTypes; 553 554 SmallVector<OpAsmParser::OperandType> allocates, allocators; 555 SmallVector<Type> allocateTypes, allocatorTypes; 556 557 SmallVector<SymbolRefAttr> reductionSymbols; 558 SmallVector<OpAsmParser::OperandType> reductionVars; 559 SmallVector<Type> reductionVarTypes; 560 561 SmallVector<OpAsmParser::OperandType> linears; 562 SmallVector<Type> linearTypes; 563 SmallVector<OpAsmParser::OperandType> linearSteps; 564 565 SmallString<8> schedule; 566 SmallVector<SmallString<12>> modifiers; 567 Optional<OpAsmParser::OperandType> scheduleChunkSize; 568 569 // Compute the position of clauses in operand segments 570 int currPos = 0; 571 for (ClauseType clause : clauses) { 572 573 // Skip the following clauses - they do not take any position in operand 574 // segments 575 if (clause == defaultClause || clause == procBindClause || 576 clause == nowaitClause || clause == collapseClause || 577 clause == orderClause || clause == orderedClause || 578 clause == inclusiveClause) 579 continue; 580 581 pos[clause] = currPos++; 582 583 // For the following clauses, two positions are reserved in the operand 584 // segments 585 if (clause == allocateClause || clause == linearClause) 586 currPos++; 587 } 588 589 SmallVector<int> clauseSegments(currPos); 590 591 // Helper function to check if a clause is allowed/repeated or not 592 auto checkAllowed = [&](ClauseType clause, 593 bool allowRepeat = false) -> ParseResult { 594 if (!llvm::is_contained(clauses, clause)) 595 return parser.emitError(parser.getCurrentLocation()) 596 << clauseKeyword << "is not a valid clause for the " << opName 597 << " operation"; 598 if (done[clause] && !allowRepeat) 599 return parser.emitError(parser.getCurrentLocation()) 600 << "at most one " << clauseKeyword << " clause can appear on the " 601 << opName << " operation"; 602 done[clause] = true; 603 return success(); 604 }; 605 606 while (succeeded(parser.parseOptionalKeyword(&clauseKeyword))) { 607 if (clauseKeyword == "if") { 608 if (checkAllowed(ifClause) || parser.parseLParen() || 609 parser.parseOperand(ifCond.first) || 610 parser.parseColonType(ifCond.second) || parser.parseRParen()) 611 return failure(); 612 clauseSegments[pos[ifClause]] = 1; 613 } else if (clauseKeyword == "num_threads") { 614 if (checkAllowed(numThreadsClause) || parser.parseLParen() || 615 parser.parseOperand(numThreads.first) || 616 parser.parseColonType(numThreads.second) || parser.parseRParen()) 617 return failure(); 618 clauseSegments[pos[numThreadsClause]] = 1; 619 } else if (clauseKeyword == "private") { 620 if (checkAllowed(privateClause) || 621 parseOperandAndTypeList(parser, privates, privateTypes)) 622 return failure(); 623 clauseSegments[pos[privateClause]] = privates.size(); 624 } else if (clauseKeyword == "firstprivate") { 625 if (checkAllowed(firstprivateClause) || 626 parseOperandAndTypeList(parser, firstprivates, firstprivateTypes)) 627 return failure(); 628 clauseSegments[pos[firstprivateClause]] = firstprivates.size(); 629 } else if (clauseKeyword == "lastprivate") { 630 if (checkAllowed(lastprivateClause) || 631 parseOperandAndTypeList(parser, lastprivates, lastprivateTypes)) 632 return failure(); 633 clauseSegments[pos[lastprivateClause]] = lastprivates.size(); 634 } else if (clauseKeyword == "shared") { 635 if (checkAllowed(sharedClause) || 636 parseOperandAndTypeList(parser, shareds, sharedTypes)) 637 return failure(); 638 clauseSegments[pos[sharedClause]] = shareds.size(); 639 } else if (clauseKeyword == "copyin") { 640 if (checkAllowed(copyinClause) || 641 parseOperandAndTypeList(parser, copyins, copyinTypes)) 642 return failure(); 643 clauseSegments[pos[copyinClause]] = copyins.size(); 644 } else if (clauseKeyword == "allocate") { 645 if (checkAllowed(allocateClause) || 646 parseAllocateAndAllocator(parser, allocates, allocateTypes, 647 allocators, allocatorTypes)) 648 return failure(); 649 clauseSegments[pos[allocateClause]] = allocates.size(); 650 clauseSegments[pos[allocateClause] + 1] = allocators.size(); 651 } else if (clauseKeyword == "default") { 652 StringRef defval; 653 if (checkAllowed(defaultClause) || parser.parseLParen() || 654 parser.parseKeyword(&defval) || parser.parseRParen()) 655 return failure(); 656 // The def prefix is required for the attribute as "private" is a keyword 657 // in C++. 658 auto attr = parser.getBuilder().getStringAttr("def" + defval); 659 result.addAttribute("default_val", attr); 660 } else if (clauseKeyword == "proc_bind") { 661 StringRef bind; 662 if (checkAllowed(procBindClause) || parser.parseLParen() || 663 parser.parseKeyword(&bind) || parser.parseRParen()) 664 return failure(); 665 auto attr = parser.getBuilder().getStringAttr(bind); 666 result.addAttribute("proc_bind_val", attr); 667 } else if (clauseKeyword == "reduction") { 668 if (checkAllowed(reductionClause) || 669 parseReductionVarList(parser, reductionSymbols, reductionVars, 670 reductionVarTypes)) 671 return failure(); 672 clauseSegments[pos[reductionClause]] = reductionVars.size(); 673 } else if (clauseKeyword == "nowait") { 674 if (checkAllowed(nowaitClause)) 675 return failure(); 676 auto attr = UnitAttr::get(parser.getBuilder().getContext()); 677 result.addAttribute("nowait", attr); 678 } else if (clauseKeyword == "linear") { 679 if (checkAllowed(linearClause) || 680 parseLinearClause(parser, linears, linearTypes, linearSteps)) 681 return failure(); 682 clauseSegments[pos[linearClause]] = linears.size(); 683 clauseSegments[pos[linearClause] + 1] = linearSteps.size(); 684 } else if (clauseKeyword == "schedule") { 685 if (checkAllowed(scheduleClause) || 686 parseScheduleClause(parser, schedule, modifiers, scheduleChunkSize)) 687 return failure(); 688 if (scheduleChunkSize) { 689 clauseSegments[pos[scheduleClause]] = 1; 690 } 691 } else if (clauseKeyword == "collapse") { 692 auto type = parser.getBuilder().getI64Type(); 693 mlir::IntegerAttr attr; 694 if (checkAllowed(collapseClause) || parser.parseLParen() || 695 parser.parseAttribute(attr, type) || parser.parseRParen()) 696 return failure(); 697 result.addAttribute("collapse_val", attr); 698 } else if (clauseKeyword == "ordered") { 699 mlir::IntegerAttr attr; 700 if (checkAllowed(orderedClause)) 701 return failure(); 702 if (succeeded(parser.parseOptionalLParen())) { 703 auto type = parser.getBuilder().getI64Type(); 704 if (parser.parseAttribute(attr, type) || parser.parseRParen()) 705 return failure(); 706 } else { 707 // Use 0 to represent no ordered parameter was specified 708 attr = parser.getBuilder().getI64IntegerAttr(0); 709 } 710 result.addAttribute("ordered_val", attr); 711 } else if (clauseKeyword == "order") { 712 StringRef order; 713 if (checkAllowed(orderClause) || parser.parseLParen() || 714 parser.parseKeyword(&order) || parser.parseRParen()) 715 return failure(); 716 auto attr = parser.getBuilder().getStringAttr(order); 717 result.addAttribute("order", attr); 718 } else if (clauseKeyword == "inclusive") { 719 if (checkAllowed(inclusiveClause)) 720 return failure(); 721 auto attr = UnitAttr::get(parser.getBuilder().getContext()); 722 result.addAttribute("inclusive", attr); 723 } else { 724 return parser.emitError(parser.getNameLoc()) 725 << clauseKeyword << " is not a valid clause"; 726 } 727 } 728 729 // Add if parameter. 730 if (done[ifClause] && clauseSegments[pos[ifClause]] && 731 failed( 732 parser.resolveOperand(ifCond.first, ifCond.second, result.operands))) 733 return failure(); 734 735 // Add num_threads parameter. 736 if (done[numThreadsClause] && clauseSegments[pos[numThreadsClause]] && 737 failed(parser.resolveOperand(numThreads.first, numThreads.second, 738 result.operands))) 739 return failure(); 740 741 // Add private parameters. 742 if (done[privateClause] && clauseSegments[pos[privateClause]] && 743 failed(parser.resolveOperands(privates, privateTypes, 744 privates[0].location, result.operands))) 745 return failure(); 746 747 // Add firstprivate parameters. 748 if (done[firstprivateClause] && clauseSegments[pos[firstprivateClause]] && 749 failed(parser.resolveOperands(firstprivates, firstprivateTypes, 750 firstprivates[0].location, 751 result.operands))) 752 return failure(); 753 754 // Add lastprivate parameters. 755 if (done[lastprivateClause] && clauseSegments[pos[lastprivateClause]] && 756 failed(parser.resolveOperands(lastprivates, lastprivateTypes, 757 lastprivates[0].location, result.operands))) 758 return failure(); 759 760 // Add shared parameters. 761 if (done[sharedClause] && clauseSegments[pos[sharedClause]] && 762 failed(parser.resolveOperands(shareds, sharedTypes, shareds[0].location, 763 result.operands))) 764 return failure(); 765 766 // Add copyin parameters. 767 if (done[copyinClause] && clauseSegments[pos[copyinClause]] && 768 failed(parser.resolveOperands(copyins, copyinTypes, copyins[0].location, 769 result.operands))) 770 return failure(); 771 772 // Add allocate parameters. 773 if (done[allocateClause] && clauseSegments[pos[allocateClause]] && 774 failed(parser.resolveOperands(allocates, allocateTypes, 775 allocates[0].location, result.operands))) 776 return failure(); 777 778 // Add allocator parameters. 779 if (done[allocateClause] && clauseSegments[pos[allocateClause] + 1] && 780 failed(parser.resolveOperands(allocators, allocatorTypes, 781 allocators[0].location, result.operands))) 782 return failure(); 783 784 // Add reduction parameters and symbols 785 if (done[reductionClause] && clauseSegments[pos[reductionClause]]) { 786 if (failed(parser.resolveOperands(reductionVars, reductionVarTypes, 787 parser.getNameLoc(), result.operands))) 788 return failure(); 789 790 SmallVector<Attribute> reductions(reductionSymbols.begin(), 791 reductionSymbols.end()); 792 result.addAttribute("reductions", 793 parser.getBuilder().getArrayAttr(reductions)); 794 } 795 796 // Add linear parameters 797 if (done[linearClause] && clauseSegments[pos[linearClause]]) { 798 auto linearStepType = parser.getBuilder().getI32Type(); 799 SmallVector<Type> linearStepTypes(linearSteps.size(), linearStepType); 800 if (failed(parser.resolveOperands(linears, linearTypes, linears[0].location, 801 result.operands)) || 802 failed(parser.resolveOperands(linearSteps, linearStepTypes, 803 linearSteps[0].location, 804 result.operands))) 805 return failure(); 806 } 807 808 // Add schedule parameters 809 if (done[scheduleClause] && !schedule.empty()) { 810 schedule[0] = llvm::toUpper(schedule[0]); 811 auto attr = parser.getBuilder().getStringAttr(schedule); 812 result.addAttribute("schedule_val", attr); 813 if (modifiers.size() > 0) { 814 auto mod = parser.getBuilder().getStringAttr(modifiers[0]); 815 result.addAttribute("schedule_modifier", mod); 816 } 817 if (scheduleChunkSize) { 818 auto chunkSizeType = parser.getBuilder().getI32Type(); 819 parser.resolveOperand(*scheduleChunkSize, chunkSizeType, result.operands); 820 } 821 } 822 823 segments.insert(segments.end(), clauseSegments.begin(), clauseSegments.end()); 824 825 return success(); 826 } 827 828 /// Parses a parallel operation. 829 /// 830 /// operation ::= `omp.parallel` clause-list 831 /// clause-list ::= clause | clause clause-list 832 /// clause ::= if | num-threads | private | firstprivate | shared | copyin | 833 /// allocate | default | proc-bind 834 /// 835 static ParseResult parseParallelOp(OpAsmParser &parser, 836 OperationState &result) { 837 SmallVector<ClauseType> clauses = { 838 ifClause, numThreadsClause, privateClause, 839 firstprivateClause, sharedClause, copyinClause, 840 allocateClause, defaultClause, procBindClause}; 841 842 SmallVector<int> segments; 843 844 if (failed(parseClauses(parser, result, clauses, segments))) 845 return failure(); 846 847 result.addAttribute("operand_segment_sizes", 848 parser.getBuilder().getI32VectorAttr(segments)); 849 850 Region *body = result.addRegion(); 851 SmallVector<OpAsmParser::OperandType> regionArgs; 852 SmallVector<Type> regionArgTypes; 853 if (parser.parseRegion(*body, regionArgs, regionArgTypes)) 854 return failure(); 855 return success(); 856 } 857 858 /// Parses an OpenMP Workshare Loop operation 859 /// 860 /// wsloop ::= `omp.wsloop` loop-control clause-list 861 /// loop-control ::= `(` ssa-id-list `)` `:` type `=` loop-bounds 862 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` steps 863 /// steps := `step` `(`ssa-id-list`)` 864 /// clause-list ::= clause clause-list | empty 865 /// clause ::= private | firstprivate | lastprivate | linear | schedule | 866 // collapse | nowait | ordered | order | inclusive | reduction 867 static ParseResult parseWsLoopOp(OpAsmParser &parser, OperationState &result) { 868 869 // Parse an opening `(` followed by induction variables followed by `)` 870 SmallVector<OpAsmParser::OperandType> ivs; 871 if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1, 872 OpAsmParser::Delimiter::Paren)) 873 return failure(); 874 875 int numIVs = static_cast<int>(ivs.size()); 876 Type loopVarType; 877 if (parser.parseColonType(loopVarType)) 878 return failure(); 879 880 // Parse loop bounds. 881 SmallVector<OpAsmParser::OperandType> lower; 882 if (parser.parseEqual() || 883 parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) || 884 parser.resolveOperands(lower, loopVarType, result.operands)) 885 return failure(); 886 887 SmallVector<OpAsmParser::OperandType> upper; 888 if (parser.parseKeyword("to") || 889 parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) || 890 parser.resolveOperands(upper, loopVarType, result.operands)) 891 return failure(); 892 893 // Parse step values. 894 SmallVector<OpAsmParser::OperandType> steps; 895 if (parser.parseKeyword("step") || 896 parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) || 897 parser.resolveOperands(steps, loopVarType, result.operands)) 898 return failure(); 899 900 SmallVector<ClauseType> clauses = { 901 privateClause, firstprivateClause, lastprivateClause, linearClause, 902 reductionClause, collapseClause, orderClause, orderedClause, 903 nowaitClause, scheduleClause}; 904 SmallVector<int> segments{numIVs, numIVs, numIVs}; 905 if (failed(parseClauses(parser, result, clauses, segments))) 906 return failure(); 907 908 result.addAttribute("operand_segment_sizes", 909 parser.getBuilder().getI32VectorAttr(segments)); 910 911 // Now parse the body. 912 Region *body = result.addRegion(); 913 SmallVector<Type> ivTypes(numIVs, loopVarType); 914 SmallVector<OpAsmParser::OperandType> blockArgs(ivs); 915 if (parser.parseRegion(*body, blockArgs, ivTypes)) 916 return failure(); 917 return success(); 918 } 919 920 static void printWsLoopOp(OpAsmPrinter &p, WsLoopOp op) { 921 auto args = op.getRegion().front().getArguments(); 922 p << " (" << args << ") : " << args[0].getType() << " = (" << op.lowerBound() 923 << ") to (" << op.upperBound() << ") step (" << op.step() << ") "; 924 925 printDataVars(p, op.private_vars(), "private"); 926 printDataVars(p, op.firstprivate_vars(), "firstprivate"); 927 printDataVars(p, op.lastprivate_vars(), "lastprivate"); 928 929 if (op.linear_vars().size()) { 930 p << "linear"; 931 printLinearClause(p, op.linear_vars(), op.linear_step_vars()); 932 } 933 934 if (auto sched = op.schedule_val()) { 935 p << "schedule"; 936 printScheduleClause(p, sched.getValue(), op.schedule_modifier(), 937 op.schedule_chunk_var()); 938 } 939 940 if (auto collapse = op.collapse_val()) 941 p << "collapse(" << collapse << ") "; 942 943 if (op.nowait()) 944 p << "nowait "; 945 946 if (auto ordered = op.ordered_val()) 947 p << "ordered(" << ordered << ") "; 948 949 if (!op.reduction_vars().empty()) { 950 p << "reduction("; 951 printReductionVarList(p, op.reductions(), op.reduction_vars()); 952 } 953 954 if (op.inclusive()) { 955 p << "inclusive "; 956 } 957 958 p.printRegion(op.region(), /*printEntryBlockArgs=*/false); 959 } 960 961 //===----------------------------------------------------------------------===// 962 // ReductionOp 963 //===----------------------------------------------------------------------===// 964 965 static ParseResult parseAtomicReductionRegion(OpAsmParser &parser, 966 Region ®ion) { 967 if (parser.parseOptionalKeyword("atomic")) 968 return success(); 969 return parser.parseRegion(region); 970 } 971 972 static void printAtomicReductionRegion(OpAsmPrinter &printer, 973 ReductionDeclareOp op, Region ®ion) { 974 if (region.empty()) 975 return; 976 printer << "atomic "; 977 printer.printRegion(region); 978 } 979 980 static LogicalResult verifyReductionDeclareOp(ReductionDeclareOp op) { 981 if (op.initializerRegion().empty()) 982 return op.emitOpError() << "expects non-empty initializer region"; 983 Block &initializerEntryBlock = op.initializerRegion().front(); 984 if (initializerEntryBlock.getNumArguments() != 1 || 985 initializerEntryBlock.getArgument(0).getType() != op.type()) { 986 return op.emitOpError() << "expects initializer region with one argument " 987 "of the reduction type"; 988 } 989 990 for (YieldOp yieldOp : op.initializerRegion().getOps<YieldOp>()) { 991 if (yieldOp.results().size() != 1 || 992 yieldOp.results().getTypes()[0] != op.type()) 993 return op.emitOpError() << "expects initializer region to yield a value " 994 "of the reduction type"; 995 } 996 997 if (op.reductionRegion().empty()) 998 return op.emitOpError() << "expects non-empty reduction region"; 999 Block &reductionEntryBlock = op.reductionRegion().front(); 1000 if (reductionEntryBlock.getNumArguments() != 2 || 1001 reductionEntryBlock.getArgumentTypes()[0] != 1002 reductionEntryBlock.getArgumentTypes()[1] || 1003 reductionEntryBlock.getArgumentTypes()[0] != op.type()) 1004 return op.emitOpError() << "expects reduction region with two arguments of " 1005 "the reduction type"; 1006 for (YieldOp yieldOp : op.reductionRegion().getOps<YieldOp>()) { 1007 if (yieldOp.results().size() != 1 || 1008 yieldOp.results().getTypes()[0] != op.type()) 1009 return op.emitOpError() << "expects reduction region to yield a value " 1010 "of the reduction type"; 1011 } 1012 1013 if (op.atomicReductionRegion().empty()) 1014 return success(); 1015 1016 Block &atomicReductionEntryBlock = op.atomicReductionRegion().front(); 1017 if (atomicReductionEntryBlock.getNumArguments() != 2 || 1018 atomicReductionEntryBlock.getArgumentTypes()[0] != 1019 atomicReductionEntryBlock.getArgumentTypes()[1]) 1020 return op.emitOpError() << "expects atomic reduction region with two " 1021 "arguments of the same type"; 1022 auto ptrType = atomicReductionEntryBlock.getArgumentTypes()[0] 1023 .dyn_cast<PointerLikeType>(); 1024 if (!ptrType || ptrType.getElementType() != op.type()) 1025 return op.emitOpError() << "expects atomic reduction region arguments to " 1026 "be accumulators containing the reduction type"; 1027 return success(); 1028 } 1029 1030 static LogicalResult verifyReductionOp(ReductionOp op) { 1031 // TODO: generalize this to an op interface when there is more than one op 1032 // that supports reductions. 1033 auto container = op->getParentOfType<WsLoopOp>(); 1034 for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i) 1035 if (container.reduction_vars()[i] == op.accumulator()) 1036 return success(); 1037 1038 return op.emitOpError() << "the accumulator is not used by the parent"; 1039 } 1040 1041 //===----------------------------------------------------------------------===// 1042 // WsLoopOp 1043 //===----------------------------------------------------------------------===// 1044 1045 void WsLoopOp::build(OpBuilder &builder, OperationState &state, 1046 ValueRange lowerBound, ValueRange upperBound, 1047 ValueRange step, ArrayRef<NamedAttribute> attributes) { 1048 build(builder, state, TypeRange(), lowerBound, upperBound, step, 1049 /*private_vars=*/ValueRange(), 1050 /*firstprivate_vars=*/ValueRange(), /*lastprivate_vars=*/ValueRange(), 1051 /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(), 1052 /*reduction_vars=*/ValueRange(), /*schedule_val=*/nullptr, 1053 /*schedule_chunk_var=*/nullptr, /*collapse_val=*/nullptr, 1054 /*nowait=*/nullptr, /*ordered_val=*/nullptr, /*order_val=*/nullptr, 1055 /*inclusive=*/nullptr, /*buildBody=*/false); 1056 state.addAttributes(attributes); 1057 } 1058 1059 void WsLoopOp::build(OpBuilder &, OperationState &state, TypeRange resultTypes, 1060 ValueRange operands, ArrayRef<NamedAttribute> attributes) { 1061 state.addOperands(operands); 1062 state.addAttributes(attributes); 1063 (void)state.addRegion(); 1064 assert(resultTypes.empty() && "mismatched number of return types"); 1065 state.addTypes(resultTypes); 1066 } 1067 1068 void WsLoopOp::build(OpBuilder &builder, OperationState &result, 1069 TypeRange typeRange, ValueRange lowerBounds, 1070 ValueRange upperBounds, ValueRange steps, 1071 ValueRange privateVars, ValueRange firstprivateVars, 1072 ValueRange lastprivateVars, ValueRange linearVars, 1073 ValueRange linearStepVars, ValueRange reductionVars, 1074 StringAttr scheduleVal, Value scheduleChunkVar, 1075 IntegerAttr collapseVal, UnitAttr nowait, 1076 IntegerAttr orderedVal, StringAttr orderVal, 1077 UnitAttr inclusive, bool buildBody) { 1078 result.addOperands(lowerBounds); 1079 result.addOperands(upperBounds); 1080 result.addOperands(steps); 1081 result.addOperands(privateVars); 1082 result.addOperands(firstprivateVars); 1083 result.addOperands(linearVars); 1084 result.addOperands(linearStepVars); 1085 if (scheduleChunkVar) 1086 result.addOperands(scheduleChunkVar); 1087 1088 if (scheduleVal) 1089 result.addAttribute("schedule_val", scheduleVal); 1090 if (collapseVal) 1091 result.addAttribute("collapse_val", collapseVal); 1092 if (nowait) 1093 result.addAttribute("nowait", nowait); 1094 if (orderedVal) 1095 result.addAttribute("ordered_val", orderedVal); 1096 if (orderVal) 1097 result.addAttribute("order", orderVal); 1098 if (inclusive) 1099 result.addAttribute("inclusive", inclusive); 1100 result.addAttribute( 1101 WsLoopOp::getOperandSegmentSizeAttr(), 1102 builder.getI32VectorAttr( 1103 {static_cast<int32_t>(lowerBounds.size()), 1104 static_cast<int32_t>(upperBounds.size()), 1105 static_cast<int32_t>(steps.size()), 1106 static_cast<int32_t>(privateVars.size()), 1107 static_cast<int32_t>(firstprivateVars.size()), 1108 static_cast<int32_t>(lastprivateVars.size()), 1109 static_cast<int32_t>(linearVars.size()), 1110 static_cast<int32_t>(linearStepVars.size()), 1111 static_cast<int32_t>(reductionVars.size()), 1112 static_cast<int32_t>(scheduleChunkVar != nullptr ? 1 : 0)})); 1113 1114 Region *bodyRegion = result.addRegion(); 1115 if (buildBody) { 1116 OpBuilder::InsertionGuard guard(builder); 1117 unsigned numIVs = steps.size(); 1118 SmallVector<Type, 8> argTypes(numIVs, steps.getType().front()); 1119 builder.createBlock(bodyRegion, {}, argTypes); 1120 } 1121 } 1122 1123 static LogicalResult verifyWsLoopOp(WsLoopOp op) { 1124 return verifyReductionVarList(op, op.reductions(), op.reduction_vars()); 1125 } 1126 1127 //===----------------------------------------------------------------------===// 1128 // Verifier for critical construct (2.17.1) 1129 //===----------------------------------------------------------------------===// 1130 1131 static LogicalResult verifyCriticalDeclareOp(CriticalDeclareOp op) { 1132 return verifySynchronizationHint(op, op.hint()); 1133 } 1134 1135 static LogicalResult verifyCriticalOp(CriticalOp op) { 1136 1137 if (op.nameAttr()) { 1138 auto symbolRef = op.nameAttr().cast<SymbolRefAttr>(); 1139 auto decl = 1140 SymbolTable::lookupNearestSymbolFrom<CriticalDeclareOp>(op, symbolRef); 1141 if (!decl) { 1142 return op.emitOpError() << "expected symbol reference " << symbolRef 1143 << " to point to a critical declaration"; 1144 } 1145 } 1146 1147 return success(); 1148 } 1149 1150 //===----------------------------------------------------------------------===// 1151 // Verifier for ordered construct 1152 //===----------------------------------------------------------------------===// 1153 1154 static LogicalResult verifyOrderedOp(OrderedOp op) { 1155 auto container = op->getParentOfType<WsLoopOp>(); 1156 if (!container || !container.ordered_valAttr() || 1157 container.ordered_valAttr().getInt() == 0) 1158 return op.emitOpError() << "ordered depend directive must be closely " 1159 << "nested inside a worksharing-loop with ordered " 1160 << "clause with parameter present"; 1161 1162 if (container.ordered_valAttr().getInt() != 1163 (int64_t)op.num_loops_val().getValue()) 1164 return op.emitOpError() << "number of variables in depend clause does not " 1165 << "match number of iteration variables in the " 1166 << "doacross loop"; 1167 1168 return success(); 1169 } 1170 1171 static LogicalResult verifyOrderedRegionOp(OrderedRegionOp op) { 1172 // TODO: The code generation for ordered simd directive is not supported yet. 1173 if (op.simd()) 1174 return failure(); 1175 1176 if (auto container = op->getParentOfType<WsLoopOp>()) { 1177 if (!container.ordered_valAttr() || 1178 container.ordered_valAttr().getInt() != 0) 1179 return op.emitOpError() << "ordered region must be closely nested inside " 1180 << "a worksharing-loop region with an ordered " 1181 << "clause without parameter present"; 1182 } 1183 1184 return success(); 1185 } 1186 1187 #define GET_OP_CLASSES 1188 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc" 1189