[Mlir-commits] [mlir] [mlir][acc] Introduce MappableType interface (PR #122146)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Jan 8 09:41:30 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-openacc

Author: Razvan Lupusoru (razvanlupusoru)

<details>
<summary>Changes</summary>

OpenACC data clause operations previously required that the variable operand implemented PointerLikeType interface. This was a reasonable constraint because the dialects currently mixed with `acc` do use pointers to represent variables. However, this forces the "pointer" abstraction to be exposed too early and some cases are not cleanly representable through this approach (more specifically FIR's `fix.box` abstraction).

Thus, relax this by allowing a variable to be a type which implements either `PointerLikeType` interface or `MappableType` interface.

---

Patch is 72.52 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122146.diff


6 Files Affected:

- (modified) mlir/docs/Dialects/OpenACCDialect.md (+83-15) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACC.h (+36-8) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td (+172-112) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td (+93) 
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp (+206-14) 
- (modified) mlir/unittests/Dialect/OpenACC/OpenACCOpsTest.cpp (+106-22) 


``````````diff
diff --git a/mlir/docs/Dialects/OpenACCDialect.md b/mlir/docs/Dialects/OpenACCDialect.md
index 2f1bb194a167d4..39218a9676ff34 100755
--- a/mlir/docs/Dialects/OpenACCDialect.md
+++ b/mlir/docs/Dialects/OpenACCDialect.md
@@ -274,28 +274,96 @@ reference counters are zero, a delete action is performed.
 
 ### Types
 
-There are a few acc dialect type categories to describe:
-* type of acc data clause operation input `varPtr`
-	- The type of `varPtr` must be pointer-like. This is done by
-	attaching the `PointerLikeType` interface to the appropriate MLIR
-	type. Although memory/storage concept is a lower level abstraction,
-	it is useful because the OpenACC model distinguishes between host
-	and device memory explicitly - and the mapping between the two is
-	done through pointers. Thus, by explicitly requiring it in the
-	dialect, the appropriate language frontend must create storage or
-	use type that satisfies the mapping constraint.
+Since the `acc dialect` is meant to be used alongside other dialects which
+represent the source language, appropriate use of types and type interfaces is
+key to ensuring compatibility. This section describes those considerations.
+
+#### Data Clause Operation Types
+
+Data clause operations (eg. `acc.copyin`) rely on the following type
+considerations:
+* type of acc data clause operation input `var`
+	- The type of `var` must be one with `PointerLikeType` or `MappableType`
+    interfaces attached. The first, `PointerLikeType`, is useful because
+	the OpenACC memory model distinguishes between host and device memory
+	explicitly - and the mapping between the two is	done through pointers. Thus,
+	by explicitly requiring it in the dialect, the appropriate language
+	frontend must create storage or	use type that satisfies the mapping
+	constraint. The second possibility, `MappableType` was added because
+	memory/storage concept is a lower level abstraction and not all dialects
+	choose to use a pointer abstraction especially in the case where semantics
+	are more complex (such as `fir.box` which represents Fortran descriptors
+	and is defined in the `fir` dialect used from `flang`).
 * type of result of acc data clause operations
 	- The type of the acc data clause operation is exactly the same as
-	`varPtr`. This was done intentionally instead of introducing an
-	`acc.ref/ptr` type so that IR compatibility and the dialect's
+	`var`. This was done intentionally instead of introducing specific `acc`
+	output types so that so that IR compatibility and the dialect's
 	existing strong type checking can be maintained. This is needed
 	since the `acc` dialect must live within another dialect whose type
-	system is unknown to it. The only constraint is that the appropriate
-	dialect type must use the `PointerLikeType` interface.
+	system is unknown to it.
+* variable type captured in `varType`
+    - When `var`'s type is `PointerLikeType`, the actual type of the target
+    may be lost. More specifically, dialects like `llvm` which use opaque
+	pointers, do not record the target variable's type. The use of this field
+	bridges this gap.
 * type of decomposed clauses
 	- Decomposed clauses, such as `acc.bounds` and `acc.declare_enter`
 	produce types to allow their results to be used only in specific
-	operations.
+	operations. These are synthetic types solely used for proper IR
+	construction.
+
+#### Pointer-Like Requirement
+
+The need to have pointer-type requirement in the acc dialect stems from
+a few different aspects:
+- Existing dialects like `hlfir`, `fir`, `cir`, `llvm` use a pointer
+representation for variables.
+- Reference counters (for data clauses) are described in terms of
+memory. In OpenACC spec 3.3 in section 2.6.7. It says: "A structured reference
+counter is incremented when entering each data or compute region that contain an
+explicit data clause or implicitly-determined data attributes for that section
+of memory". This implies addressability of memory.
+- Attach semantics (2.6.8 attachment counter) are specified using
+"address" terminology: "The attachment counter for a pointer is set to
+one whenever the pointer is attached to new target address, and
+incremented whenever an attach action for that pointer is performed for
+the same target address.
+
+#### Type Interfaces
+
+The `acc` dialect describes two different type interfaces which must be
+implemented and attached to the source dialect's types in order to allow use
+of data clause operations (eg. `acc.copyin`). They are as follows:
+* `PointerLikeType`
+  - The idea behind this interface is that variables end up being represented
+  as pointers in many dialects. More specifically, `fir`, `cir`, `llvm`
+  represent user declared local variables with some dialect specific form of
+  `alloca` operation which produce pointers. Globals, similarly, are referred by
+  their address through some form of `address_of` operation. Additionally, an
+  implementation for OpenACC runtime needs to distinguish between device and
+  host memory - also typically done by talking about pointers. So this type
+  interface requirement fits in naturally with OpenACC specification. Data
+  mapping operation semantics can often be simply described by a pointer and
+  size of the data it points to.
+* `MappableType`
+   - This interface was introduced because the `PointerLikeType` requirement
+  cannot represent cases when the source dialect does not use pointers. Also,
+  some cases, such as Fortran descriptor-backed arrays and Fortran optional
+  arguments, require decomposition into multiple steps. For example, in the
+  descriptor case, mapping of descriptor is needed, mapping of the data, and
+  implicit attach into device descriptor. In order to allow capturing all of
+  this complexity with a single data clause operation, the `MappableType`
+  interface was introduced. This is consistent with the dialect's goals
+  including being "able to regenerate the semantic equivalent of the user
+  pragmas".
+
+The intent is that a dialect's type system implements one of these two
+interfaces. And to be precise, a type should only implement one or the other
+(and not both) - since keeping them separate avoids ambiguity on what actually
+needs mapped. When `var` is `PointerLikeType`, the assumption is that the data
+pointed-to will be mapped. If the pointer-like type also implemented
+`MappableType` interface, it becomes ambiguous whether the data pointed to or
+the pointer itself is being mapped.
 
 ### Recipes
 
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
index cda07d6a913649..748cb7f28fc8c4 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
@@ -25,6 +25,7 @@
 #include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.h.inc"
 #include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.h.inc"
 #include "mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.h"
+#include "mlir/IR/Value.h"
 #include "mlir/Interfaces/ControlFlowInterfaces.h"
 #include "mlir/Interfaces/LoopLikeInterface.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
@@ -83,16 +84,31 @@ namespace acc {
 /// combined and the final mapping value would be 5 (4 | 1).
 enum OpenACCExecMapping { NONE = 0, VECTOR = 1, WORKER = 2, GANG = 4 };
 
-/// Used to obtain the `varPtr` from a data clause operation.
+/// Used to obtain the `var` from a data clause operation.
 /// Returns empty value if not a data clause operation or is a data exit
-/// operation with no `varPtr`.
-mlir::Value getVarPtr(mlir::Operation *accDataClauseOp);
-
-/// Used to obtain the `accPtr` from a data clause operation.
-/// When a data entry operation, it obtains its result `accPtr` value.
-/// If a data exit operation, it obtains its operand `accPtr` value.
+/// operation with no `var`.
+mlir::Value getVar(mlir::Operation *accDataClauseOp);
+
+/// Used to obtain the `var` from a data clause operation if it implements
+/// `PointerLikeType`.
+mlir::TypedValue<mlir::acc::PointerLikeType>
+getVarPtr(mlir::Operation *accDataClauseOp);
+
+/// Used to obtains the `varType` from a data clause operation which records
+/// the type of variable. When `var` is `PointerLikeType`, this returns
+/// the type of the pointer target.
+mlir::Type getVarType(mlir::Operation *accDataClauseOp);
+
+/// Used to obtain the `accVar` from a data clause operation.
+/// When a data entry operation, it obtains its result `accVar` value.
+/// If a data exit operation, it obtains its operand `accVar` value.
 /// Returns empty value if not a data clause operation.
-mlir::Value getAccPtr(mlir::Operation *accDataClauseOp);
+mlir::Value getAccVar(mlir::Operation *accDataClauseOp);
+
+/// Used to obtain the `accVar` from a data clause operation if it implements
+/// `PointerLikeType`.
+mlir::TypedValue<mlir::acc::PointerLikeType>
+getAccPtr(mlir::Operation *accDataClauseOp);
 
 /// Used to obtain the `varPtrPtr` from a data clause operation.
 /// Returns empty value if not a data clause operation.
@@ -136,6 +152,18 @@ mlir::ValueRange getDataOperands(mlir::Operation *accOp);
 /// Used to get a mutable range iterating over the data operands.
 mlir::MutableOperandRange getMutableDataOperands(mlir::Operation *accOp);
 
+/// Used to check whether the provided `type` implements the `PointerLikeType`
+/// interface.
+inline bool isPointerLikeType(mlir::Type type) {
+  return mlir::isa<mlir::acc::PointerLikeType>(type);
+}
+
+/// Used to check whether the provided `type` implements the `MappableType`
+/// interface.
+inline bool isMappableType(mlir::Type type) {
+  return mlir::isa<mlir::acc::MappableType>(type);
+}
+
 /// Used to obtain the attribute name for declare.
 static constexpr StringLiteral getDeclareAttrName() {
   return StringLiteral("acc.declare");
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 3ac265ac687561..a47f70b168066e 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -70,7 +70,14 @@ def IntOrIndex : AnyTypeOf<[AnyInteger, Index]>;
 
 // Simple alias to pointer-like interface to reduce verbosity.
 def OpenACC_PointerLikeType : TypeAlias<OpenACC_PointerLikeTypeInterface,
-	"pointer-like type">;
+    "pointer-like type">;
+def OpenACC_MappableType : TypeAlias<OpenACC_MappableTypeInterface,
+    "mappable type">;
+
+def OpenACC_AnyPointerOrMappableLike : TypeConstraint<Or<[OpenACC_PointerLikeType.predicate,
+    OpenACC_MappableType.predicate]>, "any pointer or mappable">;
+def OpenACC_AnyPointerOrMappableType : Type<OpenACC_AnyPointerOrMappableLike.predicate,
+    "any pointer or mappable">;
 
 // Define the OpenACC data clauses. There are a few cases where a modifier
 // is used, like create(zero), copyin(readonly), and copyout(zero). Since in
@@ -353,7 +360,8 @@ def OpenACC_DataBoundsOp : OpenACC_Op<"bounds",
         build($_builder, $_state,
           ::mlir::acc::DataBoundsType::get($_builder.getContext()),
           /*lowerbound=*/{}, /*upperbound=*/{}, extent,
-          /*stride=*/{}, /*strideInBytes=*/nullptr, /*startIdx=*/{});
+          /*stride=*/{}, /*strideInBytes=*/$_builder.getBoolAttr(false),
+          /*startIdx=*/{});
       }]
     >,
     OpBuilder<(ins "::mlir::Value":$lowerbound,
@@ -361,7 +369,8 @@ def OpenACC_DataBoundsOp : OpenACC_Op<"bounds",
         build($_builder, $_state,
           ::mlir::acc::DataBoundsType::get($_builder.getContext()),
           lowerbound, upperbound, /*extent=*/{},
-          /*stride=*/{}, /*strideInBytes=*/nullptr, /*startIdx=*/{});
+          /*stride=*/{}, /*strideInBytes=*/$_builder.getBoolAttr(false),
+          /*startIdx=*/{});
       }]
     >
   ];
