Skip to content

Conversation

@erichkeane
Copy link
Collaborator

OpenACC's C++ version has a variant of capture that permits a update followed by a write. Therefore the verifier was overly strict in this case.

According to our reading of the OpenMP 6.0 spec, it appears that atomic captured update (page 495) also requires this form, so it seems reasonable to allow this for both languages.

OpenACC's C++ version has a variant of capture that permits a update
followed by a write. Therefore the verifier was overly strict in this
case.

According to our reading of the OpenMP 6.0 spec, it appears that `atomic
captured update` (page 495) also requires this form, so it seems
reasonable to allow this for both languages.
@erichkeane
Copy link
Collaborator Author

@razvanlupusoru : can you suggest OMP reviewers for me?

@llvmbot
Copy link
Member

llvmbot commented Nov 13, 2025

@llvm/pr-subscribers-mlir-openacc
@llvm/pr-subscribers-mlir-openmp

@llvm/pr-subscribers-mlir

Author: Erich Keane (erichkeane)

Changes

OpenACC's C++ version has a variant of capture that permits a update followed by a write. Therefore the verifier was overly strict in this case.

According to our reading of the OpenMP 6.0 spec, it appears that atomic captured update (page 495) also requires this form, so it seems reasonable to allow this for both languages.


Full diff: https://github.com/llvm/llvm-project/pull/167963.diff

4 Files Affected:

  • (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td (+6)
  • (modified) mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td (+6-2)
  • (modified) mlir/test/Dialect/OpenACC/invalid.mlir (+17-1)
  • (modified) mlir/test/Dialect/OpenMP/invalid.mlir (+32-1)
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 5b89f741e296d..8c33be7ff1747 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -2999,6 +2999,12 @@ def AtomicCaptureOp : OpenACC_Op<"atomic.capture",
         acc.atomic.write ...
         acc.terminator
       }
+
+      acc.atomic.capture {
+        acc.atomic.update ...
+        acc.atomic.write ...
+        acc.terminator
+      }
     ```
 
   }];
diff --git a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
index 223bee9ab1c27..9df6b907eb326 100644
--- a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
@@ -239,6 +239,7 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
     implement one of the atomic interfaces. It can be found in one of these
     forms:
       `{ atomic.update, atomic.read }`
+      `{ atomic.update, atomic.write }`
       `{ atomic.read, atomic.update }`
       `{ atomic.read, atomic.write }`
   }];
@@ -291,12 +292,15 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
         auto secondWriteStmt = dyn_cast<AtomicWriteOpInterface>(secondOp);
 
         if (!((firstUpdateStmt && secondReadStmt) ||
+              (firstUpdateStmt && secondWriteStmt) ||
               (firstReadStmt && secondUpdateStmt) ||
               (firstReadStmt && secondWriteStmt)))
           return ops.front().emitError()
                 << "invalid sequence of operations in the capture region";
-        if (firstUpdateStmt && secondReadStmt &&
-            firstUpdateStmt.getX() != secondReadStmt.getX())
+        if ((firstUpdateStmt && secondReadStmt &&
+             firstUpdateStmt.getX() != secondReadStmt.getX()) ||
+            (firstUpdateStmt && secondWriteStmt &&
+             firstUpdateStmt.getX() != secondWriteStmt.getX()))
           return firstUpdateStmt.emitError()
                 << "updated variable in atomic.update must be captured in "
                     "second operation";
diff --git a/mlir/test/Dialect/OpenACC/invalid.mlir b/mlir/test/Dialect/OpenACC/invalid.mlir
index 0e75894eaeceb..173c6a74545f8 100644
--- a/mlir/test/Dialect/OpenACC/invalid.mlir
+++ b/mlir/test/Dialect/OpenACC/invalid.mlir
@@ -690,7 +690,6 @@ func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
 
 func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
   acc.atomic.capture {
-    // expected-error @below {{invalid sequence of operations in the capture region}}
     acc.atomic.update %x : memref<i32> {
     ^bb0(%xval: i32):
       %newval = llvm.add %xval, %expr : i32
@@ -704,6 +703,23 @@ func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
 
 // -----
 
+func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
+  acc.atomic.capture {
+    // expected-error @below {{updated variable in atomic.update must be captured in second operation}}
+    acc.atomic.update %x : memref<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      acc.yield %newval : i32
+    }
+    acc.atomic.write %v = %expr : memref<i32>, i32
+
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
 func.func @acc_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
   acc.atomic.capture {
     // expected-error @below {{invalid sequence of operations in the capture region}}
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index af24d969064ab..6224a236e7c78 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -1263,7 +1263,22 @@ func.func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
 
 func.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}}
+    // expected-error @below {{updated variable in atomic.update must be captured in second operation}}
+    omp.atomic.update %x : memref<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield (%newval : i32)
+    }
+    omp.atomic.write %v = %expr : memref<i32>, i32
+    omp.terminator
+  }
+  return
+}
+
+// -----
+
+func.func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
+  omp.atomic.capture {
     omp.atomic.update %x : memref<i32> {
     ^bb0(%xval: i32):
       %newval = llvm.add %xval, %expr : i32
@@ -1289,6 +1304,22 @@ func.func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
 
 // -----
 
+func.func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
+  omp.atomic.capture {
+    omp.atomic.update %x : memref<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield (%newval : i32)
+    }
+    omp.atomic.write %x = %expr : memref<i32>, i32
+
+    omp.terminator
+  }
+  return
+}
+
+// -----
+
 func.func @omp_atomic_capture(%x: memref<i32>, %y: memref<i32>, %v: memref<i32>, %expr: i32) {
   omp.atomic.capture {
     // expected-error @below {{updated variable in atomic.update must be captured in second operation}}


// -----

func.func @omp_atomic_capture(%x: memref<i32>, %v: memref<i32>, %expr: i32) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to be a 'valid' config test, so I don't have diagnostics. That said, I seem to have double-added this 'is valid' test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the "valid" tests to ops.mlir (mlir/test/Dialect/OpenACC/ops.mlir and mlir/test/Dialect/OpenMP/ops.mlir)?

The invalid tests look great to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Yes, I did that. Thanks for the help on where to put those!

Copy link
Contributor

@razvanlupusoru razvanlupusoru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me! Can you please allow a bit of time for the omp reviewers to take a look also? Thank you for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants