[Mlir-commits] [mlir] [mlir][acc] Add ACCImplicitData pass for implicit data attributes (PR #166472)

Razvan Lupusoru llvmlistbot at llvm.org
Wed Nov 5 19:56:32 PST 2025


================
@@ -0,0 +1,884 @@
+//===- ACCImplicitData.cpp ------------------------------------------------===//
+//
+// 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 pass implements the OpenACC specification for "Variables with
+// Implicitly Determined Data Attributes" (OpenACC 3.4 spec, section 2.6.2).
+//
+// Overview:
+// ---------
+// The pass automatically generates data clause operations for variables used
+// within OpenACC compute constructs (parallel, kernels, serial) that do not
+// already have explicit data clauses. The semantics follow these rules:
+//
+// 1. If there is a default(none) clause visible, no implicit data actions
+//    apply.
+//
+// 2. An aggregate variable (arrays, derived types, etc.) will be treated as:
+//    - In a present clause when default(present) is visible.
+//    - In a copy clause otherwise.
+//
+// 3. A scalar variable will be treated as if it appears in:
+//    - A copy clause if the compute construct is a kernels construct.
+//    - A firstprivate clause otherwise (parallel, serial).
+//
+// Requirements:
+// -------------
+// To use this pass in a pipeline, the following requirements must be met:
+//
+// 1. Type Interface Implementation: Variables from the dialect being used
+//    must implement one or both of the following MLIR interfaces:
+//    `acc::MappableType` and/or `acc::PointerLikeType`
+//
+//    These interfaces provide the necessary methods for the pass to:
+//    - Determine variable type categories (scalar vs. aggregate)
+//    - Generate appropriate bounds information
+//    - Generate privatization recipes
+//
+// 2. Operation Interface Implementation: Operations that access partial
+//    entities or create views should implement the following MLIR
+//    interfaces: `acc::PartialEntityAccess` and/or
+//    `mlir::ViewLikeOpInterface`
+//
+//    These interfaces are used for proper data clause ordering, ensuring
+//    that base entities are mapped before derived entities (e.g., a
+//    struct is mapped before its fields, an array is mapped before
+//    subarray views).
+//
+// 3. Analysis Registration (Optional): If custom behavior is needed for
+//    variable name extraction or alias analysis, the dialect should
+//    pre-register the `acc::OpenACCSupport` and `mlir::AliasAnalysis` analyses.
+//
+//    If not registered, default behavior will be used.
+//
+// Implementation Details:
+// -----------------------
+// The pass performs the following operations:
+//
+// 1. Finds candidate variables which are live-in to the compute region and
+//    are not already in a data clause or private clause.
+//
+// 2. Generates both data "entry" and "exit" clause operations that match
+//    the intended action depending on variable type:
+//    - copy -> acc.copyin (entry) + acc.copyout (exit)
+//    - present -> acc.present (entry) + acc.delete (exit)
+//    - firstprivate -> acc.firstprivate (entry only, no exit)
+//
+// 3. Ensures that default clause is taken into consideration by looking
+//    through current construct and parent constructs to find the "visible
+//    default clause".
+//
+// 4. Fixes up SSA value links so that uses in the acc region reference the
+//    result of the newly created data clause operations.
+//
+// 5. When generating implicit data clause operations, it also adds variable
+//    name information and marks them with the implicit flag.
+//
+// 6. Recipes are generated by calling the appropriate entrypoints in the
+//    MappableType and PointerLikeType interfaces.
+//
+// 7. AliasAnalysis is used to determine if a variable is already covered by
+//    an existing data clause (e.g., an interior pointer covered by its parent).
+//
+// Examples:
+// ---------
+//
+// Example 1: Scalar in parallel construct (implicit firstprivate)
+//
+// Before:
+//   func.func @test() {
+//     %scalar = memref.alloca() {acc.var_name = "x"} : memref<f32>
+//     acc.parallel {
+//       %val = memref.load %scalar[] : memref<f32>
+//       acc.yield
+//     }
+//   }
+//
+// After:
+//   func.func @test() {
+//     %scalar = memref.alloca() {acc.var_name = "x"} : memref<f32>
+//     %firstpriv = acc.firstprivate varPtr(%scalar : memref<f32>)
+//                    -> memref<f32> {implicit = true, name = "x"}
+//     acc.parallel firstprivate(@recipe -> %firstpriv : memref<f32>) {
+//       %val = memref.load %firstpriv[] : memref<f32>
+//       acc.yield
+//     }
+//   }
+//
+// Example 2: Scalar in kernels construct (implicit copy)
+//
+// Before:
+//   func.func @test() {
+//     %scalar = memref.alloca() {acc.var_name = "n"} : memref<i32>
+//     acc.kernels {
+//       %val = memref.load %scalar[] : memref<i32>
+//       acc.terminator
+//     }
+//   }
+//
+// After:
+//   func.func @test() {
+//     %scalar = memref.alloca() {acc.var_name = "n"} : memref<i32>
+//     %copyin = acc.copyin varPtr(%scalar : memref<i32>) -> memref<i32>
+//                 {dataClause = #acc<data_clause acc_copy>,
+//                  implicit = true, name = "n"}
+//     acc.kernels dataOperands(%copyin : memref<i32>) {
+//       %val = memref.load %copyin[] : memref<i32>
+//       acc.terminator
+//     }
+//     acc.copyout accPtr(%copyin : memref<i32>)
+//                 to varPtr(%scalar : memref<i32>)
+//                 {dataClause = #acc<data_clause acc_copy>,
+//                  implicit = true, name = "n"}
+//   }
+//
+// Example 3: Array (aggregate) in parallel (implicit copy)
+//
+// Before:
+//   func.func @test() {
+//     %array = memref.alloca() {acc.var_name = "arr"} : memref<100xf32>
+//     acc.parallel {
+//       %c0 = arith.constant 0 : index
+//       %val = memref.load %array[%c0] : memref<100xf32>
+//       acc.yield
+//     }
+//   }
+//
+// After:
+//   func.func @test() {
+//     %array = memref.alloca() {acc.var_name = "arr"} : memref<100xf32>
+//     %copyin = acc.copyin varPtr(%array : memref<100xf32>)
+//                 -> memref<100xf32>
+//                 {dataClause = #acc<data_clause acc_copy>,
+//                  implicit = true, name = "arr"}
+//     acc.parallel dataOperands(%copyin : memref<100xf32>) {
+//       %c0 = arith.constant 0 : index
+//       %val = memref.load %copyin[%c0] : memref<100xf32>
+//       acc.yield
+//     }
+//     acc.copyout accPtr(%copyin : memref<100xf32>)
+//                 to varPtr(%array : memref<100xf32>)
+//                 {dataClause = #acc<data_clause acc_copy>,
+//                  implicit = true, name = "arr"}
+//   }
+//
+// Example 4: Array with default(present)
+//
+// Before:
+//   func.func @test() {
+//     %array = memref.alloca() {acc.var_name = "arr"} : memref<100xf32>
+//     acc.parallel {
+//       %c0 = arith.constant 0 : index
+//       %val = memref.load %array[%c0] : memref<100xf32>
+//       acc.yield
+//     } attributes {defaultAttr = #acc<defaultvalue present>}
+//   }
+//
+// After:
+//   func.func @test() {
+//     %array = memref.alloca() {acc.var_name = "arr"} : memref<100xf32>
+//     %present = acc.present varPtr(%array : memref<100xf32>)
+//                  -> memref<100xf32>
+//                  {implicit = true, name = "arr"}
+//     acc.parallel dataOperands(%present : memref<100xf32>)
+//                  attributes {defaultAttr = #acc<defaultvalue present>} {
+//       %c0 = arith.constant 0 : index
+//       %val = memref.load %present[%c0] : memref<100xf32>
+//       acc.yield
+//     }
+//     acc.delete accPtr(%present : memref<100xf32>)
+//                {dataClause = #acc<data_clause acc_present>,
+//                 implicit = true, name = "arr"}
+//   }
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/OpenACC/Transforms/Passes.h"
+
+#include "mlir/Analysis/AliasAnalysis.h"
+#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/Dominance.h"
+#include "mlir/IR/Operation.h"
+#include "mlir/IR/Value.h"
+#include "mlir/Interfaces/FunctionInterfaces.h"
+#include "mlir/Interfaces/ViewLikeInterface.h"
+#include "mlir/Transforms/RegionUtils.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <type_traits>
+
+namespace mlir {
+namespace acc {
+#define GEN_PASS_DEF_ACCIMPLICITDATA
+#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
+} // namespace acc
+} // namespace mlir
+
+#define DEBUG_TYPE "acc-implicit-data"
+
+using namespace mlir;
+
+namespace {
+
+class ACCImplicitData
+    : public mlir::acc::impl::ACCImplicitDataBase<ACCImplicitData> {
+public:
+  using mlir::acc::impl::ACCImplicitDataBase<
+      ACCImplicitData>::ACCImplicitDataBase;
+
+  void runOnOperation() override;
+
+private:
+  /// Collects all data clauses that dominate the compute construct.
+  /// Needed to determine if a variable is already covered by an existing data
+  /// clause.
+  SmallVector<mlir::Value>
+  getDominatingDataClauses(Operation *computeConstructOp);
+
+  /// Looks through the `dominatingDataClauses` to find the original data clause
+  /// op for an alias. Returns nullptr if no original data clause op is found.
+  template <typename OpT>
+  Operation *getOriginalDataClauseOpForAlias(
+      Value var, OpBuilder &builder, OpT computeConstructOp,
+      const SmallVector<mlir::Value> &dominatingDataClauses);
+
+  /// Generates the appropriate `acc.copyin`, `acc.present`,`acc.firstprivate`,
+  /// etc. data clause op for a candidate variable.
+  template <typename OpT>
+  Operation *generateDataClauseOpForCandidate(
+      Value var, ModuleOp &module, OpBuilder &builder, OpT computeConstructOp,
+      const SmallVector<mlir::Value> &dominatingDataClauses,
+      const std::optional<acc::ClauseDefaultValue> &defaultClause);
+
+  /// Generates the implicit data ops for a compute construct.
+  template <typename OpT>
+  void generateImplicitDataOps(
+      ModuleOp &module, OpT computeConstructOp,
+      std::optional<acc::ClauseDefaultValue> &defaultClause);
+
+  /// Generates a private recipe for a variable.
+  acc::PrivateRecipeOp generatePrivateRecipe(ModuleOp &module, mlir::Value var,
+                                             mlir::Location loc,
+                                             OpBuilder &builder,
+                                             acc::OpenACCSupport &accSupport);
+
+  /// Generates a firstprivate recipe for a variable.
+  acc::FirstprivateRecipeOp
+  generateFirstprivateRecipe(ModuleOp &module, mlir::Value var,
+                             mlir::Location loc, OpBuilder &builder,
+                             acc::OpenACCSupport &accSupport);
+
+  /// Generates recipes for a list of variables.
+  void generateRecipes(ModuleOp &module, OpBuilder &builder,
+                       Operation *computeConstructOp,
+                       const SmallVector<Value> &newOperands,
+                       SmallVector<Attribute> &newRecipeSyms);
+};
+
+/// Determines if a variable is a candidate for implicit data mapping.
+/// Returns true if the variable is a candidate, false otherwise.
+static bool isCandidateForImplicitData(Value val, Region &accRegion) {
+  // Ensure the variable is an allowed type for data clause.
+  if (!acc::isPointerLikeType(val.getType()) &&
+      !acc::isMappableType(val.getType()))
+    return false;
+
+  // If this is already coming from a data clause, we do not need to generate
+  // another.
+  if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.getDefiningOp()))
+    return false;
+
+  // If this is only used by private clauses, it is not a real live-in.
+  if (acc::isOnlyUsedByPrivateClauses(val, accRegion))
+    return false;
+
+  return true;
+}
+
+SmallVector<mlir::Value>
+ACCImplicitData::getDominatingDataClauses(Operation *computeConstructOp) {
+  llvm::SmallSetVector<mlir::Value, 8> dominatingDataClauses;
+
+  llvm::TypeSwitch<Operation *>(computeConstructOp)
+      .Case<acc::ParallelOp, acc::KernelsOp, acc::SerialOp>([&](auto op) {
+        for (auto dataClause : op.getDataClauseOperands()) {
+          dominatingDataClauses.insert(dataClause);
+        }
+      })
+      .Default([](Operation *) {});
+
+  // Collect the data clauses from enclosing data constructs.
+  Operation *currParentOp = computeConstructOp->getParentOp();
+  while (currParentOp) {
+    if (isa<acc::DataOp>(currParentOp)) {
+      for (auto dataClause :
+           dyn_cast<acc::DataOp>(currParentOp).getDataClauseOperands()) {
+        dominatingDataClauses.insert(dataClause);
+      }
+    }
+    currParentOp = currParentOp->getParentOp();
+  }
+
+  // Find the enclosing function/subroutine
+  auto funcOp = computeConstructOp->getParentOfType<FunctionOpInterface>();
+  if (!funcOp)
+    return dominatingDataClauses.takeVector();
+
+  // Walk the function to find `acc.declare_enter`/`acc.declare_exit` pairs that
+  // dominate and post-dominate the compute construct and add their data
+  // clauses to the list.
+  auto &domInfo = this->getAnalysis<DominanceInfo>();
+  auto &postDomInfo = this->getAnalysis<PostDominanceInfo>();
+  funcOp->walk([&](mlir::acc::DeclareEnterOp declareEnterOp) {
+    if (domInfo.dominates(declareEnterOp.getOperation(), computeConstructOp)) {
+      // Collect all `acc.declare_exit` ops for this token.
+      SmallVector<acc::DeclareExitOp> exits;
+      for (auto *user : declareEnterOp.getToken().getUsers())
+        if (auto declareExit = dyn_cast<acc::DeclareExitOp>(user))
+          exits.push_back(declareExit);
+
+      // Only add clauses if every `acc.declare_exit` op post-dominates the
+      // compute construct.
+      if (!exits.empty() && llvm::all_of(exits, [&](acc::DeclareExitOp exitOp) {
+            return postDomInfo.postDominates(exitOp, computeConstructOp);
+          })) {
+        for (auto dataClause : declareEnterOp.getDataClauseOperands())
+          dominatingDataClauses.insert(dataClause);
+      }
+    }
+  });
+
+  return dominatingDataClauses.takeVector();
+}
+
+template <typename OpT>
+Operation *ACCImplicitData::getOriginalDataClauseOpForAlias(
+    Value var, OpBuilder &builder, OpT computeConstructOp,
+    const SmallVector<mlir::Value> &dominatingDataClauses) {
+  auto &aliasAnalysis = this->getAnalysis<AliasAnalysis>();
+  for (auto dataClause : dominatingDataClauses) {
+    if (auto *dataClauseOp = dataClause.getDefiningOp()) {
+      // Only accept clauses that guarantee that the alias is present.
+      if (isa<acc::CopyinOp, acc::CreateOp, acc::PresentOp, acc::NoCreateOp,
+              acc::DevicePtrOp>(dataClauseOp))
+        if (aliasAnalysis.alias(acc::getVar(dataClauseOp), var).isMust())
+          return dataClauseOp;
+    }
+  }
+  return nullptr;
+}
+
+// Generates bounds for variables that have unknown dimensions
+static void fillInBoundsForUnknownDimensions(mlir::Operation *dataClauseOp,
+                                             OpBuilder &builder) {
+
+  if (!mlir::acc::getBounds(dataClauseOp).empty())
+    // If bounds are already present, do not overwrite them.
+    return;
+
+  // For types that have unknown dimensions, attempt to generate bounds by
+  // relying on MappableType being able to extract it from the IR.
+  auto var = mlir::acc::getVar(dataClauseOp);
+  auto type = var.getType();
+  if (auto mappableTy = dyn_cast<acc::MappableType>(type)) {
+    if (mappableTy.hasUnknownDimensions()) {
+      TypeSwitch<mlir::Operation *>(dataClauseOp)
+          .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClauseOp) {
+            if (std::is_same_v<decltype(dataClauseOp), mlir::acc::DevicePtrOp>)
+              return;
+            OpBuilder::InsertionGuard guard(builder);
+            builder.setInsertionPoint(dataClauseOp);
+            auto bounds = mappableTy.generateAccBounds(var, builder);
+            if (!bounds.empty())
+              dataClauseOp.getBoundsMutable().assign(bounds);
+          });
+    }
+  }
+}
+
+acc::PrivateRecipeOp
+ACCImplicitData::generatePrivateRecipe(ModuleOp &module, mlir::Value var,
+                                       mlir::Location loc, OpBuilder &builder,
+                                       acc::OpenACCSupport &accSupport) {
+  auto type = var.getType();
+  std::string recipeName =
+      accSupport.getRecipeName(acc::RecipeKind::private_recipe, type, var);
+
+  // Check if recipe already exists
+  auto existingRecipe = module.lookupSymbol<acc::PrivateRecipeOp>(recipeName);
+  if (existingRecipe)
+    return existingRecipe;
+
+  // Set insertion point to module body in a scoped way
+  OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPointToStart(module.getBody());
+
+  auto recipe = mlir::acc::PrivateRecipeOp::createAndPopulate(builder, loc,
+                                                              recipeName, type);
+  if (!recipe.has_value())
+    return accSupport.emitNYI(loc, "implicit private"), nullptr;
+  return recipe.value();
+}
+
+acc::FirstprivateRecipeOp ACCImplicitData::generateFirstprivateRecipe(
+    ModuleOp &module, mlir::Value var, mlir::Location loc, OpBuilder &builder,
+    acc::OpenACCSupport &accSupport) {
+  auto type = var.getType();
+  std::string recipeName =
+      accSupport.getRecipeName(acc::RecipeKind::firstprivate_recipe, type, var);
+
+  // Check if recipe already exists
+  auto existingRecipe =
+      module.lookupSymbol<acc::FirstprivateRecipeOp>(recipeName);
+  if (existingRecipe)
+    return existingRecipe;
+
+  // Set insertion point to module body in a scoped way
+  OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPointToStart(module.getBody());
+
+  auto recipe = mlir::acc::FirstprivateRecipeOp::createAndPopulate(
+      builder, loc, recipeName, type);
+  if (!recipe.has_value())
+    return accSupport.emitNYI(loc, "implicit firstprivate"), nullptr;
+  return recipe.value();
+}
+
+void ACCImplicitData::generateRecipes(ModuleOp &module, OpBuilder &builder,
+                                      Operation *computeConstructOp,
+                                      const SmallVector<Value> &newOperands,
+                                      SmallVector<Attribute> &newRecipeSyms) {
+  auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
+  for (auto var : newOperands) {
+    auto loc{var.getLoc()};
+    if (isa<acc::PrivateOp>(var.getDefiningOp())) {
+      auto recipe = generatePrivateRecipe(
+          module, acc::getVar(var.getDefiningOp()), loc, builder, accSupport);
+      if (recipe)
+        newRecipeSyms.push_back(mlir::SymbolRefAttr::get(
+            module->getContext(), recipe.getSymName().str()));
+    } else if (isa<acc::FirstprivateOp>(var.getDefiningOp())) {
+      auto recipe = generateFirstprivateRecipe(
+          module, acc::getVar(var.getDefiningOp()), loc, builder, accSupport);
+      if (recipe)
+        newRecipeSyms.push_back(mlir::SymbolRefAttr::get(
+            module->getContext(), recipe.getSymName().str()));
+    } else {
+      accSupport.emitNYI(var.getLoc(), "implicit reduction");
+    }
+  }
+}
+
+// Generates the data entry data op clause so that it adheres to OpenACC
+// rules as follows (line numbers and specification from OpenACC 3.4):
+// 1388 An aggregate variable will be treated as if it appears either:
+// 1389 - In a present clause if there is a default(present) clause visible at
+// the compute construct.
+// 1391 - In a copy clause otherwise.
+// 1392 A scalar variable will be treated as if it appears either:
+// 1393 - In a copy clause if the compute construct is a kernels construct.
+// 1394 - In a firstprivate clause otherwise.
+template <typename OpT>
+Operation *ACCImplicitData::generateDataClauseOpForCandidate(
+    Value var, ModuleOp &module, OpBuilder &builder, OpT computeConstructOp,
+    const SmallVector<mlir::Value> &dominatingDataClauses,
+    const std::optional<acc::ClauseDefaultValue> &defaultClause) {
+  auto &accSupport = this->getAnalysis<acc::OpenACCSupport>();
+  acc::VariableTypeCategory typeCategory =
+      acc::VariableTypeCategory::uncategorized;
+  if (auto mappableTy = dyn_cast<acc::MappableType>(var.getType())) {
+    typeCategory = mappableTy.getTypeCategory(var);
+  } else if (auto pointerLikeTy =
+                 dyn_cast<acc::PointerLikeType>(var.getType())) {
+    typeCategory = pointerLikeTy.getPointeeTypeCategory(
+        cast<TypedValue<acc::PointerLikeType>>(var),
+        pointerLikeTy.getElementType());
+  }
+
+  bool isScalar =
+      acc::bitEnumContainsAny(typeCategory, acc::VariableTypeCategory::scalar);
+  bool isAnyAggregate = acc::bitEnumContainsAny(
+      typeCategory, acc::VariableTypeCategory::aggregate);
+  mlir::Location loc = computeConstructOp->getLoc();
+
+  Operation *op = nullptr;
+  op = getOriginalDataClauseOpForAlias(var, builder, computeConstructOp,
+                                       dominatingDataClauses);
+  if (op) {
+    if (isa<acc::NoCreateOp>(op))
+      return acc::NoCreateOp::create(builder, loc, var,
+                                     /*structured=*/true, /*implicit=*/true,
+                                     accSupport.getVariableName(var),
+                                     acc::getBounds(op));
+
+    if (isa<acc::DevicePtrOp>(op))
+      return acc::DevicePtrOp::create(builder, loc, var,
+                                      /*structured=*/true, /*implicit=*/true,
+                                      accSupport.getVariableName(var),
+                                      acc::getBounds(op));
+
+    // The original data clause op is a PresentOp, CopyinOp, or CreateOp,
+    // hence guaranteed to be present.
+    return acc::PresentOp::create(builder, loc, var,
+                                  /*structured=*/true, /*implicit=*/true,
+                                  accSupport.getVariableName(var),
+                                  acc::getBounds(op));
+  } else if (isScalar) {
+    if (enableImplicitReductionCopy &&
+        acc::isOnlyUsedByReductionClauses(var,
+                                          computeConstructOp->getRegion(0))) {
+      auto copyinOp =
+          acc::CopyinOp::create(builder, loc, var,
+                                /*structured=*/true, /*implicit=*/true,
+                                accSupport.getVariableName(var));
+      copyinOp.setDataClause(acc::DataClause::acc_reduction);
+      return copyinOp.getOperation();
+    }
+    if constexpr (std::is_same_v<OpT, acc::KernelsOp> ||
+                  std::is_same_v<OpT, acc::KernelEnvironmentOp>) {
+      // Scalars are implicit copyin in kernels construct.
+      // We also do the same for acc.kernel_environment because semantics
+      // of user variable mappings should be applied while ACC construct exists
+      // and at this point we should only be dealing with unmapped variables
+      // that were made live-in by the compiler.
+      // TODO: This may be revisited.
+      auto copyinOp =
+          acc::CopyinOp::create(builder, loc, var,
+                                /*structured=*/true, /*implicit=*/true,
+                                accSupport.getVariableName(var));
+      copyinOp.setDataClause(acc::DataClause::acc_copy);
+      return copyinOp.getOperation();
+    } else {
+      // Scalars are implicit firstprivate in parallel and serial construct.
+      return acc::FirstprivateOp::create(builder, loc, var,
+                                         /*structured=*/true, /*implicit=*/true,
+                                         accSupport.getVariableName(var));
+    }
+  } else if (isAnyAggregate) {
+    Operation *newDataOp = nullptr;
+
+    // When default(present) is true, the implicit behavior is present.
+    if (defaultClause.has_value() &&
+        defaultClause.value() == acc::ClauseDefaultValue::Present) {
+      newDataOp = acc::PresentOp::create(builder, loc, var,
+                                         /*structured=*/true, /*implicit=*/true,
+                                         accSupport.getVariableName(var));
+    } else {
+      SmallVector<mlir::Value> bounds;
+      auto copyinOp =
+          acc::CopyinOp::create(builder, loc, var,
+                                /*structured=*/true, /*implicit=*/true,
+                                accSupport.getVariableName(var));
+      copyinOp.setDataClause(acc::DataClause::acc_copy);
+      newDataOp = copyinOp.getOperation();
+    }
+
+    return newDataOp;
+  } else {
+    // This is not a fatal error - for example when the element type is
+    // pointer type (aka we have a pointer of pointer), it is potentially a
+    // deep copy scenario which is not being handled here.
+    // Other types need to be canonicalized. Thus just log unhandled cases.
+    LLVM_DEBUG(llvm::dbgs()
+               << "Unhandled case for implicit data mapping " << var << "\n");
+  }
+  return nullptr;
+}
+
+// Ensures that result values from the acc data clause ops are used inside the
+// acc region. ie:
+// acc.kernels {
+//   use %val
+// }
+// =>
+// %dev = acc.dataop %val
+// acc.kernels {
+//   use %dev
+// }
+static void legalizeValuesInRegion(Region &accRegion,
+                                   SmallVector<Value> &newPrivateOperands,
+                                   SmallVector<Value> &newDataClauseOperands) {
+  for (Value dataClause :
+       llvm::concat<Value>(newDataClauseOperands, newPrivateOperands)) {
+    Value var = acc::getVar(dataClause.getDefiningOp());
+    replaceAllUsesInRegionWith(var, dataClause, accRegion);
+  }
+}
+
+// Adds the private operands and private recipes to the data construct
+// operation in a valid way (ensures that the index in the privatizationRecipes
+// array matches the position of the private operand).
+template <typename OpT>
+static void
+addNewPrivateOperands(OpT &accOp, mlir::SmallVector<Value> &privateOperands,
+                      mlir::SmallVector<Attribute> &privateRecipeSyms) {
----------------
razvanlupusoru wrote:

Done here and other places I found.

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


More information about the Mlir-commits mailing list