[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 04:16:55 PDT 2026


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

This is a proposal for a new data structure that allows threading language specific information into the OpenACC interfaces.
The language front-ends can implement their extension like done for Fortran in flang in this patch.
The language information is created from the hostVar value via a new interface.

The advantage is that this allows not threading the hostVar mlir::Value into the methods that are already taking the block arguments as values and where threading an additional Value just for the sake of retrieving information could be confusing.

This also allows controlling and limiting the places where information is obtained via getDefiningOp with the idea that this should be limited and replace with Attribute and Type information in the IR instead as much as possible in the future.

The current motivation is to detect OPTIONAL inside the generatePrivateInit, generateCopy, and generatePrivateDestroy APIs implementation for Fortran.

>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] [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;



More information about the Mlir-commits mailing list