[flang][openacc] Allow open acc routines from other modules. (#136012)

OpenACC routines annotations in separate compilation units currently get
ignored, which leads to errors in compilation. There are two reason for
currently ignoring open acc routine information and this PR is
addressing both.
- The module file reader doesn't read back in openacc directives from
module files.
  - Simple fix in `flang/lib/Semantics/mod-file.cpp`
- The lowering to HLFIR doesn't generate routine directives for symbols
imported from other modules that are openacc routines.
- This is the majority of this diff, and is address by the changes that
start in `flang/lib/Lower/CallInterface.cpp`.
This commit is contained in:
Andre Kuhlenschmidt
2025-05-09 11:12:24 -07:00
committed by GitHub
parent b3a6d434a7
commit 4d9479fa8f
12 changed files with 461 additions and 369 deletions

View File

@@ -22,6 +22,9 @@ class StringRef;
} // namespace llvm
namespace mlir {
namespace func {
class FuncOp;
} // namespace func
class Location;
class Type;
class ModuleOp;
@@ -31,9 +34,13 @@ class Value;
namespace fir {
class FirOpBuilder;
}
} // namespace fir
namespace Fortran {
namespace evaluate {
struct ProcedureDesignator;
} // namespace evaluate
namespace parser {
struct AccClauseList;
struct OpenACCConstruct;
@@ -42,6 +49,7 @@ struct OpenACCRoutineConstruct;
} // namespace parser
namespace semantics {
class OpenACCRoutineInfo;
class SemanticsContext;
class Symbol;
} // namespace semantics
@@ -55,9 +63,6 @@ namespace pft {
struct Evaluation;
} // namespace pft
using AccRoutineInfoMappingList =
llvm::SmallVector<std::pair<std::string, mlir::SymbolRefAttr>>;
static constexpr llvm::StringRef declarePostAllocSuffix =
"_acc_declare_update_desc_post_alloc";
static constexpr llvm::StringRef declarePreDeallocSuffix =
@@ -71,19 +76,12 @@ mlir::Value genOpenACCConstruct(AbstractConverter &,
Fortran::semantics::SemanticsContext &,
pft::Evaluation &,
const parser::OpenACCConstruct &);
void genOpenACCDeclarativeConstruct(AbstractConverter &,
Fortran::semantics::SemanticsContext &,
StatementContext &,
const parser::OpenACCDeclarativeConstruct &,
AccRoutineInfoMappingList &);
void genOpenACCRoutineConstruct(AbstractConverter &,
Fortran::semantics::SemanticsContext &,
mlir::ModuleOp,
const parser::OpenACCRoutineConstruct &,
AccRoutineInfoMappingList &);
void finalizeOpenACCRoutineAttachment(mlir::ModuleOp,
AccRoutineInfoMappingList &);
void genOpenACCDeclarativeConstruct(
AbstractConverter &, Fortran::semantics::SemanticsContext &,
StatementContext &, const parser::OpenACCDeclarativeConstruct &);
void genOpenACCRoutineConstruct(
AbstractConverter &, mlir::ModuleOp, mlir::func::FuncOp,
const std::vector<Fortran::semantics::OpenACCRoutineInfo> &);
/// Get a acc.private.recipe op for the given type or create it if it does not
/// exist yet.

View File

@@ -22,6 +22,7 @@
#include <list>
#include <optional>
#include <set>
#include <variant>
#include <vector>
namespace llvm {
@@ -127,6 +128,9 @@ private:
// Device type specific OpenACC routine information
class OpenACCRoutineDeviceTypeInfo {
public:
explicit OpenACCRoutineDeviceTypeInfo(
Fortran::common::OpenACCDeviceType dType)
: deviceType_{dType} {}
bool isSeq() const { return isSeq_; }
void set_isSeq(bool value = true) { isSeq_ = value; }
bool isVector() const { return isVector_; }
@@ -137,22 +141,30 @@ public:
void set_isGang(bool value = true) { isGang_ = value; }
unsigned gangDim() const { return gangDim_; }
void set_gangDim(unsigned value) { gangDim_ = value; }
const std::string *bindName() const {
return bindName_ ? &*bindName_ : nullptr;
const std::variant<std::string, SymbolRef> *bindName() const {
return bindName_.has_value() ? &*bindName_ : nullptr;
}
void set_bindName(std::string &&name) { bindName_ = std::move(name); }
void set_dType(Fortran::common::OpenACCDeviceType dType) {
deviceType_ = dType;
const std::optional<std::variant<std::string, SymbolRef>> &
bindNameOpt() const {
return bindName_;
}
void set_bindName(std::string &&name) { bindName_.emplace(std::move(name)); }
void set_bindName(SymbolRef symbol) { bindName_.emplace(symbol); }
Fortran::common::OpenACCDeviceType dType() const { return deviceType_; }
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const OpenACCRoutineDeviceTypeInfo &);
private:
bool isSeq_{false};
bool isVector_{false};
bool isWorker_{false};
bool isGang_{false};
unsigned gangDim_{0};
std::optional<std::string> bindName_;
// bind("name") -> std::string
// bind(sym) -> SymbolRef (requires namemangling in lowering)
std::optional<std::variant<std::string, SymbolRef>> bindName_;
Fortran::common::OpenACCDeviceType deviceType_{
Fortran::common::OpenACCDeviceType::None};
};
@@ -162,15 +174,29 @@ private:
// in as objects in the OpenACCRoutineDeviceTypeInfo list.
class OpenACCRoutineInfo : public OpenACCRoutineDeviceTypeInfo {
public:
OpenACCRoutineInfo()
: OpenACCRoutineDeviceTypeInfo(Fortran::common::OpenACCDeviceType::None) {
}
bool isNohost() const { return isNohost_; }
void set_isNohost(bool value = true) { isNohost_ = value; }
std::list<OpenACCRoutineDeviceTypeInfo> &deviceTypeInfos() {
const std::list<OpenACCRoutineDeviceTypeInfo> &deviceTypeInfos() const {
return deviceTypeInfos_;
}
void add_deviceTypeInfo(OpenACCRoutineDeviceTypeInfo &info) {
deviceTypeInfos_.push_back(info);
OpenACCRoutineDeviceTypeInfo &add_deviceTypeInfo(
Fortran::common::OpenACCDeviceType type) {
return add_deviceTypeInfo(OpenACCRoutineDeviceTypeInfo(type));
}
OpenACCRoutineDeviceTypeInfo &add_deviceTypeInfo(
OpenACCRoutineDeviceTypeInfo &&info) {
deviceTypeInfos_.push_back(std::move(info));
return deviceTypeInfos_.back();
}
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const OpenACCRoutineInfo &);
private:
std::list<OpenACCRoutineDeviceTypeInfo> deviceTypeInfos_;
bool isNohost_{false};

View File

@@ -398,37 +398,39 @@ public:
// they are available before lowering any function that may use them.
bool hasMainProgram = false;
const Fortran::semantics::Symbol *globalOmpRequiresSymbol = nullptr;
for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
Fortran::common::visit(
Fortran::common::visitors{
[&](Fortran::lower::pft::FunctionLikeUnit &f) {
if (f.isMainProgram())
hasMainProgram = true;
declareFunction(f);
if (!globalOmpRequiresSymbol)
globalOmpRequiresSymbol = f.getScope().symbol();
},
[&](Fortran::lower::pft::ModuleLikeUnit &m) {
lowerModuleDeclScope(m);
for (Fortran::lower::pft::ContainedUnit &unit :
m.containedUnitList)
if (auto *f =
std::get_if<Fortran::lower::pft::FunctionLikeUnit>(
&unit))
declareFunction(*f);
},
[&](Fortran::lower::pft::BlockDataUnit &b) {
if (!globalOmpRequiresSymbol)
globalOmpRequiresSymbol = b.symTab.symbol();
},
[&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
[&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {},
},
u);
}
createBuilderOutsideOfFuncOpAndDo([&]() {
for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
Fortran::common::visit(
Fortran::common::visitors{
[&](Fortran::lower::pft::FunctionLikeUnit &f) {
if (f.isMainProgram())
hasMainProgram = true;
declareFunction(f);
if (!globalOmpRequiresSymbol)
globalOmpRequiresSymbol = f.getScope().symbol();
},
[&](Fortran::lower::pft::ModuleLikeUnit &m) {
lowerModuleDeclScope(m);
for (Fortran::lower::pft::ContainedUnit &unit :
m.containedUnitList)
if (auto *f =
std::get_if<Fortran::lower::pft::FunctionLikeUnit>(
&unit))
declareFunction(*f);
},
[&](Fortran::lower::pft::BlockDataUnit &b) {
if (!globalOmpRequiresSymbol)
globalOmpRequiresSymbol = b.symTab.symbol();
},
[&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
[&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {},
},
u);
}
});
// Create definitions of intrinsic module constants.
createGlobalOutsideOfFunctionLowering(
createBuilderOutsideOfFuncOpAndDo(
[&]() { createIntrinsicModuleDefinitions(pft); });
// Primary translation pass.
@@ -439,14 +441,7 @@ public:
[&](Fortran::lower::pft::ModuleLikeUnit &m) { lowerMod(m); },
[&](Fortran::lower::pft::BlockDataUnit &b) {},
[&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
[&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {
builder = new fir::FirOpBuilder(
bridge.getModule(), bridge.getKindMap(), &mlirSymbolTable);
Fortran::lower::genOpenACCRoutineConstruct(
*this, bridge.getSemanticsContext(), bridge.getModule(),
d.routine, accRoutineInfos);
builder = nullptr;
},
[&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {},
},
u);
}
@@ -454,24 +449,24 @@ public:
// Once all the code has been translated, create global runtime type info
// data structures for the derived types that have been processed, as well
// as fir.type_info operations for the dispatch tables.
createGlobalOutsideOfFunctionLowering(
createBuilderOutsideOfFuncOpAndDo(
[&]() { typeInfoConverter.createTypeInfo(*this); });
// Generate the `main` entry point if necessary
if (hasMainProgram)
createGlobalOutsideOfFunctionLowering([&]() {
createBuilderOutsideOfFuncOpAndDo([&]() {
fir::runtime::genMain(*builder, toLocation(),
bridge.getEnvironmentDefaults(),
getFoldingContext().languageFeatures().IsEnabled(
Fortran::common::LanguageFeature::CUDA));
});
finalizeOpenACCLowering();
finalizeOpenMPLowering(globalOmpRequiresSymbol);
}
/// Declare a function.
void declareFunction(Fortran::lower::pft::FunctionLikeUnit &funit) {
CHECK(builder && "declareFunction called with uninitialized builder");
setCurrentPosition(funit.getStartingSourceLoc());
for (int entryIndex = 0, last = funit.entryPointList.size();
entryIndex < last; ++entryIndex) {
@@ -1036,7 +1031,10 @@ public:
return bridge.getSemanticsContext().FindScope(currentPosition);
}
fir::FirOpBuilder &getFirOpBuilder() override final { return *builder; }
fir::FirOpBuilder &getFirOpBuilder() override final {
CHECK(builder && "builder is not set before calling getFirOpBuilder");
return *builder;
}
mlir::ModuleOp getModuleOp() override final { return bridge.getModule(); }
@@ -3063,8 +3061,7 @@ private:
void genFIR(const Fortran::parser::OpenACCDeclarativeConstruct &accDecl) {
genOpenACCDeclarativeConstruct(*this, bridge.getSemanticsContext(),
bridge.openAccCtx(), accDecl,
accRoutineInfos);
bridge.openAccCtx(), accDecl);
for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations())
genFIR(e);
}
@@ -5661,6 +5658,10 @@ private:
LLVM_DEBUG(llvm::dbgs() << "\n[bridge - startNewFunction]";
if (auto *sym = scope.symbol()) llvm::dbgs() << " " << *sym;
llvm::dbgs() << "\n");
// Setting the builder is not necessary here, because callee
// always looks up the FuncOp from the module. If there was a function that
// was not declared yet, this call to callee will cause an assertion
// failure.
Fortran::lower::CalleeInterface callee(funit, *this);
mlir::func::FuncOp func = callee.addEntryBlockAndMapArguments();
builder =
@@ -5930,8 +5931,9 @@ private:
/// Helper to generate GlobalOps when the builder is not positioned in any
/// region block. This is required because the FirOpBuilder assumes it is
/// always positioned inside a region block when creating globals, the easiest
/// way comply is to create a dummy function and to throw it afterwards.
void createGlobalOutsideOfFunctionLowering(
/// way to comply is to create a dummy function and to throw it away
/// afterwards.
void createBuilderOutsideOfFuncOpAndDo(
const std::function<void()> &createGlobals) {
// FIXME: get rid of the bogus function context and instantiate the
// globals directly into the module.
@@ -5943,6 +5945,7 @@ private:
mlir::FunctionType::get(context, std::nullopt, std::nullopt),
symbolTable);
func.addEntryBlock();
CHECK(!builder && "Expected builder to be uninitialized");
builder = new fir::FirOpBuilder(func, bridge.getKindMap(), symbolTable);
assert(builder && "FirOpBuilder did not instantiate");
builder->setFastMathFlags(bridge.getLoweringOptions().getMathOptions());
@@ -5958,7 +5961,7 @@ private:
/// Instantiate the data from a BLOCK DATA unit.
void lowerBlockData(Fortran::lower::pft::BlockDataUnit &bdunit) {
createGlobalOutsideOfFunctionLowering([&]() {
createBuilderOutsideOfFuncOpAndDo([&]() {
Fortran::lower::AggregateStoreMap fakeMap;
for (const auto &[_, sym] : bdunit.symTab) {
if (sym->has<Fortran::semantics::ObjectEntityDetails>()) {
@@ -5972,7 +5975,7 @@ private:
/// Create fir::Global for all the common blocks that appear in the program.
void
lowerCommonBlocks(const Fortran::semantics::CommonBlockList &commonBlocks) {
createGlobalOutsideOfFunctionLowering(
createBuilderOutsideOfFuncOpAndDo(
[&]() { Fortran::lower::defineCommonBlocks(*this, commonBlocks); });
}
@@ -6042,36 +6045,34 @@ private:
/// declarative construct.
void lowerModuleDeclScope(Fortran::lower::pft::ModuleLikeUnit &mod) {
setCurrentPosition(mod.getStartingSourceLoc());
createGlobalOutsideOfFunctionLowering([&]() {
auto &scopeVariableListMap =
Fortran::lower::pft::getScopeVariableListMap(mod);
for (const auto &var : Fortran::lower::pft::getScopeVariableList(
mod.getScope(), scopeVariableListMap)) {
auto &scopeVariableListMap =
Fortran::lower::pft::getScopeVariableListMap(mod);
for (const auto &var : Fortran::lower::pft::getScopeVariableList(
mod.getScope(), scopeVariableListMap)) {
// Only define the variables owned by this module.
const Fortran::semantics::Scope *owningScope = var.getOwningScope();
if (owningScope && mod.getScope() != *owningScope)
// Only define the variables owned by this module.
const Fortran::semantics::Scope *owningScope = var.getOwningScope();
if (owningScope && mod.getScope() != *owningScope)
continue;
// Very special case: The value of numeric_storage_size depends on
// compilation options and therefore its value is not yet known when
// building the builtins runtime. Instead, the parameter is folding a
// __numeric_storage_size() expression which is loaded into the user
// program. For the iso_fortran_env object file, omit the symbol as it
// is never used.
if (var.hasSymbol()) {
const Fortran::semantics::Symbol &sym = var.getSymbol();
const Fortran::semantics::Scope &owner = sym.owner();
if (sym.name() == "numeric_storage_size" && owner.IsModule() &&
DEREF(owner.symbol()).name() == "iso_fortran_env")
continue;
// Very special case: The value of numeric_storage_size depends on
// compilation options and therefore its value is not yet known when
// building the builtins runtime. Instead, the parameter is folding a
// __numeric_storage_size() expression which is loaded into the user
// program. For the iso_fortran_env object file, omit the symbol as it
// is never used.
if (var.hasSymbol()) {
const Fortran::semantics::Symbol &sym = var.getSymbol();
const Fortran::semantics::Scope &owner = sym.owner();
if (sym.name() == "numeric_storage_size" && owner.IsModule() &&
DEREF(owner.symbol()).name() == "iso_fortran_env")
continue;
}
Fortran::lower::defineModuleVariable(*this, var);
}
Fortran::lower::defineModuleVariable(*this, var);
}
for (auto &eval : mod.evaluationList)
genFIR(eval);
});
}
/// Lower functions contained in a module.
@@ -6372,13 +6373,6 @@ private:
expr.u);
}
/// Performing OpenACC lowering action that were deferred to the end of
/// lowering.
void finalizeOpenACCLowering() {
Fortran::lower::finalizeOpenACCRoutineAttachment(getModuleOp(),
accRoutineInfos);
}
/// Performing OpenMP lowering actions that were deferred to the end of
/// lowering.
void finalizeOpenMPLowering(
@@ -6470,9 +6464,6 @@ private:
/// A counter for uniquing names in `literalNamesMap`.
std::uint64_t uniqueLitId = 0;
/// Deferred OpenACC routine attachment.
Fortran::lower::AccRoutineInfoMappingList accRoutineInfos;
/// Whether an OpenMP target region or declare target function/subroutine
/// intended for device offloading has been detected
bool ompDeviceCodeFound = false;

View File

@@ -10,6 +10,7 @@
#include "flang/Evaluate/fold.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/Mangler.h"
#include "flang/Lower/OpenACC.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/StatementContext.h"
#include "flang/Lower/Support/Utils.h"
@@ -715,6 +716,17 @@ void Fortran::lower::CallInterface<T>::declare() {
func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes);
setCUDAAttributes(func, side().getProcedureSymbol(), characteristic);
if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol()) {
if (const auto &info{
sym->GetUltimate()
.detailsIf<Fortran::semantics::SubprogramDetails>()}) {
if (!info->openACCRoutineInfos().empty()) {
genOpenACCRoutineConstruct(converter, module, func,
info->openACCRoutineInfos());
}
}
}
}
}
}

View File

@@ -33,11 +33,13 @@
#include "flang/Semantics/scope.h"
#include "flang/Semantics/tools.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Frontend/OpenACC/ACC.h.inc"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#define DEBUG_TYPE "flang-lower-openacc"
@@ -4493,125 +4495,27 @@ static void attachRoutineInfo(mlir::func::FuncOp func,
mlir::acc::RoutineInfoAttr::get(func.getContext(), routines));
}
void Fortran::lower::genOpenACCRoutineConstruct(
Fortran::lower::AbstractConverter &converter,
Fortran::semantics::SemanticsContext &semanticsContext, mlir::ModuleOp mod,
const Fortran::parser::OpenACCRoutineConstruct &routineConstruct,
Fortran::lower::AccRoutineInfoMappingList &accRoutineInfos) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Location loc = converter.genLocation(routineConstruct.source);
std::optional<Fortran::parser::Name> name =
std::get<std::optional<Fortran::parser::Name>>(routineConstruct.t);
const auto &clauses =
std::get<Fortran::parser::AccClauseList>(routineConstruct.t);
mlir::func::FuncOp funcOp;
std::string funcName;
if (name) {
funcName = converter.mangleName(*name->symbol);
funcOp =
builder.getNamedFunction(mod, builder.getMLIRSymbolTable(), funcName);
static mlir::ArrayAttr
getArrayAttrOrNull(fir::FirOpBuilder &builder,
llvm::SmallVector<mlir::Attribute> &attributes) {
if (attributes.empty()) {
return nullptr;
} else {
Fortran::semantics::Scope &scope =
semanticsContext.FindScope(routineConstruct.source);
const Fortran::semantics::Scope &progUnit{GetProgramUnitContaining(scope)};
const auto *subpDetails{
progUnit.symbol()
? progUnit.symbol()
->detailsIf<Fortran::semantics::SubprogramDetails>()
: nullptr};
if (subpDetails && subpDetails->isInterface()) {
funcName = converter.mangleName(*progUnit.symbol());
funcOp =
builder.getNamedFunction(mod, builder.getMLIRSymbolTable(), funcName);
} else {
funcOp = builder.getFunction();
funcName = funcOp.getName();
}
return builder.getArrayAttr(attributes);
}
bool hasNohost = false;
}
llvm::SmallVector<mlir::Attribute> seqDeviceTypes, vectorDeviceTypes,
workerDeviceTypes, bindNameDeviceTypes, bindNames, gangDeviceTypes,
gangDimDeviceTypes, gangDimValues;
// device_type attribute is set to `none` until a device_type clause is
// encountered.
llvm::SmallVector<mlir::Attribute> crtDeviceTypes;
crtDeviceTypes.push_back(mlir::acc::DeviceTypeAttr::get(
builder.getContext(), mlir::acc::DeviceType::None));
for (const Fortran::parser::AccClause &clause : clauses.v) {
if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
for (auto crtDeviceTypeAttr : crtDeviceTypes)
seqDeviceTypes.push_back(crtDeviceTypeAttr);
} else if (const auto *gangClause =
std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
if (gangClause->v) {
const Fortran::parser::AccGangArgList &x = *gangClause->v;
for (const Fortran::parser::AccGangArg &gangArg : x.v) {
if (const auto *dim =
std::get_if<Fortran::parser::AccGangArg::Dim>(&gangArg.u)) {
const std::optional<int64_t> dimValue = Fortran::evaluate::ToInt64(
*Fortran::semantics::GetExpr(dim->v));
if (!dimValue)
mlir::emitError(loc,
"dim value must be a constant positive integer");
mlir::Attribute gangDimAttr =
builder.getIntegerAttr(builder.getI64Type(), *dimValue);
for (auto crtDeviceTypeAttr : crtDeviceTypes) {
gangDimValues.push_back(gangDimAttr);
gangDimDeviceTypes.push_back(crtDeviceTypeAttr);
}
}
}
} else {
for (auto crtDeviceTypeAttr : crtDeviceTypes)
gangDeviceTypes.push_back(crtDeviceTypeAttr);
}
} else if (std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) {
for (auto crtDeviceTypeAttr : crtDeviceTypes)
vectorDeviceTypes.push_back(crtDeviceTypeAttr);
} else if (std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) {
for (auto crtDeviceTypeAttr : crtDeviceTypes)
workerDeviceTypes.push_back(crtDeviceTypeAttr);
} else if (std::get_if<Fortran::parser::AccClause::Nohost>(&clause.u)) {
hasNohost = true;
} else if (const auto *bindClause =
std::get_if<Fortran::parser::AccClause::Bind>(&clause.u)) {
if (const auto *name =
std::get_if<Fortran::parser::Name>(&bindClause->v.u)) {
mlir::Attribute bindNameAttr =
builder.getStringAttr(converter.mangleName(*name->symbol));
for (auto crtDeviceTypeAttr : crtDeviceTypes) {
bindNames.push_back(bindNameAttr);
bindNameDeviceTypes.push_back(crtDeviceTypeAttr);
}
} else if (const auto charExpr =
std::get_if<Fortran::parser::ScalarDefaultCharExpr>(
&bindClause->v.u)) {
const std::optional<std::string> name =
Fortran::semantics::GetConstExpr<std::string>(semanticsContext,
*charExpr);
if (!name)
mlir::emitError(loc, "Could not retrieve the bind name");
mlir::Attribute bindNameAttr = builder.getStringAttr(*name);
for (auto crtDeviceTypeAttr : crtDeviceTypes) {
bindNames.push_back(bindNameAttr);
bindNameDeviceTypes.push_back(crtDeviceTypeAttr);
}
}
} else if (const auto *deviceTypeClause =
std::get_if<Fortran::parser::AccClause::DeviceType>(
&clause.u)) {
crtDeviceTypes.clear();
gatherDeviceTypeAttrs(builder, deviceTypeClause, crtDeviceTypes);
}
}
mlir::OpBuilder modBuilder(mod.getBodyRegion());
std::stringstream routineOpName;
routineOpName << accRoutinePrefix.str() << routineCounter++;
void createOpenACCRoutineConstruct(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
mlir::ModuleOp mod, mlir::func::FuncOp funcOp, std::string funcName,
bool hasNohost, llvm::SmallVector<mlir::Attribute> &bindNames,
llvm::SmallVector<mlir::Attribute> &bindNameDeviceTypes,
llvm::SmallVector<mlir::Attribute> &gangDeviceTypes,
llvm::SmallVector<mlir::Attribute> &gangDimValues,
llvm::SmallVector<mlir::Attribute> &gangDimDeviceTypes,
llvm::SmallVector<mlir::Attribute> &seqDeviceTypes,
llvm::SmallVector<mlir::Attribute> &workerDeviceTypes,
llvm::SmallVector<mlir::Attribute> &vectorDeviceTypes) {
for (auto routineOp : mod.getOps<mlir::acc::RoutineOp>()) {
if (routineOp.getFuncName().str().compare(funcName) == 0) {
@@ -4626,47 +4530,117 @@ void Fortran::lower::genOpenACCRoutineConstruct(
mlir::emitError(loc, "Routine already specified with different clauses");
}
}
std::stringstream routineOpName;
routineOpName << accRoutinePrefix.str() << routineCounter++;
std::string routineOpStr = routineOpName.str();
mlir::OpBuilder modBuilder(mod.getBodyRegion());
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
modBuilder.create<mlir::acc::RoutineOp>(
loc, routineOpName.str(), funcName,
bindNames.empty() ? nullptr : builder.getArrayAttr(bindNames),
bindNameDeviceTypes.empty() ? nullptr
: builder.getArrayAttr(bindNameDeviceTypes),
workerDeviceTypes.empty() ? nullptr
: builder.getArrayAttr(workerDeviceTypes),
vectorDeviceTypes.empty() ? nullptr
: builder.getArrayAttr(vectorDeviceTypes),
seqDeviceTypes.empty() ? nullptr : builder.getArrayAttr(seqDeviceTypes),
hasNohost, /*implicit=*/false,
gangDeviceTypes.empty() ? nullptr : builder.getArrayAttr(gangDeviceTypes),
gangDimValues.empty() ? nullptr : builder.getArrayAttr(gangDimValues),
gangDimDeviceTypes.empty() ? nullptr
: builder.getArrayAttr(gangDimDeviceTypes));
loc, routineOpStr, funcName, getArrayAttrOrNull(builder, bindNames),
getArrayAttrOrNull(builder, bindNameDeviceTypes),
getArrayAttrOrNull(builder, workerDeviceTypes),
getArrayAttrOrNull(builder, vectorDeviceTypes),
getArrayAttrOrNull(builder, seqDeviceTypes), hasNohost,
/*implicit=*/false, getArrayAttrOrNull(builder, gangDeviceTypes),
getArrayAttrOrNull(builder, gangDimValues),
getArrayAttrOrNull(builder, gangDimDeviceTypes));
if (funcOp)
attachRoutineInfo(funcOp, builder.getSymbolRefAttr(routineOpName.str()));
else
// FuncOp is not lowered yet. Keep the information so the routine info
// can be attached later to the funcOp.
accRoutineInfos.push_back(std::make_pair(
funcName, builder.getSymbolRefAttr(routineOpName.str())));
attachRoutineInfo(funcOp, builder.getSymbolRefAttr(routineOpStr));
}
void Fortran::lower::finalizeOpenACCRoutineAttachment(
mlir::ModuleOp mod,
Fortran::lower::AccRoutineInfoMappingList &accRoutineInfos) {
for (auto &mapping : accRoutineInfos) {
mlir::func::FuncOp funcOp =
mod.lookupSymbol<mlir::func::FuncOp>(mapping.first);
if (!funcOp)
mlir::emitWarning(mod.getLoc(),
llvm::Twine("function '") + llvm::Twine(mapping.first) +
llvm::Twine("' in acc routine directive is not "
"found in this translation unit."));
else
attachRoutineInfo(funcOp, mapping.second);
static void interpretRoutineDeviceInfo(
Fortran::lower::AbstractConverter &converter,
const Fortran::semantics::OpenACCRoutineDeviceTypeInfo &dinfo,
llvm::SmallVector<mlir::Attribute> &seqDeviceTypes,
llvm::SmallVector<mlir::Attribute> &vectorDeviceTypes,
llvm::SmallVector<mlir::Attribute> &workerDeviceTypes,
llvm::SmallVector<mlir::Attribute> &bindNameDeviceTypes,
llvm::SmallVector<mlir::Attribute> &bindNames,
llvm::SmallVector<mlir::Attribute> &gangDeviceTypes,
llvm::SmallVector<mlir::Attribute> &gangDimValues,
llvm::SmallVector<mlir::Attribute> &gangDimDeviceTypes) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
auto getDeviceTypeAttr = [&]() -> mlir::Attribute {
auto context = builder.getContext();
auto value = getDeviceType(dinfo.dType());
return mlir::acc::DeviceTypeAttr::get(context, value);
};
if (dinfo.isSeq()) {
seqDeviceTypes.push_back(getDeviceTypeAttr());
}
accRoutineInfos.clear();
if (dinfo.isVector()) {
vectorDeviceTypes.push_back(getDeviceTypeAttr());
}
if (dinfo.isWorker()) {
workerDeviceTypes.push_back(getDeviceTypeAttr());
}
if (dinfo.isGang()) {
unsigned gangDim = dinfo.gangDim();
auto deviceType = getDeviceTypeAttr();
if (!gangDim) {
gangDeviceTypes.push_back(deviceType);
} else {
gangDimValues.push_back(
builder.getIntegerAttr(builder.getI64Type(), gangDim));
gangDimDeviceTypes.push_back(deviceType);
}
}
if (dinfo.bindNameOpt().has_value()) {
const auto &bindName = dinfo.bindNameOpt().value();
mlir::Attribute bindNameAttr;
if (const auto &bindStr{std::get_if<std::string>(&bindName)}) {
bindNameAttr = builder.getStringAttr(*bindStr);
} else if (const auto &bindSym{
std::get_if<Fortran::semantics::SymbolRef>(&bindName)}) {
bindNameAttr = builder.getStringAttr(converter.mangleName(*bindSym));
} else {
llvm_unreachable("Unsupported bind name type");
}
bindNames.push_back(bindNameAttr);
bindNameDeviceTypes.push_back(getDeviceTypeAttr());
}
}
void Fortran::lower::genOpenACCRoutineConstruct(
Fortran::lower::AbstractConverter &converter, mlir::ModuleOp mod,
mlir::func::FuncOp funcOp,
const std::vector<Fortran::semantics::OpenACCRoutineInfo> &routineInfos) {
CHECK(funcOp && "Expected a valid function operation");
mlir::Location loc{funcOp.getLoc()};
std::string funcName{funcOp.getName()};
// Collect the routine clauses
bool hasNohost{false};
llvm::SmallVector<mlir::Attribute> seqDeviceTypes, vectorDeviceTypes,
workerDeviceTypes, bindNameDeviceTypes, bindNames, gangDeviceTypes,
gangDimDeviceTypes, gangDimValues;
for (const Fortran::semantics::OpenACCRoutineInfo &info : routineInfos) {
// Device Independent Attributes
if (info.isNohost()) {
hasNohost = true;
}
// Note: Device Independent Attributes are set to the
// none device type in `info`.
interpretRoutineDeviceInfo(converter, info, seqDeviceTypes,
vectorDeviceTypes, workerDeviceTypes,
bindNameDeviceTypes, bindNames, gangDeviceTypes,
gangDimValues, gangDimDeviceTypes);
// Device Dependent Attributes
for (const Fortran::semantics::OpenACCRoutineDeviceTypeInfo &dinfo :
info.deviceTypeInfos()) {
interpretRoutineDeviceInfo(
converter, dinfo, seqDeviceTypes, vectorDeviceTypes,
workerDeviceTypes, bindNameDeviceTypes, bindNames, gangDeviceTypes,
gangDimValues, gangDimDeviceTypes);
}
}
createOpenACCRoutineConstruct(
converter, loc, mod, funcOp, funcName, hasNohost, bindNames,
bindNameDeviceTypes, gangDeviceTypes, gangDimValues, gangDimDeviceTypes,
seqDeviceTypes, workerDeviceTypes, vectorDeviceTypes);
}
static void
@@ -4774,8 +4748,7 @@ void Fortran::lower::genOpenACCDeclarativeConstruct(
Fortran::lower::AbstractConverter &converter,
Fortran::semantics::SemanticsContext &semanticsContext,
Fortran::lower::StatementContext &openAccCtx,
const Fortran::parser::OpenACCDeclarativeConstruct &accDeclConstruct,
Fortran::lower::AccRoutineInfoMappingList &accRoutineInfos) {
const Fortran::parser::OpenACCDeclarativeConstruct &accDeclConstruct) {
Fortran::common::visit(
common::visitors{
@@ -4784,14 +4757,7 @@ void Fortran::lower::genOpenACCDeclarativeConstruct(
genACC(converter, semanticsContext, openAccCtx,
standaloneDeclarativeConstruct);
},
[&](const Fortran::parser::OpenACCRoutineConstruct
&routineConstruct) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::ModuleOp mod = builder.getModule();
Fortran::lower::genOpenACCRoutineConstruct(
converter, semanticsContext, mod, routineConstruct,
accRoutineInfos);
},
[&](const Fortran::parser::OpenACCRoutineConstruct &x) {},
},
accDeclConstruct.u);
}

View File

@@ -24,6 +24,7 @@
#include <fstream>
#include <set>
#include <string_view>
#include <variant>
#include <vector>
namespace Fortran::semantics {
@@ -638,8 +639,14 @@ static void PutOpenACCDeviceTypeRoutineInfo(
if (info.isWorker()) {
os << " worker";
}
if (info.bindName()) {
os << " bind(" << *info.bindName() << ")";
if (const std::variant<std::string, SymbolRef> *bindName{info.bindName()}) {
os << " bind(";
if (std::holds_alternative<std::string>(*bindName)) {
os << "\"" << std::get<std::string>(*bindName) << "\"";
} else {
os << std::get<SymbolRef>(*bindName)->name();
}
os << ")";
}
}
@@ -1388,6 +1395,9 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
parser::Options options;
options.isModuleFile = true;
options.features.Enable(common::LanguageFeature::BackslashEscapes);
if (context_.languageFeatures().IsEnabled(common::LanguageFeature::OpenACC)) {
options.features.Enable(common::LanguageFeature::OpenACC);
}
options.features.Enable(common::LanguageFeature::OpenMP);
options.features.Enable(common::LanguageFeature::CUDA);
if (!isIntrinsic.value_or(false) && !notAModule) {

View File

@@ -1047,88 +1047,78 @@ void AccAttributeVisitor::AddRoutineInfoToSymbol(
Symbol &symbol, const parser::OpenACCRoutineConstruct &x) {
if (symbol.has<SubprogramDetails>()) {
Fortran::semantics::OpenACCRoutineInfo info;
const auto &clauses = std::get<Fortran::parser::AccClauseList>(x.t);
std::vector<OpenACCRoutineDeviceTypeInfo *> currentDevices;
currentDevices.push_back(&info);
const auto &clauses{std::get<Fortran::parser::AccClauseList>(x.t)};
for (const Fortran::parser::AccClause &clause : clauses.v) {
if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
if (info.deviceTypeInfos().empty()) {
info.set_isSeq();
} else {
info.deviceTypeInfos().back().set_isSeq();
}
} else if (const auto *gangClause =
std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
if (info.deviceTypeInfos().empty()) {
info.set_isGang();
} else {
info.deviceTypeInfos().back().set_isGang();
}
if (gangClause->v) {
const Fortran::parser::AccGangArgList &x = *gangClause->v;
for (const Fortran::parser::AccGangArg &gangArg : x.v) {
if (const auto *dim =
std::get_if<Fortran::parser::AccGangArg::Dim>(&gangArg.u)) {
if (const auto v{EvaluateInt64(context_, dim->v)}) {
if (info.deviceTypeInfos().empty()) {
info.set_gangDim(*v);
} else {
info.deviceTypeInfos().back().set_gangDim(*v);
}
}
}
}
}
} else if (std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) {
if (info.deviceTypeInfos().empty()) {
info.set_isVector();
} else {
info.deviceTypeInfos().back().set_isVector();
}
} else if (std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) {
if (info.deviceTypeInfos().empty()) {
info.set_isWorker();
} else {
info.deviceTypeInfos().back().set_isWorker();
if (const auto *dTypeClause{
std::get_if<Fortran::parser::AccClause::DeviceType>(&clause.u)}) {
currentDevices.clear();
for (const auto &deviceTypeExpr : dTypeClause->v.v) {
currentDevices.push_back(&info.add_deviceTypeInfo(deviceTypeExpr.v));
}
} else if (std::get_if<Fortran::parser::AccClause::Nohost>(&clause.u)) {
info.set_isNohost();
} else if (const auto *bindClause =
std::get_if<Fortran::parser::AccClause::Bind>(&clause.u)) {
if (const auto *name =
std::get_if<Fortran::parser::Name>(&bindClause->v.u)) {
if (Symbol *sym = ResolveFctName(*name)) {
if (info.deviceTypeInfos().empty()) {
info.set_bindName(sym->name().ToString());
} else {
info.deviceTypeInfos().back().set_bindName(
sym->name().ToString());
} else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
for (auto &device : currentDevices) {
device->set_isSeq();
}
} else if (std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) {
for (auto &device : currentDevices) {
device->set_isVector();
}
} else if (std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) {
for (auto &device : currentDevices) {
device->set_isWorker();
}
} else if (const auto *gangClause{
std::get_if<Fortran::parser::AccClause::Gang>(
&clause.u)}) {
for (auto &device : currentDevices) {
device->set_isGang();
}
if (gangClause->v) {
const Fortran::parser::AccGangArgList &x = *gangClause->v;
int numArgs{0};
for (const Fortran::parser::AccGangArg &gangArg : x.v) {
CHECK(numArgs <= 1 && "expecting 0 or 1 gang dim args");
if (const auto *dim{std::get_if<Fortran::parser::AccGangArg::Dim>(
&gangArg.u)}) {
if (const auto v{EvaluateInt64(context_, dim->v)}) {
for (auto &device : currentDevices) {
device->set_gangDim(*v);
}
}
}
numArgs++;
}
}
} else if (const auto *bindClause{
std::get_if<Fortran::parser::AccClause::Bind>(
&clause.u)}) {
if (const auto *name{
std::get_if<Fortran::parser::Name>(&bindClause->v.u)}) {
if (Symbol * sym{ResolveFctName(*name)}) {
Symbol &ultimate{sym->GetUltimate()};
for (auto &device : currentDevices) {
device->set_bindName(SymbolRef{ultimate});
}
} else {
context_.Say((*name).source,
"No function or subroutine declared for '%s'"_err_en_US,
(*name).source);
}
} else if (const auto charExpr =
} else if (const auto charExpr{
std::get_if<Fortran::parser::ScalarDefaultCharExpr>(
&bindClause->v.u)) {
auto *charConst =
&bindClause->v.u)}) {
auto *charConst{
Fortran::parser::Unwrap<Fortran::parser::CharLiteralConstant>(
*charExpr);
*charExpr)};
std::string str{std::get<std::string>(charConst->t)};
std::stringstream bindName;
bindName << "\"" << str << "\"";
if (info.deviceTypeInfos().empty()) {
info.set_bindName(bindName.str());
} else {
info.deviceTypeInfos().back().set_bindName(bindName.str());
for (auto &device : currentDevices) {
device->set_bindName(std::string(str));
}
}
} else if (const auto *dType =
std::get_if<Fortran::parser::AccClause::DeviceType>(
&clause.u)) {
const parser::AccDeviceTypeExprList &deviceTypeExprList = dType->v;
OpenACCRoutineDeviceTypeInfo dtypeInfo;
dtypeInfo.set_dType(deviceTypeExprList.v.front().v);
info.add_deviceTypeInfo(dtypeInfo);
}
}
symbol.get<SubprogramDetails>().add_openACCRoutineInfo(info);

View File

@@ -144,6 +144,52 @@ llvm::raw_ostream &operator<<(
os << ' ' << x;
}
}
if (!x.openACCRoutineInfos_.empty()) {
os << " openACCRoutineInfos:";
for (const auto &x : x.openACCRoutineInfos_) {
os << x;
}
}
return os;
}
llvm::raw_ostream &operator<<(
llvm::raw_ostream &os, const OpenACCRoutineDeviceTypeInfo &x) {
if (x.dType() != common::OpenACCDeviceType::None) {
os << " deviceType(" << common::EnumToString(x.dType()) << ')';
}
if (x.isSeq()) {
os << " seq";
}
if (x.isVector()) {
os << " vector";
}
if (x.isWorker()) {
os << " worker";
}
if (x.isGang()) {
os << " gang(" << x.gangDim() << ')';
}
if (const auto *bindName{x.bindName()}) {
if (const auto &symbol{std::get_if<std::string>(bindName)}) {
os << " bindName(\"" << *symbol << "\")";
} else {
const SymbolRef s{std::get<SymbolRef>(*bindName)};
os << " bindName(" << s->name() << ")";
}
}
return os;
}
llvm::raw_ostream &operator<<(
llvm::raw_ostream &os, const OpenACCRoutineInfo &x) {
if (x.isNohost()) {
os << " nohost";
}
os << static_cast<const OpenACCRoutineDeviceTypeInfo &>(x);
for (const auto &d : x.deviceTypeInfos_) {
os << d;
}
return os;
}

View File

@@ -0,0 +1,17 @@
! RUN: rm -fr %t && mkdir -p %t && cd %t
! RUN: bbc -fopenacc -emit-fir %s
! RUN: cat mod1.mod | FileCheck %s
!CHECK-LABEL: module mod1
module mod1
contains
!CHECK subroutine callee(aa)
subroutine callee(aa)
!CHECK: !$acc routine seq
!$acc routine seq
integer :: aa
aa = 1
end subroutine
!CHECK: end
!CHECK: end
end module

View File

@@ -4,8 +4,8 @@
module acc_routines
! CHECK: acc.routine @acc_routine_1 func(@_QMacc_routinesPacc2)
! CHECK: acc.routine @acc_routine_0 func(@_QMacc_routinesPacc1) seq
! CHECK: acc.routine @[[r0:.*]] func(@_QMacc_routinesPacc2)
! CHECK: acc.routine @[[r1:.*]] func(@_QMacc_routinesPacc1) seq
!$acc routine(acc1) seq
@@ -14,12 +14,14 @@ contains
subroutine acc1()
end subroutine
! CHECK-LABEL: func.func @_QMacc_routinesPacc1() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
! CHECK-LABEL: func.func @_QMacc_routinesPacc1()
! CHECK-SAME:attributes {acc.routine_info = #acc.routine_info<[@[[r1]]]>}
subroutine acc2()
!$acc routine(acc2)
end subroutine
! CHECK-LABEL: func.func @_QMacc_routinesPacc2() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
! CHECK-LABEL: func.func @_QMacc_routinesPacc2()
! CHECK-SAME:attributes {acc.routine_info = #acc.routine_info<[@[[r0]]]>}
end module

View File

@@ -0,0 +1,23 @@
! RUN: rm -fr %t && mkdir -p %t && cd %t
! RUN: bbc -fopenacc -emit-fir %S/acc-module-definition.f90
! RUN: bbc -fopenacc -emit-fir %s -o - | FileCheck %s
! This test module is based off of flang/test/Lower/use_module.f90
! The first runs ensures the module file is generated.
module use_mod1
use mod1
contains
!CHECK: acc.routine @acc_routine_0 func(@_QMmod1Pcallee) seq
!CHECK: func.func @_QMuse_mod1Pcaller
!CHECK-SAME {
subroutine caller(aa)
integer :: aa
!$acc serial
!CHECK: fir.call @_QMmod1Pcallee
call callee(aa)
!$acc end serial
end subroutine
!CHECK: }
!CHECK: func.func private @_QMmod1Pcallee(!fir.ref<i32>) attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
end module

View File

@@ -2,69 +2,77 @@
! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
! CHECK: acc.routine @acc_routine_17 func(@_QPacc_routine19) bind("_QPacc_routine17" [#acc.device_type<host>], "_QPacc_routine17" [#acc.device_type<default>], "_QPacc_routine16" [#acc.device_type<multicore>])
! CHECK: acc.routine @acc_routine_16 func(@_QPacc_routine18) bind("_QPacc_routine17" [#acc.device_type<host>], "_QPacc_routine16" [#acc.device_type<multicore>])
! CHECK: acc.routine @acc_routine_15 func(@_QPacc_routine17) worker ([#acc.device_type<host>]) vector ([#acc.device_type<multicore>])
! CHECK: acc.routine @acc_routine_14 func(@_QPacc_routine16) gang([#acc.device_type<nvidia>]) seq ([#acc.device_type<host>])
! CHECK: acc.routine @acc_routine_10 func(@_QPacc_routine11) seq
! CHECK: acc.routine @acc_routine_9 func(@_QPacc_routine10) seq
! CHECK: acc.routine @acc_routine_8 func(@_QPacc_routine9) bind("_QPacc_routine9a")
! CHECK: acc.routine @acc_routine_7 func(@_QPacc_routine8) bind("routine8_")
! CHECK: acc.routine @acc_routine_6 func(@_QPacc_routine7) gang(dim: 1 : i64)
! CHECK: acc.routine @acc_routine_5 func(@_QPacc_routine6) nohost
! CHECK: acc.routine @acc_routine_4 func(@_QPacc_routine5) worker
! CHECK: acc.routine @acc_routine_3 func(@_QPacc_routine4) vector
! CHECK: acc.routine @acc_routine_2 func(@_QPacc_routine3) gang
! CHECK: acc.routine @acc_routine_1 func(@_QPacc_routine2) seq
! CHECK: acc.routine @acc_routine_0 func(@_QPacc_routine1)
! CHECK: acc.routine @[[r14:.*]] func(@_QPacc_routine19) bind("_QPacc_routine17" [#acc.device_type<host>], "_QPacc_routine17" [#acc.device_type<default>], "_QPacc_routine16" [#acc.device_type<multicore>])
! CHECK: acc.routine @[[r13:.*]] func(@_QPacc_routine18) bind("_QPacc_routine17" [#acc.device_type<host>], "_QPacc_routine16" [#acc.device_type<multicore>])
! CHECK: acc.routine @[[r12:.*]] func(@_QPacc_routine17) worker ([#acc.device_type<host>]) vector ([#acc.device_type<multicore>])
! CHECK: acc.routine @[[r11:.*]] func(@_QPacc_routine16) gang([#acc.device_type<nvidia>]) seq ([#acc.device_type<host>])
! CHECK: acc.routine @[[r10:.*]] func(@_QPacc_routine11) seq
! CHECK: acc.routine @[[r09:.*]] func(@_QPacc_routine10) seq
! CHECK: acc.routine @[[r08:.*]] func(@_QPacc_routine9) bind("_QPacc_routine9a")
! CHECK: acc.routine @[[r07:.*]] func(@_QPacc_routine8) bind("routine8_")
! CHECK: acc.routine @[[r06:.*]] func(@_QPacc_routine7) gang(dim: 1 : i64)
! CHECK: acc.routine @[[r05:.*]] func(@_QPacc_routine6) nohost
! CHECK: acc.routine @[[r04:.*]] func(@_QPacc_routine5) worker
! CHECK: acc.routine @[[r03:.*]] func(@_QPacc_routine4) vector
! CHECK: acc.routine @[[r02:.*]] func(@_QPacc_routine3) gang
! CHECK: acc.routine @[[r01:.*]] func(@_QPacc_routine2) seq
! CHECK: acc.routine @[[r00:.*]] func(@_QPacc_routine1)
subroutine acc_routine1()
!$acc routine
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine1() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
! CHECK-LABEL: func.func @_QPacc_routine1()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r00]]]>}
subroutine acc_routine2()
!$acc routine seq
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine2() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
! CHECK-LABEL: func.func @_QPacc_routine2()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r01]]]>}
subroutine acc_routine3()
!$acc routine gang
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine3() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_2]>}
! CHECK-LABEL: func.func @_QPacc_routine3()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r02]]]>}
subroutine acc_routine4()
!$acc routine vector
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine4() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_3]>}
! CHECK-LABEL: func.func @_QPacc_routine4()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r03]]]>}
subroutine acc_routine5()
!$acc routine worker
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine5() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_4]>}
! CHECK-LABEL: func.func @_QPacc_routine5()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r04]]]>}
subroutine acc_routine6()
!$acc routine nohost
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine6() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_5]>}
! CHECK-LABEL: func.func @_QPacc_routine6()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r05]]]>}
subroutine acc_routine7()
!$acc routine gang(dim:1)
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine7() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_6]>}
! CHECK-LABEL: func.func @_QPacc_routine7()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r06]]]>}
subroutine acc_routine8()
!$acc routine bind("routine8_")
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine8() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_7]>}
! CHECK-LABEL: func.func @_QPacc_routine8()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r07]]]>}
subroutine acc_routine9a()
end subroutine
@@ -73,20 +81,23 @@ subroutine acc_routine9()
!$acc routine bind(acc_routine9a)
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine9() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_8]>}
! CHECK-LABEL: func.func @_QPacc_routine9()
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r08]]]>}
function acc_routine10()
!$acc routine(acc_routine10) seq
end function
! CHECK-LABEL: func.func @_QPacc_routine10() -> f32 attributes {acc.routine_info = #acc.routine_info<[@acc_routine_9]>}
! CHECK-LABEL: func.func @_QPacc_routine10() -> f32
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r09]]]>}
subroutine acc_routine11(a)
real :: a
!$acc routine(acc_routine11) seq
end subroutine
! CHECK-LABEL: func.func @_QPacc_routine11(%arg0: !fir.ref<f32> {fir.bindc_name = "a"}) attributes {acc.routine_info = #acc.routine_info<[@acc_routine_10]>}
! CHECK-LABEL: func.func @_QPacc_routine11(%arg0: !fir.ref<f32> {fir.bindc_name = "a"})
! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r10]]]>}
subroutine acc_routine12()