aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Lower/OpenACC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Lower/OpenACC.cpp')
-rw-r--r--flang/lib/Lower/OpenACC.cpp284
1 files changed, 225 insertions, 59 deletions
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index b3e8b69..1fc59c7 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -718,6 +718,84 @@ static void genDataOperandOperations(
}
}
+template <typename GlobalCtorOrDtorOp, typename EntryOp, typename DeclareOp,
+ typename ExitOp>
+static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
+ fir::FirOpBuilder &builder,
+ mlir::Location loc, fir::GlobalOp globalOp,
+ mlir::acc::DataClause clause,
+ const std::string &declareGlobalName,
+ bool implicit, std::stringstream &asFortran) {
+ GlobalCtorOrDtorOp declareGlobalOp =
+ GlobalCtorOrDtorOp::create(modBuilder, loc, declareGlobalName);
+ builder.createBlock(&declareGlobalOp.getRegion(),
+ declareGlobalOp.getRegion().end(), {}, {});
+ builder.setInsertionPointToEnd(&declareGlobalOp.getRegion().back());
+
+ fir::AddrOfOp addrOp = fir::AddrOfOp::create(
+ builder, loc, fir::ReferenceType::get(globalOp.getType()),
+ globalOp.getSymbol());
+ addDeclareAttr(builder, addrOp, clause);
+
+ llvm::SmallVector<mlir::Value> bounds;
+ EntryOp entryOp = createDataEntryOp<EntryOp>(
+ builder, loc, addrOp.getResTy(), asFortran, bounds,
+ /*structured=*/false, implicit, clause, addrOp.getResTy().getType(),
+ /*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
+ if constexpr (std::is_same_v<DeclareOp, mlir::acc::DeclareEnterOp>)
+ DeclareOp::create(builder, loc,
+ mlir::acc::DeclareTokenType::get(entryOp.getContext()),
+ mlir::ValueRange(entryOp.getAccVar()));
+ else
+ DeclareOp::create(builder, loc, mlir::Value{},
+ mlir::ValueRange(entryOp.getAccVar()));
+ if constexpr (std::is_same_v<GlobalCtorOrDtorOp,
+ mlir::acc::GlobalDestructorOp>) {
+ if constexpr (std::is_same_v<ExitOp, mlir::acc::DeclareLinkOp>) {
+ // No destructor emission for declare link in this path to avoid
+ // complex var/varType/varPtrPtr signatures. The ctor registers the link.
+ } else if constexpr (std::is_same_v<ExitOp, mlir::acc::CopyoutOp> ||
+ std::is_same_v<ExitOp, mlir::acc::UpdateHostOp>) {
+ ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
+ entryOp.getVar(), entryOp.getVarType(),
+ entryOp.getBounds(), entryOp.getAsyncOperands(),
+ entryOp.getAsyncOperandsDeviceTypeAttr(),
+ entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
+ /*structured=*/false, /*implicit=*/false,
+ builder.getStringAttr(*entryOp.getName()));
+ } else {
+ ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
+ entryOp.getBounds(), entryOp.getAsyncOperands(),
+ entryOp.getAsyncOperandsDeviceTypeAttr(),
+ entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
+ /*structured=*/false, /*implicit=*/false,
+ builder.getStringAttr(*entryOp.getName()));
+ }
+ }
+ mlir::acc::TerminatorOp::create(builder, loc);
+ modBuilder.setInsertionPointAfter(declareGlobalOp);
+}
+
+template <typename EntryOp, typename ExitOp>
+static void
+emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder,
+ mlir::Location operandLocation, fir::GlobalOp globalOp,
+ mlir::acc::DataClause clause, std::stringstream &asFortran,
+ const std::string &ctorName) {
+ createDeclareGlobalOp<mlir::acc::GlobalConstructorOp, EntryOp,
+ mlir::acc::DeclareEnterOp, ExitOp>(
+ modBuilder, builder, operandLocation, globalOp, clause, ctorName,
+ /*implicit=*/false, asFortran);
+
+ std::stringstream dtorName;
+ dtorName << globalOp.getSymName().str() << "_acc_dtor";
+ createDeclareGlobalOp<mlir::acc::GlobalDestructorOp,
+ mlir::acc::GetDevicePtrOp, mlir::acc::DeclareExitOp,
+ ExitOp>(modBuilder, builder, operandLocation, globalOp,
+ clause, dtorName.str(),
+ /*implicit=*/false, asFortran);
+}
+
template <typename EntryOp, typename ExitOp>
static void genDeclareDataOperandOperations(
const Fortran::parser::AccObjectList &objectList,
@@ -733,6 +811,37 @@ static void genDeclareDataOperandOperations(
std::stringstream asFortran;
mlir::Location operandLocation = genOperandLocation(converter, accObject);
Fortran::semantics::Symbol &symbol = getSymbolFromAccObject(accObject);
+ // Handle COMMON/global symbols via module-level ctor/dtor path.
+ if (symbol.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
+ Fortran::semantics::FindCommonBlockContaining(symbol)) {
+ emitCommonGlobal(
+ converter, builder, accObject, dataClause,
+ [&](mlir::OpBuilder &modBuilder, mlir::Location loc,
+ fir::GlobalOp globalOp, mlir::acc::DataClause clause,
+ std::stringstream &asFortranStr, const std::string &ctorName) {
+ if constexpr (std::is_same_v<EntryOp, mlir::acc::DeclareLinkOp>) {
+ createDeclareGlobalOp<
+ mlir::acc::GlobalConstructorOp, mlir::acc::DeclareLinkOp,
+ mlir::acc::DeclareEnterOp, mlir::acc::DeclareLinkOp>(
+ modBuilder, builder, loc, globalOp, clause, ctorName,
+ /*implicit=*/false, asFortranStr);
+ } else if constexpr (std::is_same_v<EntryOp, mlir::acc::CreateOp> ||
+ std::is_same_v<EntryOp, mlir::acc::CopyinOp> ||
+ std::is_same_v<
+ EntryOp,
+ mlir::acc::DeclareDeviceResidentOp> ||
+ std::is_same_v<ExitOp, mlir::acc::CopyoutOp>) {
+ emitCtorDtorPair<EntryOp, ExitOp>(modBuilder, builder, loc,
+ globalOp, clause, asFortranStr,
+ ctorName);
+ } else {
+ // No module-level ctor/dtor for this clause (e.g., deviceptr,
+ // present). Handled via structured declare region only.
+ return;
+ }
+ });
+ continue;
+ }
Fortran::semantics::MaybeExpr designator = Fortran::common::visit(
[&](auto &&s) { return ea.Analyze(s); }, accObject.u);
fir::factory::AddrAndBoundsInfo info =
@@ -2257,6 +2366,23 @@ static void processDoLoopBounds(
}
}
+static void remapCommonBlockMember(
+ Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+ const Fortran::semantics::Symbol &member,
+ mlir::Value newCommonBlockBaseAddress,
+ const Fortran::semantics::Symbol &commonBlockSymbol,
+ llvm::SmallPtrSetImpl<const Fortran::semantics::Symbol *> &seenSymbols) {
+ if (seenSymbols.contains(&member))
+ return;
+ mlir::Value accMemberValue = Fortran::lower::genCommonBlockMember(
+ converter, loc, member, newCommonBlockBaseAddress,
+ commonBlockSymbol.size());
+ fir::ExtendedValue hostExv = converter.getSymbolExtendedValue(member);
+ fir::ExtendedValue accExv = fir::substBase(hostExv, accMemberValue);
+ converter.bindSymbol(member, accExv);
+ seenSymbols.insert(&member);
+}
+
/// Remap symbols that appeared in OpenACC data clauses to use the results of
/// the corresponding data operations. This allows isolating symbol accesses
/// inside the OpenACC region from accesses in the host and other regions while
@@ -2282,14 +2408,39 @@ static void remapDataOperandSymbols(
builder.setInsertionPointToStart(&regionOp.getRegion().front());
llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 8> seenSymbols;
mlir::IRMapping mapper;
+ mlir::Location loc = regionOp.getLoc();
for (auto [value, symbol] : dataOperandSymbolPairs) {
-
- // If A symbol appears on several data clause, just map it to the first
+ // If a symbol appears on several data clause, just map it to the first
// result (all data operations results for a symbol are pointing same
// memory, so it does not matter which one is used).
if (seenSymbols.contains(&symbol.get()))
continue;
seenSymbols.insert(&symbol.get());
+ // When a common block appears in a directive, remap its members.
+ // Note: this will instantiate all common block members even if they are not
+ // used inside the region. If hlfir.declare DCE is not made possible, this
+ // could be improved to reduce IR noise.
+ if (const auto *commonBlock = symbol->template detailsIf<
+ Fortran::semantics::CommonBlockDetails>()) {
+ const Fortran::semantics::Scope &commonScope = symbol->owner();
+ if (commonScope.equivalenceSets().empty()) {
+ for (auto member : commonBlock->objects())
+ remapCommonBlockMember(converter, loc, *member, value, *symbol,
+ seenSymbols);
+ } else {
+ // Objects equivalenced with common block members still belong to the
+ // common block storage even if they are not part of the common block
+ // declaration. The easiest and most robust way to find all symbols
+ // belonging to the common block is to loop through the scope symbols
+ // and check if they belong to the common.
+ for (const auto &scopeSymbol : commonScope)
+ if (Fortran::semantics::FindCommonBlockContaining(
+ *scopeSymbol.second) == &symbol.get())
+ remapCommonBlockMember(converter, loc, *scopeSymbol.second, value,
+ *symbol, seenSymbols);
+ }
+ continue;
+ }
std::optional<fir::FortranVariableOpInterface> hostDef =
symbolMap.lookupVariableDefinition(symbol);
assert(hostDef.has_value() && llvm::isa<hlfir::DeclareOp>(*hostDef) &&
@@ -2306,10 +2457,8 @@ static void remapDataOperandSymbols(
"box type mismatch between compute region variable and "
"hlfir.declare input unexpected");
if (Fortran::semantics::IsOptional(symbol))
- TODO(regionOp.getLoc(),
- "remapping OPTIONAL symbol in OpenACC compute region");
- auto rawValue =
- fir::BoxAddrOp::create(builder, regionOp.getLoc(), hostType, value);
+ TODO(loc, "remapping OPTIONAL symbol in OpenACC compute region");
+ auto rawValue = fir::BoxAddrOp::create(builder, loc, hostType, value);
mapper.map(hostInput, rawValue);
} else {
assert(!llvm::isa<fir::BaseBoxType>(hostType) &&
@@ -2321,8 +2470,7 @@ static void remapDataOperandSymbols(
assert(fir::isa_ref_type(hostType) && fir::isa_ref_type(computeType) &&
"compute region variable and host variable should both be raw "
"addresses");
- mlir::Value cast =
- builder.createConvert(regionOp.getLoc(), hostType, value);
+ mlir::Value cast = builder.createConvert(loc, hostType, value);
mapper.map(hostInput, cast);
}
if (mlir::Value dummyScope = hostDeclare.getDummyScope()) {
@@ -4098,49 +4246,6 @@ static void genACC(Fortran::lower::AbstractConverter &converter,
waitOp.setAsyncAttr(firOpBuilder.getUnitAttr());
}
-template <typename GlobalOp, typename EntryOp, typename DeclareOp,
- typename ExitOp>
-static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
- fir::FirOpBuilder &builder,
- mlir::Location loc, fir::GlobalOp globalOp,
- mlir::acc::DataClause clause,
- const std::string &declareGlobalName,
- bool implicit, std::stringstream &asFortran) {
- GlobalOp declareGlobalOp =
- GlobalOp::create(modBuilder, loc, declareGlobalName);
- builder.createBlock(&declareGlobalOp.getRegion(),
- declareGlobalOp.getRegion().end(), {}, {});
- builder.setInsertionPointToEnd(&declareGlobalOp.getRegion().back());
-
- fir::AddrOfOp addrOp = fir::AddrOfOp::create(
- builder, loc, fir::ReferenceType::get(globalOp.getType()),
- globalOp.getSymbol());
- addDeclareAttr(builder, addrOp, clause);
-
- llvm::SmallVector<mlir::Value> bounds;
- EntryOp entryOp = createDataEntryOp<EntryOp>(
- builder, loc, addrOp.getResTy(), asFortran, bounds,
- /*structured=*/false, implicit, clause, addrOp.getResTy().getType(),
- /*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
- if constexpr (std::is_same_v<DeclareOp, mlir::acc::DeclareEnterOp>)
- DeclareOp::create(builder, loc,
- mlir::acc::DeclareTokenType::get(entryOp.getContext()),
- mlir::ValueRange(entryOp.getAccVar()));
- else
- DeclareOp::create(builder, loc, mlir::Value{},
- mlir::ValueRange(entryOp.getAccVar()));
- if constexpr (std::is_same_v<GlobalOp, mlir::acc::GlobalDestructorOp>) {
- ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
- entryOp.getBounds(), entryOp.getAsyncOperands(),
- entryOp.getAsyncOperandsDeviceTypeAttr(),
- entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
- /*structured=*/false, /*implicit=*/false,
- builder.getStringAttr(*entryOp.getName()));
- }
- mlir::acc::TerminatorOp::create(builder, loc);
- modBuilder.setInsertionPointAfter(declareGlobalOp);
-}
-
template <typename EntryOp>
static void createDeclareAllocFunc(mlir::OpBuilder &modBuilder,
fir::FirOpBuilder &builder,
@@ -4317,6 +4422,66 @@ genGlobalCtorsWithModifier(Fortran::lower::AbstractConverter &converter,
dataClause);
}
+static fir::GlobalOp
+lookupGlobalBySymbolOrEquivalence(Fortran::lower::AbstractConverter &converter,
+ fir::FirOpBuilder &builder,
+ const Fortran::semantics::Symbol &sym) {
+ const Fortran::semantics::Symbol *commonBlock =
+ Fortran::semantics::FindCommonBlockContaining(sym);
+ std::string globalName = commonBlock ? converter.mangleName(*commonBlock)
+ : converter.mangleName(sym);
+ if (fir::GlobalOp g = builder.getNamedGlobal(globalName)) {
+ return g;
+ }
+ // Not found: if not a COMMON member, try equivalence members
+ if (!commonBlock) {
+ if (const Fortran::semantics::EquivalenceSet *eqSet =
+ Fortran::semantics::FindEquivalenceSet(sym)) {
+ for (const Fortran::semantics::EquivalenceObject &eqObj : *eqSet) {
+ std::string eqName = converter.mangleName(eqObj.symbol);
+ if (fir::GlobalOp g = builder.getNamedGlobal(eqName))
+ return g;
+ }
+ }
+ }
+ return {};
+}
+
+template <typename EmitterFn>
+static void emitCommonGlobal(Fortran::lower::AbstractConverter &converter,
+ fir::FirOpBuilder &builder,
+ const Fortran::parser::AccObject &obj,
+ mlir::acc::DataClause clause,
+ EmitterFn &&emitCtorDtor) {
+ Fortran::semantics::Symbol &sym = getSymbolFromAccObject(obj);
+ if (!(sym.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
+ Fortran::semantics::FindCommonBlockContaining(sym)))
+ return;
+
+ fir::GlobalOp globalOp =
+ lookupGlobalBySymbolOrEquivalence(converter, builder, sym);
+ if (!globalOp)
+ llvm::report_fatal_error("could not retrieve global symbol");
+
+ std::stringstream ctorName;
+ ctorName << globalOp.getSymName().str() << "_acc_ctor";
+ if (builder.getModule().lookupSymbol<mlir::acc::GlobalConstructorOp>(
+ ctorName.str()))
+ return;
+
+ mlir::Location operandLocation = genOperandLocation(converter, obj);
+ addDeclareAttr(builder, globalOp.getOperation(), clause);
+ mlir::OpBuilder modBuilder(builder.getModule().getBodyRegion());
+ modBuilder.setInsertionPointAfter(globalOp);
+ std::stringstream asFortran;
+ asFortran << sym.name().ToString();
+
+ auto savedIP = builder.saveInsertionPoint();
+ emitCtorDtor(modBuilder, operandLocation, globalOp, clause, asFortran,
+ ctorName.str());
+ builder.restoreInsertionPoint(savedIP);
+}
+
static void
genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
Fortran::semantics::SemanticsContext &semanticsContext,
@@ -4342,11 +4507,9 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
dataClauseOperands.end());
} else if (const auto *createClause =
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
- const Fortran::parser::AccObjectListWithModifier &listWithModifier =
- createClause->v;
- const auto &accObjectList =
- std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
auto crtDataStart = dataClauseOperands.size();
+ const auto &accObjectList =
+ std::get<Fortran::parser::AccObjectList>(createClause->v.t);
genDeclareDataOperandOperations<mlir::acc::CreateOp, mlir::acc::DeleteOp>(
accObjectList, converter, semanticsContext, stmtCtx,
dataClauseOperands, mlir::acc::DataClause::acc_create,
@@ -4378,11 +4541,9 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
} else if (const auto *copyoutClause =
std::get_if<Fortran::parser::AccClause::Copyout>(
&clause.u)) {
- const Fortran::parser::AccObjectListWithModifier &listWithModifier =
- copyoutClause->v;
- const auto &accObjectList =
- std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
auto crtDataStart = dataClauseOperands.size();
+ const auto &accObjectList =
+ std::get<Fortran::parser::AccObjectList>(copyoutClause->v.t);
genDeclareDataOperandOperations<mlir::acc::CreateOp,
mlir::acc::CopyoutOp>(
accObjectList, converter, semanticsContext, stmtCtx,
@@ -4423,6 +4584,11 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
}
}
+ // If no structured operands were generated (all objects were COMMON),
+ // do not create a declare region.
+ if (dataClauseOperands.empty())
+ return;
+
mlir::func::FuncOp funcOp = builder.getFunction();
auto ops = funcOp.getOps<mlir::acc::DeclareEnterOp>();
mlir::Value declareToken;