[Mlir-commits] [flang] [mlir] [mlir][acc] add VariableInfo class to thread langauge specific information about privatized variables (PR #186368)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Mar 13 12:48:53 PDT 2026


https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/186368

>From a566eafe32e5f70bd8227cbbbb599cd2f0492210 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 13 Mar 2026 03:09:11 -0700
Subject: [PATCH 1/2] [mlir][acc] add VariableInfo class to thread langauge
 specific information about privatized variables

---
 .../Support/FIROpenACCTypeInterfaces.h        | 11 ++-
 .../OpenACC/Support/FortranVariableInfo.h     | 41 ++++++++++
 .../Support/FIROpenACCTypeInterfaces.cpp      | 71 +++++++++++++----
 .../OpenACC/Support/FIROpenACCUtils.cpp       | 14 ++--
 mlir/include/mlir/Dialect/OpenACC/OpenACC.h   |  1 +
 .../mlir/Dialect/OpenACC/OpenACCOps.td        |  8 +-
 .../Dialect/OpenACC/OpenACCTypeInterfaces.td  | 43 +++++++++-
 .../Dialect/OpenACC/OpenACCVariableInfo.h     | 78 +++++++++++++++++++
 mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp       | 44 +++++++----
 .../OpenACC/Transforms/ACCImplicitData.cpp    |  4 +-
 .../Dialect/OpenACC/TestRecipePopulate.cpp    |  8 +-
 11 files changed, 276 insertions(+), 47 deletions(-)
 create mode 100644 flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
 create mode 100644 mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h

diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
index 9d952a6130f0e..a31d3d9798ff8 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
@@ -80,22 +80,29 @@ struct OpenACCMappableModel
   mlir::acc::VariableTypeCategory getTypeCategory(mlir::Type type,
                                                   mlir::Value var) const;
 
+  mlir::acc::VariableInfo
+  genPrivateVariableInfo(mlir::Type type,
+                         mlir::TypedValue<mlir::acc::MappableType> var) const;
+
   mlir::Value generatePrivateInit(mlir::Type type, mlir::OpBuilder &builder,
                                   mlir::Location loc,
                                   mlir::TypedValue<mlir::acc::MappableType> var,
                                   llvm::StringRef varName,
                                   mlir::ValueRange extents, mlir::Value initVal,
+                                  const mlir::acc::VariableInfo &varInfo,
                                   bool &needsDestroy) const;
 
   bool generatePrivateDestroy(mlir::Type type, mlir::OpBuilder &builder,
                               mlir::Location loc, mlir::Value privatized,
-                              mlir::ValueRange bounds) const;
+                              mlir::ValueRange bounds,
+                              const mlir::acc::VariableInfo &varInfo) const;
 
   bool generateCopy(mlir::Type type, mlir::OpBuilder &mlirBuilder,
                     mlir::Location loc,
                     mlir::TypedValue<mlir::acc::MappableType> source,
                     mlir::TypedValue<mlir::acc::MappableType> dest,
-                    mlir::ValueRange bounds) const;
+                    mlir::ValueRange bounds,
+                    const mlir::acc::VariableInfo &varInfo) const;
 
   bool generateCombiner(mlir::Type type, mlir::OpBuilder &mlirBuilder,
                         mlir::Location loc,
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h b/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
new file mode 100644
index 0000000000000..0b5a9518bd00b
--- /dev/null
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
@@ -0,0 +1,41 @@
+//===- FortranVariableInfo.h - Fortran variable info for OpenACC -*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Fortran-specific variable information carried through the OpenACC type
+// interface helpers (generatePrivateInit, generateCopy,
+// generatePrivateDestroy). This allows recovering properties such as
+// OPTIONAL that are not representable in the FIR type system.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
+#define FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
+
+#include "mlir/Dialect/OpenACC/OpenACCVariableInfo.h"
+
+namespace fir::acc {
+
+class FortranVariableInfo : public mlir::acc::VariableInfoBase {
+public:
+  explicit FortranVariableInfo(bool mayBeOptional)
+      : VariableInfoBase(Language::Fortran), mayBeOptional_(mayBeOptional) {}
+
+  bool getMayBeOptional() const { return mayBeOptional_; }
+
+  static bool classof(const VariableInfoBase *base) {
+    return base->getLanguage() == Language::Fortran;
+  }
+
+private:
+  bool mayBeOptional_;
+};
+
+} // namespace fir::acc
+
+#endif // FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 5d427c9aab8ab..697ec33c167b8 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -24,6 +24,7 @@
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
 #include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+#include "flang/Optimizer/OpenACC/Support/FortranVariableInfo.h"
 #include "flang/Optimizer/Support/Utils.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
@@ -542,6 +543,30 @@ template mlir::acc::VariableTypeCategory
 OpenACCMappableModel<fir::PointerType>::getTypeCategory(mlir::Type type,
                                                         mlir::Value var) const;
 
+template <typename Ty>
+mlir::acc::VariableInfo OpenACCMappableModel<Ty>::genPrivateVariableInfo(
+    mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const {
+  hlfir::Entity entity{var};
+  return mlir::acc::VariableInfo(
+      std::make_unique<FortranVariableInfo>(entity.mayBeOptional()));
+}
+
+template mlir::acc::VariableInfo
+OpenACCMappableModel<fir::BaseBoxType>::genPrivateVariableInfo(
+    mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
+
+template mlir::acc::VariableInfo
+OpenACCMappableModel<fir::ReferenceType>::genPrivateVariableInfo(
+    mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
+
+template mlir::acc::VariableInfo
+OpenACCMappableModel<fir::HeapType>::genPrivateVariableInfo(
+    mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
+
+template mlir::acc::VariableInfo
+OpenACCMappableModel<fir::PointerType>::genPrivateVariableInfo(
+    mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
+
 static mlir::acc::VariableTypeCategory
 categorizePointee(mlir::Type pointer,
                   mlir::TypedValue<mlir::acc::PointerLikeType> varPtr,
@@ -717,7 +742,8 @@ template <typename Ty>
 mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
-    mlir::ValueRange bounds, mlir::Value initVal, bool &needsDestroy) const {
+    mlir::ValueRange bounds, mlir::Value initVal,
+    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const {
   mlir::ModuleOp mod = mlirBuilder.getInsertionBlock()
                            ->getParent()
                            ->getParentOfType<mlir::ModuleOp>();
@@ -900,31 +926,35 @@ template mlir::Value
 OpenACCMappableModel<fir::BaseBoxType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
-    mlir::ValueRange extents, mlir::Value initVal, bool &needsDestroy) const;
+    mlir::ValueRange extents, mlir::Value initVal,
+    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::ReferenceType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
-    mlir::ValueRange extents, mlir::Value initVal, bool &needsDestroy) const;
+    mlir::ValueRange extents, mlir::Value initVal,
+    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
 
 template mlir::Value OpenACCMappableModel<fir::HeapType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
-    mlir::ValueRange extents, mlir::Value initVal, bool &needsDestroy) const;
+    mlir::ValueRange extents, mlir::Value initVal,
+    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::PointerType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
-    mlir::ValueRange extents, mlir::Value initVal, bool &needsDestroy) const;
+    mlir::ValueRange extents, mlir::Value initVal,
+    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
 
 template <typename Ty>
 bool OpenACCMappableModel<Ty>::generateCopy(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> src,
-    mlir::TypedValue<mlir::acc::MappableType> dest,
-    mlir::ValueRange bounds) const {
+    mlir::TypedValue<mlir::acc::MappableType> dest, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const {
   mlir::ModuleOp mod =
       mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
   assert(mod && "failed to retrieve parent module");
@@ -961,19 +991,23 @@ bool OpenACCMappableModel<Ty>::generateCopy(
 template bool OpenACCMappableModel<fir::BaseBoxType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
-    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange) const;
+    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
+    const mlir::acc::VariableInfo &) const;
 template bool OpenACCMappableModel<fir::ReferenceType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
-    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange) const;
+    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
+    const mlir::acc::VariableInfo &) const;
 template bool OpenACCMappableModel<fir::PointerType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
-    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange) const;
+    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
+    const mlir::acc::VariableInfo &) const;
 template bool OpenACCMappableModel<fir::HeapType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
-    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange) const;
+    mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
+    const mlir::acc::VariableInfo &) const;
 
 template <typename Op>
 static mlir::Value genLogicalCombiner(fir::FirOpBuilder &builder,
@@ -1175,7 +1209,8 @@ template bool OpenACCMappableModel<fir::HeapType>::generateCombiner(
 template <typename Ty>
 bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds) const {
+    mlir::Value privatized, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const {
   hlfir::Entity inputVar = hlfir::Entity{privatized};
   mlir::ModuleOp mod =
       mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
@@ -1210,16 +1245,20 @@ bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
 
 template bool OpenACCMappableModel<fir::BaseBoxType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds) const;
+    mlir::Value privatized, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const;
 template bool OpenACCMappableModel<fir::ReferenceType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds) const;
+    mlir::Value privatized, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const;
 template bool OpenACCMappableModel<fir::HeapType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds) const;
