[flang-commits] [flang] [flang][acc] Add infrastructure and tests for ACCImplicitData (PR #166797)
Razvan Lupusoru via flang-commits
flang-commits at lists.llvm.org
Thu Nov 6 08:23:12 PST 2025
https://github.com/razvanlupusoru created https://github.com/llvm/llvm-project/pull/166797
This PR adds the necessary infrastructure to enable testing of the ACCImplicitData pass for FIR/HLFIR, along with comprehensive test coverage for implicit data clause generation in OpenACC constructs.
New Infrastructure:
- Add FIROpenACCSupport analysis providing FIR-specific implementations of OpenACCSupport interface methods for variable name extraction, recipe name generation, and NYI emission
- Add FIROpenACCUtils with helper functions for:
* Variable name extraction from FIR operations (getVariableName)
* Recipe name generation with FIR type string representation
* Bounds checking for constant array sections
- Add ACCInitializeFIRAnalyses pass to pre-register FIR analyses (OpenACCSupport and AliasAnalysis) for use by subsequent OpenACC passes in the pipeline
Refactoring in flang/lib/Lower/OpenACC.cpp:
- Move bounds string generation and bounds checking to FIROpenACCUtils
- Refactor recipe name generation to use fir::acc::getRecipeName
Test Coverage:
- acc-implicit-firstprivate.fir: Tests implicit firstprivate behavior for scalar types (i8, i16, i32, i64, f32, f64, logical, complex) in parallel/serial constructs with recipe generation verification
- acc-implicit-data.fir: Tests implicit data clauses for scalars, arrays, derived types, and boxes in kernels/parallel/serial with default(none) and default(present) variations
- acc-implicit-data-fortran.F90: Fortran tests verifying implicit data generation through bbc with both HLFIR and FIR
- acc-implicit-data-derived-type-member.F90: Tests correct ordering of parent/child data clause operations for derived type members
- acc-implicit-copy-reduction.fir: Tests enable-implicit-reduction-copy flag controlling whether reduction variables use copy or firstprivate
This enables proper testing of implicit data clause generation through the flang optimizer pipeline for OpenACC directives.
>From 2f15a1a93130216217dcade3ebc3c164385e0876 Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Thu, 6 Nov 2025 08:21:59 -0800
Subject: [PATCH] [flang][acc] Add infrastructure and tests for ACCImplicitData
This PR adds the necessary infrastructure to enable testing of
the ACCImplicitData pass for FIR/HLFIR, along with comprehensive
test coverage for implicit data clause generation in OpenACC
constructs.
New Infrastructure:
- Add FIROpenACCSupport analysis providing FIR-specific
implementations of OpenACCSupport interface methods for variable
name extraction, recipe name generation, and NYI emission
- Add FIROpenACCUtils with helper functions for:
* Variable name extraction from FIR operations
(getVariableName)
* Recipe name generation with FIR type string representation
* Bounds checking for constant array sections
- Add ACCInitializeFIRAnalyses pass to pre-register FIR analyses
(OpenACCSupport and AliasAnalysis) for use by subsequent
OpenACC passes in the pipeline
Refactoring in flang/lib/Lower/OpenACC.cpp:
- Move bounds string generation and bounds checking to
FIROpenACCUtils
- Refactor recipe name generation to use
fir::acc::getRecipeName
Test Coverage:
- acc-implicit-firstprivate.fir: Tests implicit firstprivate
behavior for scalar types (i8, i16, i32, i64, f32, f64,
logical, complex) in parallel/serial constructs with recipe
generation verification
- acc-implicit-data.fir: Tests implicit data clauses for
scalars, arrays, derived types, and boxes in
kernels/parallel/serial with default(none) and
default(present) variations
- acc-implicit-data-fortran.F90: Fortran tests verifying implicit
data generation through bbc with both HLFIR and FIR
- acc-implicit-data-derived-type-member.F90: Tests correct
ordering of parent/child data clause operations for derived
type members
- acc-implicit-copy-reduction.fir: Tests
enable-implicit-reduction-copy flag controlling whether
reduction variables use copy or firstprivate
This enables proper testing of implicit data clause generation
through the flang optimizer pipeline for OpenACC directives.
---
.../Analysis/FIROpenACCSupportAnalysis.h | 51 +++
.../include/flang/Optimizer/OpenACC/Passes.h | 4 +
.../include/flang/Optimizer/OpenACC/Passes.td | 16 +
.../OpenACC/Support/FIROpenACCUtils.h | 57 +++
flang/lib/Lower/OpenACC.cpp | 71 +---
.../Optimizer/OpenACC/Analysis/CMakeLists.txt | 22 ++
.../Analysis/FIROpenACCSupportAnalysis.cpp | 40 ++
flang/lib/Optimizer/OpenACC/CMakeLists.txt | 1 +
.../Optimizer/OpenACC/Support/CMakeLists.txt | 1 +
.../OpenACC/Support/FIROpenACCUtils.cpp | 275 ++++++++++++++
.../Transforms/ACCInitializeFIRAnalyses.cpp | 56 +++
.../OpenACC/Transforms/CMakeLists.txt | 3 +
.../OpenACC/acc-implicit-copy-reduction.fir | 134 +++++++
.../acc-implicit-data-derived-type-member.F90 | 38 ++
.../OpenACC/acc-implicit-data-fortran.F90 | 79 ++++
.../Transforms/OpenACC/acc-implicit-data.fir | 358 ++++++++++++++++++
.../OpenACC/acc-implicit-firstprivate.fir | 284 ++++++++++++++
17 files changed, 1432 insertions(+), 58 deletions(-)
create mode 100644 flang/include/flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h
create mode 100644 flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
create mode 100644 flang/lib/Optimizer/OpenACC/Analysis/CMakeLists.txt
create mode 100644 flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
create mode 100644 flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
create mode 100644 flang/lib/Optimizer/OpenACC/Transforms/ACCInitializeFIRAnalyses.cpp
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-copy-reduction.fir
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-data-derived-type-member.F90
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-data-fortran.F90
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-data.fir
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
diff --git a/flang/include/flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h b/flang/include/flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h
new file mode 100644
index 0000000000000..c798681306c10
--- /dev/null
+++ b/flang/include/flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h
@@ -0,0 +1,51 @@
+//===- FIROpenACCSupportAnalysis.h - FIR OpenACCSupport Analysis ----------===//
+//
+// 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 FIR-specific implementation of OpenACCSupport analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_OPENACC_ANALYSIS_FIROPENACCSUPPORTANALYSIS_H
+#define FORTRAN_OPTIMIZER_OPENACC_ANALYSIS_FIROPENACCSUPPORTANALYSIS_H
+
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Value.h"
+#include <string>
+
+namespace fir {
+namespace acc {
+
+/// FIR-specific implementation for the OpenACCSupport analysis interface.
+///
+/// This class provides the custom implementations of the OpenACCSupport
+/// interface methods that are tailored to FIR's requirements and
+/// can handle FIR dialect operations and types.
+/// Its primary intent is to be registered with the OpenACCSupport analysis
+/// using setImplementation()
+///
+/// Usage:
+/// auto &support = getAnalysis<mlir::acc::OpenACCSupport>();
+/// support.setImplementation(fir::acc::FIROpenACCSupportAnalysis());
+///
+class FIROpenACCSupportAnalysis {
+public:
+ FIROpenACCSupportAnalysis() = default;
+
+ std::string getVariableName(mlir::Value v);
+
+ std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type,
+ mlir::Value var);
+
+ mlir::InFlightDiagnostic emitNYI(mlir::Location loc,
+ const mlir::Twine &message);
+};
+
+} // namespace acc
+} // namespace fir
+
+#endif // FORTRAN_OPTIMIZER_OPENACC_ANALYSIS_FIROPENACCSUPPORTANALYSIS_H
diff --git a/flang/include/flang/Optimizer/OpenACC/Passes.h b/flang/include/flang/Optimizer/OpenACC/Passes.h
index 0627cc8ce4a6d..c27c7ebc3b06f 100644
--- a/flang/include/flang/Optimizer/OpenACC/Passes.h
+++ b/flang/include/flang/Optimizer/OpenACC/Passes.h
@@ -13,6 +13,9 @@
#ifndef FORTRAN_OPTIMIZER_OPENACC_PASSES_H
#define FORTRAN_OPTIMIZER_OPENACC_PASSES_H
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
@@ -25,6 +28,7 @@ namespace acc {
#define GEN_PASS_REGISTRATION
#include "flang/Optimizer/OpenACC/Passes.h.inc"
+std::unique_ptr<mlir::Pass> createACCInitializeFIRAnalysesPass();
std::unique_ptr<mlir::Pass> createACCRecipeBufferizationPass();
} // namespace acc
diff --git a/flang/include/flang/Optimizer/OpenACC/Passes.td b/flang/include/flang/Optimizer/OpenACC/Passes.td
index 3c127b30aa9b8..d947aa470494a 100644
--- a/flang/include/flang/Optimizer/OpenACC/Passes.td
+++ b/flang/include/flang/Optimizer/OpenACC/Passes.td
@@ -11,6 +11,22 @@
include "mlir/Pass/PassBase.td"
+def ACCInitializeFIRAnalyses
+ : Pass<"acc-initialize-fir-analyses", "mlir::ModuleOp"> {
+ let summary = "Initialize FIR analyses for OpenACC passes";
+ let description = [{
+ This pass initializes analyses that can be used by subsequent OpenACC passes
+ in the pipeline. It creates and caches the OpenACCSupport analysis with a
+ FIR-specific implementation that can handle FIR types and operations.
+ It also initializes FIR's AliasAnalysis for use in OpenACC passes.
+ This pass needs to rerun if any analyses were invalidated by MLIR's framework.
+ }];
+ // In addition to pre-registering the needed analyses, this pass also
+ // pre-registers the dialects that various OpenACC passes may generate.
+ let dependentDialects = ["fir::FIROpsDialect", "hlfir::hlfirDialect",
+ "mlir::acc::OpenACCDialect"];
+}
+
def ACCRecipeBufferization
: Pass<"fir-acc-recipe-bufferization", "mlir::ModuleOp"> {
let summary = "Rewrite acc.*.recipe box values to ref<box> and update uses";
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
new file mode 100644
index 0000000000000..5ca0925ea681f
--- /dev/null
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
@@ -0,0 +1,57 @@
+//===- FIROpenACCUtils.h - FIR OpenACC Utilities ----------------*- 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 declares utility functions for FIR OpenACC support.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
+#define FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
+
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Value.h"
+#include <string>
+
+namespace fir {
+namespace acc {
+
+/// Attempts to extract the variable name from a value by walking through
+/// FIR operations and looking for variable names.
+/// \param v The value to extract the variable name from
+/// \param preferDemangledName If true, prefers demangled/bindc names over
+/// mangled/unique names. If false, prefers mangled names.
+/// Returns empty string if no name is found.
+std::string getVariableName(mlir::Value v, bool preferDemangledName = true);
+
+/// Get the recipe name for a given recipe kind, FIR type, and optional
+/// variable. Uses FIR's type string representation with appropriate prefix. For
+/// firstprivate and reduction recipes, handles bounds suffix when all bounds
+/// are constant. For reduction recipes, embeds the operator name in the recipe.
+/// \param kind The recipe kind (private, firstprivate, or reduction)
+/// \param type The FIR type (must be a FIR type)
+/// \param var Optional variable value
+/// \param bounds Optional bounds for array sections (used for suffix
+/// generation)
+/// \param reductionOp Optional reduction operator (required for reduction
+/// recipes)
+/// \return The complete recipe name with all necessary suffixes
+std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type,
+ mlir::Value var = nullptr,
+ llvm::ArrayRef<mlir::Value> bounds = {},
+ mlir::acc::ReductionOperator reductionOp =
+ mlir::acc::ReductionOperator::AccNone);
+
+/// Check if all bounds are expressed with constant values.
+/// \param bounds Array of DataBoundsOp values to check
+/// \return true if all bounds have constant lowerbound/upperbound or extent
+bool areAllBoundsConstant(llvm::ArrayRef<mlir::Value> bounds);
+
+} // namespace acc
+} // namespace fir
+
+#endif // FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 1f75ed1d8e6a1..224ce44917773 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -28,6 +28,7 @@
#include "flang/Optimizer/Builder/IntrinsicCall.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
#include "flang/Parser/parse-tree-visitor.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/tools.h"
@@ -1159,18 +1160,6 @@ bool isConstantBound(mlir::acc::DataBoundsOp &op) {
return false;
}
-/// Return true iff all the bounds are expressed with constant values.
-bool areAllBoundConstant(const llvm::SmallVector<mlir::Value> &bounds) {
- for (auto bound : bounds) {
- auto dataBound =
- mlir::dyn_cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
- assert(dataBound && "Must be DataBoundOp operation");
- if (!isConstantBound(dataBound))
- return false;
- }
- return true;
-}
-
static llvm::SmallVector<mlir::Value>
genConstantBounds(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::acc::DataBoundsOp &dataBound) {
@@ -1207,7 +1196,7 @@ static mlir::Value genShapeFromBoundsOrArgs(
return hlfir::genShape(loc, builder, entity);
}
return genShapeOp(builder, seqTy, loc).getResult();
- } else if (areAllBoundConstant(bounds)) {
+ } else if (fir::acc::areAllBoundsConstant(bounds)) {
for (auto bound : llvm::reverse(bounds)) {
auto dataBound =
mlir::cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
@@ -1274,7 +1263,7 @@ mlir::acc::FirstprivateRecipeOp Fortran::lower::createOrGetFirstprivateRecipe(
auto ip = builder.saveInsertionPoint();
auto recipe = genRecipeOp<mlir::acc::FirstprivateRecipeOp>(
builder, mod, recipeName, loc, ty);
- bool allConstantBound = areAllBoundConstant(bounds);
+ bool allConstantBound = fir::acc::areAllBoundsConstant(bounds);
llvm::SmallVector<mlir::Type> argsTy{ty, ty};
llvm::SmallVector<mlir::Location> argsLoc{loc, loc};
if (!allConstantBound) {
@@ -1358,33 +1347,6 @@ mlir::acc::FirstprivateRecipeOp Fortran::lower::createOrGetFirstprivateRecipe(
return recipe;
}
-/// Get a string representation of the bounds.
-std::string getBoundsString(llvm::SmallVector<mlir::Value> &bounds) {
- std::stringstream boundStr;
- if (!bounds.empty())
- boundStr << "_section_";
- llvm::interleave(
- bounds,
- [&](mlir::Value bound) {
- auto boundsOp =
- mlir::cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
- if (boundsOp.getLowerbound() &&
- fir::getIntIfConstant(boundsOp.getLowerbound()) &&
- boundsOp.getUpperbound() &&
- fir::getIntIfConstant(boundsOp.getUpperbound())) {
- boundStr << "lb" << *fir::getIntIfConstant(boundsOp.getLowerbound())
- << ".ub" << *fir::getIntIfConstant(boundsOp.getUpperbound());
- } else if (boundsOp.getExtent() &&
- fir::getIntIfConstant(boundsOp.getExtent())) {
- boundStr << "ext" << *fir::getIntIfConstant(boundsOp.getExtent());
- } else {
- boundStr << "?";
- }
- },
- [&] { boundStr << "x"; });
- return boundStr.str();
-}
-
/// Rebuild the array type from the acc.bounds operation with constant
/// lowerbound/upperbound or extent.
mlir::Type getTypeFromBounds(llvm::SmallVector<mlir::Value> &bounds,
@@ -1458,9 +1420,8 @@ static void genPrivatizationRecipes(
RecipeOp recipe;
mlir::Type retTy = getTypeFromBounds(bounds, info.addr.getType());
if constexpr (std::is_same_v<RecipeOp, mlir::acc::PrivateRecipeOp>) {
- std::string recipeName =
- fir::getTypeAsString(retTy, converter.getKindMap(),
- Fortran::lower::privatizationRecipePrefix);
+ std::string recipeName = fir::acc::getRecipeName(
+ mlir::acc::RecipeKind::private_recipe, retTy, info.addr, bounds);
recipe = Fortran::lower::createOrGetPrivateRecipe(builder, recipeName,
operandLocation, retTy);
auto op = createDataEntryOp<mlir::acc::PrivateOp>(
@@ -1474,10 +1435,8 @@ static void genPrivatizationRecipes(
symbolPairs->emplace_back(op.getAccVar(),
Fortran::semantics::SymbolRef(symbol));
} else {
- std::string suffix =
- areAllBoundConstant(bounds) ? getBoundsString(bounds) : "";
- std::string recipeName = fir::getTypeAsString(
- retTy, converter.getKindMap(), "firstprivatization" + suffix);
+ std::string recipeName = fir::acc::getRecipeName(
+ mlir::acc::RecipeKind::firstprivate_recipe, retTy, info.addr, bounds);
recipe = Fortran::lower::createOrGetFirstprivateRecipe(
builder, recipeName, operandLocation, retTy, bounds);
auto op = createDataEntryOp<mlir::acc::FirstprivateOp>(
@@ -1829,7 +1788,7 @@ mlir::acc::ReductionRecipeOp Fortran::lower::createOrGetReductionRecipe(
// for the combiner if needed.
llvm::SmallVector<mlir::Type> argsTy{ty, ty};
llvm::SmallVector<mlir::Location> argsLoc{loc, loc};
- bool allConstantBound = areAllBoundConstant(bounds);
+ bool allConstantBound = fir::acc::areAllBoundsConstant(bounds);
if (!allConstantBound) {
for (mlir::Value bound : llvm::reverse(bounds)) {
auto dataBound =
@@ -1911,15 +1870,12 @@ genReductions(const Fortran::parser::AccObjectListWithReduction &objectList,
mlir::acc::DataClause::acc_reduction, info.addr.getType(), async,
asyncDeviceTypes, asyncOnlyDeviceTypes, /*unwrapBoxAddr=*/true);
mlir::Type ty = op.getAccVar().getType();
- if (!areAllBoundConstant(bounds) ||
+ if (!fir::acc::areAllBoundsConstant(bounds) ||
fir::isAssumedShape(info.addr.getType()) ||
fir::isAllocatableOrPointerArray(info.addr.getType()))
ty = info.addr.getType();
- std::string suffix =
- areAllBoundConstant(bounds) ? getBoundsString(bounds) : "";
- std::string recipeName = fir::getTypeAsString(
- ty, converter.getKindMap(),
- ("reduction_" + stringifyReductionOperator(mlirOp)).str() + suffix);
+ std::string recipeName = fir::acc::getRecipeName(
+ mlir::acc::RecipeKind::reduction_recipe, ty, info.addr, bounds, mlirOp);
mlir::acc::ReductionRecipeOp recipe =
Fortran::lower::createOrGetReductionRecipe(
@@ -2164,9 +2120,8 @@ static void privatizeIv(
}
if (privateOp == nullptr) {
- std::string recipeName =
- fir::getTypeAsString(ivValue.getType(), converter.getKindMap(),
- Fortran::lower::privatizationRecipePrefix);
+ std::string recipeName = fir::acc::getRecipeName(
+ mlir::acc::RecipeKind::private_recipe, ivValue.getType(), ivValue, {});
auto recipe = Fortran::lower::createOrGetPrivateRecipe(
builder, recipeName, loc, ivValue.getType());
diff --git a/flang/lib/Optimizer/OpenACC/Analysis/CMakeLists.txt b/flang/lib/Optimizer/OpenACC/Analysis/CMakeLists.txt
new file mode 100644
index 0000000000000..e05d1456e6dba
--- /dev/null
+++ b/flang/lib/Optimizer/OpenACC/Analysis/CMakeLists.txt
@@ -0,0 +1,22 @@
+add_flang_library(FIROpenACCAnalysis
+ FIROpenACCSupportAnalysis.cpp
+
+ DEPENDS
+ FIRAnalysis
+ FIRDialect
+ FIROpenACCSupport
+ HLFIRDialect
+
+ LINK_LIBS
+ FIRAnalysis
+ FIRDialect
+ FIROpenACCSupport
+ HLFIRDialect
+
+ MLIR_DEPS
+ MLIROpenACCDialect
+
+ MLIR_LIBS
+ MLIROpenACCDialect
+)
+
diff --git a/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp b/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
new file mode 100644
index 0000000000000..8cdbe1d5b170e
--- /dev/null
+++ b/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
@@ -0,0 +1,40 @@
+//===- FIROpenACCSupportAnalysis.cpp - FIR OpenACCSupport Analysis -------===//
+//
+// 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 implements the FIR-specific OpenACCSupport analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h"
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+
+using namespace mlir;
+
+namespace fir {
+namespace acc {
+
+std::string FIROpenACCSupportAnalysis::getVariableName(Value v) {
+ return fir::acc::getVariableName(v, /*preferDemangledName=*/true);
+}
+
+std::string FIROpenACCSupportAnalysis::getRecipeName(mlir::acc::RecipeKind kind,
+ Type type, Value var) {
+ return fir::acc::getRecipeName(kind, type, var);
+}
+
+mlir::InFlightDiagnostic
+FIROpenACCSupportAnalysis::emitNYI(Location loc, const Twine &message) {
+ TODO(loc, message);
+ // Should be unreachable, but we return an actual diagnostic
+ // to satisfy the interface.
+ return mlir::emitError(loc, "not yet implemented: " + message.str());
+}
+
+} // namespace acc
+} // namespace fir
diff --git a/flang/lib/Optimizer/OpenACC/CMakeLists.txt b/flang/lib/Optimizer/OpenACC/CMakeLists.txt
index 790b9fdb1589a..16a40254dbfe9 100644
--- a/flang/lib/Optimizer/OpenACC/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenACC/CMakeLists.txt
@@ -1,2 +1,3 @@
+add_subdirectory(Analysis)
add_subdirectory(Support)
add_subdirectory(Transforms)
diff --git a/flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt b/flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt
index 898fb00d41dfe..9c6f0ee74f4cf 100644
--- a/flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenACC/Support/CMakeLists.txt
@@ -4,6 +4,7 @@ add_flang_library(FIROpenACCSupport
FIROpenACCAttributes.cpp
FIROpenACCOpsInterfaces.cpp
FIROpenACCTypeInterfaces.cpp
+ FIROpenACCUtils.cpp
RegisterOpenACCExtensions.cpp
DEPENDS
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
new file mode 100644
index 0000000000000..cc4bb4175c5f3
--- /dev/null
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -0,0 +1,275 @@
+//===- FIROpenACCUtils.cpp - FIR OpenACC Utilities ------------------------===//
+//
+// 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 implements utility functions for FIR OpenACC support.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "flang/Optimizer/Dialect/Support/KindMapping.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/Support/InternalNames.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Matchers.h"
+#include "mlir/Interfaces/ViewLikeInterface.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace mlir;
+
+namespace fir {
+namespace acc {
+
+std::string getVariableName(Value v, bool preferDemangledName) {
+ std::string srcName;
+ std::string prefix;
+ llvm::SmallVector<std::string, 4> arrayIndices;
+ bool iterate = true;
+ mlir::Operation *defOp;
+
+ // For integer constants, no need to further iterate - print their value
+ // immediately.
+ if (v.getDefiningOp()) {
+ IntegerAttr::ValueType val;
+ if (matchPattern(v.getDefiningOp(), m_ConstantInt(&val))) {
+ llvm::raw_string_ostream os(prefix);
+ val.print(os, /*isSigned=*/true);
+ return prefix;
+ }
+ }
+
+ while (v && (defOp = v.getDefiningOp()) && iterate) {
+ iterate =
+ llvm::TypeSwitch<mlir::Operation *, bool>(defOp)
+ .Case<mlir::ViewLikeOpInterface>(
+ [&v](mlir::ViewLikeOpInterface op) {
+ v = op.getViewSource();
+ return true;
+ })
+ .Case<fir::ReboxOp>([&v](fir::ReboxOp op) {
+ v = op.getBox();
+ return true;
+ })
+ .Case<fir::EmboxOp>([&v](fir::EmboxOp op) {
+ v = op.getMemref();
+ return true;
+ })
+ .Case<fir::ConvertOp>([&v](fir::ConvertOp op) {
+ v = op.getValue();
+ return true;
+ })
+ .Case<fir::LoadOp>([&v](fir::LoadOp op) {
+ v = op.getMemref();
+ return true;
+ })
+ .Case<fir::BoxAddrOp>([&v](fir::BoxAddrOp op) {
+ // The box holds the name of the variable.
+ v = op.getVal();
+ return true;
+ })
+ .Case<fir::AddrOfOp>([&](fir::AddrOfOp op) {
+ // Only use address_of symbol if mangled name is preferred
+ if (!preferDemangledName) {
+ auto symRef = op.getSymbol();
+ srcName = symRef.getLeafReference().getValue().str();
+ }
+ return false;
+ })
+ .Case<fir::ArrayCoorOp>([&](fir::ArrayCoorOp op) {
+ v = op.getMemref();
+ for (auto coor : op.getIndices()) {
+ auto idxName = getVariableName(coor, preferDemangledName);
+ arrayIndices.push_back(idxName.empty() ? "?" : idxName);
+ }
+ return true;
+ })
+ .Case<fir::CoordinateOp>([&](fir::CoordinateOp op) {
+ std::optional<llvm::ArrayRef<int32_t>> fieldIndices =
+ op.getFieldIndices();
+ if (fieldIndices && fieldIndices->size() > 0 &&
+ (*fieldIndices)[0] != fir::CoordinateOp::kDynamicIndex) {
+ int fieldId = (*fieldIndices)[0];
+ mlir::Type baseType =
+ fir::getFortranElementType(op.getRef().getType());
+ if (auto recType = llvm::dyn_cast<fir::RecordType>(baseType)) {
+ srcName = recType.getTypeList()[fieldId].first;
+ }
+ }
+ if (!srcName.empty()) {
+ // If the field name is known - attempt to continue building
+ // name by looking at its parents.
+ prefix =
+ getVariableName(op.getRef(), preferDemangledName) + "%";
+ }
+ return false;
+ })
+ .Case<hlfir::DesignateOp>([&](hlfir::DesignateOp op) {
+ if (op.getComponent()) {
+ srcName = op.getComponent().value().str();
+ prefix =
+ getVariableName(op.getMemref(), preferDemangledName) + "%";
+ return false;
+ }
+ for (auto coor : op.getIndices()) {
+ auto idxName = getVariableName(coor, preferDemangledName);
+ arrayIndices.push_back(idxName.empty() ? "?" : idxName);
+ }
+ v = op.getMemref();
+ return true;
+ })
+ .Case<fir::DeclareOp, hlfir::DeclareOp>([&](auto op) {
+ srcName = op.getUniqName().str();
+ return false;
+ })
+ .Case<fir::AllocaOp>([&](fir::AllocaOp op) {
+ if (preferDemangledName) {
+ // Prefer demangled name (bindc_name over uniq_name)
+ srcName = op.getBindcName() ? *op.getBindcName()
+ : op.getUniqName() ? *op.getUniqName()
+ : "";
+ } else {
+ // Prefer mangled name (uniq_name over bindc_name)
+ srcName = op.getUniqName() ? *op.getUniqName()
+ : op.getBindcName() ? *op.getBindcName()
+ : "";
+ }
+ return false;
+ })
+ .Default([](mlir::Operation *) { return false; });
+ }
+
+ if (srcName.empty()) {
+ // Fallback to the default implementation.
+ return acc::getVariableName(v);
+ }
+
+ // Build array index suffix if present
+ std::string suffix;
+ if (!arrayIndices.empty()) {
+ llvm::raw_string_ostream os(suffix);
+ os << "(";
+ llvm::interleaveComma(arrayIndices, os);
+ os << ")";
+ }
+
+ // Names from FIR operations may be mangled.
+ // When the demangled name is requested - demangle it.
+ if (preferDemangledName) {
+ auto [kind, deconstructed] = fir::NameUniquer::deconstruct(srcName);
+ if (kind != fir::NameUniquer::NameKind::NOT_UNIQUED) {
+ return prefix + deconstructed.name + suffix;
+ }
+ }
+
+ return prefix + srcName + suffix;
+}
+
+bool areAllBoundsConstant(llvm::ArrayRef<Value> bounds) {
+ for (auto bound : bounds) {
+ auto dataBound =
+ mlir::dyn_cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
+ if (!dataBound)
+ return false;
+
+ // Check if this bound has constant values
+ bool hasConstant = false;
+ if (dataBound.getLowerbound() && dataBound.getUpperbound()) {
+ hasConstant =
+ fir::getIntIfConstant(dataBound.getLowerbound()).has_value() &&
+ fir::getIntIfConstant(dataBound.getUpperbound()).has_value();
+ } else if (dataBound.getExtent()) {
+ hasConstant = fir::getIntIfConstant(dataBound.getExtent()).has_value();
+ }
+
+ if (!hasConstant)
+ return false;
+ }
+ return true;
+}
+
+static std::string getBoundsString(llvm::ArrayRef<Value> bounds) {
+ if (bounds.empty())
+ return "";
+
+ std::string boundStr;
+ llvm::raw_string_ostream os(boundStr);
+ os << "_section_";
+
+ llvm::interleave(
+ bounds,
+ [&](Value bound) {
+ auto boundsOp =
+ mlir::cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
+ if (boundsOp.getLowerbound() &&
+ fir::getIntIfConstant(boundsOp.getLowerbound()) &&
+ boundsOp.getUpperbound() &&
+ fir::getIntIfConstant(boundsOp.getUpperbound())) {
+ os << "lb" << *fir::getIntIfConstant(boundsOp.getLowerbound())
+ << ".ub" << *fir::getIntIfConstant(boundsOp.getUpperbound());
+ } else if (boundsOp.getExtent() &&
+ fir::getIntIfConstant(boundsOp.getExtent())) {
+ os << "ext" << *fir::getIntIfConstant(boundsOp.getExtent());
+ } else {
+ os << "?";
+ }
+ },
+ [&] { os << "x"; });
+
+ return os.str();
+}
+
+std::string getRecipeName(mlir::acc::RecipeKind kind, Type type, Value var,
+ llvm::ArrayRef<Value> bounds,
+ mlir::acc::ReductionOperator reductionOp) {
+ assert(fir::isa_fir_type(type) && "getRecipeName expects a FIR type");
+
+ // Build the complete prefix with all components before calling
+ // getTypeAsString
+ std::string prefixStr;
+ llvm::raw_string_ostream prefixOS(prefixStr);
+
+ switch (kind) {
+ case mlir::acc::RecipeKind::private_recipe:
+ prefixOS << "privatization";
+ // Private recipes do not currently include bounds in the name
+ // TODO: They should include them - but lowering tests would need to
+ // be updated.
+ break;
+ case mlir::acc::RecipeKind::firstprivate_recipe:
+ prefixOS << "firstprivatization";
+ // Add bounds to the prefix if applicable (only for firstprivate)
+ if (!bounds.empty() && areAllBoundsConstant(bounds)) {
+ prefixOS << getBoundsString(bounds);
+ }
+ break;
+ case mlir::acc::RecipeKind::reduction_recipe:
+ prefixOS << "reduction";
+ // Embed the reduction operator in the prefix
+ if (reductionOp != mlir::acc::ReductionOperator::AccNone) {
+ prefixOS << "_"
+ << mlir::acc::stringifyReductionOperator(reductionOp).str();
+ }
+ // Add bounds to the prefix if applicable (only for reduction)
+ if (!bounds.empty() && areAllBoundsConstant(bounds)) {
+ prefixOS << getBoundsString(bounds);
+ }
+ break;
+ }
+
+ auto kindMap = var && var.getDefiningOp()
+ ? fir::getKindMapping(var.getDefiningOp())
+ : fir::KindMapping(type.getContext());
+ return fir::getTypeAsString(type, kindMap, prefixOS.str());
+}
+
+} // namespace acc
+} // namespace fir
diff --git a/flang/lib/Optimizer/OpenACC/Transforms/ACCInitializeFIRAnalyses.cpp b/flang/lib/Optimizer/OpenACC/Transforms/ACCInitializeFIRAnalyses.cpp
new file mode 100644
index 0000000000000..679b29bb462b5
--- /dev/null
+++ b/flang/lib/Optimizer/OpenACC/Transforms/ACCInitializeFIRAnalyses.cpp
@@ -0,0 +1,56 @@
+//===- ACCInitializeFIRAnalyses.cpp - Initialize FIR analyses ------------===//
+//
+// 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 initializes analyses that can be reused by subsequent OpenACC
+// passes in the pipeline.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Analysis/AliasAnalysis.h"
+#include "flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h"
+#include "flang/Optimizer/OpenACC/Passes.h"
+#include "mlir/Analysis/AliasAnalysis.h"
+#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
+
+namespace fir {
+namespace acc {
+#define GEN_PASS_DEF_ACCINITIALIZEFIRANALYSES
+#include "flang/Optimizer/OpenACC/Passes.h.inc"
+} // namespace acc
+} // namespace fir
+
+#define DEBUG_TYPE "acc-initialize-fir-analyses"
+
+namespace {
+
+/// This pass initializes analyses for reuse by subsequent OpenACC passes in the
+/// pipeline. It creates and caches analyses like OpenACCSupport so they can be
+/// retrieved by later passes using getAnalysis() or getCachedAnalysis().
+class ACCInitializeFIRAnalysesPass
+ : public fir::acc::impl::ACCInitializeFIRAnalysesBase<
+ ACCInitializeFIRAnalysesPass> {
+public:
+ void runOnOperation() override {
+ // Initialize OpenACCSupport with FIR-specific implementation.
+ auto &openACCSupport = getAnalysis<mlir::acc::OpenACCSupport>();
+ openACCSupport.setImplementation(fir::acc::FIROpenACCSupportAnalysis());
+
+ // Initialize AliasAnalysis with FIR-specific implementation.
+ auto &aliasAnalysis = getAnalysis<mlir::AliasAnalysis>();
+ aliasAnalysis.addAnalysisImplementation(fir::AliasAnalysis());
+
+ // Mark all analyses as preserved since this pass only initializes them
+ markAllAnalysesPreserved();
+ }
+};
+
+} // namespace
+
+std::unique_ptr<mlir::Pass> fir::acc::createACCInitializeFIRAnalysesPass() {
+ return std::make_unique<ACCInitializeFIRAnalysesPass>();
+}
diff --git a/flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt b/flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt
index ed177baf52bea..963b7351d3a45 100644
--- a/flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt
@@ -1,4 +1,5 @@
add_flang_library(FIROpenACCTransforms
+ ACCInitializeFIRAnalyses.cpp
ACCRecipeBufferization.cpp
DEPENDS
@@ -6,6 +7,8 @@ add_flang_library(FIROpenACCTransforms
LINK_LIBS
FIRDialect
+ FIROpenACCAnalysis
+ HLFIRDialect
MLIR_LIBS
MLIRIR
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-copy-reduction.fir b/flang/test/Transforms/OpenACC/acc-implicit-copy-reduction.fir
new file mode 100644
index 0000000000000..d0fc5b7a2ee0b
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-copy-reduction.fir
@@ -0,0 +1,134 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data{enable-implicit-reduction-copy=true})" -split-input-file | FileCheck %s --check-prefix=COPY
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data{enable-implicit-reduction-copy=false})" -split-input-file | FileCheck %s --check-prefix=FIRSTPRIVATE
+
+// Test case: integer reduction in parallel loop
+// This corresponds to Fortran code:
+// integer :: r, i
+// r = 0
+// !$acc parallel
+// !$acc loop gang reduction(+:r)
+// do i = 1, N
+// r = r + 1
+// enddo
+// !$acc end parallel
+
+acc.reduction.recipe @reduction_add_ref_i32 : !fir.ref<i32> reduction_operator <add> init {
+^bb0(%arg0: !fir.ref<i32>):
+ %c0_i32 = arith.constant 0 : i32
+ %0 = fir.alloca i32
+ %1 = fir.declare %0 {uniq_name = "acc.reduction.init"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ fir.store %c0_i32 to %1 : !fir.ref<i32>
+ acc.yield %1 : !fir.ref<i32>
+} combiner {
+^bb0(%arg0: !fir.ref<i32>, %arg1: !fir.ref<i32>):
+ %0 = fir.load %arg0 : !fir.ref<i32>
+ %1 = fir.load %arg1 : !fir.ref<i32>
+ %2 = arith.addi %0, %1 : i32
+ fir.store %2 to %arg0 : !fir.ref<i32>
+ acc.yield %arg0 : !fir.ref<i32>
+}
+
+func.func @test_reduction_implicit_copy() {
+ %c1_i32 = arith.constant 1 : i32
+ %cN = arith.constant 100 : i32
+ %r = fir.alloca i32 {bindc_name = "r", uniq_name = "_QFEr"}
+ %i = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"}
+ %r_decl = fir.declare %r {uniq_name = "_QFEr"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %i_decl = fir.declare %i {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %c0_i32 = arith.constant 0 : i32
+ fir.store %c0_i32 to %r_decl : !fir.ref<i32>
+
+ acc.parallel {
+ %red_var = acc.reduction varPtr(%r_decl : !fir.ref<i32>) -> !fir.ref<i32> {name = "r"}
+ acc.loop reduction(@reduction_add_ref_i32 -> %red_var : !fir.ref<i32>) control(%iv : i32) = (%c1_i32 : i32) to (%cN : i32) step (%c1_i32 : i32) {
+ fir.store %iv to %i_decl : !fir.ref<i32>
+ %cur_r = fir.load %red_var : !fir.ref<i32>
+ %new_r = arith.addi %cur_r, %c1_i32 : i32
+ fir.store %new_r to %red_var : !fir.ref<i32>
+ acc.yield
+ } attributes {inclusiveUpperbound = array<i1: true>, independent = [#acc.device_type<none>]}
+ acc.yield
+ }
+ return
+}
+
+// When enable-implicit-reduction-copy=true: expect copyin/copyout for reduction variable
+// COPY: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {dataClause = #acc<data_clause acc_reduction>, implicit = true, name = "r"}
+// COPY: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<i32>) to varPtr({{.*}} : !fir.ref<i32>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "r"}
+
+// When enable-implicit-reduction-copy=false: expect firstprivate for reduction variable
+// FIRSTPRIVATE: acc.firstprivate varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {implicit = true, name = "r"}
+// FIRSTPRIVATE-NOT: acc.copyin
+// FIRSTPRIVATE-NOT: acc.copyout
+
+// -----
+
+// Test case: reduction variable used both in loop and outside (should be firstprivate)
+// This corresponds to Fortran code:
+// integer :: r = 0, i, out
+// !$acc parallel num_gangs(1)
+// !$acc loop reduction(+:r) copyout(out)
+// do i = 1, N
+// r = r + 1
+// enddo
+// out = r
+// !$acc end parallel
+
+acc.reduction.recipe @reduction_add_ref_i32 : !fir.ref<i32> reduction_operator <add> init {
+^bb0(%arg0: !fir.ref<i32>):
+ %c0_i32 = arith.constant 0 : i32
+ %0 = fir.alloca i32
+ %1 = fir.declare %0 {uniq_name = "acc.reduction.init"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ fir.store %c0_i32 to %1 : !fir.ref<i32>
+ acc.yield %1 : !fir.ref<i32>
+} combiner {
+^bb0(%arg0: !fir.ref<i32>, %arg1: !fir.ref<i32>):
+ %0 = fir.load %arg0 : !fir.ref<i32>
+ %1 = fir.load %arg1 : !fir.ref<i32>
+ %2 = arith.addi %0, %1 : i32
+ fir.store %2 to %arg0 : !fir.ref<i32>
+ acc.yield %arg0 : !fir.ref<i32>
+}
+
+func.func @test_reduction_with_usage_outside_loop() {
+ %c1_i32 = arith.constant 1 : i32
+ %cN = arith.constant 100 : i32
+ %c0_i32 = arith.constant 0 : i32
+
+ %r = fir.alloca i32 {bindc_name = "r", uniq_name = "_QFEr"}
+ %i = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFEi"}
+ %out = fir.alloca i32 {bindc_name = "out", uniq_name = "_QFEout"}
+
+ %r_decl = fir.declare %r {uniq_name = "_QFEr"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %i_decl = fir.declare %i {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ %out_decl = fir.declare %out {uniq_name = "_QFEout"} : (!fir.ref<i32>) -> !fir.ref<i32>
+ fir.store %c0_i32 to %r_decl : !fir.ref<i32>
+
+ %out_copyout = acc.create varPtr(%out_decl : !fir.ref<i32>) -> !fir.ref<i32> {dataClause = #acc<data_clause acc_copyout>, name = "out"}
+ acc.parallel dataOperands(%out_copyout : !fir.ref<i32>) {
+ %red_var = acc.reduction varPtr(%r_decl : !fir.ref<i32>) -> !fir.ref<i32> {name = "r"}
+ acc.loop reduction(@reduction_add_ref_i32 -> %red_var : !fir.ref<i32>) control(%iv : i32) = (%c1_i32 : i32) to (%cN : i32) step (%c1_i32 : i32) {
+ fir.store %iv to %i_decl : !fir.ref<i32>
+ %cur_r = fir.load %red_var : !fir.ref<i32>
+ %new_r = arith.addi %cur_r, %c1_i32 : i32
+ fir.store %new_r to %red_var : !fir.ref<i32>
+ acc.yield
+ } attributes {inclusiveUpperbound = array<i1: true>, independent = [#acc.device_type<none>]}
+ // out = r (usage of r outside the loop)
+ %final_r = fir.load %r_decl : !fir.ref<i32>
+ fir.store %final_r to %out_copyout : !fir.ref<i32>
+ acc.yield
+ }
+ acc.copyout accPtr(%out_copyout : !fir.ref<i32>) to varPtr(%out_decl : !fir.ref<i32>) {dataClause = #acc<data_clause acc_copyout>, name = "out"}
+ return
+}
+
+// In this case, r should be firstprivate regardless of the flag setting because it's used outside the reduction context
+// COPY-LABEL: func.func @test_reduction_with_usage_outside_loop
+// COPY: acc.firstprivate varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {implicit = true, name = "r"}
+// COPY-NOT: acc.copyin varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {{.*}} name = "r"
+
+// FIRSTPRIVATE-LABEL: func.func @test_reduction_with_usage_outside_loop
+// FIRSTPRIVATE: acc.firstprivate varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {implicit = true, name = "r"}
+// FIRSTPRIVATE-NOT: acc.copyin varPtr({{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {{.*}} name = "r"
+
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data-derived-type-member.F90 b/flang/test/Transforms/OpenACC/acc-implicit-data-derived-type-member.F90
new file mode 100644
index 0000000000000..71e7d79b7260f
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data-derived-type-member.F90
@@ -0,0 +1,38 @@
+!RUN: rm -rf %t && mkdir %t && cd %t && \
+!RUN: bbc %s -fopenacc -emit-hlfir -o - \
+!RUN: | fir-opt --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" \
+!RUN: | FileCheck %s
+
+! This test exercises whether the ACCImplicitData pass inserts its new
+! data operations in appropriate position so that parents are copied in before
+! their children.
+
+module types
+ type derivc8r4
+ complex(8) :: member0
+ real(4) :: member1
+ end type derivc8r4
+end module
+program test
+ use types
+ implicit none
+ type (derivc8r4) :: d2
+ type (derivc8r4) :: d4
+ integer(4) :: i0
+ d2%member0 = 123
+ !$acc serial copyin(d2%member0) copyout(d4%member0)
+ do i0 = 1, 1
+ d4%member0 = d2%member0
+ end do
+ !$acc end serial
+end program
+
+!CHECK: acc.copyin {{.*}} {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "d2"}
+!CHECK: acc.copyin {{.*}} {name = "d2%member0"}
+!CHECK: acc.copyin {{.*}} {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "d4"}
+!CHECK: acc.create {{.*}} {dataClause = #acc<data_clause acc_copyout>, name = "d4%member0"}
+!CHECK: acc.delete {{.*}} {dataClause = #acc<data_clause acc_copyin>, name = "d2%member0"}
+!CHECK: acc.copyout {{.*}} {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "d2"}
+!CHECK: acc.copyout {{.*}} {name = "d4%member0"}
+!CHECK: acc.copyout {{.*}} {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "d4"}
+
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data-fortran.F90 b/flang/test/Transforms/OpenACC/acc-implicit-data-fortran.F90
new file mode 100644
index 0000000000000..228aba1b1164d
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data-fortran.F90
@@ -0,0 +1,79 @@
+!RUN: rm -rf %t && mkdir %t && cd %t && \
+!RUN: bbc %s -fopenacc -emit-hlfir -o - \
+!RUN: | fir-opt --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" \
+!RUN: | FileCheck %s --check-prefix=CHECKHLFIR
+
+!RUN: rm -rf %t && mkdir %t && cd %t && \
+!RUN: bbc %s -fopenacc -emit-hlfir -o - \
+!RUN: | fir-opt --pass-pipeline="builtin.module(cse,acc-initialize-fir-analyses,acc-implicit-data)" \
+!RUN: | FileCheck %s --check-prefix=CHECKCSE
+
+!RUN: rm -rf %t && mkdir %t && cd %t && \
+!RUN: bbc %s -fopenacc -emit-fir -o - \
+!RUN: | fir-opt --pass-pipeline="builtin.module(cse,acc-initialize-fir-analyses,acc-implicit-data)" \
+!RUN: | FileCheck %s --check-prefix=CHECKCSE
+
+! This test uses bbc to generate both HLFIR and FIR for this test. The intent is
+! that it is exercising the acc implicit data pipeline and ensures that
+! correct clauses are generated. It also runs CSE which eliminates redundant
+! interior pointer computations (and thus different live-ins are found).
+
+program main
+ type aggr
+ real :: field
+ end type
+ type nested
+ type(aggr) :: outer
+ end type
+ type(aggr) :: aggrvar
+ type(nested) :: nestaggrvar
+ real :: scalarvar
+ real :: arrayvar(10)
+ complex :: scalarcomp
+
+ aggrvar%field = 1
+ scalarvar = aggrvar%field
+ nestaggrvar%outer%field = scalarvar
+ scalarcomp = scalarvar
+ arrayvar = real(scalarcomp)
+ arrayvar(2) = aggrvar%field
+
+ !$acc kernels
+ arrayvar = aggrvar%field + scalarvar + nestaggrvar%outer%field + real(scalarcomp) + arrayvar(2)
+ !$acc end kernels
+
+ !$acc parallel
+ arrayvar = aggrvar%field + scalarvar + nestaggrvar%outer%field + real(scalarcomp) + arrayvar(2)
+ !$acc end parallel
+end program
+
+!CHECKHLFIR-LABEL: @_QQmain
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.type<_QFTnested{outer:!fir.type<_QFTaggr{field:f32}>}>>) -> !fir.ref<!fir.type<_QFTnested{outer:!fir.type<_QFTaggr{field:f32}>}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "nestaggrvar"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<complex<f32>>) -> !fir.ref<complex<f32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarcomp"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+!CHECKHLFIR: acc.kernels
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.type<_QFTnested{outer:!fir.type<_QFTaggr{field:f32}>}>>) -> !fir.ref<!fir.type<_QFTnested{outer:!fir.type<_QFTaggr{field:f32}>}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "nestaggrvar"}
+!CHECKHLFIR-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref<complex<f32>>) -> !fir.ref<complex<f32>> {implicit = true, name = "scalarcomp"}
+!CHECKHLFIR-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {implicit = true, name = "scalarvar"}
+!CHECKHLFIR: acc.parallel
+
+!CHECKCSE-LABEL: @_QQmain
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<complex<f32>>) -> !fir.ref<complex<f32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarcomp"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar%field"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "nestaggrvar%outer%field"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar(2)"}
+!CHECKCSE: acc.kernels
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+!CHECKCSE-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref<complex<f32>>) -> !fir.ref<complex<f32>> {implicit = true, name = "scalarcomp"}
+!CHECKCSE-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {implicit = true, name = "scalarvar"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar%field"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "nestaggrvar%outer%field"}
+!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar(2)"}
+!CHECKCSE: acc.parallel
+
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data.fir b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
new file mode 100644
index 0000000000000..7f6a57cb4d8c6
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
@@ -0,0 +1,358 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" -split-input-file | FileCheck %s
+
+// -----
+
+func.func @test_fir_scalar_in_serial() {
+ %livein = fir.alloca i64 {bindc_name = "scalarvar"}
+ acc.serial {
+ %load = fir.load %livein : !fir.ref<i64>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref<i64>) -> !fir.ref<i64> {implicit = true, name = "scalarvar"}
+
+// -----
+
+func.func @test_fir_scalar_in_parallel() {
+ %livein = fir.alloca f32 {bindc_name = "scalarvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<f32>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {implicit = true, name = "scalarvar"}
+
+// -----
+
+func.func @test_fir_scalar_in_kernels() {
+ %livein = fir.alloca f64 {bindc_name = "scalarvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<f64>
+ acc.terminator
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<f64>) -> !fir.ref<f64> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<f64>) to varPtr({{.*}} : !fir.ref<f64>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+
+// -----
+
+func.func @test_fir_scalar_in_parallel_defaultnone() {
+ %livein = fir.alloca f32 {bindc_name = "scalarvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<f32>
+ acc.yield
+ } attributes {defaultAttr = #acc<defaultvalue none>}
+ return
+}
+
+// CHECK-NOT: acc.firstprivate
+
+// -----
+
+func.func @test_fir_scalar_in_kernels_defaultnone() {
+ %livein = fir.alloca f64 {bindc_name = "scalarvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<f64>
+ acc.terminator
+ } attributes {defaultAttr = #acc<defaultvalue none>}
+ return
+}
+
+// CHECK-NOT: acc.copyin
+
+// -----
+
+func.func @test_fir_derivedtype_in_parallel() {
+ %livein = fir.alloca !fir.type<_QFTaggr{field:f32}> {bindc_name = "aggrvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<!fir.type<_QFTaggr{field:f32}>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) to varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+
+// -----
+
+func.func @test_fir_derivedtype_in_kernels() {
+ %livein = fir.alloca !fir.type<_QFTaggr{field:f32}> {bindc_name = "aggrvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<!fir.type<_QFTaggr{field:f32}>>
+ acc.terminator
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) to varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aggrvar"}
+
+// -----
+
+func.func @test_fir_array_in_parallel() {
+ %livein = fir.alloca !fir.array<10xf32> {bindc_name = "arrayvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<!fir.array<10xf32>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<!fir.array<10xf32>>) to varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+
+// -----
+
+func.func @test_fir_array_in_kernels() {
+ %livein = fir.alloca !fir.array<10xf32> {bindc_name = "arrayvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<!fir.array<10xf32>>
+ acc.terminator
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<!fir.array<10xf32>>) to varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "arrayvar"}
+
+// -----
+
+func.func @test_fir_derivedtype_in_parallel_defaultpresent() {
+ %livein = fir.alloca !fir.type<_QFTaggr{field:f32}> {bindc_name = "aggrvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<!fir.type<_QFTaggr{field:f32}>>
+ acc.yield
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {implicit = true, name = "aggrvar"}
+// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) {dataClause = #acc<data_clause acc_present>, implicit = true, name = "aggrvar"}
+
+// -----
+
+func.func @test_fir_derivedtype_in_kernels_defaultpresent() {
+ %livein = fir.alloca !fir.type<_QFTaggr{field:f32}> {bindc_name = "aggrvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<!fir.type<_QFTaggr{field:f32}>>
+ acc.terminator
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) -> !fir.ref<!fir.type<_QFTaggr{field:f32}>> {implicit = true, name = "aggrvar"}
+// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref<!fir.type<_QFTaggr{field:f32}>>) {dataClause = #acc<data_clause acc_present>, implicit = true, name = "aggrvar"}
+
+// -----
+
+func.func @test_fir_array_in_parallel_defaultpresent() {
+ %livein = fir.alloca !fir.array<10xf32> {bindc_name = "arrayvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<!fir.array<10xf32>>
+ acc.yield
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {implicit = true, name = "arrayvar"}
+// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref<!fir.array<10xf32>>) {dataClause = #acc<data_clause acc_present>, implicit = true, name = "arrayvar"}
+
+// -----
+
+func.func @test_fir_array_in_kernels_defaultpresent() {
+ %livein = fir.alloca !fir.array<10xf32> {bindc_name = "arrayvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<!fir.array<10xf32>>
+ acc.terminator
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref<!fir.array<10xf32>>) -> !fir.ref<!fir.array<10xf32>> {implicit = true, name = "arrayvar"}
+// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref<!fir.array<10xf32>>) {dataClause = #acc<data_clause acc_present>, implicit = true, name = "arrayvar"}
+
+// -----
+
+func.func @test_fir_scalar_in_parallel_defaultpresent() {
+ %livein = fir.alloca f32 {bindc_name = "scalarvar"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<f32>
+ acc.yield
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {implicit = true, name = "scalarvar"}
+
+// -----
+
+func.func @test_fir_scalar_in_kernels_defaultpresent() {
+ %livein = fir.alloca f64 {bindc_name = "scalarvar"}
+ acc.kernels {
+ %load = fir.load %livein : !fir.ref<f64>
+ acc.terminator
+ } attributes {defaultAttr = #acc<defaultvalue present>}
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<f64>) -> !fir.ref<f64> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<f64>) to varPtr({{.*}} : !fir.ref<f64>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "scalarvar"}
+
+// -----
+
+func.func @test_fir_box_ref() {
+ %livein = fir.alloca !fir.box<!fir.array<?xi32>> {bindc_name = "descriptor"}
+ acc.parallel {
+ %load = fir.load %livein : !fir.ref<!fir.box<!fir.array<?xi32>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref<!fir.box<!fir.array<?xi32>>>) -> !fir.ref<!fir.box<!fir.array<?xi32>>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "descriptor"}
+// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref<!fir.box<!fir.array<?xi32>>>) to varPtr({{.*}} : !fir.ref<!fir.box<!fir.array<?xi32>>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "descriptor"}
+
+// -----
+
+func.func @test_fir_box_val() {
+ %desc = fir.alloca !fir.box<!fir.array<?xi32>> {bindc_name = "descriptor"}
+ %livein = fir.load %desc : !fir.ref<!fir.box<!fir.array<?xi32>>>
+ acc.parallel {
+ %addr = fir.box_addr %livein : (!fir.box<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[COPYIN:.*]] = acc.copyin var({{.*}} : !fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "descriptor"}
+// CHECK: acc.copyout accVar(%[[COPYIN]] : !fir.box<!fir.array<?xi32>>) to var({{.*}} : !fir.box<!fir.array<?xi32>>) {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "descriptor"}
+
+
+// -----
+
+// This test has an explicit data clause for the box - but the pointer held
+// inside the box is used in the region instead of the box itself. Test that
+// implicit present is actually used.
+func.func @test_explicit_box_implicit_ptr() {
+ %c1 = arith.constant 1 : index
+ %c10 = arith.constant 10 : index
+ %arr = fir.alloca !fir.array<10xf32> {bindc_name = "aa"}
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %arr_decl = fir.declare %arr(%shape) {uniq_name = "aa"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xf32>>
+ %box = fir.embox %arr_decl(%shape) : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<10xf32>>
+ %copyin = acc.copyin var(%box : !fir.box<!fir.array<10xf32>>) -> !fir.box<!fir.array<10xf32>> {dataClause = #acc<data_clause acc_copy>, name = "aa"}
+ acc.serial dataOperands(%copyin : !fir.box<!fir.array<10xf32>>) {
+ // Use the pointer, not the box
+ %elem = fir.array_coor %arr_decl(%shape) %c1 : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, index) -> !fir.ref<f32>
+ acc.yield
+ }
+ acc.copyout accVar(%copyin : !fir.box<!fir.array<10xf32>>) to var(%box : !fir.box<!fir.array<10xf32>>) {dataClause = #acc<data_clause acc_copy>, name = "aa"}
+ return
+}
+
+// CHECK: acc.present varPtr(%{{.*}} : !fir.ref<!fir.array<10xf32>>){{.*}}-> !fir.ref<!fir.array<10xf32>> {implicit = true, name = "aa"}
+
+// -----
+
+// This test uses an explicit-shape array with no data clause - it also has
+// an optimization where the pointer is used instead of the boxed entity.
+// It tests that the implicit data pass is able to recover the size despite
+// it not being encoded in the FIR type.
+// It was generated from the following Fortran source:
+// subroutine array(aa,nn)
+// integer :: nn
+// real :: aa(10:nn)
+// !$acc kernels loop
+// do ii = 10, nn
+// aa(ii) = ii
+// end do
+// !$acc end kernels
+// end subroutine
+
+func.func @_QParray(%arg0: !fir.ref<!fir.array<?xf32>> {fir.bindc_name = "aa"}, %arg1: !fir.ref<i32> {fir.bindc_name = "nn"}) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c10_i64 = arith.constant 10 : i64
+ %0 = fir.dummy_scope : !fir.dscope
+ %1 = fir.declare %arg1 dummy_scope %0 {uniq_name = "_QFarrayEnn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+ %4 = fir.convert %c10_i64 : (i64) -> index
+ %5 = fir.load %1 : !fir.ref<i32>
+ %6 = fir.convert %5 : (i32) -> i64
+ %7 = fir.convert %6 : (i64) -> index
+ %8 = arith.subi %7, %4 : index
+ %9 = arith.addi %8, %c1 : index
+ %10 = arith.cmpi sgt, %9, %c0 : index
+ %11 = arith.select %10, %9, %c0 : index
+ %12 = fir.shape_shift %4, %11 : (index, index) -> !fir.shapeshift<1>
+ %13 = fir.declare %arg0(%12) dummy_scope %0 {uniq_name = "_QFarrayEaa"} : (!fir.ref<!fir.array<?xf32>>, !fir.shapeshift<1>, !fir.dscope) -> !fir.ref<!fir.array<?xf32>>
+ acc.kernels {
+ %elem = fir.array_coor %13(%12) %4 : (!fir.ref<!fir.array<?xf32>>, !fir.shapeshift<1>, index) -> !fir.ref<f32>
+ acc.terminator
+ }
+ return
+}
+
+// This tries to confirm that the acc.bounds operation is as expected.
+// Effectively the extent needs to be max(0, nn), stride needs to be 1,
+// adjusted lowerbound is 0, and actual language start index is 10.
+// CHECK: %[[NN:.*]] = fir.declare %{{.*}} dummy_scope %{{.*}} {uniq_name = "_QFarrayEnn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+// CHECK: %[[C10:.*]] = fir.convert %c10{{.*}} : (i64) -> index
+// CHECK: %[[LOADEDNN:.*]] = fir.load %[[NN]] : !fir.ref<i32>
+// CHECK: %[[CAST1:.*]] = fir.convert %[[LOADEDNN]] : (i32) -> i64
+// CHECK: %[[CAST2:.*]] = fir.convert %[[CAST1]] : (i64) -> index
+// CHECK: %[[SUBI:.*]] = arith.subi %[[CAST2]], %[[C10]] : index
+// CHECK: %[[ADDI:.*]] = arith.addi %[[SUBI]], %c1{{.*}} : index
+// CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[ADDI]], %c0{{.*}} : index
+// CHECK: %[[SELECT:.*]] = arith.select %[[CMPI]], %[[ADDI]], %c0{{.*}} : index
+// CHECK: %[[BOUNDS:.*]] = acc.bounds lowerbound(%c0{{.*}} : index) upperbound(%{{.*}} : index) extent(%[[SELECT]] : index) stride(%c1{{.*}} : index) startIdx(%[[C10]] : index)
+// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<?xf32>>) bounds(%[[BOUNDS]]) -> !fir.ref<!fir.array<?xf32>> {dataClause = #acc<data_clause acc_copy>, implicit = true, name = "aa"}
+
+// -----
+
+// Test to confirm that a copyin clause is not implicitly generated for deviceptr symbol.
+func.func @test_deviceptr_no_implicit_copy() {
+ %c10 = arith.constant 10 : index
+ %arr = fir.alloca !fir.array<10xf64> {bindc_name = "a"}
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %arr_box = fir.embox %arr(%shape) : (!fir.ref<!fir.array<10xf64>>, !fir.shape<1>) -> !fir.box<!fir.array<10xf64>>
+ %devptr = acc.deviceptr var(%arr_box : !fir.box<!fir.array<10xf64>>) -> !fir.box<!fir.array<10xf64>> {name = "a"}
+ acc.parallel dataOperands(%devptr : !fir.box<!fir.array<10xf64>>) {
+ %elem = fir.box_addr %arr_box : (!fir.box<!fir.array<10xf64>>) -> !fir.ref<!fir.array<10xf64>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr
+
+// -----
+
+// Test that acc.declare with deviceptr doesn't generate implicit copyin
+func.func @test_acc_declare_deviceptr() {
+ %c10 = arith.constant 10 : index
+ %arr = fir.alloca !fir.array<10xf64> {bindc_name = "a"}
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %arr_box = fir.embox %arr(%shape) : (!fir.ref<!fir.array<10xf64>>, !fir.shape<1>) -> !fir.box<!fir.array<10xf64>>
+ %devptr = acc.deviceptr var(%arr_box : !fir.box<!fir.array<10xf64>>) -> !fir.box<!fir.array<10xf64>> {name = "a"}
+ %token = acc.declare_enter dataOperands(%devptr : !fir.box<!fir.array<10xf64>>)
+ acc.parallel {
+ %elem = fir.box_addr %arr_box : (!fir.box<!fir.array<10xf64>>) -> !fir.ref<!fir.array<10xf64>>
+ acc.yield
+ }
+ acc.declare_exit token(%token)
+ return
+}
+
+// CHECK-LABEL: func.func @test_acc_declare_deviceptr
+// CHECK: acc.deviceptr
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr
+
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir b/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
new file mode 100644
index 0000000000000..e4a7b8b18bc2a
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
@@ -0,0 +1,284 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" -split-input-file | FileCheck %s
+
+// Test implicit firstprivate behavior for various scalar types in parallel and serial constructs.
+// Scalars in parallel/serial constructs should be implicitly firstprivate according to OpenACC spec.
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i32 : !fir.ref<i32> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<i32>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca i32
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<i32>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i32>, %[[DST:.*]]: !fir.ref<i32>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i32>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i32>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_i32_scalar_in_parallel
+func.func @test_i32_scalar_in_parallel() {
+ %scalar = fir.alloca i32 {bindc_name = "i32_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<i32>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {implicit = true, name = "i32_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i32 -> %[[FIRSTPRIV]] : !fir.ref<i32>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i64 : !fir.ref<i64> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<i64>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca i64
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<i64>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i64>, %[[DST:.*]]: !fir.ref<i64>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i64>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i64>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_i64_scalar_in_parallel
+func.func @test_i64_scalar_in_parallel() {
+ %scalar = fir.alloca i64 {bindc_name = "i64_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<i64>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<i64>) -> !fir.ref<i64> {implicit = true, name = "i64_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i64 -> %[[FIRSTPRIV]] : !fir.ref<i64>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_f32 : !fir.ref<f32> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<f32>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca f32
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<f32>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<f32>, %[[DST:.*]]: !fir.ref<f32>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<f32>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<f32>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_f32_scalar_in_parallel
+func.func @test_f32_scalar_in_parallel() {
+ %scalar = fir.alloca f32 {bindc_name = "f32_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<f32>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<f32>) -> !fir.ref<f32> {implicit = true, name = "f32_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_f32 -> %[[FIRSTPRIV]] : !fir.ref<f32>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_f64 : !fir.ref<f64> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<f64>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca f64
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<f64>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<f64>, %[[DST:.*]]: !fir.ref<f64>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<f64>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<f64>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_f64_scalar_in_parallel
+func.func @test_f64_scalar_in_parallel() {
+ %scalar = fir.alloca f64 {bindc_name = "f64_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<f64>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<f64>) -> !fir.ref<f64> {implicit = true, name = "f64_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_f64 -> %[[FIRSTPRIV]] : !fir.ref<f64>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_l32 : !fir.ref<!fir.logical<4>> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.logical<4>>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca !fir.logical<4>
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<!fir.logical<4>>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.logical<4>>, %[[DST:.*]]: !fir.ref<!fir.logical<4>>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.logical<4>>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<!fir.logical<4>>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_logical_scalar_in_parallel
+func.func @test_logical_scalar_in_parallel() {
+ %scalar = fir.alloca !fir.logical<4> {bindc_name = "logical_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<!fir.logical<4>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<!fir.logical<4>>) -> !fir.ref<!fir.logical<4>> {implicit = true, name = "logical_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_l32 -> %[[FIRSTPRIV]] : !fir.ref<!fir.logical<4>>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_z32 : !fir.ref<complex<f32>> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<complex<f32>>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca complex<f32>
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<complex<f32>>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<complex<f32>>, %[[DST:.*]]: !fir.ref<complex<f32>>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<complex<f32>>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<complex<f32>>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_complex_scalar_in_parallel
+func.func @test_complex_scalar_in_parallel() {
+ %scalar = fir.alloca complex<f32> {bindc_name = "complex_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<complex<f32>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<complex<f32>>) -> !fir.ref<complex<f32>> {implicit = true, name = "complex_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_z32 -> %[[FIRSTPRIV]] : !fir.ref<complex<f32>>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_z64 : !fir.ref<complex<f64>> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<complex<f64>>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca complex<f64>
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<complex<f64>>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<complex<f64>>, %[[DST:.*]]: !fir.ref<complex<f64>>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<complex<f64>>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<complex<f64>>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_complex8_scalar_in_parallel
+func.func @test_complex8_scalar_in_parallel() {
+ %scalar = fir.alloca complex<f64> {bindc_name = "complex8_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<complex<f64>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<complex<f64>>) -> !fir.ref<complex<f64>> {implicit = true, name = "complex8_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_z64 -> %[[FIRSTPRIV]] : !fir.ref<complex<f64>>)
+
+// -----
+
+// Test with serial construct
+
+// CHECK-LABEL: func.func @test_i32_scalar_in_serial
+func.func @test_i32_scalar_in_serial() {
+ %scalar = fir.alloca i32 {bindc_name = "serial_i32_var"}
+ acc.serial {
+ %load = fir.load %scalar : !fir.ref<i32>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<i32>) -> !fir.ref<i32> {implicit = true, name = "serial_i32_var"}
+// CHECK: acc.serial firstprivate(@firstprivatization_ref_i32 -> %[[FIRSTPRIV]] : !fir.ref<i32>)
+
+// -----
+
+// Test with serial construct and f64
+
+// CHECK-LABEL: func.func @test_f64_scalar_in_serial
+func.func @test_f64_scalar_in_serial() {
+ %scalar = fir.alloca f64 {bindc_name = "serial_f64_var"}
+ acc.serial {
+ %load = fir.load %scalar : !fir.ref<f64>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<f64>) -> !fir.ref<f64> {implicit = true, name = "serial_f64_var"}
+// CHECK: acc.serial firstprivate(@firstprivatization_ref_f64 -> %[[FIRSTPRIV]] : !fir.ref<f64>)
+
+// -----
+
+// Test i8 and i16 scalar types
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i8 : !fir.ref<i8> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<i8>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca i8
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<i8>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i8>, %[[DST:.*]]: !fir.ref<i8>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i8>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i8>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_i8_scalar_in_parallel
+func.func @test_i8_scalar_in_parallel() {
+ %scalar = fir.alloca i8 {bindc_name = "i8_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<i8>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<i8>) -> !fir.ref<i8> {implicit = true, name = "i8_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i8 -> %[[FIRSTPRIV]] : !fir.ref<i8>)
+
+// -----
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i16 : !fir.ref<i16> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<i16>):
+// CHECK: %[[ALLOC:.*]] = fir.alloca i16
+// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]]
+// CHECK: acc.yield %[[DECL]]#0 : !fir.ref<i16>
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i16>, %[[DST:.*]]: !fir.ref<i16>):
+// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i16>
+// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i16>
+// CHECK: acc.terminator
+// CHECK: }
+
+// CHECK-LABEL: func.func @test_i16_scalar_in_parallel
+func.func @test_i16_scalar_in_parallel() {
+ %scalar = fir.alloca i16 {bindc_name = "i16_var"}
+ acc.parallel {
+ %load = fir.load %scalar : !fir.ref<i16>
+ acc.yield
+ }
+ return
+}
+
+// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref<i16>) -> !fir.ref<i16> {implicit = true, name = "i16_var"}
+// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i16 -> %[[FIRSTPRIV]] : !fir.ref<i16>)
+
More information about the flang-commits
mailing list