[mlir][OpenMP] Added omp.atomic.capture operation

This patch supports the atomic construct (capture) following section 2.17.7 of OpenMP 5.0 standard. Also added tests for the same.

Reviewed By: peixin, kiranchandramohan

Differential Revision: https://reviews.llvm.org/D115851
This commit is contained in:
Shraiysh Vaishay 2022-01-24 18:42:39 +05:30
parent f6984b299a
commit 320dc8c4df
4 changed files with 258 additions and 0 deletions

View File

@ -708,6 +708,50 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update"> {
let verifier = [{ return verifyAtomicUpdateOp(*this); }];
}
def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
[SingleBlockImplicitTerminator<"TerminatorOp">]> {
let summary = "performs an atomic capture";
let description = [{
This operation performs an atomic capture.
`hint` is the value of hint (as used in the hint clause). It is a compile
time constant. As the name suggests, this is just a hint for optimization.
`memory_order` indicates the memory ordering behavior of the construct. It
can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`.
The region has the following allowed forms:
```
omp.atomic.capture {
omp.atomic.update ...
omp.atomic.read ...
omp.terminator
}
omp.atomic.capture {
omp.atomic.read ...
omp.atomic.update ...
omp.terminator
}
omp.atomic.capture {
omp.atomic.read ...
omp.atomic.write ...
omp.terminator
}
```
}];
let arguments = (ins DefaultValuedAttr<I64Attr, "0">:$hint,
OptionalAttr<MemoryOrderKind>:$memory_order);
let regions = (region SizedRegion<1>:$region);
let parser = [{ return parseAtomicCaptureOp(parser, result); }];
let printer = [{ return printAtomicCaptureOp(p, *this); }];
let verifier = [{ return verifyAtomicCaptureOp(*this); }];
}
//===----------------------------------------------------------------------===//
// 2.19.5.7 declare reduction Directive
//===----------------------------------------------------------------------===//

View File

@ -1539,6 +1539,68 @@ static LogicalResult verifyAtomicUpdateOp(AtomicUpdateOp op) {
return success();
}
//===----------------------------------------------------------------------===//
// AtomicCaptureOp
//===----------------------------------------------------------------------===//
/// Parser for AtomicCaptureOp
static LogicalResult parseAtomicCaptureOp(OpAsmParser &parser,
OperationState &result) {
SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
SmallVector<int> segments;
if (parseClauses(parser, result, clauses, segments) ||
parser.parseRegion(*result.addRegion()))
return failure();
return success();
}
/// Printer for AtomicCaptureOp
static void printAtomicCaptureOp(OpAsmPrinter &p, AtomicCaptureOp op) {
if (op.memory_order())
p << "memory_order(" << op.memory_order() << ") ";
if (op.hintAttr())
printSynchronizationHint(p, op, op.hintAttr());
p.printRegion(op.region());
}
/// Verifier for AtomicCaptureOp
static LogicalResult verifyAtomicCaptureOp(AtomicCaptureOp op) {
Block::OpListType &ops = op.region().front().getOperations();
if (ops.size() != 3)
return emitError(op.getLoc())
<< "expected three operations in omp.atomic.capture region (one "
"terminator, and two atomic ops)";
auto &firstOp = ops.front();
auto &secondOp = *ops.getNextNode(firstOp);
auto firstReadStmt = dyn_cast<AtomicReadOp>(firstOp);
auto firstUpdateStmt = dyn_cast<AtomicUpdateOp>(firstOp);
auto secondReadStmt = dyn_cast<AtomicReadOp>(secondOp);
auto secondUpdateStmt = dyn_cast<AtomicUpdateOp>(secondOp);
auto secondWriteStmt = dyn_cast<AtomicWriteOp>(secondOp);
if (!((firstUpdateStmt && secondReadStmt) ||
(firstReadStmt && secondUpdateStmt) ||
(firstReadStmt && secondWriteStmt)))
return emitError(ops.front().getLoc())
<< "invalid sequence of operations in the capture region";
if (firstUpdateStmt && secondReadStmt &&
firstUpdateStmt.x() != secondReadStmt.x())
return emitError(firstUpdateStmt.getLoc())
<< "updated variable in omp.atomic.update must be captured in "
"second operation";
if (firstReadStmt && secondUpdateStmt &&
firstReadStmt.x() != secondUpdateStmt.x())
return emitError(firstReadStmt.getLoc())
<< "captured variable in omp.atomic.read must be updated in second "
"operation";
if (firstReadStmt && secondWriteStmt &&
firstReadStmt.x() != secondWriteStmt.address())
return emitError(firstReadStmt.getLoc())
<< "captured variable in omp.atomic.read must be updated in "
"second operation";
return success();
}
#define GET_ATTRDEF_CLASSES
#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"

View File

@ -650,6 +650,122 @@ func @omp_atomic_update5(%x: memref<i32>, %expr: i32) {
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
// expected-error @below {{expected three operations in omp.atomic.capture region}}
omp.atomic.capture {
omp.atomic.read %v = %x : memref<i32>
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.read %v = %x : memref<i32>
omp.atomic.read %v = %x : memref<i32>
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.update %x = %x add %expr : memref<i32>, i32
omp.atomic.update %x = %x sub %expr : memref<i32>, i32
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.write %x = %expr : memref<i32>, i32
omp.atomic.write %x = %expr : memref<i32>, i32
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.write %x = %expr : memref<i32>, i32
omp.atomic.update %x = %x add %expr : memref<i32>, i32
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.update %x = %x add %expr : memref<i32>, i32
omp.atomic.write %x = %expr : memref<i32>, i32
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{invalid sequence of operations in the capture region}}
omp.atomic.write %x = %expr : memref<i32>, i32
omp.atomic.read %v = %x : memref<i32>
omp.terminator
}
return
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{updated variable in omp.atomic.update must be captured in second operation}}
omp.atomic.update %x = %x add %expr : memref<i32>, i32
omp.atomic.read %v = %y : memref<i32>
omp.terminator
}
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}}
omp.atomic.read %v = %y : memref<i32>
omp.atomic.update %x = %x add %expr : memref<i32>, i32
omp.terminator
}
}
// -----
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
omp.atomic.capture {
// expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}}
omp.atomic.read %v = %x : memref<i32>
omp.atomic.write %y = %expr : memref<i32>, i32
omp.terminator
}
}
// -----
func @omp_sections(%data_var1 : memref<i32>, %data_var2 : memref<i32>, %data_var3 : memref<i32>) -> () {
// expected-error @below {{operand used in both private and firstprivate clauses}}
omp.sections private(%data_var1 : memref<i32>) firstprivate(%data_var1 : memref<i32>) {

View File

@ -584,6 +584,42 @@ func @omp_atomic_update(%x : memref<i32>, %expr : i32, %xBool : memref<i1>, %exp
return
}
// CHECK-LABEL: omp_atomic_capture
// CHECK-SAME: (%[[v:.*]]: memref<i32>, %[[x:.*]]: memref<i32>, %[[expr:.*]]: i32)
func @omp_atomic_capture(%v: memref<i32>, %x: memref<i32>, %expr: i32) {
// CHECK: omp.atomic.capture{
// CHECK-NEXT: omp.atomic.update %[[x]] = %[[expr]] add %[[x]] : memref<i32>, i32
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
// CHECK-NEXT: omp.terminator
// CHECK-NEXT: }
omp.atomic.capture{
omp.atomic.update %x = %expr add %x : memref<i32>, i32
omp.atomic.read %v = %x : memref<i32>
omp.terminator
}
// CHECK: omp.atomic.capture{
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
// CHECK-NEXT: omp.atomic.update %[[x]] = %[[expr]] add %[[x]] : memref<i32>, i32
// CHECK-NEXT: omp.terminator
// CHECK-NEXT: }
omp.atomic.capture{
omp.atomic.read %v = %x : memref<i32>
omp.atomic.update %x = %expr add %x : memref<i32>, i32
omp.terminator
}
// CHECK: omp.atomic.capture{
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
// CHECK-NEXT: omp.atomic.write %[[x]] = %[[expr]] : memref<i32>, i32
// CHECK-NEXT: omp.terminator
// CHECK-NEXT: }
omp.atomic.capture{
omp.atomic.read %v = %x : memref<i32>
omp.atomic.write %x = %expr : memref<i32>, i32
omp.terminator
}
return
}
// CHECK-LABEL: omp_sectionsop
func @omp_sectionsop(%data_var1 : memref<i32>, %data_var2 : memref<i32>,
%data_var3 : memref<i32>, %redn_var : !llvm.ptr<f32>) {