+    mlir::Value privatized, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const;
 template bool OpenACCMappableModel<fir::PointerType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds) const;
+    mlir::Value privatized, mlir::ValueRange bounds,
+    const mlir::acc::VariableInfo &varInfo) const;
 
 template <typename Ty>
 mlir::Value OpenACCPointerLikeModel<Ty>::genAllocate(
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index a53ea9216f7ab..e331d968301fb 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -492,13 +492,15 @@ static RecipeOp genRecipeOp(
   auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
   assert(mappableTy &&
          "Expected that all variable types are considered mappable");
+  auto initArg = mlir::cast<MappableValue>(initBlock->getArgument(0));
+  mlir::acc::VariableInfo varInfo = mappableTy.genPrivateVariableInfo(initArg);
   bool needsDestroy = false;
   llvm::SmallVector<mlir::Value> initBounds =
       getRecipeBounds(builder, loc, dataOperationBounds,
                       initBlock->getArguments().drop_front(1));
   mlir::Value retVal = mappableTy.generatePrivateInit(
-      builder, loc, mlir::cast<MappableValue>(initBlock->getArgument(0)),
-      initName, initBounds, initValue, needsDestroy);
+      builder, loc, initArg, initName, initBounds, initValue, varInfo,
+      needsDestroy);
   mlir::acc::YieldOp::create(builder, loc, retVal);
   // Create destroy region and generate destruction if requested.
   if (needsDestroy) {
@@ -524,7 +526,7 @@ static RecipeOp genRecipeOp(
         getRecipeBounds(builder, loc, dataOperationBounds,
                         destroyBlock->getArguments().drop_front(2));
     [[maybe_unused]] bool success = mappableTy.generatePrivateDestroy(
-        builder, loc, destroyBlock->getArgument(1), destroyBounds);
+        builder, loc, destroyBlock->getArgument(1), destroyBounds, varInfo);
     assert(success && "failed to generate destroy region");
     mlir::acc::TerminatorOp::create(builder, loc);
   }
@@ -576,8 +578,10 @@ mlir::SymbolRefAttr fir::acc::createOrGetFirstprivateRecipe(
   auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
   assert(mappableTy &&
          "Expected that all variable types are considered mappable");
-  [[maybe_unused]] bool success =
-      mappableTy.generateCopy(builder, loc, source, destination, copyBounds);
+  mlir::acc::VariableInfo copyVarInfo =
+      mappableTy.genPrivateVariableInfo(source);
+  [[maybe_unused]] bool success = mappableTy.generateCopy(
+      builder, loc, source, destination, copyBounds, copyVarInfo);
   assert(success && "failed to generate copy");
   mlir::acc::TerminatorOp::create(builder, loc);
   return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
index 55fc8251a9bbd..9e7a15feb2e70 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
@@ -13,6 +13,7 @@
 #ifndef MLIR_DIALECT_OPENACC_OPENACC_H_
 #define MLIR_DIALECT_OPENACC_OPENACC_H_
 
+#include "mlir/Dialect/OpenACC/OpenACCVariableInfo.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/OpDefinition.h"
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 25e3dbd29043d..513e9c6d1dab1 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1444,11 +1444,13 @@ def OpenACC_PrivateRecipeOp
     /// and it must be a valid place for this operation to be inserted. The
     /// `recipeName` must be a unique name to prevent "redefinition of symbol"
     /// IR errors.
+    /// The `hostVar` is the original host variable from which the type and
+    /// language-specific metadata are derived.
     static std::optional<PrivateRecipeOp> createAndPopulate(
         ::mlir::OpBuilder &builder,
         ::mlir::Location loc,
         ::llvm::StringRef recipeName,
-        ::mlir::Type varType,
+        ::mlir::Value hostVar,
         ::llvm::StringRef varName = "",
         ::mlir::ValueRange bounds = {});
 
@@ -1569,11 +1571,13 @@ def OpenACC_FirstprivateRecipeOp
     /// and it must be a valid place for this operation to be inserted. The
     /// `recipeName` must be a unique name to prevent "redefinition of symbol"
     /// IR errors.
+    /// The `hostVar` is the original host variable from which the type and
+    /// language-specific metadata are derived.
     static std::optional<FirstprivateRecipeOp> createAndPopulate(
         ::mlir::OpBuilder &builder,
         ::mlir::Location loc,
         ::llvm::StringRef recipeName,
-        ::mlir::Type varType,
+        ::mlir::Value hostVar,
         ::llvm::StringRef varName = "",
         ::mlir::ValueRange bounds = {});
   }];
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
index 97def012990c0..9ba5c4d1ad250 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
@@ -354,6 +354,26 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
         return ::mlir::acc::VariableTypeCategory::uncategorized;
       }]
     >,
