diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -148,6 +148,68 @@ let cppNamespace = "::mlir::omp"; } +//===----------------------------------------------------------------------===// +// 2.8.1 Sections Construct +//===----------------------------------------------------------------------===// + +def SectionOp : OpenMP_Op<"section", [HasParent<"SectionsOp">]> { + let summary = "section directive"; + let description = [{ + A section operation encloses a region which represents one section in a + sections construct. A section op should always be surrounded by an + `omp.sections` operation. + }]; + let regions = (region AnyRegion:$region); + let assemblyFormat = "$region attr-dict"; +} + +def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments]> { + let summary = "sections construct"; + let description = [{ + The sections construct is a non-iterative worksharing construct that + contains `omp.section` operations. The `omp.section` operations are to be + distributed among and executed by the threads in a team. Each `omp.section` + is executed once by one of the threads in the team in the context of its + implicit task. + + `private_vars`, `firstprivate_vars` and`lastprivate_vars` arguments are + variadic list of operands that specify the data sharing attributes of the + list of values. They are optional. + + Reductions can be performed in a sections construct by specifying reduction + accumulator variables in `reduction_vars` and symbols referring to reduction + declarations in the `reductions` attribute. Each reduction is identified + by the accumulator it uses and accumulators must not be repeated in the same + reduction. The `omp.reduction` operation accepts the accumulator and a + partial value which is considered to be produced by the section for the + given reduction. If multiple values are produced for the same accumulator, + i.e. there are multiple `omp.reduction`s, the last value is taken. The + reduction declaration specifies how to combine the values from each section + into the final value, which is available in the accumulator after all the + sections complete. + + The $allocators_vars and $allocate_vars parameters are a variadic list of values + that specify the memory allocator to be used to obtain storage for private values. + + The `nowait` attribute, when present, signifies that there should be no + implicit barrier at the end of the construct. + }]; + let arguments = (ins Variadic:$private_vars, + Variadic:$firstprivate_vars, + Variadic:$lastprivate_vars, + Variadic:$reduction_vars, + OptionalAttr:$reductions, + Variadic:$allocate_vars, + Variadic:$allocators_vars, + UnitAttr:$nowait); + + let regions = (region SizedRegion<1>:$region); + + let parser = [{ return parseSectionsOp(parser, result); }]; + let printer = [{ return printSectionsOp(p, *this); }]; + let verifier = [{ return verifySectionsOp(*this); }]; +} + //===----------------------------------------------------------------------===// // 2.9.2 Workshare Loop Construct //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -152,9 +152,6 @@ static void printAllocateAndAllocator(OpAsmPrinter &p, OperandRange varsAllocate, OperandRange varsAllocator) { - if (varsAllocate.empty()) - return; - p << "allocate("; for (unsigned i = 0; i < varsAllocate.size(); ++i) { std::string separator = i == varsAllocate.size() - 1 ? ") " : ", "; @@ -182,7 +179,9 @@ printDataVars(p, op.firstprivate_vars(), "firstprivate"); printDataVars(p, op.shared_vars(), "shared"); printDataVars(p, op.copyin_vars(), "copyin"); - printAllocateAndAllocator(p, op.allocate_vars(), op.allocators_vars()); + + if (!op.allocate_vars().empty()) + printAllocateAndAllocator(p, op.allocate_vars(), op.allocators_vars()); if (auto def = op.default_val()) p << "default(" << def->drop_front(3) << ") "; @@ -231,7 +230,7 @@ static void printLinearClause(OpAsmPrinter &p, OperandRange linearVars, OperandRange linearStepVars) { size_t linearVarsSize = linearVars.size(); - p << "("; + p << "linear("; for (unsigned i = 0; i < linearVarsSize; ++i) { std::string separator = i == linearVarsSize - 1 ? ") " : ", "; p << linearVars[i]; @@ -296,7 +295,7 @@ llvm::Optional modifier, Value scheduleChunkVar) { std::string schedLower = sched.lower(); - p << "(" << schedLower; + p << "schedule(" << schedLower; if (scheduleChunkVar) p << " = " << scheduleChunkVar; if (modifier && modifier.getValue() != "none") @@ -333,6 +332,7 @@ static void printReductionVarList(OpAsmPrinter &p, Optional reductions, OperandRange reduction_vars) { + p << "reduction("; for (unsigned i = 0, e = reductions->size(); i < e; ++i) { if (i != 0) p << ", "; @@ -864,6 +864,83 @@ return success(); } +//===----------------------------------------------------------------------===// +// Parser, printer and verifier for SectionsOp +//===----------------------------------------------------------------------===// + +/// Parses an OpenMP Sections operation +/// +/// sections ::= `omp.sections` clause-list +/// clause-list ::= clause clause-list | empty +/// clause ::= private | firstprivate | lastprivate | reduction | allocate | +/// nowait +static ParseResult parseSectionsOp(OpAsmParser &parser, + OperationState &result) { + + SmallVector clauses = {privateClause, firstprivateClause, + lastprivateClause, reductionClause, + allocateClause, nowaitClause}; + + SmallVector segments; + + if (failed(parseClauses(parser, result, clauses, segments))) + return failure(); + + result.addAttribute("operand_segment_sizes", + parser.getBuilder().getI32VectorAttr(segments)); + + // Now parse the body. + Region *body = result.addRegion(); + if (parser.parseRegion(*body)) + return failure(); + return success(); +} + +static void printSectionsOp(OpAsmPrinter &p, SectionsOp op) { + p << " "; + printDataVars(p, op.private_vars(), "private"); + printDataVars(p, op.firstprivate_vars(), "firstprivate"); + printDataVars(p, op.lastprivate_vars(), "lastprivate"); + + if (!op.reduction_vars().empty()) + printReductionVarList(p, op.reductions(), op.reduction_vars()); + + if (!op.allocate_vars().empty()) + printAllocateAndAllocator(p, op.allocate_vars(), op.allocators_vars()); + + if (op.nowait()) + p << "nowait "; + + p.printRegion(op.region()); +} + +static LogicalResult verifySectionsOp(SectionsOp op) { + + // A list item may not appear in more than one clause on the same directive, + // except that it may be specified in both firstprivate and lastprivate + // clauses. + for (auto var : op.private_vars()) { + if (llvm::is_contained(op.firstprivate_vars(), var)) + return op.emitOpError() + << "operand used in both private and firstprivate clauses"; + if (llvm::is_contained(op.lastprivate_vars(), var)) + return op.emitOpError() + << "operand used in both private and lastprivate clauses"; + } + + if (op.allocate_vars().size() != op.allocators_vars().size()) + return op.emitError( + "expected equal sizes for allocate and allocator variables"); + + for (auto &inst : *op.region().begin()) { + if (!(isa(inst) || isa(inst))) + op.emitOpError() + << "expected omp.section op or terminator op inside region"; + } + + return verifyReductionVarList(op, op.reductions(), op.reduction_vars()); +} + /// Parses an OpenMP Workshare Loop operation /// /// wsloop ::= `omp.wsloop` loop-control clause-list @@ -944,16 +1021,12 @@ printDataVars(p, op.firstprivate_vars(), "firstprivate"); printDataVars(p, op.lastprivate_vars(), "lastprivate"); - if (op.linear_vars().size()) { - p << "linear"; + if (op.linear_vars().size()) printLinearClause(p, op.linear_vars(), op.linear_step_vars()); - } - if (auto sched = op.schedule_val()) { - p << "schedule"; + if (auto sched = op.schedule_val()) printScheduleClause(p, sched.getValue(), op.schedule_modifier(), op.schedule_chunk_var()); - } if (auto collapse = op.collapse_val()) p << "collapse(" << collapse << ") "; @@ -967,10 +1040,8 @@ if (auto order = op.order_val()) p << "order(" << order << ") "; - if (!op.reduction_vars().empty()) { - p << "reduction("; + if (!op.reduction_vars().empty()) printReductionVarList(p, op.reductions(), op.reduction_vars()); - } p.printRegion(op.region(), /*printEntryBlockArgs=*/false); } diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -598,3 +598,188 @@ omp.atomic.write %addr, %val memory_order(xyz) : memref, i32 return } + +// ----- + +func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { + // expected-error @below {{operand used in both private and firstprivate clauses}} + omp.sections private(%data_var1 : memref) firstprivate(%data_var1 : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { + // expected-error @below {{operand used in both private and lastprivate clauses}} + omp.sections private(%data_var1 : memref) lastprivate(%data_var1 : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { + // expected-error @below {{operand used in both private and lastprivate clauses}} + omp.sections private(%data_var1 : memref, %data_var2 : memref) lastprivate(%data_var3 : memref, %data_var2 : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%data_var : memref) -> () { + // expected-error @below {{expected equal sizes for allocate and allocator variables}} + "omp.sections" (%data_var) ({ + omp.terminator + }) {operand_segment_sizes = dense<[0,0,0,0,1,0]> : vector<6xi32>} : (memref) -> () + return +} + +// ----- + +func @omp_sections(%data_var : memref) -> () { + // expected-error @below {{expected as many reduction symbol references as reduction variables}} + "omp.sections" (%data_var) ({ + omp.terminator + }) {operand_segment_sizes = dense<[0,0,0,1,0,0]> : vector<6xi32>} : (memref) -> () + return +} + +// ----- + +func @omp_sections(%data_var : memref) -> () { + // expected-error @below {{expected omp.section op or terminator op inside region}} + omp.sections { + "test.payload" () : () -> () + } + return +} + +// ----- + +func @omp_sections(%cond : i1) { + // expected-error @below {{if is not a valid clause for the omp.sections operation}} + omp.sections if(%cond) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{num_threads is not a valid clause for the omp.sections operation}} + omp.sections num_threads(10) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%datavar : memref) { + // expected-error @below {{shared is not a valid clause for the omp.sections operation}} + omp.sections shared(%datavar : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%datavar : memref) { + // expected-error @below {{copyin is not a valid clause for the omp.sections operation}} + omp.sections copyin(%datavar : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{default is not a valid clause for the omp.sections operation}} + omp.sections default(private) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{proc_bind is not a valid clause for the omp.sections operation}} + omp.sections proc_bind(close) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections(%data_var : memref, %linear_var : i32) { + // expected-error @below {{linear is not a valid clause for the omp.sections operation}} + omp.sections linear(%data_var = %linear_var : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{schedule is not a valid clause for the omp.sections operation}} + omp.sections schedule(static, none) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{collapse is not a valid clause for the omp.sections operation}} + omp.sections collapse(3) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{ordered is not a valid clause for the omp.sections operation}} + omp.sections ordered(2) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{order is not a valid clause for the omp.sections operation}} + omp.sections order(concurrent) { + omp.terminator + } + return +} + +// ----- + +func @omp_sections() { + // expected-error @below {{failed to verify constraint: region with 1 blocks}} + omp.sections { + omp.section { + omp.terminator + } + omp.terminator + ^bb2: + omp.terminator + } + return +} diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir --- a/mlir/test/Dialect/OpenMP/ops.mlir +++ b/mlir/test/Dialect/OpenMP/ops.mlir @@ -523,3 +523,124 @@ omp.atomic.write %addr, %val hint(speculative, uncontended) : memref, i32 return } + +// CHECK-LABEL: omp_sectionsop +func @omp_sectionsop(%data_var1 : memref, %data_var2 : memref, + %data_var3 : memref, %redn_var : !llvm.ptr) { + + // CHECK: omp.sections private(%{{.*}} : memref) { + "omp.sections" (%data_var1) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[1,0,0,0,0,0]> : vector<6xi32>} : (memref) -> () + + // CHECK: omp.sections firstprivate(%{{.*}} : memref) { + "omp.sections" (%data_var1) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[0,1,0,0,0,0]> : vector<6xi32>} : (memref) -> () + + // CHECK: omp.sections lastprivate(%{{.*}} : memref) { + "omp.sections" (%data_var1) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[0,0,1,0,0,0]> : vector<6xi32>} : (memref) -> () + + // CHECK: omp.sections private(%{{.*}} : memref) firstprivate(%{{.*}} : memref) lastprivate(%{{.*}} : memref) { + "omp.sections" (%data_var1, %data_var2, %data_var3) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[1,1,1,0,0,0]> : vector<6xi32>} : (memref, memref, memref) -> () + + // CHECK: omp.sections allocate(%{{.*}} : memref -> %{{.*}} : memref) + "omp.sections" (%data_var1, %data_var1) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[0,0,0,0,1,1]> : vector<6xi32>} : (memref, memref) -> () + + // CHECK: omp.sections reduction(@add_f32 -> %{{.*}} : !llvm.ptr) + "omp.sections" (%redn_var) ({ + // CHECK: omp.terminator + omp.terminator + }) {operand_segment_sizes = dense<[0,0,0,1,0,0]> : vector<6xi32>, reductions=[@add_f32]} : (!llvm.ptr) -> () + + // CHECK: omp.sections private(%{{.*}} : memref) { + omp.sections private(%data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections firstprivate(%{{.*}} : memref) + omp.sections firstprivate(%data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections lastprivate(%{{.*}} : memref) + omp.sections lastprivate(%data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections private(%{{.*}} : memref) firstprivate(%{{.*}} : memref) lastprivate(%{{.*}} : memref) { + omp.sections private(%data_var1 : memref) firstprivate(%data_var2 : memref) lastprivate(%data_var3 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections private(%{{.*}} : memref) firstprivate(%{{.*}} : memref) lastprivate(%{{.*}} : memref) { + omp.sections lastprivate(%data_var1 : memref) firstprivate(%data_var2 : memref) private(%data_var3 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections private(%{{.*}} : memref) nowait { + omp.sections nowait private(%data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections firstprivate(%{{.*}} : memref, %{{.*}} : memref) lastprivate(%{{.*}} : memref) { + omp.sections firstprivate(%data_var1 : memref, %data_var2 : memref) lastprivate(%data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections reduction(@add_f32 -> %{{.*}} : !llvm.ptr) { + omp.sections reduction(@add_f32 -> %redn_var : !llvm.ptr) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections allocate(%{{.*}} : memref -> %{{.*}} : memref) + omp.sections allocate(%data_var1 : memref -> %data_var1 : memref) { + // CHECK: omp.terminator + omp.terminator + } + + // CHECK: omp.sections nowait + omp.sections nowait { + // CHECK: omp.section + omp.section { + // CHECK: %{{.*}} = "test.payload"() : () -> i32 + %1 = "test.payload"() : () -> i32 + // CHECK: %{{.*}} = "test.payload"() : () -> i32 + %2 = "test.payload"() : () -> i32 + // CHECK: %{{.*}} = "test.payload"(%{{.*}}, %{{.*}}) : (i32, i32) -> i32 + %3 = "test.payload"(%1, %2) : (i32, i32) -> i32 + } + // CHECK: omp.section + omp.section { + // CHECK: %{{.*}} = "test.payload"(%{{.*}}) : (!llvm.ptr) -> i32 + %1 = "test.payload"(%redn_var) : (!llvm.ptr) -> i32 + } + // CHECK: omp.section + omp.section { + // CHECK: "test.payload"(%{{.*}}) : (!llvm.ptr) -> () + "test.payload"(%redn_var) : (!llvm.ptr) -> () + } + // CHECK: omp.terminator + omp.terminator + } + return +}