diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPInterfaces.h b/mlir/include/mlir/Dialect/OpenMP/OpenMPInterfaces.h index b3184db885216..787c48b05c5c5 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPInterfaces.h +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPInterfaces.h @@ -21,6 +21,9 @@ #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/SideEffectInterfaces.h" +#define GET_OP_FWD_DEFINES +#include "mlir/Dialect/OpenMP/OpenMPOps.h.inc" + #include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.h.inc" namespace mlir::omp { diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td index 0ddbd01938bbb..cf3bc9683cc89 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -235,7 +235,8 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove]> { //===----------------------------------------------------------------------===// def ParallelOp : OpenMP_Op<"parallel", [ - AutomaticAllocationScope, AttrSizedOperandSegments, + AttrSizedOperandSegments, AutomaticAllocationScope, + DeclareOpInterfaceMethods, DeclareOpInterfaceMethods, RecursiveMemoryEffects, ReductionClauseInterface]> { let summary = "parallel construct"; @@ -278,17 +279,20 @@ def ParallelOp : OpenMP_Op<"parallel", [ OptionalAttr:$reductions, OptionalAttr:$proc_bind_val, Variadic:$private_vars, - OptionalAttr:$privatizers); + OptionalAttr:$privatizers, + UnitAttr:$composite); let regions = (region AnyRegion:$region); let builders = [ OpBuilder<(ins CArg<"ArrayRef", "{}">:$attributes)> ]; + let extraClassDeclaration = [{ /// Returns the number of reduction variables. unsigned getNumReductionVars() { return getReductionVars().size(); } }]; + let assemblyFormat = [{ oilist( `if` `(` $if_expr_var `:` type($if_expr_var) `)` @@ -299,10 +303,12 @@ def ParallelOp : OpenMP_Op<"parallel", [ $allocators_vars, type($allocators_vars) ) `)` | `proc_bind` `(` custom($proc_bind_val) `)` + | `composite` $composite ) custom($region, $reduction_vars, type($reduction_vars), $reductions, $private_vars, type($private_vars), $privatizers) attr-dict }]; + let hasVerifier = 1; } @@ -526,7 +532,7 @@ def LoopNestOp : OpenMP_Op<"loopnest", [SameVariadicOperandSize, attribute is specified then the upper bound is also included. The body region can contain any number of blocks. The region is terminated - by "omp.yield" instruction without operands. The induction variables, + by `omp.yield` instruction without operands. The induction variables, represented as entry block arguments to the loop nest operation's single region, match the types of the `lowerBound`, `upperBound` and `step` arguments. @@ -560,6 +566,10 @@ def LoopNestOp : OpenMP_Op<"loopnest", [SameVariadicOperandSize, /// Returns the induction variables of the loop nest. ArrayRef getIVs() { return getRegion().getArguments(); } + + /// Returns the list of wrapper operations around this loop nest. Wrappers + /// in the resulting vector will be ordered from innermost to outermost. + llvm::SmallVector getWrappers(); }]; let hasCustomAssemblyFormat = 1; @@ -571,28 +581,29 @@ def LoopNestOp : OpenMP_Op<"loopnest", [SameVariadicOperandSize, //===----------------------------------------------------------------------===// def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, - AllTypesMatch<["lowerBound", "upperBound", "step"]>, - RecursiveMemoryEffects, ReductionClauseInterface]> { + DeclareOpInterfaceMethods, + RecursiveMemoryEffects, ReductionClauseInterface, + SingleBlockImplicitTerminator<"TerminatorOp">]> { let summary = "worksharing-loop construct"; let description = [{ The worksharing-loop construct specifies that the iterations of the loop(s) will be executed in parallel by threads in the current context. These iterations are spread across threads that already exist in the enclosing - parallel region. The lower and upper bounds specify a half-open range: the - range includes the lower bound but does not include the upper bound. If the - `inclusive` attribute is specified then the upper bound is also included. + parallel region. - The body region can contain any number of blocks. The region is terminated - by "omp.yield" instruction without operands. + The body region can contain a single block which must contain a single + operation and a terminator. The operation must be another compatible loop + wrapper or an `omp.loopnest`. ``` - omp.wsloop - for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { - %a = load %arrA[%i1, %i2] : memref - %b = load %arrB[%i1, %i2] : memref - %sum = arith.addf %a, %b : f32 - store %sum, %arrC[%i1, %i2] : memref - omp.yield + omp.wsloop { + omp.loopnest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } } ``` @@ -616,10 +627,6 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, The optional `schedule_chunk_var` associated with this determines further controls this distribution. - Collapsed loops are represented by the worksharing-loop having a list of - indices, bounds and steps where the size of the list is equal to the - collapse value. - The `nowait` attribute, when present, signifies that there should be no implicit barrier at the end of the loop. @@ -632,10 +639,7 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, attribute is "concurrent". }]; - let arguments = (ins Variadic:$lowerBound, - Variadic:$upperBound, - Variadic:$step, - Variadic:$linear_vars, + let arguments = (ins Variadic:$linear_vars, Variadic:$linear_step_vars, Variadic:$reduction_vars, OptionalAttr:$reductions, @@ -646,24 +650,21 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, UnitAttr:$nowait, ConfinedAttr, [IntMinValue<0>]>:$ordered_val, OptionalAttr:$order_val, - UnitAttr:$inclusive); + UnitAttr:$composite); + + let regions = (region AnyRegion:$region); let builders = [ - OpBuilder<(ins "ValueRange":$lowerBound, "ValueRange":$upperBound, - "ValueRange":$step, - CArg<"ArrayRef", "{}">:$attributes)>, + OpBuilder<(ins CArg<"ArrayRef", "{}">:$attributes)> ]; - let regions = (region AnyRegion:$region); - let extraClassDeclaration = [{ - /// Returns the number of loops in the worksharing-loop nest. - unsigned getNumLoops() { return getLowerBound().size(); } - /// Returns the number of reduction variables. unsigned getNumReductionVars() { return getReductionVars().size(); } }]; + let hasCustomAssemblyFormat = 1; + let assemblyFormat = [{ oilist(`linear` `(` custom($linear_vars, type($linear_vars), @@ -675,10 +676,11 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, |`nowait` $nowait |`ordered` `(` $ordered_val `)` |`order` `(` custom($order_val) `)` - ) custom($region, $lowerBound, $upperBound, $step, - type($step), $reduction_vars, type($reduction_vars), $reductions, - $inclusive) attr-dict + |`composite` $composite + ) custom($region, $reduction_vars, type($reduction_vars), + $reductions) attr-dict }]; + let hasVerifier = 1; } @@ -687,21 +689,20 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, //===----------------------------------------------------------------------===// def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments, - AllTypesMatch<["lowerBound", "upperBound", "step"]>]> { + DeclareOpInterfaceMethods, + RecursiveMemoryEffects, + SingleBlockImplicitTerminator<"TerminatorOp">]> { let summary = "simd loop construct"; let description = [{ The simd construct can be applied to a loop to indicate that the loop can be transformed into a SIMD loop (that is, multiple iterations of the loop can - be executed concurrently using SIMD instructions).. The lower and upper + be executed concurrently using SIMD instructions). The lower and upper bounds specify a half-open range: the range includes the lower bound but does not include the upper bound. If the `inclusive` attribute is specified then the upper bound is also included. - The body region can contain any number of blocks. The region is terminated - by "omp.yield" instruction without operands. - - Collapsed loops are represented by the simd-loop having a list of indices, - bounds and steps where the size of the list is equal to the collapse value. + The body region can contain a single block which must contain a single + `omp.loopnest` operation and a terminator. The `alignment_values` attribute additionally specifies alignment of each corresponding aligned operand. Note that `$aligned_vars` and @@ -725,29 +726,35 @@ def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments, SIMD chunk can have a distance in the logical iteration space that is greater than or equal to the value given in the clause. ``` - omp.simdloop - for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { - // block operations - omp.yield + omp.simdloop { + omp.loopnest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } } ``` }]; // TODO: Add other clauses - let arguments = (ins Variadic:$lowerBound, - Variadic:$upperBound, - Variadic:$step, - Variadic:$aligned_vars, + let arguments = (ins Variadic:$aligned_vars, OptionalAttr:$alignment_values, Optional:$if_expr, Variadic:$nontemporal_vars, OptionalAttr:$order_val, ConfinedAttr, [IntPositive]>:$simdlen, ConfinedAttr, [IntPositive]>:$safelen, - UnitAttr:$inclusive + UnitAttr:$composite ); let regions = (region AnyRegion:$region); + + let builders = [ + OpBuilder<(ins CArg<"ArrayRef", "{}">:$attributes)> + ]; + let assemblyFormat = [{ oilist(`aligned` `(` custom($aligned_vars, type($aligned_vars), @@ -757,14 +764,8 @@ def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments, |`order` `(` custom($order_val) `)` |`simdlen` `(` $simdlen `)` |`safelen` `(` $safelen `)` - ) `for` custom($region, $lowerBound, $upperBound, $step, - type($step), $inclusive) attr-dict - }]; - - let extraClassDeclaration = [{ - /// Returns the number of loops in the simd loop nest. - unsigned getNumLoops() { return getLowerBound().size(); } - + |`composite` $composite + ) $region attr-dict }]; let hasCustomAssemblyFormat = 1; @@ -774,8 +775,8 @@ def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments, def YieldOp : OpenMP_Op<"yield", [Pure, ReturnLike, Terminator, - ParentOneOf<["LoopNestOp", "WsLoopOp", "ReductionDeclareOp", - "AtomicUpdateOp", "SimdLoopOp", "PrivateClauseOp"]>]> { + ParentOneOf<["LoopNestOp", "ReductionDeclareOp", "AtomicUpdateOp", + "PrivateClauseOp"]>]> { let summary = "loop yield and termination operation"; let description = [{ "omp.yield" yields SSA values from the OpenMP dialect op region and @@ -795,8 +796,11 @@ def YieldOp : OpenMP_Op<"yield", //===----------------------------------------------------------------------===// // Distribute construct [2.9.4.1] //===----------------------------------------------------------------------===// + def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments, - MemoryEffects<[MemWrite]>]> { + DeclareOpInterfaceMethods, + RecursiveMemoryEffects, + SingleBlockImplicitTerminator<"TerminatorOp">]> { let summary = "distribute construct"; let description = [{ The distribute construct specifies that the iterations of one or more loops @@ -811,15 +815,28 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments, The distribute loop construct specifies that the iterations of the loop(s) will be executed in parallel by threads in the current context. These iterations are spread across threads that already exist in the enclosing - region. The lower and upper bounds specify a half-open range: the - range includes the lower bound but does not include the upper bound. If the - `inclusive` attribute is specified then the upper bound is also included. + region. + + The body region can contain a single block which must contain a single + operation and a terminator. The operation must be another compatible loop + wrapper or an `omp.loopnest`. The `dist_schedule_static` attribute specifies the schedule for this loop, determining how the loop is distributed across the parallel threads. The optional `schedule_chunk` associated with this determines further controls this distribution. + ```mlir + omp.distribute { + omp.loopnest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } + } + ``` // TODO: private_var, firstprivate_var, lastprivate_var, collapse }]; let arguments = (ins @@ -827,10 +844,15 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments, Optional:$chunk_size, Variadic:$allocate_vars, Variadic:$allocators_vars, - OptionalAttr:$order_val); + OptionalAttr:$order_val, + UnitAttr:$composite); let regions = (region AnyRegion:$region); + let builders = [ + OpBuilder<(ins CArg<"ArrayRef", "{}">:$attributes)> + ]; + let assemblyFormat = [{ oilist(`dist_schedule_static` $dist_schedule_static |`chunk_size` `(` $chunk_size `:` type($chunk_size) `)` @@ -840,6 +862,7 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments, $allocate_vars, type($allocate_vars), $allocators_vars, type($allocators_vars) ) `)` + |`composite` $composite ) $region attr-dict }]; @@ -965,9 +988,9 @@ def TaskOp : OpenMP_Op<"task", [AttrSizedOperandSegments, } def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments, - AutomaticAllocationScope, RecursiveMemoryEffects, - AllTypesMatch<["lowerBound", "upperBound", "step"]>, - ReductionClauseInterface]> { + DeclareOpInterfaceMethods, + RecursiveMemoryEffects, ReductionClauseInterface, + SingleBlockImplicitTerminator<"TerminatorOp">]> { let summary = "taskloop construct"; let description = [{ The taskloop construct specifies that the iterations of one or more @@ -975,21 +998,19 @@ def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments, iterations are distributed across tasks generated by the construct and scheduled to be executed. - The `lowerBound` and `upperBound` specify a half-open range: the range - includes the lower bound but does not include the upper bound. If the - `inclusive` attribute is specified then the upper bound is also included. - The `step` specifies the loop step. - - The body region can contain any number of blocks. + The body region can contain a single block which must contain a single + operation and a terminator. The operation must be another compatible loop + wrapper or an `omp.loopnest`. ``` - omp.taskloop - for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { - %a = load %arrA[%i1, %i2] : memref - %b = load %arrB[%i1, %i2] : memref - %sum = arith.addf %a, %b : f32 - store %sum, %arrC[%i1, %i2] : memref - omp.terminator + omp.taskloop { + omp.loopnest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } } ``` @@ -1066,11 +1087,7 @@ def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments, created. }]; - let arguments = (ins Variadic:$lowerBound, - Variadic:$upperBound, - Variadic:$step, - UnitAttr:$inclusive, - Optional:$if_expr, + let arguments = (ins Optional:$if_expr, Optional:$final_expr, UnitAttr:$untied, UnitAttr:$mergeable, @@ -1081,12 +1098,23 @@ def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments, Optional:$priority, Variadic:$allocate_vars, Variadic:$allocators_vars, - Optional: $grain_size, - Optional: $num_tasks, - UnitAttr: $nogroup); + Optional:$grain_size, + Optional:$num_tasks, + UnitAttr:$nogroup, + UnitAttr:$composite); let regions = (region AnyRegion:$region); + let builders = [ + OpBuilder<(ins CArg<"ArrayRef", "{}">:$attributes)> + ]; + + let extraClassDeclaration = [{ + /// Returns the reduction variables + SmallVector getAllReductionVars(); + void getEffects(SmallVectorImpl &effects); + }]; + let assemblyFormat = [{ oilist(`if` `(` $if_expr `)` |`final` `(` $final_expr `)` @@ -1109,14 +1137,8 @@ def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments, |`grain_size` `(` $grain_size `:` type($grain_size) `)` |`num_tasks` `(` $num_tasks `:` type($num_tasks) `)` |`nogroup` $nogroup - ) `for` custom($region, $lowerBound, $upperBound, $step, - type($step), $inclusive) attr-dict - }]; - - let extraClassDeclaration = [{ - /// Returns the reduction variables - SmallVector getAllReductionVars(); - void getEffects(SmallVectorImpl &effects); + |`composite` $composite + ) $region attr-dict }]; let hasVerifier = 1; @@ -1396,11 +1418,11 @@ def MapInfoOp : OpenMP_Op<"map_info", [AttrSizedOperandSegments]> { - `var_ptr`: The address of variable to copy. - `var_type`: The type of the variable to copy. - `var_ptr_ptr`: Used when the variable copied is a member of a class, structure - or derived type and refers to the originating struct. - - `members`: Used to indicate mapped child members for the current MapInfoOp, - represented as other MapInfoOp's, utilised in cases where a parent structure - type and members of the structure type are being mapped at the same time. - For example: map(to: parent, parent->member, parent->member2[:10]) + or derived type and refers to the originating struct. + - `members`: Used to indicate mapped child members for the current MapInfoOp, + represented as other MapInfoOp's, utilised in cases where a parent structure + type and members of the structure type are being mapped at the same time. + For example: map(to: parent, parent->member, parent->member2[:10]) - `bounds`: Used when copying slices of array's, pointers or pointer members of objects (e.g. derived types or classes), indicates the bounds to be copied of the variable. When it's an array slice it is in rank order where rank 0 @@ -1441,7 +1463,7 @@ def MapInfoOp : OpenMP_Op<"map_info", [AttrSizedOperandSegments]> { // 2.14.2 target data Construct //===---------------------------------------------------------------------===// -def Target_DataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments, +def Target_DataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments, MapClauseOwningOpInterface]>{ let summary = "target data construct"; let description = [{ @@ -1499,7 +1521,7 @@ def Target_DataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments, //===---------------------------------------------------------------------===// def Target_EnterDataOp: OpenMP_Op<"target_enter_data", - [AttrSizedOperandSegments, + [AttrSizedOperandSegments, MapClauseOwningOpInterface]>{ let summary = "target enter data construct"; let description = [{ diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td index ed086d36424c1..45b0ac5df0abf 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td @@ -69,6 +69,79 @@ def ReductionClauseInterface : OpInterface<"ReductionClauseInterface"> { ]; } +// TODO Test these methods +def LoopWrapperInterface : OpInterface<"LoopWrapperInterface"> { + let description = [{ + OpenMP operations that can wrap a single loop nest. When taking a wrapper + role, these operations must only contain a single region with a single block + in which there's a single operation and a terminator. That nested operation + must be another loop wrapper or an `omp.loopnest`. + }]; + + let cppNamespace = "::mlir::omp"; + + let methods = [ + InterfaceMethod< + /*description=*/[{ + Tell whether the operation could be taking the role of a loop wrapper. + That is, it has a single region with a single block in which there are + two operations: another wrapper or loop nest operation and a terminator. + }], + /*retTy=*/"bool", + /*methodName=*/"isWrapper", + (ins ), [{}], [{ + if ($_op->getNumRegions() != 1) + return false; + + ::mlir::Region &r = $_op->getRegion(0); + if (!r.hasOneBlock()) + return false; + + if (std::distance(r.op_begin(), r.op_end()) != 2) + return false; + + ::mlir::Operation &firstOp = *r.op_begin(); + ::mlir::Operation &secondOp = *(++r.op_begin()); + return ::llvm::isa<::mlir::omp::LoopNestOp, + ::mlir::omp::LoopWrapperInterface>(firstOp) && + secondOp.hasTrait<::mlir::OpTrait::IsTerminator>(); + }] + >, + InterfaceMethod< + /*description=*/[{ + If this is a loop wrapper and there is another wrapper immediately + nested inside, return that operation. + }], + /*retTy=*/"::mlir::omp::LoopWrapperInterface", + /*methodName=*/"getNestedWrapper", + (ins), [{}], [{ + if (!$_op.isWrapper()) + return nullptr; + + ::mlir::Operation *nested = &*$_op->getRegion(0).op_begin(); + return ::llvm::dyn_cast<::mlir::omp::LoopWrapperInterface>(nested); + }] + >, + InterfaceMethod< + /*description=*/[{ + If this is a loop wrapper, return the loop nest nested directly or + indirectly inside. + }], + /*retTy=*/"::mlir::Operation *", + /*methodName=*/"getWrappedLoop", + (ins), [{}], [{ + if (!$_op.isWrapper()) + return nullptr; + + if (::mlir::omp::LoopWrapperInterface nested = $_op.getNestedWrapper()) + return nested.getWrappedLoop(); + + return &*$_op->getRegion(0).op_begin(); + }] + > + ]; +} + def DeclareTargetInterface : OpInterface<"DeclareTargetInterface"> { let description = [{ OpenMP operations that support declare target have this interface. diff --git a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp index e72746fa91947..83eb1653ca950 100644 --- a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp +++ b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp @@ -249,12 +249,12 @@ void mlir::configureOpenMPToLLVMConversionLegality( return typeConverter.isLegal(op->getOperandTypes()); }); target.addDynamicallyLegalOp< - mlir::omp::AtomicUpdateOp, mlir::omp::CriticalOp, mlir::omp::TargetOp, - mlir::omp::TeamsOp, mlir::omp::DistributeOp, mlir::omp::DataOp, - mlir::omp::OrderedRegionOp, mlir::omp::ParallelOp, mlir::omp::WsLoopOp, - mlir::omp::SimdLoopOp, mlir::omp::MasterOp, mlir::omp::SectionOp, - mlir::omp::SectionsOp, mlir::omp::SingleOp, mlir::omp::TaskGroupOp, - mlir::omp::TaskOp, mlir::omp::ReductionDeclareOp, + mlir::omp::AtomicUpdateOp, mlir::omp::LoopNestOp, mlir::omp::CriticalOp, + mlir::omp::TargetOp, mlir::omp::TeamsOp, mlir::omp::DistributeOp, + mlir::omp::DataOp, mlir::omp::OrderedRegionOp, mlir::omp::ParallelOp, + mlir::omp::WsLoopOp, mlir::omp::SimdLoopOp, mlir::omp::MasterOp, + mlir::omp::SectionOp, mlir::omp::SectionsOp, mlir::omp::SingleOp, + mlir::omp::TaskGroupOp, mlir::omp::TaskOp, mlir::omp::ReductionDeclareOp, mlir::omp::PrivateClauseOp>([&](Operation *op) { return std::all_of(op->getRegions().begin(), op->getRegions().end(), [&](Region ®ion) { @@ -285,6 +285,7 @@ void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter, RegionOpConversion, RegionOpConversion, RegionOpConversion, RegionOpConversion, RegionOpConversion, RegionOpConversion, + RegionOpConversion, RegionLessOpWithVarOperandsConversion, RegionOpWithVarOperandsConversion, RegionLessOpWithVarOperandsConversion, diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp index b2f57399b2754..c7130e8e2c9f3 100644 --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -1312,10 +1312,14 @@ Operation *TargetOp::getInnermostCapturedOmpOp() { bool TargetOp::isTargetSPMDLoop() { Operation *capturedOp = getInnermostCapturedOmpOp(); - if (!isa_and_present(capturedOp)) + if (!isa_and_present(capturedOp)) return false; - Operation *parallelOp = capturedOp->getParentOp(); + Operation *wsloopOp = capturedOp->getParentOp(); + if (!isa_and_present(wsloopOp)) + return false; + + Operation *parallelOp = wsloopOp->getParentOp(); if (!isa_and_present(parallelOp)) return false; @@ -1391,6 +1395,11 @@ static LogicalResult verifyPrivateVarList(OpType &op) { } LogicalResult ParallelOp::verify() { + if (getComposite()) { + // TODO Check if wrapper: Single omp.wsloop + } + + // This can be checked after adding a 'composite' attribute. if (getAllocateVars().size() != getAllocatorsVars().size()) return emitError( "expected equal sizes for allocate and allocator variables"); @@ -1558,87 +1567,48 @@ LogicalResult LoopNestOp::verify() { return success(); } +SmallVector LoopNestOp::getWrappers() { + SmallVector wrappers; + Operation *parent = (*this)->getParentOp(); + while (auto wrapper = + llvm::dyn_cast_if_present(parent)) { + wrappers.push_back(wrapper); + parent = parent->getParentOp(); + } + return wrappers; +} + //===----------------------------------------------------------------------===// // WsLoopOp //===----------------------------------------------------------------------===// -/// loop-control ::= `(` ssa-id-list `)` `:` type `=` loop-bounds -/// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` inclusive? steps -/// steps := `step` `(`ssa-id-list`)` ParseResult parseWsLoop(OpAsmParser &parser, Region ®ion, - SmallVectorImpl &lowerBound, - SmallVectorImpl &upperBound, - SmallVectorImpl &steps, - SmallVectorImpl &loopVarTypes, SmallVectorImpl &reductionOperands, - SmallVectorImpl &reductionTypes, ArrayAttr &reductionSymbols, - UnitAttr &inclusive) { + SmallVectorImpl &reductionTypes, + ArrayAttr &reductionSymbols) { // Parse an optional reduction clause llvm::SmallVector privates; - bool hasReduction = succeeded(parser.parseOptionalKeyword("reduction")) && - succeeded(parseClauseWithRegionArgs( - parser, region, reductionOperands, reductionTypes, - reductionSymbols, privates)); - - if (parser.parseKeyword("for")) - return failure(); - - // Parse an opening `(` followed by induction variables followed by `)` - SmallVector ivs; - Type loopVarType; - if (parser.parseArgumentList(ivs, OpAsmParser::Delimiter::Paren) || - parser.parseColonType(loopVarType) || - // Parse loop bounds. - parser.parseEqual() || - parser.parseOperandList(lowerBound, ivs.size(), - OpAsmParser::Delimiter::Paren) || - parser.parseKeyword("to") || - parser.parseOperandList(upperBound, ivs.size(), - OpAsmParser::Delimiter::Paren)) - return failure(); - - if (succeeded(parser.parseOptionalKeyword("inclusive"))) - inclusive = UnitAttr::get(parser.getBuilder().getContext()); - - // Parse step values. - if (parser.parseKeyword("step") || - parser.parseOperandList(steps, ivs.size(), OpAsmParser::Delimiter::Paren)) - return failure(); - - // Now parse the body. - loopVarTypes = SmallVector(ivs.size(), loopVarType); - for (auto &iv : ivs) - iv.type = loopVarType; - - SmallVector regionArgs{ivs}; - if (hasReduction) - llvm::copy(privates, std::back_inserter(regionArgs)); + if (succeeded(parser.parseOptionalKeyword("reduction"))) { + if (failed(parseClauseWithRegionArgs(parser, region, reductionOperands, + reductionTypes, reductionSymbols, + privates))) + return failure(); + } - return parser.parseRegion(region, regionArgs); + return parser.parseRegion(region, privates); } void printWsLoop(OpAsmPrinter &p, Operation *op, Region ®ion, - ValueRange lowerBound, ValueRange upperBound, ValueRange steps, - TypeRange loopVarTypes, ValueRange reductionOperands, - TypeRange reductionTypes, ArrayAttr reductionSymbols, - UnitAttr inclusive) { + ValueRange reductionOperands, TypeRange reductionTypes, + ArrayAttr reductionSymbols) { if (reductionSymbols) { - auto reductionArgs = - region.front().getArguments().drop_front(loopVarTypes.size()); + auto reductionArgs = region.front().getArguments(); printClauseWithRegionArgs(p, op, reductionArgs, "reduction", reductionOperands, reductionTypes, reductionSymbols); } - - p << " for "; - auto args = region.front().getArguments().drop_back(reductionOperands.size()); - p << " (" << args << ") : " << args[0].getType() << " = (" << lowerBound - << ") to (" << upperBound << ") "; - if (inclusive) - p << "inclusive "; - p << "step (" << steps << ") "; p.printRegion(region, /*printEntryBlockArgs=*/false); } @@ -1695,13 +1665,20 @@ void printLoopControl(OpAsmPrinter &p, Operation *op, Region ®ion, } //===----------------------------------------------------------------------===// -// Verifier for Simd construct [2.9.3.1] +// Simd construct [2.9.3.1] //===----------------------------------------------------------------------===// +void SimdLoopOp::build(OpBuilder &builder, OperationState &state, + ArrayRef attributes) { + build(builder, state, /*aligned_vars=*/ValueRange(), + /*alignment_values=*/nullptr, /*if_expr=*/nullptr, + /*nontemporal_vars=*/ValueRange(), /*order_val=*/nullptr, + /*simdlen=*/nullptr, /*safelen=*/nullptr, + /*composite=*/false); + state.addAttributes(attributes); +} + LogicalResult SimdLoopOp::verify() { - if (this->getLowerBound().empty()) { - return emitOpError() << "empty lowerbound for simd loop operation"; - } if (this->getSimdlen().has_value() && this->getSafelen().has_value() && this->getSimdlen().value() > this->getSafelen().value()) { return emitOpError() @@ -1714,13 +1691,31 @@ LogicalResult SimdLoopOp::verify() { return failure(); if (verifyNontemporalClause(*this, this->getNontemporalVars()).failed()) return failure(); + + if (!this->isWrapper()) + return emitOpError() << "must be a loop wrapper"; + + if (!isa(this->getRegion().front().front())) + return emitOpError() << "must wrap an 'omp.loopnest' directly"; + + // TODO If composite, must have compatible wrapper parent (taskloop do + // distribute). Otherwise, no wrapper. return success(); } //===----------------------------------------------------------------------===// -// Verifier for Distribute construct [2.9.4.1] +// Distribute construct [2.9.4.1] //===----------------------------------------------------------------------===// +void DistributeOp::build(OpBuilder &builder, OperationState &state, + ArrayRef attributes) { + build(builder, state, /*dist_schedule_static=*/false, + /*chunk_size=*/nullptr, /*allocate_vars=*/ValueRange(), + /*allocators_vars=*/ValueRange(), /*order_val=*/nullptr, + /*composite=*/false); + state.addAttributes(attributes); +} + LogicalResult DistributeOp::verify() { if (this->getChunkSize() && !this->getDistScheduleStatic()) return emitOpError() << "chunk size set without " @@ -1730,6 +1725,17 @@ LogicalResult DistributeOp::verify() { return emitError( "expected equal sizes for allocate and allocator variables"); + if (!this->isWrapper()) + return emitOpError() << "must be a loop wrapper"; + + // TODO If has a nested omp.parallel, check that both are composite. + // TODO Accept simdloop and parallel if composite. loopnest if not. + // TODO Check there's no wrapper parent. + if (!isa( + this->getRegion().front().front())) + return emitOpError() + << "must wrap an 'omp.loopnest', 'omp.simd' or 'omp.parallel'"; + return success(); } @@ -1849,6 +1855,21 @@ SmallVector TaskLoopOp::getAllReductionVars() { return allReductionNvars; } +void TaskLoopOp::build(OpBuilder &builder, OperationState &state, + ArrayRef attributes) { + build(builder, state, + /*if_expr=*/nullptr, /*final_expr=*/nullptr, /*untied=*/false, + /*mergeable=*/false, + /*in_reduction_vars=*/ValueRange(), /*in_reductions=*/nullptr, + /*reduction_vars=*/ValueRange(), + /*reductions=*/nullptr, /*priority=*/nullptr, + /*allocate_vars=*/ValueRange(), + /*allocators_vars=*/ValueRange(), /*grain_size=*/nullptr, + /*num_tasks=*/nullptr, + /*nogroup=*/false, /*composite=*/false); + state.addAttributes(attributes); +} + LogicalResult TaskLoopOp::verify() { if (getAllocateVars().size() != getAllocatorsVars().size()) return emitError( @@ -1873,6 +1894,15 @@ LogicalResult TaskLoopOp::verify() { "the grainsize clause and num_tasks clause are mutually exclusive and " "may not appear on the same taskloop directive"); } + + if (!this->isWrapper()) + return emitOpError() << "must be a loop wrapper"; + + // TODO Accept simd if composite, loopnest if not + // TODO Check that it's not wrapped + if (!isa(this->getRegion().front().front())) + return emitOpError() << "must wrap an 'omp.loopnest' or 'omp.simd'"; + return success(); } @@ -1881,19 +1911,28 @@ LogicalResult TaskLoopOp::verify() { //===----------------------------------------------------------------------===// void WsLoopOp::build(OpBuilder &builder, OperationState &state, - ValueRange lowerBound, ValueRange upperBound, - ValueRange step, ArrayRef attributes) { - build(builder, state, lowerBound, upperBound, step, - /*linear_vars=*/ValueRange(), + ArrayRef attributes) { + build(builder, state, /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(), /*reduction_vars=*/ValueRange(), /*reductions=*/nullptr, /*schedule_val=*/nullptr, /*schedule_chunk_var=*/nullptr, /*schedule_modifier=*/nullptr, /*simd_modifier=*/false, /*nowait=*/false, /*ordered_val=*/nullptr, - /*order_val=*/nullptr, /*inclusive=*/false); + /*order_val=*/nullptr, /*composite=*/false); state.addAttributes(attributes); } LogicalResult WsLoopOp::verify() { + if (!isWrapper()) + return emitOpError() << "must be a loop wrapper"; + + // TODO Do not accept loopnest if composite. Do not accept simd if not + // composite. + if (!isa(this->getRegion().front().front())) + return emitOpError() << "must wrap an 'omp.loopnest' or 'omp.simd'"; + + // TODO If composite, must have composite parallel parent or simd and no + // wrapper parent. Otherwise, no composite parent. + return verifyReductionVarList(*this, getReductions(), getReductionVars()); }