+    InterfaceMethod<
+      /*description=*/[{
+        Produces a `VariableInfo` for a host variable. This enables passing
+        language-specific metadata (that is not captured in the type system)
+        to recipe code-generation helpers such as `generatePrivateInit`,
+        `generateCopy`, and `generatePrivateDestroy`.
+
+        The `var` is the host variable from which to extract information.
+        Implementations may use `getDefiningOp()` on this value.
+
+        The default implementation returns a null `VariableInfo`.
+      }],
+      /*retTy=*/"::mlir::acc::VariableInfo",
+      /*methodName=*/"genPrivateVariableInfo",
+      /*args=*/(ins "::mlir::TypedValue<::mlir::acc::MappableType>":$var),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return ::mlir::acc::VariableInfo();
+      }]
+    >,
     InterfaceMethod<
       /*description=*/[{
         Generates the operations that would be normally placed in a recipe's
@@ -368,6 +388,10 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
         The `initVal` can be empty - it is primarily needed for reductions
         to ensure the variable is also initialized with appropriate value.
 
+        The `varInfo` carries language-specific metadata about the original
+        host variable (produced by `genPrivateVariableInfo`). Implementations
+        can `dyn_cast` to their language-specific subclass.
+
         The `needsDestroy` out-parameter is set by implementations to indicate
         that destruction code must be generated after the returned private
         variable usages, typically in the destroy region of recipe operations
@@ -387,6 +411,7 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
                     "::llvm::StringRef":$varName,
                     "::mlir::ValueRange":$extents,
                     "::mlir::Value":$initVal,
+                    "const ::mlir::acc::VariableInfo &":$varInfo,
                     "bool &":$needsDestroy),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
@@ -395,6 +420,12 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
     >,
     InterfaceMethod<
       /*description=*/[{
+        Generates copy operations from one mappable variable to another.
+        Typically used to implement firstprivate initialization.
+
+        The `varInfo` carries language-specific metadata about the original
+        host variable (produced by `genPrivateVariableInfo`). Implementations
+        can `dyn_cast` to their language-specific subclass.
       }],
       /*retTy=*/"bool",
       /*methodName=*/"generateCopy",
@@ -402,7 +433,8 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
                     "::mlir::Location":$loc,
                     "::mlir::TypedValue<::mlir::acc::MappableType>":$from,
                     "::mlir::TypedValue<::mlir::acc::MappableType>":$to,
-                    "::mlir::ValueRange":$bounds),
+                    "::mlir::ValueRange":$bounds,
+                    "const ::mlir::acc::VariableInfo &":$varInfo),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return false;
@@ -438,7 +470,11 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
         this function should be a no-op and return `true`.
 
         The `bounds` must be passed when only a section of the variable was
-        privatized. 
+        privatized.
+
+        The `varInfo` carries language-specific metadata about the original
+        host variable (produced by `genPrivateVariableInfo`). Implementations
+        can `dyn_cast` to their language-specific subclass.
 
         Returns true if destruction was successfully generated or deemed not
         necessary, false otherwise.
