forked from OSchip/llvm-project
[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:
parent
f6984b299a
commit
320dc8c4df
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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>) {
|
||||
|
|
Loading…
Reference in New Issue