@@ -396,10 +405,15 @@ class OpenACC_DataEntryOp<string mnemonic, string clause, string extraDescriptio
 
   let description = !strconcat(extraDescription, [{
     Description of arguments:
-    - `varPtr`: The address of variable to copy.
-    - `varPtrPtr`: Specifies the address of varPtr - only used when the variable
-    copied is a field in a struct. This is important for OpenACC due to implicit
-    attach semantics on data clauses (2.6.4).
+    - `var`: The variable to copy. Must be either `MappableType` or
+    `PointerLikeType`.
+    - `varType`: The type of the variable that is being copied. When `var` is
+    a `MappableType`, this matches the type of `var`. When `var` is a
+    `PointerLikeType`, this type holds information about the target of the
+    pointer.
+    - `varPtrPtr`: Specifies the address of the address of `var` - only used
+    when the variable copied is a field in a struct. This is important for
+    OpenACC due to implicit attach semantics on data clauses (2.6.4).
     - `bounds`: Used when copying just slice of array or array's bounds are not
     encoded in type. They are in rank order where rank 0 is inner-most dimension.
     - `asyncOperands` and `asyncOperandsDeviceType`:
@@ -456,42 +470,74 @@ class OpenACC_DataEntryOp<string mnemonic, string clause, string extraDescriptio
       }
       return nullptr;
     }
+    mlir::TypedValue<mlir::acc::PointerLikeType> getVarPtr() {
+      return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getVar());
+    }
+    mlir::TypedValue<mlir::acc::PointerLikeType> getAccPtr() {
+      return mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(getAccVar());
+    }
   }];
 
   let assemblyFormat = [{
-    `varPtr` `(` $varPtr `:` custom<VarPtrType>(type($varPtr), $varType)
+    custom<Var>($var) `:` custom<VarPtrType>(type($var), $varType)
     oilist(
         `varPtrPtr` `(` $varPtrPtr `:` type($varPtrPtr) `)`
       | `bounds` `(` $bounds `)`
       | `async` `(` custom<DeviceTypeOperands>($asyncOperands,
             type($asyncOperands), $asyncOperandsDeviceType) `)`
-    ) `->` type($accPtr) attr-dict
+    ) `->` type($accVar) attr-dict
   }];
 
   let hasVerifier = 1;
 