@@ -448,7 +484,8 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
       /*args=*/(ins "::mlir::OpBuilder &":$builder,
                     "::mlir::Location":$loc,
                     "::mlir::Value":$privatized,
-                    "::mlir::ValueRange":$extents),
+                    "::mlir::ValueRange":$extents,
+                    "const ::mlir::acc::VariableInfo &":$varInfo),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return true;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h
new file mode 100644
index 0000000000000..75bd92a4935ce
--- /dev/null
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h
@@ -0,0 +1,78 @@
+//===- OpenACCVariableInfo.h - OpenACC Variable Info -------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the VariableInfo classes used to carry language-specific
+// variable metadata through the OpenACC type interfaces. The OpenACC dialect
+// type interface methods (e.g., generatePrivateInit, generateCopy,
+// generatePrivateDestroy) receive a VariableInfo that language implementations
+// can dyn_cast to their own subclass to recover information not available from
+// the type system alone (e.g., whether a Fortran variable is OPTIONAL).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
+#define MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
+
+#include <memory>
+
+namespace mlir::acc {
+
+/// Base class for language-specific variable information.
+///
+/// Languages should derive from this class and use LLVM-style RTTI
+/// (via a `classof` static method and the `getLanguage()` discriminator) so
+/// that consumers can dyn_cast to the concrete type.
+///
+/// See llvm/docs/HowToSetUpLLVMStyleRTTI.rst for details on the pattern.
+class VariableInfoBase {
+public:
+  enum Language { Fortran, C, CPP };
+
+  Language getLanguage() const { return lang; }
+  virtual ~VariableInfoBase() = default;
+
+protected:
+  explicit VariableInfoBase(Language lang) : lang(lang) {}
+
+private:
+  Language lang;
+};
+
+/// A type-erased, move-only wrapper for language-specific variable
+/// information. This is modeled after the PointerUnion discrimination pattern:
+/// language implementations can use `dyn_cast<ConcreteType>()` to recover
+/// their specific metadata.
+///
+/// A default-constructed VariableInfo is null and carries no information.
+class VariableInfo {
+public:
+  VariableInfo() = default;
+  VariableInfo(std::nullptr_t) {}
+  explicit VariableInfo(std::unique_ptr<VariableInfoBase> impl)
+      : impl(std::move(impl)) {}
+
+  VariableInfo(VariableInfo &&) = default;
+  VariableInfo &operator=(VariableInfo &&) = default;
+
+  template <typename T>
+  const T *dyn_cast() const {
+    if (!impl || !T::classof(impl.get()))
+      return nullptr;
+    return static_cast<const T *>(impl.get());
+  }
+
+  explicit operator bool() const { return impl != nullptr; }
+
+private:
+  std::unique_ptr<VariableInfoBase> impl;
+};
+
+} // namespace mlir::acc
+
+#endif // MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 800305ffb36c5..c8545a8373a97 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -1543,10 +1543,16 @@ struct RemoveConstantIfConditionWithRegion : public OpRewritePattern<OpTy> {
 /// Create and populate an init region for privatization recipes.
 /// Returns success if the region is populated, failure otherwise.
 /// Sets needsFree to indicate if the allocated memory requires deallocation.
+/// The `hostVar` is the original host variable used to derive
+/// language-specific metadata via `genPrivateVariableInfo`.
+/// The `varInfo` output parameter is set to the variable info produced.
 static LogicalResult createInitRegion(OpBuilder &builder, Location loc,
-                                      Region &initRegion, Type varType,
+                                      Region &initRegion, Value hostVar,
                                       StringRef varName, ValueRange bounds,
-                                      bool &needsFree) {
+                                      bool &needsFree,
+                                      acc::VariableInfo &varInfo) {
+  Type varType = hostVar.getType();
+
   // Create init block with arguments: original value + bounds
   SmallVector<Type> argTypes{varType};
   SmallVector<Location> argLocs{loc};
@@ -1568,8 +1574,10 @@ static LogicalResult createInitRegion(OpBuilder &builder, Location loc,
   if (isa<MappableType>(varType)) {
     auto mappableTy = cast<MappableType>(varType);
     auto typedVar = cast<TypedValue<MappableType>>(blockArgVar);
+    auto typedHostVar = cast<TypedValue<MappableType>>(hostVar);
+    varInfo = mappableTy.genPrivateVariableInfo(typedHostVar);
     privatizedValue = mappableTy.generatePrivateInit(
-        builder, loc, typedVar, varName, bounds, {}, needsFree);
+        builder, loc, typedVar, varName, bounds, {}, varInfo, needsFree);
     if (!privatizedValue)
       return failure();
   } else {
@@ -1635,9 +1643,12 @@ static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,
 
 /// Create and populate a destroy region for privatization recipes.
 /// Returns success if the region is populated, failure otherwise.
+/// The `varInfo` carries language-specific metadata produced by
+/// `createInitRegion`.
 static LogicalResult createDestroyRegion(OpBuilder &builder, Location loc,
                                          Region &destroyRegion, Type varType,
-                                         Value allocRes, ValueRange bounds) {
+                                         Value allocRes, ValueRange bounds,
+                                         const acc::VariableInfo &varInfo) {
   // Create destroy block with arguments: original value + privatized value +
   // bounds
   SmallVector<Type> destroyArgTypes{varType, varType};
@@ -1655,7 +1666,8 @@ static LogicalResult createDestroyRegion(OpBuilder &builder, Location loc,
       cast<TypedValue<PointerLikeType>>(destroyBlock->getArgument(1));
   if (isa<MappableType>(varType)) {
     auto mappableTy = cast<MappableType>(varType);
-    if (!mappableTy.generatePrivateDestroy(builder, loc, varToFree, bounds))
+    if (!mappableTy.generatePrivateDestroy(builder, loc, varToFree, bounds,
+                                           varInfo))
       return failure();
   } else {
     assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");
@@ -1717,8 +1729,10 @@ LogicalResult acc::PrivateRecipeOp::verifyRegions() {
 
 std::optional<PrivateRecipeOp>
 PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
-                                   StringRef recipeName, Type varType,
+                                   StringRef recipeName, Value hostVar,
                                    StringRef varName, ValueRange bounds) {
+  Type varType = hostVar.getType();
+
   // First, validate that we can handle this variable type
   bool isMappable = isa<MappableType>(varType);
   bool isPointerLike = isa<PointerLikeType>(varType);
@@ -1734,8 +1748,9 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
 
   // Populate the init region
   bool needsFree = false;
-  if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType,
-                              varName, bounds, needsFree))) {
+  acc::VariableInfo varInfo;
+  if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
+                              varName, bounds, needsFree, varInfo))) {
     recipe.erase();
     return std::nullopt;
   }
@@ -1748,7 +1763,7 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
     Value allocRes = yieldOp.getOperand(0);
 
     if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),
-                                   varType, allocRes, bounds))) {
+                                   varType, allocRes, bounds, varInfo))) {
       recipe.erase();
       return std::nullopt;
     }
@@ -1811,8 +1826,10 @@ LogicalResult acc::FirstprivateRecipeOp::verifyRegions() {
 
 std::optional<FirstprivateRecipeOp>
 FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
-                                        StringRef recipeName, Type varType,
+                                        StringRef recipeName, Value hostVar,
                                         StringRef varName, ValueRange bounds) {
+  Type varType = hostVar.getType();
+
   // First, validate that we can handle this variable type
   bool isMappable = isa<MappableType>(varType);
   bool isPointerLike = isa<PointerLikeType>(varType);
@@ -1828,8 +1845,9 @@ FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
 
   // Populate the init region
   bool needsFree = false;
-  if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType,
-                              varName, bounds, needsFree))) {
+  acc::VariableInfo varInfo;
+  if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
+                              varName, bounds, needsFree, varInfo))) {
     recipe.erase();
     return std::nullopt;
   }
@@ -1849,7 +1867,7 @@ FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
     Value allocRes = yieldOp.getOperand(0);
 
     if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),
-                                   varType, allocRes, bounds))) {
+                                   varType, allocRes, bounds, varInfo))) {
       recipe.erase();
       return std::nullopt;
     }
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
index 3b83a25429234..95c8d1076ccb0 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
@@ -365,7 +365,7 @@ ACCImplicitData::generatePrivateRecipe(ModuleOp &module, Value var,
   builder.setInsertionPointToStart(module.getBody());
 
   auto recipe =
-      acc::PrivateRecipeOp::createAndPopulate(builder, loc, recipeName, type);
+      acc::PrivateRecipeOp::createAndPopulate(builder, loc, recipeName, var);
   if (!recipe.has_value())
     return accSupport.emitNYI(loc, "implicit private"), nullptr;
   return recipe.value();
@@ -390,7 +390,7 @@ ACCImplicitData::generateFirstprivateRecipe(ModuleOp &module, Value var,
   builder.setInsertionPointToStart(module.getBody());
 
   auto recipe = acc::FirstprivateRecipeOp::createAndPopulate(builder, loc,
-                                                             recipeName, type);
+                                                             recipeName, var);
   if (!recipe.has_value())
     return accSupport.emitNYI(loc, "implicit firstprivate"), nullptr;
   return recipe.value();
diff --git a/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp b/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
index 2506ca4fc39ce..7b71f2c5f3b20 100644
--- a/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
+++ b/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
@@ -80,15 +80,15 @@ void TestRecipePopulatePass::runOnOperation() {
     ValueRange bounds; // No bounds for memref tests
 
     if (recipeType == "private") {
-      auto recipe = PrivateRecipeOp::createAndPopulate(
-          builder, loc, recipeName, var.getType(), varName, bounds);
+      auto recipe = PrivateRecipeOp::createAndPopulate(builder, loc, recipeName,
+                                                       var, varName, bounds);
 
       if (!recipe) {
         op->emitError("Failed to create private recipe for ") << varName;
       }
     } else if (recipeType == "firstprivate") {
       auto recipe = FirstprivateRecipeOp::createAndPopulate(
-          builder, loc, recipeName, var.getType(), varName, bounds);
+          builder, loc, recipeName, var, varName, bounds);
 
       if (!recipe) {
         op->emitError("Failed to create firstprivate recipe for ") << varName;
@@ -101,7 +101,7 @@ void TestRecipePopulatePass::runOnOperation() {
       std::string privName = "private_from_firstprivate_" + varName;
 
       auto firstpriv = FirstprivateRecipeOp::createAndPopulate(
-          builder, loc, firstprivName, var.getType(), varName, bounds);
+          builder, loc, firstprivName, var, varName, bounds);
 
       if (!firstpriv) {
         op->emitError("Failed to create firstprivate recipe for ") << varName;

>From cbbc83c135a0a1a9582bc76454b1d902937c1307 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 13 Mar 2026 12:24:38 -0700
Subject: [PATCH 2/2] use Attribute instead of custom class

---
 .../include/flang/Optimizer/Dialect/FIRAttr.h |  1 +
 .../flang/Optimizer/Dialect/FIRAttr.td        | 13 +++
 .../Support/FIROpenACCTypeInterfaces.h        |  8 +-
 .../OpenACC/Support/FortranVariableInfo.h     | 41 ----------
 .../Support/FIROpenACCTypeInterfaces.cpp      | 45 +++++------
 .../OpenACC/Support/FIROpenACCUtils.cpp       |  5 +-
 .../mlir/Dialect/OpenACC/OpenACCAttributes.td | 24 ++++++
 .../mlir/Dialect/OpenACC/OpenACCOps.td        |  1 +
 .../Dialect/OpenACC/OpenACCTypeInterfaces.td  | 10 +--
 .../Dialect/OpenACC/OpenACCVariableInfo.h     | 80 ++++++-------------
 mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp       |  8 +-
 11 files changed, 101 insertions(+), 135 deletions(-)
 delete mode 100644 flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
 create mode 100644 mlir/include/mlir/Dialect/OpenACC/OpenACCAttributes.td

diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.h b/flang/include/flang/Optimizer/Dialect/FIRAttr.h
index 06ec1f57173c3..ec6b7ff48329e 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.h
@@ -13,6 +13,7 @@
 #ifndef FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H
 #define FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H
 
+#include "mlir/Dialect/OpenACC/OpenACCVariableInfo.h"
 #include "mlir/IR/BuiltinAttributes.h"
 
 namespace mlir {
diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
index b23d28fdde55c..3bb1f443f3a5d 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
@@ -14,6 +14,7 @@
 #define FIR_DIALECT_FIR_ATTRS
 
 include "flang/Optimizer/Dialect/FIRDialect.td"
+include "mlir/Dialect/OpenACC/OpenACCAttributes.td"
 include "mlir/IR/EnumAttr.td"
 
 class fir_Attr<string name> : AttrDef<FIROpsDialect, name>;
@@ -262,4 +263,16 @@ def fir_UseRenameAttr : fir_Attr<"UseRename"> {
   let assemblyFormat = "`<` $local_name `,` $symbol `>`";
 }
 
+// Fortran-specific variable information for OpenACC.
+// Carries metadata that cannot be recovered from the FIR type system alone
+// and is required in the FIR implementation of OpenACC type interfaces.
+def fir_OpenACCFortranVariableInfoAttr
+    : AttrDef<FIROpsDialect, "OpenACCFortranVariableInfo",
+              [OpenACC_IsVariableInfoAttr],
+              "::mlir::acc::VariableInfoAttr"> {
+  let mnemonic = "acc_var_info";
+  let parameters = (ins "bool":$mayBeOptional);
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
 #endif // FIR_DIALECT_FIR_ATTRS
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
index a31d3d9798ff8..01a1e19afd74b 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
@@ -80,7 +80,7 @@ struct OpenACCMappableModel
   mlir::acc::VariableTypeCategory getTypeCategory(mlir::Type type,
                                                   mlir::Value var) const;
 
-  mlir::acc::VariableInfo
+  mlir::acc::VariableInfoAttr
   genPrivateVariableInfo(mlir::Type type,
                          mlir::TypedValue<mlir::acc::MappableType> var) const;
 
@@ -89,20 +89,20 @@ struct OpenACCMappableModel
                                   mlir::TypedValue<mlir::acc::MappableType> var,
                                   llvm::StringRef varName,
                                   mlir::ValueRange extents, mlir::Value initVal,
-                                  const mlir::acc::VariableInfo &varInfo,
+                                  mlir::acc::VariableInfoAttr varInfo,
                                   bool &needsDestroy) const;
 
   bool generatePrivateDestroy(mlir::Type type, mlir::OpBuilder &builder,
                               mlir::Location loc, mlir::Value privatized,
                               mlir::ValueRange bounds,
-                              const mlir::acc::VariableInfo &varInfo) const;
+                              mlir::acc::VariableInfoAttr varInfo) const;
 
   bool generateCopy(mlir::Type type, mlir::OpBuilder &mlirBuilder,
                     mlir::Location loc,
                     mlir::TypedValue<mlir::acc::MappableType> source,
                     mlir::TypedValue<mlir::acc::MappableType> dest,
                     mlir::ValueRange bounds,
-                    const mlir::acc::VariableInfo &varInfo) const;
+                    mlir::acc::VariableInfoAttr varInfo) const;
 
   bool generateCombiner(mlir::Type type, mlir::OpBuilder &mlirBuilder,
                         mlir::Location loc,
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h b/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
deleted file mode 100644
index 0b5a9518bd00b..0000000000000
--- a/flang/include/flang/Optimizer/OpenACC/Support/FortranVariableInfo.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//===- FortranVariableInfo.h - Fortran variable info for OpenACC -*- C++
-//-*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Fortran-specific variable information carried through the OpenACC type
-// interface helpers (generatePrivateInit, generateCopy,
-// generatePrivateDestroy). This allows recovering properties such as
-// OPTIONAL that are not representable in the FIR type system.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
-#define FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
-
-#include "mlir/Dialect/OpenACC/OpenACCVariableInfo.h"
-
-namespace fir::acc {
-
-class FortranVariableInfo : public mlir::acc::VariableInfoBase {
-public:
-  explicit FortranVariableInfo(bool mayBeOptional)
-      : VariableInfoBase(Language::Fortran), mayBeOptional_(mayBeOptional) {}
-
-  bool getMayBeOptional() const { return mayBeOptional_; }
-
-  static bool classof(const VariableInfoBase *base) {
-    return base->getLanguage() == Language::Fortran;
-  }
-
-private:
-  bool mayBeOptional_;
-};
-
-} // namespace fir::acc
-
-#endif // FLANG_OPTIMIZER_OPENACC_FORTRANVARIABLEINFO_H
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 697ec33c167b8..5526dad0bffce 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -24,7 +24,6 @@
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
 #include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
-#include "flang/Optimizer/OpenACC/Support/FortranVariableInfo.h"
 #include "flang/Optimizer/Support/Utils.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
@@ -544,26 +543,26 @@ OpenACCMappableModel<fir::PointerType>::getTypeCategory(mlir::Type type,
                                                         mlir::Value var) const;
 
 template <typename Ty>
-mlir::acc::VariableInfo OpenACCMappableModel<Ty>::genPrivateVariableInfo(
+mlir::acc::VariableInfoAttr OpenACCMappableModel<Ty>::genPrivateVariableInfo(
     mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const {
   hlfir::Entity entity{var};
-  return mlir::acc::VariableInfo(
-      std::make_unique<FortranVariableInfo>(entity.mayBeOptional()));
+  return fir::OpenACCFortranVariableInfoAttr::get(var.getContext(),
+                                                  entity.mayBeOptional());
 }
 
-template mlir::acc::VariableInfo
+template mlir::acc::VariableInfoAttr
 OpenACCMappableModel<fir::BaseBoxType>::genPrivateVariableInfo(
     mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
 
-template mlir::acc::VariableInfo
+template mlir::acc::VariableInfoAttr
 OpenACCMappableModel<fir::ReferenceType>::genPrivateVariableInfo(
     mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
 
-template mlir::acc::VariableInfo
+template mlir::acc::VariableInfoAttr
 OpenACCMappableModel<fir::HeapType>::genPrivateVariableInfo(
     mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
 
-template mlir::acc::VariableInfo
+template mlir::acc::VariableInfoAttr
 OpenACCMappableModel<fir::PointerType>::genPrivateVariableInfo(
     mlir::Type type, mlir::TypedValue<mlir::acc::MappableType> var) const;
 
@@ -743,7 +742,7 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange bounds, mlir::Value initVal,
-    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const {
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const {
   mlir::ModuleOp mod = mlirBuilder.getInsertionBlock()
                            ->getParent()
                            ->getParentOfType<mlir::ModuleOp>();
@@ -927,34 +926,34 @@ OpenACCMappableModel<fir::BaseBoxType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::ReferenceType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
 
 template mlir::Value OpenACCMappableModel<fir::HeapType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::PointerType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    const mlir::acc::VariableInfo &varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
 
 template <typename Ty>
 bool OpenACCMappableModel<Ty>::generateCopy(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> src,
     mlir::TypedValue<mlir::acc::MappableType> dest, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const {
+    mlir::acc::VariableInfoAttr varInfo) const {
   mlir::ModuleOp mod =
       mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
   assert(mod && "failed to retrieve parent module");
@@ -992,22 +991,22 @@ template bool OpenACCMappableModel<fir::BaseBoxType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
     mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
-    const mlir::acc::VariableInfo &) const;
+    mlir::acc::VariableInfoAttr) const;
 template bool OpenACCMappableModel<fir::ReferenceType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
     mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
-    const mlir::acc::VariableInfo &) const;
+    mlir::acc::VariableInfoAttr) const;
 template bool OpenACCMappableModel<fir::PointerType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
     mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
-    const mlir::acc::VariableInfo &) const;
+    mlir::acc::VariableInfoAttr) const;
 template bool OpenACCMappableModel<fir::HeapType>::generateCopy(
     mlir::Type, mlir::OpBuilder &, mlir::Location,
     mlir::TypedValue<mlir::acc::MappableType>,
     mlir::TypedValue<mlir::acc::MappableType>, mlir::ValueRange,
-    const mlir::acc::VariableInfo &) const;
+    mlir::acc::VariableInfoAttr) const;
 
 template <typename Op>
 static mlir::Value genLogicalCombiner(fir::FirOpBuilder &builder,
@@ -1210,7 +1209,7 @@ template <typename Ty>
 bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::Value privatized, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const {
+    mlir::acc::VariableInfoAttr varInfo) const {
   hlfir::Entity inputVar = hlfir::Entity{privatized};
   mlir::ModuleOp mod =
       mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
@@ -1246,19 +1245,19 @@ bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
 template bool OpenACCMappableModel<fir::BaseBoxType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::Value privatized, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const;
+    mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::ReferenceType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::Value privatized, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const;
+    mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::HeapType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::Value privatized, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const;
+    mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::PointerType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::Value privatized, mlir::ValueRange bounds,
-    const mlir::acc::VariableInfo &varInfo) const;
+    mlir::acc::VariableInfoAttr varInfo) const;
 
 template <typename Ty>
 mlir::Value OpenACCPointerLikeModel<Ty>::genAllocate(
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index e331d968301fb..68bebc00cbd5f 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -493,7 +493,8 @@ static RecipeOp genRecipeOp(
   assert(mappableTy &&
          "Expected that all variable types are considered mappable");
   auto initArg = mlir::cast<MappableValue>(initBlock->getArgument(0));
-  mlir::acc::VariableInfo varInfo = mappableTy.genPrivateVariableInfo(initArg);
+  mlir::acc::VariableInfoAttr varInfo =
+      mappableTy.genPrivateVariableInfo(initArg);
   bool needsDestroy = false;
   llvm::SmallVector<mlir::Value> initBounds =
       getRecipeBounds(builder, loc, dataOperationBounds,
@@ -578,7 +579,7 @@ mlir::SymbolRefAttr fir::acc::createOrGetFirstprivateRecipe(
   auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
   assert(mappableTy &&
          "Expected that all variable types are considered mappable");
-  mlir::acc::VariableInfo copyVarInfo =
+  mlir::acc::VariableInfoAttr copyVarInfo =
       mappableTy.genPrivateVariableInfo(source);
   [[maybe_unused]] bool success = mappableTy.generateCopy(
       builder, loc, source, destination, copyBounds, copyVarInfo);
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCAttributes.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCAttributes.td
new file mode 100644
index 0000000000000..4c2efba461184
--- /dev/null
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCAttributes.td
@@ -0,0 +1,24 @@
+//===- OpenACCAttributes.td - OpenACC Attributes -----------*- tablegen -*-===//
+//
+// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines OpenACC Attributes than can be extended by the dialects outside of
+// of OpenACC.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPENACC_ATTRIBUTES
+#define OPENACC_ATTRIBUTES
+
+include "mlir/IR/AttrTypeBase.td"
+
+// Trait for attributes that carry OpenACC variable information.
+def OpenACC_IsVariableInfoAttr : NativeAttrTrait<"IsVariableInfo"> {
+  let cppNamespace = "::mlir::acc::AttributeTrait";
+}
+
+#endif // OPENACC_ATTRIBUTES
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 513e9c6d1dab1..0eec7fb352591 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -20,6 +20,7 @@ include "mlir/IR/BuiltinTypes.td"
 include "mlir/IR/EnumAttr.td"
 include "mlir/IR/OpBase.td"
 include "mlir/IR/SymbolInterfaces.td"
+include "mlir/Dialect/OpenACC/OpenACCAttributes.td"
 include "mlir/Dialect/OpenACC/OpenACCBase.td"
 include "mlir/Dialect/OpenACC/OpenACCOpsTypes.td"
 include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td"
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
index 9ba5c4d1ad250..3bd4e5c679659 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
@@ -366,12 +366,12 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
 
         The default implementation returns a null `VariableInfo`.
       }],
-      /*retTy=*/"::mlir::acc::VariableInfo",
+      /*retTy=*/"::mlir::acc::VariableInfoAttr",
       /*methodName=*/"genPrivateVariableInfo",
       /*args=*/(ins "::mlir::TypedValue<::mlir::acc::MappableType>":$var),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return ::mlir::acc::VariableInfo();
+        return ::mlir::acc::VariableInfoAttr();
       }]
     >,
     InterfaceMethod<
@@ -411,7 +411,7 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
                     "::llvm::StringRef":$varName,
                     "::mlir::ValueRange":$extents,
                     "::mlir::Value":$initVal,
-                    "const ::mlir::acc::VariableInfo &":$varInfo,
+                    "::mlir::acc::VariableInfoAttr":$varInfo,
                     "bool &":$needsDestroy),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
@@ -434,7 +434,7 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
                     "::mlir::TypedValue<::mlir::acc::MappableType>":$from,
                     "::mlir::TypedValue<::mlir::acc::MappableType>":$to,
                     "::mlir::ValueRange":$bounds,
-                    "const ::mlir::acc::VariableInfo &":$varInfo),
+                    "::mlir::acc::VariableInfoAttr":$varInfo),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return false;
@@ -485,7 +485,7 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
                     "::mlir::Location":$loc,
                     "::mlir::Value":$privatized,
                     "::mlir::ValueRange":$extents,
-                    "const ::mlir::acc::VariableInfo &":$varInfo),
+                    "::mlir::acc::VariableInfoAttr":$varInfo),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return true;
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h
index 75bd92a4935ce..95009fdb53fb4 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCVariableInfo.h
@@ -1,5 +1,4 @@
-//===- OpenACCVariableInfo.h - OpenACC Variable Info -------------*- C++
-//-*-===//
+//===- OpenACCVariableInfo.h - OpenACC Variable Info Attr -------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,72 +6,41 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file defines the VariableInfo classes used to carry language-specific
-// variable metadata through the OpenACC type interfaces. The OpenACC dialect
-// type interface methods (e.g., generatePrivateInit, generateCopy,
-// generatePrivateDestroy) receive a VariableInfo that language implementations
-// can dyn_cast to their own subclass to recover information not available from
-// the type system alone (e.g., whether a Fortran variable is OPTIONAL).
+// This file defines the VariableInfoAttr base class and the IsVariableInfo
+// trait.
+//
+// Any dialect can define Language-specific variable metadata attribute by
+// manually attaching the IsVariableInfo trait and using VariableInfoAttr as
+// the baseCppClass.
 //
 //===----------------------------------------------------------------------===//
 
 #ifndef MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
 #define MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
 
-#include <memory>
-
-namespace mlir::acc {
-
-/// Base class for language-specific variable information.
-///
-/// Languages should derive from this class and use LLVM-style RTTI
-/// (via a `classof` static method and the `getLanguage()` discriminator) so
-/// that consumers can dyn_cast to the concrete type.
-///
-/// See llvm/docs/HowToSetUpLLVMStyleRTTI.rst for details on the pattern.
-class VariableInfoBase {
-public:
-  enum Language { Fortran, C, CPP };
-
-  Language getLanguage() const { return lang; }
-  virtual ~VariableInfoBase() = default;
+#include "mlir/IR/Attributes.h"
 
-protected:
-  explicit VariableInfoBase(Language lang) : lang(lang) {}
+namespace mlir {
+namespace acc {
+namespace AttributeTrait {
+/// Trait attached to attributes that are OpenACC variable info attributes.
+template <typename ConcreteType>
+struct IsVariableInfo
+    : public mlir::AttributeTrait::TraitBase<ConcreteType, IsVariableInfo> {};
+} // namespace AttributeTrait
 
-private:
-  Language lang;
-};
-
-/// A type-erased, move-only wrapper for language-specific variable
-/// information. This is modeled after the PointerUnion discrimination pattern:
-/// language implementations can use `dyn_cast<ConcreteType>()` to recover
-/// their specific metadata.
-///
-/// A default-constructed VariableInfo is null and carries no information.
-class VariableInfo {
+/// Base attribute class for language-specific variable information carried
+/// through the OpenACC type interface helpers.
+class VariableInfoAttr : public mlir::Attribute {
 public:
-  VariableInfo() = default;
-  VariableInfo(std::nullptr_t) {}
-  explicit VariableInfo(std::unique_ptr<VariableInfoBase> impl)
-      : impl(std::move(impl)) {}
-
-  VariableInfo(VariableInfo &&) = default;
-  VariableInfo &operator=(VariableInfo &&) = default;
+  using Attribute::Attribute;
 
-  template <typename T>
-  const T *dyn_cast() const {
-    if (!impl || !T::classof(impl.get()))
-      return nullptr;
-    return static_cast<const T *>(impl.get());
+  static bool classof(mlir::Attribute attr) {
+    return attr.hasTrait<::mlir::acc::AttributeTrait::IsVariableInfo>();
   }
-
-  explicit operator bool() const { return impl != nullptr; }
-
-private:
-  std::unique_ptr<VariableInfoBase> impl;
 };
 
-} // namespace mlir::acc
+} // namespace acc
+} // namespace mlir
 
 #endif // MLIR_DIALECT_OPENACC_OPENACCVARIABLEINFO_H
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index c8545a8373a97..2f56595016a57 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -1550,7 +1550,7 @@ static LogicalResult createInitRegion(OpBuilder &builder, Location loc,
                                       Region &initRegion, Value hostVar,
                                       StringRef varName, ValueRange bounds,
                                       bool &needsFree,
-                                      acc::VariableInfo &varInfo) {
+                                      acc::VariableInfoAttr &varInfo) {
   Type varType = hostVar.getType();
 
   // Create init block with arguments: original value + bounds
@@ -1648,7 +1648,7 @@ static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,
 static LogicalResult createDestroyRegion(OpBuilder &builder, Location loc,
                                          Region &destroyRegion, Type varType,
                                          Value allocRes, ValueRange bounds,
-                                         const acc::VariableInfo &varInfo) {
+                                         acc::VariableInfoAttr varInfo) {
   // Create destroy block with arguments: original value + privatized value +
   // bounds
   SmallVector<Type> destroyArgTypes{varType, varType};
@@ -1748,7 +1748,7 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
 
   // Populate the init region
   bool needsFree = false;
-  acc::VariableInfo varInfo;
+  acc::VariableInfoAttr varInfo;
   if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
                               varName, bounds, needsFree, varInfo))) {
     recipe.erase();
@@ -1845,7 +1845,7 @@ FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
 
   // Populate the init region
   bool needsFree = false;
-  acc::VariableInfo varInfo;
+  acc::VariableInfoAttr varInfo;
   if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
                               varName, bounds, needsFree, varInfo))) {
     recipe.erase();



More information about the Mlir-commits mailing list