Skip to content

Conversation

@SusanTan
Copy link
Contributor

use last modified analysis to test if RegionBranchOpInterface is correct on acc regions

@llvmbot
Copy link
Member

llvmbot commented Dec 12, 2025

@llvm/pr-subscribers-mlir-openacc

Author: Susan Tan (ス-ザン タン) (SusanTan)

Changes

use last modified analysis to test if RegionBranchOpInterface is correct on acc regions


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

1 Files Affected:

  • (added) mlir/test/Dialect/OpenACC/region-branchop-interface.mlir (+145)
diff --git a/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
new file mode 100644
index 00000000000000..a97fdb7d78ad15
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
@@ -0,0 +1,145 @@
+// RUN: mlir-opt -test-last-modified %s 2>&1 | FileCheck %s
+
+// Test that RegionBranchOpInterface implementations for OpenACC single-region
+// ops behave reasonably under LastModifiedAnalysis.
+//
+// For acc.parallel / acc.serial, which only have value-based memory effects
+// coming from their bodies, the analysis can track the last writer precisely.
+// For other OpenACC region ops (e.g. acc.kernels, acc.data, acc.host_data),
+// which currently only report resource-only memory effects at the op level,
+// LastModifiedAnalysis cannot attribute a precise last writer and falls back
+// to "<unknown>" after the op for values not directly touched in the region.
+
+
+// CHECK-LABEL: test_tag: acc_parallel_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+// CHECK-LABEL: test_tag: acc_parallel_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+func.func @last_mod_openacc_parallel(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.parallel region.
+  acc.parallel {
+    memref.store %one, %arg0[] {tag_name = "parallel_region"} : memref<f32>
+    acc.yield
+  }
+
+  // With RegionBranchOpInterface wired up, the last modification at this load
+  // is the store inside the acc.parallel region.
+  memref.load %arg0[] {tag = "acc_parallel_after"} : memref<f32>
+
+  // And the same store should be seen at the function return.
+  return {tag = "acc_parallel_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_serial_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+// CHECK-LABEL: test_tag: acc_serial_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+func.func @last_mod_openacc_serial(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.serial region.
+  acc.serial {
+    memref.store %one, %arg0[] {tag_name = "serial_region"} : memref<f32>
+    acc.yield
+  }
+
+  memref.load %arg0[] {tag = "acc_serial_after"} : memref<f32>
+
+  return {tag = "acc_serial_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_kernels_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_kernels_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_kernels(%arg0: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_kernels_before"} : memref<f32>
+
+  // The acc.kernels region does not touch %arg0.
+  acc.kernels {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.kernels, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths and therefore reports the last modifier conservatively
+  // as "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_kernels_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_data_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_data_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_data_before"} : memref<f32>
+
+  // Map an unrelated buffer into device memory and run an acc.data region that
+  // does not touch %arg0.
+  %create = acc.create varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.data dataOperands(%create : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.data, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths for %arg0 and reports "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_data_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_host_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_host_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_host_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_host_before"} : memref<f32>
+
+  // Map %mapped into device memory and run an acc.host_data region that does
+  // not touch %arg0.
+  %devptr = acc.use_device varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.host_data dataOperands(%devptr : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.host_data, the analysis cannot prove "pre" still dominates and
+  // reports "<unknown>" for the last writer on %arg0.
+  memref.load %arg0[] {tag = "acc_host_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+

@llvmbot
Copy link
Member

llvmbot commented Dec 12, 2025

@llvm/pr-subscribers-openacc

Author: Susan Tan (ス-ザン タン) (SusanTan)

Changes

use last modified analysis to test if RegionBranchOpInterface is correct on acc regions


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

1 Files Affected:

  • (added) mlir/test/Dialect/OpenACC/region-branchop-interface.mlir (+145)
diff --git a/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
new file mode 100644
index 0000000000000..a97fdb7d78ad1
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
@@ -0,0 +1,145 @@
+// RUN: mlir-opt -test-last-modified %s 2>&1 | FileCheck %s
+
+// Test that RegionBranchOpInterface implementations for OpenACC single-region
+// ops behave reasonably under LastModifiedAnalysis.
+//
+// For acc.parallel / acc.serial, which only have value-based memory effects
+// coming from their bodies, the analysis can track the last writer precisely.
+// For other OpenACC region ops (e.g. acc.kernels, acc.data, acc.host_data),
+// which currently only report resource-only memory effects at the op level,
+// LastModifiedAnalysis cannot attribute a precise last writer and falls back
+// to "<unknown>" after the op for values not directly touched in the region.
+
+
+// CHECK-LABEL: test_tag: acc_parallel_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+// CHECK-LABEL: test_tag: acc_parallel_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+func.func @last_mod_openacc_parallel(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.parallel region.
+  acc.parallel {
+    memref.store %one, %arg0[] {tag_name = "parallel_region"} : memref<f32>
+    acc.yield
+  }
+
+  // With RegionBranchOpInterface wired up, the last modification at this load
+  // is the store inside the acc.parallel region.
+  memref.load %arg0[] {tag = "acc_parallel_after"} : memref<f32>
+
+  // And the same store should be seen at the function return.
+  return {tag = "acc_parallel_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_serial_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+// CHECK-LABEL: test_tag: acc_serial_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+func.func @last_mod_openacc_serial(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.serial region.
+  acc.serial {
+    memref.store %one, %arg0[] {tag_name = "serial_region"} : memref<f32>
+    acc.yield
+  }
+
+  memref.load %arg0[] {tag = "acc_serial_after"} : memref<f32>
+
+  return {tag = "acc_serial_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_kernels_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_kernels_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_kernels(%arg0: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_kernels_before"} : memref<f32>
+
+  // The acc.kernels region does not touch %arg0.
+  acc.kernels {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.kernels, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths and therefore reports the last modifier conservatively
+  // as "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_kernels_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_data_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_data_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_data_before"} : memref<f32>
+
+  // Map an unrelated buffer into device memory and run an acc.data region that
+  // does not touch %arg0.
+  %create = acc.create varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.data dataOperands(%create : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.data, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths for %arg0 and reports "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_data_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_host_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_host_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_host_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_host_before"} : memref<f32>
+
+  // Map %mapped into device memory and run an acc.host_data region that does
+  // not touch %arg0.
+  %devptr = acc.use_device varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.host_data dataOperands(%devptr : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.host_data, the analysis cannot prove "pre" still dominates and
+  // reports "<unknown>" for the last writer on %arg0.
+  memref.load %arg0[] {tag = "acc_host_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+

@llvmbot
Copy link
Member

llvmbot commented Dec 12, 2025

@llvm/pr-subscribers-mlir

Author: Susan Tan (ス-ザン タン) (SusanTan)

Changes

use last modified analysis to test if RegionBranchOpInterface is correct on acc regions


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

1 Files Affected:

  • (added) mlir/test/Dialect/OpenACC/region-branchop-interface.mlir (+145)
diff --git a/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
new file mode 100644
index 0000000000000..a97fdb7d78ad1
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/region-branchop-interface.mlir
@@ -0,0 +1,145 @@
+// RUN: mlir-opt -test-last-modified %s 2>&1 | FileCheck %s
+
+// Test that RegionBranchOpInterface implementations for OpenACC single-region
+// ops behave reasonably under LastModifiedAnalysis.
+//
+// For acc.parallel / acc.serial, which only have value-based memory effects
+// coming from their bodies, the analysis can track the last writer precisely.
+// For other OpenACC region ops (e.g. acc.kernels, acc.data, acc.host_data),
+// which currently only report resource-only memory effects at the op level,
+// LastModifiedAnalysis cannot attribute a precise last writer and falls back
+// to "<unknown>" after the op for values not directly touched in the region.
+
+
+// CHECK-LABEL: test_tag: acc_parallel_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+// CHECK-LABEL: test_tag: acc_parallel_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - parallel_region
+func.func @last_mod_openacc_parallel(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.parallel region.
+  acc.parallel {
+    memref.store %one, %arg0[] {tag_name = "parallel_region"} : memref<f32>
+    acc.yield
+  }
+
+  // With RegionBranchOpInterface wired up, the last modification at this load
+  // is the store inside the acc.parallel region.
+  memref.load %arg0[] {tag = "acc_parallel_after"} : memref<f32>
+
+  // And the same store should be seen at the function return.
+  return {tag = "acc_parallel_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_serial_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+// CHECK-LABEL: test_tag: acc_serial_return:
+// CHECK:  operand #0
+// CHECK-NEXT:   - serial_region
+func.func @last_mod_openacc_serial(%arg0: memref<f32>) -> memref<f32> {
+  %one = arith.constant 1.0 : f32
+
+  // The only store to %arg0 happens inside the acc.serial region.
+  acc.serial {
+    memref.store %one, %arg0[] {tag_name = "serial_region"} : memref<f32>
+    acc.yield
+  }
+
+  memref.load %arg0[] {tag = "acc_serial_after"} : memref<f32>
+
+  return {tag = "acc_serial_return"} %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_kernels_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_kernels_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_kernels(%arg0: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_kernels_before"} : memref<f32>
+
+  // The acc.kernels region does not touch %arg0.
+  acc.kernels {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.kernels, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths and therefore reports the last modifier conservatively
+  // as "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_kernels_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_data_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_data_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_data_before"} : memref<f32>
+
+  // Map an unrelated buffer into device memory and run an acc.data region that
+  // does not touch %arg0.
+  %create = acc.create varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.data dataOperands(%create : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.data, LastModifiedAnalysis cannot prove that "pre" still
+  // dominates all paths for %arg0 and reports "<unknown>" for this load.
+  memref.load %arg0[] {tag = "acc_data_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+// -----
+
+// CHECK-LABEL: test_tag: acc_host_before:
+// CHECK:  operand #0
+// CHECK-NEXT:   - pre
+// CHECK-LABEL: test_tag: acc_host_after:
+// CHECK:  operand #0
+// CHECK-NEXT:   - <unknown>
+func.func @last_mod_openacc_host_data(%arg0: memref<f32>, %mapped: memref<f32>) -> memref<f32> {
+  %zero = arith.constant 0.0 : f32
+
+  // Single store to %arg0 before the region.
+  memref.store %zero, %arg0[] {tag_name = "pre"} : memref<f32>
+  memref.load %arg0[] {tag = "acc_host_before"} : memref<f32>
+
+  // Map %mapped into device memory and run an acc.host_data region that does
+  // not touch %arg0.
+  %devptr = acc.use_device varPtr(%mapped : memref<f32>) varType(tensor<f32>) -> memref<f32>
+  acc.host_data dataOperands(%devptr : memref<f32>) {
+    "test.openacc_dummy_op"() : () -> ()
+    acc.terminator
+  }
+
+  // After acc.host_data, the analysis cannot prove "pre" still dominates and
+  // reports "<unknown>" for the last writer on %arg0.
+  memref.load %arg0[] {tag = "acc_host_after"} : memref<f32>
+  return %arg0 : memref<f32>
+}
+
+

Copy link
Contributor

@vzakhari vzakhari left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you, Susan!

@SusanTan SusanTan merged commit 47b4c6a into llvm:main Dec 12, 2025
14 checks passed
anonymouspc pushed a commit to anonymouspc/llvm that referenced this pull request Dec 15, 2025
…vm#172073)

use last modified analysis to test if RegionBranchOpInterface is correct
on acc regions
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Dec 19, 2025
…vm#172073)

use last modified analysis to test if RegionBranchOpInterface is correct
on acc regions
Priyanshu3820 pushed a commit to Priyanshu3820/llvm-project that referenced this pull request Dec 20, 2025
…vm#172073)

use last modified analysis to test if RegionBranchOpInterface is correct
on acc regions
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