[MLIR] [OpenMP] Add basic OpenMP parallel operation

Summary:
This includes a basic implementation for the OpenMP parallel
operation without a custom pretty-printer and parser.
The if, num_threads, private, shared, first_private, last_private,
proc_bind and default clauses are included in this implementation.

Currently the reduction clause is omitted as it is more complex and
requires analysis to see if we can share implementation with the loop
dialect. The allocate clause is also omitted.

A discussion about the design of this operation can be found here:
https://llvm.discourse.group/t/openmp-parallel-operation-design-issues/686

The current OpenMP Specification can be found here:
https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5.0.pdf

Co-authored-by: Kiran Chandramohan <kiran.chandramohan@arm.com>

Reviewers: jdoerfert

Subscribers: mgorny, yaxunl, kristof.beyls, guansong, mehdi_amini, rriddle, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, grosul1, frgossen, Kayjukh, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D79410
This commit is contained in:
David Truby
2020-05-05 13:04:32 +01:00
parent 1af3705c7f
commit 5ba874e472
5 changed files with 159 additions and 15 deletions

View File

@@ -1,2 +1,8 @@
add_mlir_dialect(OpenMPOps omp)
set(LLVM_TARGET_DEFINITIONS OpenMPOps.td)
mlir_tablegen(OpenMPOpsDialect.h.inc -gen-dialect-decls -dialect=omp)
mlir_tablegen(OpenMPOps.h.inc -gen-op-decls)
mlir_tablegen(OpenMPOps.cpp.inc -gen-op-defs)
mlir_tablegen(OpenMPOpsEnums.h.inc -gen-enum-decls)
mlir_tablegen(OpenMPOpsEnums.cpp.inc -gen-enum-defs)
add_mlir_doc(OpenMPOps -gen-dialect-doc OpenMPDialect Dialects/)
add_public_tablegen_target(MLIROpenMPOpsIncGen)

View File

@@ -16,6 +16,8 @@
#include "mlir/IR/Dialect.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.h.inc"
namespace mlir {
namespace omp {

View File

@@ -18,16 +18,103 @@ include "mlir/IR/OpBase.td"
def OpenMP_Dialect : Dialect {
let name = "omp";
let cppNamespace = "omp";
}
class OpenMP_Op<string mnemonic, list<OpTrait> traits = []> :
Op<OpenMP_Dialect, mnemonic, traits>;
def BarrierOp : OpenMP_Op<"barrier"> {
let summary = "barrier construct";
//===----------------------------------------------------------------------===//
// 2.6 parallel Construct
//===----------------------------------------------------------------------===//
// Possible values for the default clause
def ClauseDefaultPrivate : StrEnumAttrCase<"defprivate">;
def ClauseDefaultFirstPrivate : StrEnumAttrCase<"deffirstprivate">;
def ClauseDefaultShared : StrEnumAttrCase<"defshared">;
def ClauseDefaultNone : StrEnumAttrCase<"defnone">;
def ClauseDefault : StrEnumAttr<
"ClauseDefault",
"default clause",
[ClauseDefaultPrivate, ClauseDefaultFirstPrivate, ClauseDefaultShared,
ClauseDefaultNone]> {
let cppNamespace = "::mlir::omp";
}
// Possible values for the proc_bind clause
def ClauseProcMaster : StrEnumAttrCase<"master">;
def ClauseProcClose : StrEnumAttrCase<"close">;
def ClauseProcSpread : StrEnumAttrCase<"spread">;
def ClauseProcBind : StrEnumAttr<
"ClauseProcBind",
"procbind clause",
[ClauseProcMaster, ClauseProcClose, ClauseProcSpread]> {
let cppNamespace = "::mlir::omp";
}
def ParallelOp : OpenMP_Op<"parallel", [AttrSizedOperandSegments]> {
let summary = "parallel construct";
let description = [{
The barrier construct specifies an explicit barrier at the point at which
the construct appears.
The parallel construct includes a region of code which is to be executed
by a team of threads.
The optional $if_expr_var parameter specifies a boolean result of a
conditional check. If this value is 1 or is not provided then the parallel
region runs as normal, if it is 0 then the parallel region is executed with
one thread.
The optional $num_threads_var parameter specifies the number of threads which
should be used to execute the parallel region.
The optional $default_val attribute specifies the default data sharing attribute
of variables used in the parallel region that are not passed explicitly as parameters
to the operation.
The $private_vars, $firstprivate_vars, $shared_vars and $copyin_vars parameters
are a variadic list of variables that specify the data sharing attribute of
those variables.
The optional $proc_bind_val attribute controls the thread affinity for the execution
of the parallel region.
}];
let arguments = (ins Optional<I1>:$if_expr_var,
Optional<AnyInteger>:$num_threads_var,
OptionalAttr<ClauseDefault>:$default_val,
Variadic<AnyType>:$private_vars,
Variadic<AnyType>:$firstprivate_vars,
Variadic<AnyType>:$shared_vars,
Variadic<AnyType>:$copyin_vars,
OptionalAttr<ClauseProcBind>:$proc_bind_val);
let regions = (region AnyRegion:$region);
}
def TerminatorOp : OpenMP_Op<"terminator", [Terminator]> {
let summary = "terminator for OpenMP regions.";
let description = [{
A terminator operation for regions that appear in the body of OpenMP
operation. These regions are not expected to return any value so the
terminator takes no operands. The terminator op returns control to the
enclosing op.
}];
let parser = [{ return success(); }];
let printer = [{ p << getOperationName(); }];
}
//===----------------------------------------------------------------------===//
// 2.10.4 taskyield Construct
//===----------------------------------------------------------------------===//
def TaskyieldOp : OpenMP_Op<"taskyield"> {
let summary = "taskyield construct";
let description = [{
The taskyield construct specifies that the current task can be suspended
in favor of execution of a different task.
}];
let assemblyFormat = "attr-dict";
@@ -50,6 +137,24 @@ def FlushOp : OpenMP_Op<"flush"> {
let assemblyFormat = "attr-dict ($varList^ `:` type($varList))?";
}
//===----------------------------------------------------------------------===//
// 2.17.2 barrier Construct
//===----------------------------------------------------------------------===//
def BarrierOp : OpenMP_Op<"barrier"> {
let summary = "barrier construct";
let description = [{
The barrier construct specifies an explicit barrier at the point at which
the construct appears.
}];
let assemblyFormat = "attr-dict";
}
//===----------------------------------------------------------------------===//
// 2.17.5 taskwait Construct
//===----------------------------------------------------------------------===//
def TaskwaitOp : OpenMP_Op<"taskwait"> {
let summary = "taskwait construct";
let description = [{
@@ -60,14 +165,4 @@ def TaskwaitOp : OpenMP_Op<"taskwait"> {
let assemblyFormat = "attr-dict";
}
def TaskyieldOp : OpenMP_Op<"taskyield"> {
let summary = "taskyield construct";
let description = [{
The taskyield construct specifies that the current task can be suspended
in favor of execution of a different task.
}];
let assemblyFormat = "attr-dict";
}
#endif // OPENMP_OPS

View File

@@ -11,8 +11,13 @@
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/OpImplementation.h"
#include "llvm/ADT/StringSwitch.h"
#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc"
using namespace mlir;
using namespace mlir::omp;

View File

@@ -35,3 +35,39 @@ func @omp_flush(%arg0 : !llvm.i32) -> () {
return
}
func @omp_terminator() -> () {
// CHECK: omp.terminator
omp.terminator
}
func @omp_parallel(%data_var : memref<i32>, %if_cond : i1, %num_threads : si32) -> () {
// CHECK: omp_parallel
"omp.parallel" (%if_cond, %num_threads, %data_var, %data_var, %data_var, %data_var) ({
// test without if condition
// CHECK: omp.parallel
"omp.parallel"(%num_threads, %data_var, %data_var, %data_var, %data_var) ({
omp.terminator
}) {operand_segment_sizes = dense<[0,1,1,1,1,1]>: vector<6xi32>, default_val = "defshared"} : (si32, memref<i32>, memref<i32>, memref<i32>, memref<i32>) -> ()
// CHECK: omp.barrier
omp.barrier
// test without num_threads
// CHECK: omp.parallel
"omp.parallel"(%if_cond, %data_var, %data_var, %data_var, %data_var) ({
omp.terminator
}) {operand_segment_sizes = dense<[1,0,1,1,1,1]> : vector<6xi32>} : (i1, memref<i32>, memref<i32>, memref<i32>, memref<i32>) -> ()
omp.terminator
}) {operand_segment_sizes = dense<[1,1,1,1,1,1]> : vector<6xi32>, proc_bind_val = "spread"} : (i1, si32, memref<i32>, memref<i32>, memref<i32>, memref<i32>) -> ()
// test with multiple parameters for single variadic argument
// CHECK: omp.parallel
"omp.parallel" (%data_var, %data_var, %data_var, %data_var, %data_var) ({
omp.terminator
}) {operand_segment_sizes = dense<[0,0,1,2,1,1]> : vector<6xi32>} : (memref<i32>, memref<i32>, memref<i32>, memref<i32>, memref<i32>) -> ()
return
}