Skip to content

Commit 320dc8c

Browse files
committed
[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
1 parent f6984b2 commit 320dc8c

File tree

4 files changed

+258
-0
lines changed

4 files changed

+258
-0
lines changed

mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,50 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update"> {
708708
let verifier = [{ return verifyAtomicUpdateOp(*this); }];
709709
}
710710

711+
def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
712+
[SingleBlockImplicitTerminator<"TerminatorOp">]> {
713+
let summary = "performs an atomic capture";
714+
let description = [{
715+
This operation performs an atomic capture.
716+
717+
`hint` is the value of hint (as used in the hint clause). It is a compile
718+
time constant. As the name suggests, this is just a hint for optimization.
719+
720+
`memory_order` indicates the memory ordering behavior of the construct. It
721+
can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`.
722+
723+
The region has the following allowed forms:
724+
725+
```
726+
omp.atomic.capture {
727+
omp.atomic.update ...
728+
omp.atomic.read ...
729+
omp.terminator
730+
}
731+
732+
omp.atomic.capture {
733+
omp.atomic.read ...
734+
omp.atomic.update ...
735+
omp.terminator
736+
}
737+
738+
omp.atomic.capture {
739+
omp.atomic.read ...
740+
omp.atomic.write ...
741+
omp.terminator
742+
}
743+
```
744+
745+
}];
746+
747+
let arguments = (ins DefaultValuedAttr<I64Attr, "0">:$hint,
748+
OptionalAttr<MemoryOrderKind>:$memory_order);
749+
let regions = (region SizedRegion<1>:$region);
750+
let parser = [{ return parseAtomicCaptureOp(parser, result); }];
751+
let printer = [{ return printAtomicCaptureOp(p, *this); }];
752+
let verifier = [{ return verifyAtomicCaptureOp(*this); }];
753+
}
754+
711755
//===----------------------------------------------------------------------===//
712756
// 2.19.5.7 declare reduction Directive
713757
//===----------------------------------------------------------------------===//

mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,68 @@ static LogicalResult verifyAtomicUpdateOp(AtomicUpdateOp op) {
15391539
return success();
15401540
}
15411541

1542+
//===----------------------------------------------------------------------===//
1543+
// AtomicCaptureOp
1544+
//===----------------------------------------------------------------------===//
1545+
1546+
/// Parser for AtomicCaptureOp
1547+
static LogicalResult parseAtomicCaptureOp(OpAsmParser &parser,
1548+
OperationState &result) {
1549+
SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
1550+
SmallVector<int> segments;
1551+
if (parseClauses(parser, result, clauses, segments) ||
1552+
parser.parseRegion(*result.addRegion()))
1553+
return failure();
1554+
return success();
1555+
}
1556+
1557+
/// Printer for AtomicCaptureOp
1558+
static void printAtomicCaptureOp(OpAsmPrinter &p, AtomicCaptureOp op) {
1559+
if (op.memory_order())
1560+
p << "memory_order(" << op.memory_order() << ") ";
1561+
if (op.hintAttr())
1562+
printSynchronizationHint(p, op, op.hintAttr());
1563+
p.printRegion(op.region());
1564+
}
1565+
1566+
/// Verifier for AtomicCaptureOp
1567+
static LogicalResult verifyAtomicCaptureOp(AtomicCaptureOp op) {
1568+
Block::OpListType &ops = op.region().front().getOperations();
1569+
if (ops.size() != 3)
1570+
return emitError(op.getLoc())
1571+
<< "expected three operations in omp.atomic.capture region (one "
1572+
"terminator, and two atomic ops)";
1573+
auto &firstOp = ops.front();
1574+
auto &secondOp = *ops.getNextNode(firstOp);
1575+
auto firstReadStmt = dyn_cast<AtomicReadOp>(firstOp);
1576+
auto firstUpdateStmt = dyn_cast<AtomicUpdateOp>(firstOp);
1577+
auto secondReadStmt = dyn_cast<AtomicReadOp>(secondOp);
1578+
auto secondUpdateStmt = dyn_cast<AtomicUpdateOp>(secondOp);
1579+
auto secondWriteStmt = dyn_cast<AtomicWriteOp>(secondOp);
1580+
1581+
if (!((firstUpdateStmt && secondReadStmt) ||
1582+
(firstReadStmt && secondUpdateStmt) ||
1583+
(firstReadStmt && secondWriteStmt)))
1584+
return emitError(ops.front().getLoc())
1585+
<< "invalid sequence of operations in the capture region";
1586+
if (firstUpdateStmt && secondReadStmt &&
1587+
firstUpdateStmt.x() != secondReadStmt.x())
1588+
return emitError(firstUpdateStmt.getLoc())
1589+
<< "updated variable in omp.atomic.update must be captured in "
1590+
"second operation";
1591+
if (firstReadStmt && secondUpdateStmt &&
1592+
firstReadStmt.x() != secondUpdateStmt.x())
1593+
return emitError(firstReadStmt.getLoc())
1594+
<< "captured variable in omp.atomic.read must be updated in second "
1595+
"operation";
1596+
if (firstReadStmt && secondWriteStmt &&
1597+
firstReadStmt.x() != secondWriteStmt.address())
1598+
return emitError(firstReadStmt.getLoc())
1599+
<< "captured variable in omp.atomic.read must be updated in "
1600+
"second operation";
1601+
return success();
1602+
}
1603+
15421604
#define GET_ATTRDEF_CLASSES
15431605
#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
15441606

mlir/test/Dialect/OpenMP/invalid.mlir

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,122 @@ func @omp_atomic_update5(%x: memref<i32>, %expr: i32) {
650650

651651
// -----
652652

653+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
654+
// expected-error @below {{expected three operations in omp.atomic.capture region}}
655+
omp.atomic.capture {
656+
omp.atomic.read %v = %x : memref<i32>
657+
omp.terminator
658+
}
659+
return
660+
}
661+
662+
// -----
663+
664+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
665+
omp.atomic.capture {
666+
// expected-error @below {{invalid sequence of operations in the capture region}}
667+
omp.atomic.read %v = %x : memref<i32>
668+
omp.atomic.read %v = %x : memref<i32>
669+
omp.terminator
670+
}
671+
return
672+
}
673+
674+
// -----
675+
676+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
677+
omp.atomic.capture {
678+
// expected-error @below {{invalid sequence of operations in the capture region}}
679+
omp.atomic.update %x = %x add %expr : memref<i32>, i32
680+
omp.atomic.update %x = %x sub %expr : memref<i32>, i32
681+
omp.terminator
682+
}
683+
return
684+
}
685+
686+
// -----
687+
688+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
689+
omp.atomic.capture {
690+
// expected-error @below {{invalid sequence of operations in the capture region}}
691+
omp.atomic.write %x = %expr : memref<i32>, i32
692+
omp.atomic.write %x = %expr : memref<i32>, i32
693+
omp.terminator
694+
}
695+
return
696+
}
697+
698+
// -----
699+
700+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
701+
omp.atomic.capture {
702+
// expected-error @below {{invalid sequence of operations in the capture region}}
703+
omp.atomic.write %x = %expr : memref<i32>, i32
704+
omp.atomic.update %x = %x add %expr : memref<i32>, i32
705+
omp.terminator
706+
}
707+
return
708+
}
709+
710+
// -----
711+
712+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
713+
omp.atomic.capture {
714+
// expected-error @below {{invalid sequence of operations in the capture region}}
715+
omp.atomic.update %x = %x add %expr : memref<i32>, i32
716+
omp.atomic.write %x = %expr : memref<i32>, i32
717+
omp.terminator
718+
}
719+
return
720+
}
721+
722+
// -----
723+
724+
func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
725+
omp.atomic.capture {
726+
// expected-error @below {{invalid sequence of operations in the capture region}}
727+
omp.atomic.write %x = %expr : memref<i32>, i32
728+
omp.atomic.read %v = %x : memref<i32>
729+
omp.terminator
730+
}
731+
return
732+
}
733+
734+
// -----
735+
736+
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
737+
omp.atomic.capture {
738+
// expected-error @below {{updated variable in omp.atomic.update must be captured in second operation}}
739+
omp.atomic.update %x = %x add %expr : memref<i32>, i32
740+
omp.atomic.read %v = %y : memref<i32>
741+
omp.terminator
742+
}
743+
}
744+
745+
// -----
746+
747+
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
748+
omp.atomic.capture {
749+
// expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}}
750+
omp.atomic.read %v = %y : memref<i32>
751+
omp.atomic.update %x = %x add %expr : memref<i32>, i32
752+
omp.terminator
753+
}
754+
}
755+
756+
// -----
757+
758+
func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
759+
omp.atomic.capture {
760+
// expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}}
761+
omp.atomic.read %v = %x : memref<i32>
762+
omp.atomic.write %y = %expr : memref<i32>, i32
763+
omp.terminator
764+
}
765+
}
766+
767+
// -----
768+
653769
func @omp_sections(%data_var1 : memref<i32>, %data_var2 : memref<i32>, %data_var3 : memref<i32>) -> () {
654770
// expected-error @below {{operand used in both private and firstprivate clauses}}
655771
omp.sections private(%data_var1 : memref<i32>) firstprivate(%data_var1 : memref<i32>) {

mlir/test/Dialect/OpenMP/ops.mlir

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,42 @@ func @omp_atomic_update(%x : memref<i32>, %expr : i32, %xBool : memref<i1>, %exp
584584
return
585585
}
586586

587+
// CHECK-LABEL: omp_atomic_capture
588+
// CHECK-SAME: (%[[v:.*]]: memref<i32>, %[[x:.*]]: memref<i32>, %[[expr:.*]]: i32)
589+
func @omp_atomic_capture(%v: memref<i32>, %x: memref<i32>, %expr: i32) {
590+
// CHECK: omp.atomic.capture{
591+
// CHECK-NEXT: omp.atomic.update %[[x]] = %[[expr]] add %[[x]] : memref<i32>, i32
592+
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
593+
// CHECK-NEXT: omp.terminator
594+
// CHECK-NEXT: }
595+
omp.atomic.capture{
596+
omp.atomic.update %x = %expr add %x : memref<i32>, i32
597+
omp.atomic.read %v = %x : memref<i32>
598+
omp.terminator
599+
}
600+
// CHECK: omp.atomic.capture{
601+
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
602+
// CHECK-NEXT: omp.atomic.update %[[x]] = %[[expr]] add %[[x]] : memref<i32>, i32
603+
// CHECK-NEXT: omp.terminator
604+
// CHECK-NEXT: }
605+
omp.atomic.capture{
606+
omp.atomic.read %v = %x : memref<i32>
607+
omp.atomic.update %x = %expr add %x : memref<i32>, i32
608+
omp.terminator
609+
}
610+
// CHECK: omp.atomic.capture{
611+
// CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref<i32>
612+
// CHECK-NEXT: omp.atomic.write %[[x]] = %[[expr]] : memref<i32>, i32
613+
// CHECK-NEXT: omp.terminator
614+
// CHECK-NEXT: }
615+
omp.atomic.capture{
616+
omp.atomic.read %v = %x : memref<i32>
617+
omp.atomic.write %x = %expr : memref<i32>, i32
618+
omp.terminator
619+
}
620+
return
621+
}
622+
587623
// CHECK-LABEL: omp_sectionsop
588624
func @omp_sectionsop(%data_var1 : memref<i32>, %data_var2 : memref<i32>,
589625
%data_var3 : memref<i32>, %redn_var : !llvm.ptr<f32>) {

0 commit comments

Comments
 (0)