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 + +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(); +/// 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 createACCInitializeFIRAnalysesPass(); std::unique_ptr 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 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 + +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 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 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 6208bed6d0aea..bb4c95aab3aa2 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 &bounds) { - for (auto bound : bounds) { - auto dataBound = - mlir::dyn_cast(bound.getDefiningOp()); - assert(dataBound && "Must be DataBoundOp operation"); - if (!isConstantBound(dataBound)) - return false; - } - return true; -} - static llvm::SmallVector genConstantBounds(fir::FirOpBuilder &builder, mlir::Location loc, mlir::acc::DataBoundsOp &dataBound) { @@ -1324,7 +1313,7 @@ mlir::acc::FirstprivateRecipeOp Fortran::lower::createOrGetFirstprivateRecipe( mlir::OpBuilder::InsertionGuard guard(builder); auto recipe = genRecipeOp( builder, mod, recipeName, loc, ty); - bool allConstantBound = areAllBoundConstant(bounds); + bool allConstantBound = fir::acc::areAllBoundsConstant(bounds); auto [source, destination] = genRecipeCombinerOrCopyRegion( builder, loc, ty, recipe.getCopyRegion(), bounds, 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 &bounds) { - std::stringstream boundStr; - if (!bounds.empty()) - boundStr << "_section_"; - llvm::interleave( - bounds, - [&](mlir::Value bound) { - auto boundsOp = - mlir::cast(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 &bounds, @@ -1458,9 +1420,8 @@ static void genPrivatizationRecipes( RecipeOp recipe; mlir::Type retTy = getTypeFromBounds(bounds, info.addr.getType()); if constexpr (std::is_same_v) { - 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( @@ -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( @@ -1623,7 +1582,7 @@ mlir::acc::ReductionRecipeOp Fortran::lower::createOrGetReductionRecipe( mlir::OpBuilder::InsertionGuard guard(builder); auto recipe = genRecipeOp( builder, mod, recipeName, loc, ty, op); - bool allConstantBound = areAllBoundConstant(bounds); + bool allConstantBound = fir::acc::areAllBoundsConstant(bounds); auto [dest, src] = genRecipeCombinerOrCopyRegion( builder, loc, ty, recipe.getCombinerRegion(), bounds, allConstantBound); @@ -1708,15 +1667,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( @@ -1961,9 +1917,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..e5b8123305c62 --- /dev/null +++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp @@ -0,0 +1,269 @@ +//===- 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 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(defOp) + .Case( + [&v](mlir::ViewLikeOpInterface op) { + v = op.getViewSource(); + return true; + }) + .Case([&v](fir::ReboxOp op) { + v = op.getBox(); + return true; + }) + .Case([&v](fir::EmboxOp op) { + v = op.getMemref(); + return true; + }) + .Case([&v](fir::ConvertOp op) { + v = op.getValue(); + return true; + }) + .Case([&v](fir::LoadOp op) { + v = op.getMemref(); + return true; + }) + .Case([&v](fir::BoxAddrOp op) { + // The box holds the name of the variable. + v = op.getVal(); + return true; + }) + .Case([&](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 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 op) { + std::optional> 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(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 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([&](auto op) { + srcName = op.getUniqName().str(); + return false; + }) + .Case([&](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; }); + } + + // Fallback to the default implementation. + if (srcName.empty()) + 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 bounds) { + for (auto bound : bounds) { + auto dataBound = + mlir::dyn_cast(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 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(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 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(); + openACCSupport.setImplementation(fir::acc::FIROpenACCSupportAnalysis()); + + // Initialize AliasAnalysis with FIR-specific implementation. + auto &aliasAnalysis = getAnalysis(); + aliasAnalysis.addAnalysisImplementation(fir::AliasAnalysis()); + + // Mark all analyses as preserved since this pass only initializes them + markAllAnalysesPreserved(); + } +}; + +} // namespace + +std::unique_ptr fir::acc::createACCInitializeFIRAnalysesPass() { + return std::make_unique(); +} 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 reduction_operator init { +^bb0(%arg0: !fir.ref): + %c0_i32 = arith.constant 0 : i32 + %0 = fir.alloca i32 + %1 = fir.declare %0 {uniq_name = "acc.reduction.init"} : (!fir.ref) -> !fir.ref + fir.store %c0_i32 to %1 : !fir.ref + acc.yield %1 : !fir.ref +} combiner { +^bb0(%arg0: !fir.ref, %arg1: !fir.ref): + %0 = fir.load %arg0 : !fir.ref + %1 = fir.load %arg1 : !fir.ref + %2 = arith.addi %0, %1 : i32 + fir.store %2 to %arg0 : !fir.ref + acc.yield %arg0 : !fir.ref +} + +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) -> !fir.ref + %i_decl = fir.declare %i {uniq_name = "_QFEi"} : (!fir.ref) -> !fir.ref + %c0_i32 = arith.constant 0 : i32 + fir.store %c0_i32 to %r_decl : !fir.ref + + acc.parallel { + %red_var = acc.reduction varPtr(%r_decl : !fir.ref) -> !fir.ref {name = "r"} + acc.loop reduction(@reduction_add_ref_i32 -> %red_var : !fir.ref) control(%iv : i32) = (%c1_i32 : i32) to (%cN : i32) step (%c1_i32 : i32) { + fir.store %iv to %i_decl : !fir.ref + %cur_r = fir.load %red_var : !fir.ref + %new_r = arith.addi %cur_r, %c1_i32 : i32 + fir.store %new_r to %red_var : !fir.ref + acc.yield + } attributes {inclusiveUpperbound = array, independent = [#acc.device_type]} + acc.yield + } + return +} + +// When enable-implicit-reduction-copy=true: expect copyin/copyout for reduction variable +// COPY: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "r"} +// COPY: acc.copyout accPtr(%[[COPYIN]] : !fir.ref) to varPtr({{.*}} : !fir.ref) {dataClause = #acc, implicit = true, name = "r"} + +// When enable-implicit-reduction-copy=false: expect firstprivate for reduction variable +// FIRSTPRIVATE: acc.firstprivate varPtr({{.*}} : !fir.ref) -> !fir.ref {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 reduction_operator init { +^bb0(%arg0: !fir.ref): + %c0_i32 = arith.constant 0 : i32 + %0 = fir.alloca i32 + %1 = fir.declare %0 {uniq_name = "acc.reduction.init"} : (!fir.ref) -> !fir.ref + fir.store %c0_i32 to %1 : !fir.ref + acc.yield %1 : !fir.ref +} combiner { +^bb0(%arg0: !fir.ref, %arg1: !fir.ref): + %0 = fir.load %arg0 : !fir.ref + %1 = fir.load %arg1 : !fir.ref + %2 = arith.addi %0, %1 : i32 + fir.store %2 to %arg0 : !fir.ref + acc.yield %arg0 : !fir.ref +} + +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) -> !fir.ref + %i_decl = fir.declare %i {uniq_name = "_QFEi"} : (!fir.ref) -> !fir.ref + %out_decl = fir.declare %out {uniq_name = "_QFEout"} : (!fir.ref) -> !fir.ref + fir.store %c0_i32 to %r_decl : !fir.ref + + %out_copyout = acc.create varPtr(%out_decl : !fir.ref) -> !fir.ref {dataClause = #acc, name = "out"} + acc.parallel dataOperands(%out_copyout : !fir.ref) { + %red_var = acc.reduction varPtr(%r_decl : !fir.ref) -> !fir.ref {name = "r"} + acc.loop reduction(@reduction_add_ref_i32 -> %red_var : !fir.ref) control(%iv : i32) = (%c1_i32 : i32) to (%cN : i32) step (%c1_i32 : i32) { + fir.store %iv to %i_decl : !fir.ref + %cur_r = fir.load %red_var : !fir.ref + %new_r = arith.addi %cur_r, %c1_i32 : i32 + fir.store %new_r to %red_var : !fir.ref + acc.yield + } attributes {inclusiveUpperbound = array, independent = [#acc.device_type]} + // out = r (usage of r outside the loop) + %final_r = fir.load %r_decl : !fir.ref + fir.store %final_r to %out_copyout : !fir.ref + acc.yield + } + acc.copyout accPtr(%out_copyout : !fir.ref) to varPtr(%out_decl : !fir.ref) {dataClause = #acc, 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) -> !fir.ref {implicit = true, name = "r"} +// COPY-NOT: acc.copyin varPtr({{.*}} : !fir.ref) -> !fir.ref {{.*}} name = "r" + +// FIRSTPRIVATE-LABEL: func.func @test_reduction_with_usage_outside_loop +// FIRSTPRIVATE: acc.firstprivate varPtr({{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "r"} +// FIRSTPRIVATE-NOT: acc.copyin varPtr({{.*}} : !fir.ref) -> !fir.ref {{.*}} 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, implicit = true, name = "d2"} +!CHECK: acc.copyin {{.*}} {name = "d2%member0"} +!CHECK: acc.copyin {{.*}} {dataClause = #acc, implicit = true, name = "d4"} +!CHECK: acc.create {{.*}} {dataClause = #acc, name = "d4%member0"} +!CHECK: acc.delete {{.*}} {dataClause = #acc, name = "d2%member0"} +!CHECK: acc.copyout {{.*}} {dataClause = #acc, implicit = true, name = "d2"} +!CHECK: acc.copyout {{.*}} {name = "d4%member0"} +!CHECK: acc.copyout {{.*}} {dataClause = #acc, 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.ref> {dataClause = #acc, implicit = true, name = "aggrvar"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref}>>) -> !fir.ref}>> {dataClause = #acc, implicit = true, name = "nestaggrvar"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "scalarcomp"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "scalarvar"} +!CHECKHLFIR: acc.kernels +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "aggrvar"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +!CHECKHLFIR-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref}>>) -> !fir.ref}>> {dataClause = #acc, implicit = true, name = "nestaggrvar"} +!CHECKHLFIR-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "scalarcomp"} +!CHECKHLFIR-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "scalarvar"} +!CHECKHLFIR: acc.parallel + +!CHECKCSE-LABEL: @_QQmain +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "scalarcomp"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "scalarvar"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "aggrvar%field"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "nestaggrvar%outer%field"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "arrayvar(2)"} +!CHECKCSE: acc.kernels +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +!CHECKCSE-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "scalarcomp"} +!CHECKCSE-DAG: acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "scalarvar"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "aggrvar%field"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "nestaggrvar%outer%field"} +!CHECKCSE-DAG: acc.copyin varPtr(%{{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, 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 + acc.yield + } + return +} + +// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref) -> !fir.ref {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 + acc.yield + } + return +} + +// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref) -> !fir.ref {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 + acc.terminator + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "scalarvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref) to varPtr({{.*}} : !fir.ref) {dataClause = #acc, 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 + acc.yield + } attributes {defaultAttr = #acc} + 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 + acc.terminator + } attributes {defaultAttr = #acc} + 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> + acc.yield + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "aggrvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref>) to varPtr({{.*}} : !fir.ref>) {dataClause = #acc, 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> + acc.terminator + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "aggrvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref>) to varPtr({{.*}} : !fir.ref>) {dataClause = #acc, 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> + acc.yield + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref>) to varPtr({{.*}} : !fir.ref>) {dataClause = #acc, 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> + acc.terminator + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref>) -> !fir.ref> {dataClause = #acc, implicit = true, name = "arrayvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref>) to varPtr({{.*}} : !fir.ref>) {dataClause = #acc, 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> + acc.yield + } attributes {defaultAttr = #acc} + return +} + +// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "aggrvar"} +// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref>) {dataClause = #acc, 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> + acc.terminator + } attributes {defaultAttr = #acc} + return +} + +// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "aggrvar"} +// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref>) {dataClause = #acc, 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> + acc.yield + } attributes {defaultAttr = #acc} + return +} + +// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "arrayvar"} +// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref>) {dataClause = #acc, 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> + acc.terminator + } attributes {defaultAttr = #acc} + return +} + +// CHECK: %[[PRESENT:.*]] = acc.present varPtr({{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "arrayvar"} +// CHECK: acc.delete accPtr(%[[PRESENT]] : !fir.ref>) {dataClause = #acc, 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 + acc.yield + } attributes {defaultAttr = #acc} + return +} + +// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref) -> !fir.ref {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 + acc.terminator + } attributes {defaultAttr = #acc} + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref) -> !fir.ref {dataClause = #acc, implicit = true, name = "scalarvar"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref) to varPtr({{.*}} : !fir.ref) {dataClause = #acc, implicit = true, name = "scalarvar"} + +// ----- + +func.func @test_fir_box_ref() { + %livein = fir.alloca !fir.box> {bindc_name = "descriptor"} + acc.parallel { + %load = fir.load %livein : !fir.ref>> + acc.yield + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin varPtr({{.*}} : !fir.ref>>) -> !fir.ref>> {dataClause = #acc, implicit = true, name = "descriptor"} +// CHECK: acc.copyout accPtr(%[[COPYIN]] : !fir.ref>>) to varPtr({{.*}} : !fir.ref>>) {dataClause = #acc, implicit = true, name = "descriptor"} + +// ----- + +func.func @test_fir_box_val() { + %desc = fir.alloca !fir.box> {bindc_name = "descriptor"} + %livein = fir.load %desc : !fir.ref>> + acc.parallel { + %addr = fir.box_addr %livein : (!fir.box>) -> !fir.ref> + acc.yield + } + return +} + +// CHECK: %[[COPYIN:.*]] = acc.copyin var({{.*}} : !fir.box>) -> !fir.box> {dataClause = #acc, implicit = true, name = "descriptor"} +// CHECK: acc.copyout accVar(%[[COPYIN]] : !fir.box>) to var({{.*}} : !fir.box>) {dataClause = #acc, 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.shape<1>) -> !fir.ref> + %box = fir.embox %arr_decl(%shape) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + %copyin = acc.copyin var(%box : !fir.box>) -> !fir.box> {dataClause = #acc, name = "aa"} + acc.serial dataOperands(%copyin : !fir.box>) { + // Use the pointer, not the box + %elem = fir.array_coor %arr_decl(%shape) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + acc.yield + } + acc.copyout accVar(%copyin : !fir.box>) to var(%box : !fir.box>) {dataClause = #acc, name = "aa"} + return +} + +// CHECK: acc.present varPtr(%{{.*}} : !fir.ref>){{.*}}-> !fir.ref> {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.bindc_name = "aa"}, %arg1: !fir.ref {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, !fir.dscope) -> !fir.ref + %4 = fir.convert %c10_i64 : (i64) -> index + %5 = fir.load %1 : !fir.ref + %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.shapeshift<1>, !fir.dscope) -> !fir.ref> + acc.kernels { + %elem = fir.array_coor %13(%12) %4 : (!fir.ref>, !fir.shapeshift<1>, index) -> !fir.ref + 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, !fir.dscope) -> !fir.ref +// CHECK: %[[C10:.*]] = fir.convert %c10{{.*}} : (i64) -> index +// CHECK: %[[LOADEDNN:.*]] = fir.load %[[NN]] : !fir.ref +// 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>) bounds(%[[BOUNDS]]) -> !fir.ref> {dataClause = #acc, 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.shape<1>) -> !fir.box> + %devptr = acc.deviceptr var(%arr_box : !fir.box>) -> !fir.box> {name = "a"} + acc.parallel dataOperands(%devptr : !fir.box>) { + %elem = fir.box_addr %arr_box : (!fir.box>) -> !fir.ref> + 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.shape<1>) -> !fir.box> + %devptr = acc.deviceptr var(%arr_box : !fir.box>) -> !fir.box> {name = "a"} + %token = acc.declare_enter dataOperands(%devptr : !fir.box>) + acc.parallel { + %elem = fir.box_addr %arr_box : (!fir.box>) -> !fir.ref> + 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 init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca i32 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "i32_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i32 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i64 : !fir.ref init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca i64 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "i64_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i64 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_f32 : !fir.ref init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca f32 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "f32_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_f32 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_f64 : !fir.ref init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca f64 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "f64_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_f64 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_l32 : !fir.ref> init { +// CHECK: ^bb0(%{{.*}}: !fir.ref>): +// CHECK: %[[ALLOC:.*]] = fir.alloca !fir.logical<4> +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref> +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref>, %[[DST:.*]]: !fir.ref>): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref> +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref> +// 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> + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "logical_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_l32 -> %[[FIRSTPRIV]] : !fir.ref>) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_z32 : !fir.ref> init { +// CHECK: ^bb0(%{{.*}}: !fir.ref>): +// CHECK: %[[ALLOC:.*]] = fir.alloca complex +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref> +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref>, %[[DST:.*]]: !fir.ref>): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref> +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref> +// CHECK: acc.terminator +// CHECK: } + +// CHECK-LABEL: func.func @test_complex_scalar_in_parallel +func.func @test_complex_scalar_in_parallel() { + %scalar = fir.alloca complex {bindc_name = "complex_var"} + acc.parallel { + %load = fir.load %scalar : !fir.ref> + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "complex_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_z32 -> %[[FIRSTPRIV]] : !fir.ref>) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_z64 : !fir.ref> init { +// CHECK: ^bb0(%{{.*}}: !fir.ref>): +// CHECK: %[[ALLOC:.*]] = fir.alloca complex +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref> +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref>, %[[DST:.*]]: !fir.ref>): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref> +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref> +// CHECK: acc.terminator +// CHECK: } + +// CHECK-LABEL: func.func @test_complex8_scalar_in_parallel +func.func @test_complex8_scalar_in_parallel() { + %scalar = fir.alloca complex {bindc_name = "complex8_var"} + acc.parallel { + %load = fir.load %scalar : !fir.ref> + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref>) -> !fir.ref> {implicit = true, name = "complex8_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_z64 -> %[[FIRSTPRIV]] : !fir.ref>) + +// ----- + +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "serial_i32_var"} +// CHECK: acc.serial firstprivate(@firstprivatization_ref_i32 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "serial_f64_var"} +// CHECK: acc.serial firstprivate(@firstprivatization_ref_f64 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// Test i8 and i16 scalar types + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i8 : !fir.ref init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca i8 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "i8_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i8 -> %[[FIRSTPRIV]] : !fir.ref) + +// ----- + +// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_ref_i16 : !fir.ref init { +// CHECK: ^bb0(%{{.*}}: !fir.ref): +// CHECK: %[[ALLOC:.*]] = fir.alloca i16 +// CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ALLOC]] +// CHECK: acc.yield %[[DECL]]#0 : !fir.ref +// CHECK: } copy { +// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref, %[[DST:.*]]: !fir.ref): +// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref +// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref +// 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 + acc.yield + } + return +} + +// CHECK: %[[FIRSTPRIV:.*]] = acc.firstprivate varPtr(%{{.*}} : !fir.ref) -> !fir.ref {implicit = true, name = "i16_var"} +// CHECK: acc.parallel firstprivate(@firstprivatization_ref_i16 -> %[[FIRSTPRIV]] : !fir.ref) +