Skip to content

Conversation

@anchuraj
Copy link
Contributor

@anchuraj anchuraj commented Sep 9, 2024

MLIR Op representation of scan operation. Scan directive helps to specify scan computations.
Related PR: Parsing and Semantic changes for scan

@llvmbot
Copy link
Member

llvmbot commented Sep 9, 2024

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

@llvm/pr-subscribers-mlir

Author: Anchu Rajendran S (anchuraj)

Changes

MLIR Op representation of scan operation. Scan directive helps to specify scan computations.
Related PR: Parsing and Semantic changes for scan


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

5 Files Affected:

  • (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h (+9)
  • (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td (+55)
  • (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td (+14)
  • (modified) mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp (+9)
  • (modified) mlir/test/Dialect/OpenMP/ops.mlir (+8)
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
index 38e4d8f245e4fa..af96f24c8fe2c2 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
@@ -105,6 +105,13 @@ struct IfClauseOps {
   Value ifVar;
 };
 
+struct InclusiveClauseOps {
+  llvm::SmallVector<Value> inclusiveVars;
+};
+
+struct ExclusiveClauseOps {
+  llvm::SmallVector<Value> exclusiveVars;
+};
 struct InReductionClauseOps {
   llvm::SmallVector<Value> inReductionVars;
   llvm::SmallVector<bool> inReductionByref;
@@ -261,6 +268,8 @@ using LoopNestOperands = detail::Clauses<LoopRelatedOps>;
 
 using MaskedOperands = detail::Clauses<FilterClauseOps>;
 
+using ScanOperands = detail::Clauses<InclusiveClauseOps, ExclusiveClauseOps>;
+
 using OrderedOperands = detail::Clauses<DoacrossClauseOps>;
 
 using OrderedRegionOperands = detail::Clauses<ParallelizationLevelClauseOps>;
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
index e703c323edbc8a..79b54545ef5331 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
@@ -503,6 +503,61 @@ class OpenMP_IsDevicePtrClauseSkip<
 
 def OpenMP_IsDevicePtrClause : OpenMP_IsDevicePtrClauseSkip<>;
 
+//===----------------------------------------------------------------------===//
+// V5.2: [5.4.7] `inclusive` clause
+//===----------------------------------------------------------------------===//
+
+class OpenMP_InclusiveClauseSkip<
+    bit traits = false, bit arguments = false, bit assemblyFormat = false,
+    bit description = false, bit extraClassDeclaration = false
+  > : OpenMP_Clause</*isRequired=*/false, traits, arguments, assemblyFormat,
+                    description, extraClassDeclaration> {
+  let arguments = (ins
+    Variadic<AnyType>:$inclusive_vars
+  );
+
+  let assemblyFormat = [{
+    `inclusive` `(` $inclusive_vars `:` type($inclusive_vars) `)`
+  }];
+
+  let description = [{
+    The inclusive clause is used on a separating directive that separates a
+    structured block into two structured block sequences. If the inclusive
+    clause is specified, the input phase includes the preceding structured block
+    sequence and the scan phase includes the following structured block sequence.
+  }];
+}
+
+def OpenMP_InclusiveClause : OpenMP_InclusiveClauseSkip<>;
+
+//===----------------------------------------------------------------------===//
+// V5.2: [5.4.7] `exclusive` clause
+//===----------------------------------------------------------------------===//
+
+class OpenMP_ExclusiveClauseSkip<
+    bit traits = false, bit arguments = false, bit assemblyFormat = false,
+    bit description = false, bit extraClassDeclaration = false
+  > : OpenMP_Clause</*isRequired=*/false, traits, arguments, assemblyFormat,
+                    description, extraClassDeclaration> {
+  let arguments = (ins
+    Variadic<AnyType>:$exclusive_vars
+  );
+
+  let assemblyFormat = [{
+    `exclusive` `(` $exclusive_vars `:` type($exclusive_vars) `)`
+  }];
+
+  let description = [{
+    The exclusive clause is used on a separating directive that separates a
+    structured block into two structured block sequences. If the exclusive clause
+    is specified, the input phase excludes the preceding structured block 
+    sequence and instead includes the following structured block sequence, 
+    while the scan phase includes the preceding structured block sequence.
+  }];
+}
+
+def OpenMP_ExclusiveClause : OpenMP_ExclusiveClauseSkip<>;
+
 //===----------------------------------------------------------------------===//
 // V5.2: [5.4.6] `linear` clause
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 1aa4e771cd4dea..2a81ac2f09072d 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -1202,6 +1202,20 @@ def OrderedRegionOp : OpenMP_Op<"ordered.region", clauses = [
   let hasVerifier = 1;
 }
 
+def ScanOp : OpenMP_Op<"scan", traits = [
+    AttrSizedOperandSegments
+  ], clauses = [OpenMP_InclusiveClause, OpenMP_ExclusiveClause]> {
+  let summary = "scan construct with the region";
+  let description = [{
+    The scan without region is a stand-alone directive that
+  }] # clausesDescription;
+
+  let builders = [
+    OpBuilder<(ins CArg<"const ScanOperands &">:$clauses)>
+  ];
+
+}
+
 //===----------------------------------------------------------------------===//
 // 2.17.5 taskwait Construct
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 1a9b87f0d68c9d..04b4f9b8e8b254 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -2619,6 +2619,15 @@ LogicalResult PrivateClauseOp::verify() {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// Spec 5.2: Scan construct (5.6)
+//===----------------------------------------------------------------------===//
+
+void ScanOp::build(OpBuilder &builder, OperationState &state,
+                   const ScanOperands &clauses) {
+  ScanOp::build(builder, state, clauses.inclusiveVars, clauses.exclusiveVars);
+}
+
 //===----------------------------------------------------------------------===//
 // Spec 5.2: Masked construct (10.5)
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index dce5b3950def49..09db285a0274ee 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -30,6 +30,14 @@ func.func @omp_masked(%filtered_thread_id : i32) -> () {
   return
 }
 
+func.func @omp_scan(%arg0 : memref<i32>) -> () {
+  // CHECK: omp.scan inclusive(%{{.*}} : memref<i32>)
+  omp.scan inclusive(%arg0 : memref<i32>)
+  // CHECK: omp.scan exclusive(%{{.*}} : memref<i32>)
+  omp.scan exclusive(%arg0 : memref<i32>)
+  return
+}
+
 func.func @omp_taskwait() -> () {
   // CHECK: omp.taskwait
   omp.taskwait

Copy link
Member

@skatrak skatrak left a comment

Choose a reason for hiding this comment

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

Thank you Anchu for this work. I have some nits, but this generally looks good. However, I think that the new operation would need a verifier.

A good start could be to check that values in a scan directive appear on a parent reduction with an inscan modifier and that there is a single omp.scan applied to that construct, but inscan is not currently supported. Maybe the check could be to check that values passed to inclusive and exclusive are block arguments attached to a reduction and that the operation defining that reduction only holds a single omp.scan, adding a TODO comment to check the inscan modifier when supported. Then, update the test in ops.mlir and add a couple to invalid.mlir to exercise these verifier checks. Let me know what you think about this.

When rebasing, notice you no longer need to add definitions to OpenMPClauseOperands.h. Also, the new clauses don't have to pass an isRequired template argument to OpenMP_Clause (instead, rename assemblyFormat to {req,opt}AssemblyFormat as needed).

// V5.2: [5.4.7] `inclusive` clause
//===----------------------------------------------------------------------===//

class OpenMP_InclusiveClauseSkip<
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Move definitions for these new clauses to keep alphabetical sorting in the file.


let description = [{
The inclusive clause is used on a separating directive that separates a
structured block into two structured block sequences. If the inclusive
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
structured block into two structured block sequences. If the inclusive
structured block into two structured block sequences. If it


let description = [{
The exclusive clause is used on a separating directive that separates a
structured block into two structured block sequences. If the exclusive clause
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
structured block into two structured block sequences. If the exclusive clause
structured block into two structured block sequences. If it

let builders = [
OpBuilder<(ins CArg<"const ScanOperands &">:$clauses)>
];

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

def ScanOp : OpenMP_Op<"scan", traits = [
AttrSizedOperandSegments
], clauses = [OpenMP_InclusiveClause, OpenMP_ExclusiveClause]> {
let summary = "scan construct with the region";
Copy link
Member

Choose a reason for hiding this comment

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

Should it be "scan directive"?

], clauses = [OpenMP_InclusiveClause, OpenMP_ExclusiveClause]> {
let summary = "scan construct with the region";
let description = [{
The scan without region is a stand-alone directive that
Copy link
Member

Choose a reason for hiding this comment

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

It looks like the description is not complete.

@anchuraj
Copy link
Contributor Author

Will open a new PR addressing these suggestions and adding the complete changes for supporting scan.

@anchuraj anchuraj closed this Oct 28, 2024
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