-  let builders = [OpBuilder<(ins "::mlir::Value":$varPtr, "bool":$structured,
-                                "bool":$implicit,
-                                CArg<"::mlir::ValueRange", "{}">:$bounds),
-                            [{
+  let builders = [
+    OpBuilder<(ins "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$varPtr,
+                   "bool":$structured, "bool":$implicit,
+                   CArg<"::mlir::ValueRange", "{}">:$bounds),
+      [{
         build($_builder, $_state, varPtr.getType(), varPtr,
           /*varType=*/::mlir::TypeAttr::get(
-            ::mlir::cast<::mlir::acc::PointerLikeType>(
-              varPtr.getType()).getElementType()),
+            varPtr.getType().getElementType()),
           /*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
           /*asyncOperandsDeviceType=*/nullptr,
           /*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
           /*structured=*/$_builder.getBoolAttr(structured),
           /*implicit=*/$_builder.getBoolAttr(implicit), /*name=*/nullptr);
       }]>,
-                  OpBuilder<(ins "::mlir::Value":$varPtr, "bool":$structured,
-                                "bool":$implicit, "const ::llvm::Twine &":$name,
-                                CArg<"::mlir::ValueRange", "{}">:$bounds),
-                            [{
+    OpBuilder<(ins "::mlir::TypedValue<::mlir::acc::PointerLikeType>":$varPtr,
+                   "bool":$structured, "bool":$implicit,
+                   "const ::llvm::Twine &":$name,
+                   CArg<"::mlir::ValueRange", "{}">:$bounds),
+      [{
         build($_builder, $_state, varPtr.getType(), varPtr,
           /*varType=*/::mlir::TypeAttr::get(
-            ::mlir::cast<::mlir::acc::PointerLikeType>(
-              varPtr.getType()).getElementType()),
+            varPtr.getType().getElementType()),
+          /*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
+          /*asyncOperandsDeviceType=*/nullptr,
+          /*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
+          /*structured=*/$_builder.getBoolAttr(structured),
+          /*implicit=*/$_builder.getBoolAttr(implicit),
+          /*name=*/$_builder.getStringAttr(name));
+      }]>,
+    OpBuilder<(ins "::mlir::TypedValue<::mlir::acc::MappableType>":$var,
+                   "bool":$structured, "bool":$implicit,
+                   CArg<"::mlir::ValueRange", "{}">:$bounds),
+      [{
+        build($_builder, $_state, var.getType(), var,
+          /*varType=*/::mlir::TypeAttr::get(var.getType()),
+          /*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
+          /*asyncOperandsDeviceType=*/nullptr,
+          /*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
+          /*structured=*/$_builder.getBoolAttr(structured),
+          /*implicit=*/$_builder.getBoolAttr(implicit), /*name=*/nullptr);
+      }]>,
+    OpBuilder<(ins "::mlir::TypedValue<::mlir::acc::MappableType>":$var,
+                   "bool":$structured, "bool":$implicit,
+                   "const ::llvm::Twine &":$name,
+                  CArg<"::mlir::ValueRange", "{}">:$bounds),
+      [{
+        build($_builder, $_state, var.getType(), var,
+          /*varType=*/::mlir::TypeAttr::get(var.getType()),
           /*varPtrPtr=*/{}, bounds, /*asyncOperands=*/{},
           /*asyncOperandsDeviceType=*/nullptr,
           /*asyncOnly=*/nullptr, /*dataClause=*/nullptr,
@@ -506,10 +552,10 @@ class OpenACC_DataEntryOp<string mnemonic, string clause, string extraDescriptio
 //===----------------------------------------------------------------------===//
 def OpenACC_PrivateOp : OpenACC_DataEntryOp<"private",
     "mlir::acc::DataClause::acc_private", "", [],
-    (ins OpenACC_PointerLikeTypeInterface:$varPtr)> {
+    (ins OpenACC_AnyPointerOrMappableType:$var)> {
   let summary = "Represents private semantics for acc private clause.";
-  let results = (outs Arg<OpenACC_PointerLikeTypeInterface,
-                          "Address of device variable",[MemWrite]>:$accPtr);
+  let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
+                          "Accelerator mapped variable",[MemWrite]>:$accVar);
   let extraClassDeclaration = extraClassDeclarationBase;
 }
 
@@ -518,11 +564,11 @@ def OpenACC_PrivateOp : OpenACC_DataEntryOp<"private",
 //===----------------------------------------------------------------------===//
 def OpenACC_FirstprivateOp : OpenACC_DataEntryOp<"firstprivate",
     "mlir::acc::DataClause::acc_firstprivate", "", [],
-    (ins Arg<OpenACC_PointerLikeTypeInterface,"Address of variable",[MemRead]>:$varPtr)> {
+    (ins Arg<OpenACC_AnyPointerOrMappableType,"Host variable",[MemRead]>:$var)> {
   let summary = "Represents firstprivate semantic for the acc firstprivate "
                 "clause.";
-  let results = (outs Arg<OpenACC_PointerLikeTypeInterface,
-                          "Address of device variable",[MemWrite]>:$accPtr);
+  let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
+                          "Accelerator mapped variable",[MemWrite]>:$accVar);
   let extraClassDeclaration = extraClassDeclarationBase;
 }
 
@@ -531,10 +577,10 @@ def OpenACC_FirstprivateOp : OpenACC_DataEntryOp<"firstprivate",
 //===----------------------------------------------------------------------===//
 def OpenACC_ReductionOp : OpenACC_DataEntryOp<"reduction",
     "mlir::acc::DataClause::acc_reduction", "", [],
-    (ins Arg<OpenACC_PointerLikeTypeInterface,"Address of variable",[MemRead]>:$varPtr)> {
+    (ins Arg<OpenACC_AnyPointerOrMappableType,"Host variable",[MemRead]>:$var)> {
   let summary = "Represents reduction semantics for acc reduction clause.";
-  let results = (outs Arg<OpenACC_PointerLikeTypeInterface,
-                          "Address of device variable",[MemWrite]>:$accPtr);
+  let results = (outs Arg<OpenACC_AnyPointerOrMappableType,
+                          "Accelerator mapped variable",[MemWrite]>:$accVar);
   let extraClassDeclaration = extraClassDeclarationBase;
 }
 
@@ -544,9 +590,9 @@ def OpenACC_ReductionOp : OpenACC_DataEntryOp<"reduction",
 def OpenACC_DevicePtrOp : OpenACC_DataEntryOp<"deviceptr",
     "mlir::acc::DataClause::acc_deviceptr", "",
     [MemoryEffects<[MemRead<OpenACC_RuntimeCounters>]>],
-    (ins OpenACC_PointerLikeTypeInterface:$varPtr)> {
+    (ins OpenACC_AnyPointerOrMappableType:$var)> {
   let summary = "Specifies that the variable pointer is a device pointer.";
-  let results = (outs OpenACC_PointerLikeTypeInterface:$accPtr);
+  let results = (outs OpenACC_AnyPointerOrMappableType:$ac...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/122146


More information about the Mlir-commits mailing list