Skip to content

Commit 0c0ad11

Browse files
authored
[acc][flang] lowering of acc declare on COMMON variables (llvm#163676)
COMMON variables are treated as locals and lowered to structured declares currently. This is incorrect because variables that are COMMON should be treated as globals. Added implementation to treat these variables differently.
1 parent 3161e16 commit 0c0ad11

File tree

2 files changed

+218
-51
lines changed

2 files changed

+218
-51
lines changed

flang/lib/Lower/OpenACC.cpp

Lines changed: 178 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,84 @@ static void genDataOperandOperations(
718718
}
719719
}
720720

721+
template <typename GlobalCtorOrDtorOp, typename EntryOp, typename DeclareOp,
722+
typename ExitOp>
723+
static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
724+
fir::FirOpBuilder &builder,
725+
mlir::Location loc, fir::GlobalOp globalOp,
726+
mlir::acc::DataClause clause,
727+
const std::string &declareGlobalName,
728+
bool implicit, std::stringstream &asFortran) {
729+
GlobalCtorOrDtorOp declareGlobalOp =
730+
GlobalCtorOrDtorOp::create(modBuilder, loc, declareGlobalName);
731+
builder.createBlock(&declareGlobalOp.getRegion(),
732+
declareGlobalOp.getRegion().end(), {}, {});
733+
builder.setInsertionPointToEnd(&declareGlobalOp.getRegion().back());
734+
735+
fir::AddrOfOp addrOp = fir::AddrOfOp::create(
736+
builder, loc, fir::ReferenceType::get(globalOp.getType()),
737+
globalOp.getSymbol());
738+
addDeclareAttr(builder, addrOp, clause);
739+
740+
llvm::SmallVector<mlir::Value> bounds;
741+
EntryOp entryOp = createDataEntryOp<EntryOp>(
742+
builder, loc, addrOp.getResTy(), asFortran, bounds,
743+
/*structured=*/false, implicit, clause, addrOp.getResTy().getType(),
744+
/*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
745+
if constexpr (std::is_same_v<DeclareOp, mlir::acc::DeclareEnterOp>)
746+
DeclareOp::create(builder, loc,
747+
mlir::acc::DeclareTokenType::get(entryOp.getContext()),
748+
mlir::ValueRange(entryOp.getAccVar()));
749+
else
750+
DeclareOp::create(builder, loc, mlir::Value{},
751+
mlir::ValueRange(entryOp.getAccVar()));
752+
if constexpr (std::is_same_v<GlobalCtorOrDtorOp,
753+
mlir::acc::GlobalDestructorOp>) {
754+
if constexpr (std::is_same_v<ExitOp, mlir::acc::DeclareLinkOp>) {
755+
// No destructor emission for declare link in this path to avoid
756+
// complex var/varType/varPtrPtr signatures. The ctor registers the link.
757+
} else if constexpr (std::is_same_v<ExitOp, mlir::acc::CopyoutOp> ||
758+
std::is_same_v<ExitOp, mlir::acc::UpdateHostOp>) {
759+
ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
760+
entryOp.getVar(), entryOp.getVarType(),
761+
entryOp.getBounds(), entryOp.getAsyncOperands(),
762+
entryOp.getAsyncOperandsDeviceTypeAttr(),
763+
entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
764+
/*structured=*/false, /*implicit=*/false,
765+
builder.getStringAttr(*entryOp.getName()));
766+
} else {
767+
ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
768+
entryOp.getBounds(), entryOp.getAsyncOperands(),
769+
entryOp.getAsyncOperandsDeviceTypeAttr(),
770+
entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
771+
/*structured=*/false, /*implicit=*/false,
772+
builder.getStringAttr(*entryOp.getName()));
773+
}
774+
}
775+
mlir::acc::TerminatorOp::create(builder, loc);
776+
modBuilder.setInsertionPointAfter(declareGlobalOp);
777+
}
778+
779+
template <typename EntryOp, typename ExitOp>
780+
static void
781+
emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder,
782+
mlir::Location operandLocation, fir::GlobalOp globalOp,
783+
mlir::acc::DataClause clause, std::stringstream &asFortran,
784+
const std::string &ctorName) {
785+
createDeclareGlobalOp<mlir::acc::GlobalConstructorOp, EntryOp,
786+
mlir::acc::DeclareEnterOp, ExitOp>(
787+
modBuilder, builder, operandLocation, globalOp, clause, ctorName,
788+
/*implicit=*/false, asFortran);
789+
790+
std::stringstream dtorName;
791+
dtorName << globalOp.getSymName().str() << "_acc_dtor";
792+
createDeclareGlobalOp<mlir::acc::GlobalDestructorOp,
793+
mlir::acc::GetDevicePtrOp, mlir::acc::DeclareExitOp,
794+
ExitOp>(modBuilder, builder, operandLocation, globalOp,
795+
clause, dtorName.str(),
796+
/*implicit=*/false, asFortran);
797+
}
798+
721799
template <typename EntryOp, typename ExitOp>
722800
static void genDeclareDataOperandOperations(
723801
const Fortran::parser::AccObjectList &objectList,
@@ -733,6 +811,37 @@ static void genDeclareDataOperandOperations(
733811
std::stringstream asFortran;
734812
mlir::Location operandLocation = genOperandLocation(converter, accObject);
735813
Fortran::semantics::Symbol &symbol = getSymbolFromAccObject(accObject);
814+
// Handle COMMON/global symbols via module-level ctor/dtor path.
815+
if (symbol.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
816+
Fortran::semantics::FindCommonBlockContaining(symbol)) {
817+
emitCommonGlobal(
818+
converter, builder, accObject, dataClause,
819+
[&](mlir::OpBuilder &modBuilder, mlir::Location loc,
820+
fir::GlobalOp globalOp, mlir::acc::DataClause clause,
821+
std::stringstream &asFortranStr, const std::string &ctorName) {
822+
if constexpr (std::is_same_v<EntryOp, mlir::acc::DeclareLinkOp>) {
823+
createDeclareGlobalOp<
824+
mlir::acc::GlobalConstructorOp, mlir::acc::DeclareLinkOp,
825+
mlir::acc::DeclareEnterOp, mlir::acc::DeclareLinkOp>(
826+
modBuilder, builder, loc, globalOp, clause, ctorName,
827+
/*implicit=*/false, asFortranStr);
828+
} else if constexpr (std::is_same_v<EntryOp, mlir::acc::CreateOp> ||
829+
std::is_same_v<EntryOp, mlir::acc::CopyinOp> ||
830+
std::is_same_v<
831+
EntryOp,
832+
mlir::acc::DeclareDeviceResidentOp> ||
833+
std::is_same_v<ExitOp, mlir::acc::CopyoutOp>) {
834+
emitCtorDtorPair<EntryOp, ExitOp>(modBuilder, builder, loc,
835+
globalOp, clause, asFortranStr,
836+
ctorName);
837+
} else {
838+
// No module-level ctor/dtor for this clause (e.g., deviceptr,
839+
// present). Handled via structured declare region only.
840+
return;
841+
}
842+
});
843+
continue;
844+
}
736845
Fortran::semantics::MaybeExpr designator = Fortran::common::visit(
737846
[&](auto &&s) { return ea.Analyze(s); }, accObject.u);
738847
fir::factory::AddrAndBoundsInfo info =
@@ -4098,49 +4207,6 @@ static void genACC(Fortran::lower::AbstractConverter &converter,
40984207
waitOp.setAsyncAttr(firOpBuilder.getUnitAttr());
40994208
}
41004209

4101-
template <typename GlobalOp, typename EntryOp, typename DeclareOp,
4102-
typename ExitOp>
4103-
static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
4104-
fir::FirOpBuilder &builder,
4105-
mlir::Location loc, fir::GlobalOp globalOp,
4106-
mlir::acc::DataClause clause,
4107-
const std::string &declareGlobalName,
4108-
bool implicit, std::stringstream &asFortran) {
4109-
GlobalOp declareGlobalOp =
4110-
GlobalOp::create(modBuilder, loc, declareGlobalName);
4111-
builder.createBlock(&declareGlobalOp.getRegion(),
4112-
declareGlobalOp.getRegion().end(), {}, {});
4113-
builder.setInsertionPointToEnd(&declareGlobalOp.getRegion().back());
4114-
4115-
fir::AddrOfOp addrOp = fir::AddrOfOp::create(
4116-
builder, loc, fir::ReferenceType::get(globalOp.getType()),
4117-
globalOp.getSymbol());
4118-
addDeclareAttr(builder, addrOp, clause);
4119-
4120-
llvm::SmallVector<mlir::Value> bounds;
4121-
EntryOp entryOp = createDataEntryOp<EntryOp>(
4122-
builder, loc, addrOp.getResTy(), asFortran, bounds,
4123-
/*structured=*/false, implicit, clause, addrOp.getResTy().getType(),
4124-
/*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
4125-
if constexpr (std::is_same_v<DeclareOp, mlir::acc::DeclareEnterOp>)
4126-
DeclareOp::create(builder, loc,
4127-
mlir::acc::DeclareTokenType::get(entryOp.getContext()),
4128-
mlir::ValueRange(entryOp.getAccVar()));
4129-
else
4130-
DeclareOp::create(builder, loc, mlir::Value{},
4131-
mlir::ValueRange(entryOp.getAccVar()));
4132-
if constexpr (std::is_same_v<GlobalOp, mlir::acc::GlobalDestructorOp>) {
4133-
ExitOp::create(builder, entryOp.getLoc(), entryOp.getAccVar(),
4134-
entryOp.getBounds(), entryOp.getAsyncOperands(),
4135-
entryOp.getAsyncOperandsDeviceTypeAttr(),
4136-
entryOp.getAsyncOnlyAttr(), entryOp.getDataClause(),
4137-
/*structured=*/false, /*implicit=*/false,
4138-
builder.getStringAttr(*entryOp.getName()));
4139-
}
4140-
mlir::acc::TerminatorOp::create(builder, loc);
4141-
modBuilder.setInsertionPointAfter(declareGlobalOp);
4142-
}
4143-
41444210
template <typename EntryOp>
41454211
static void createDeclareAllocFunc(mlir::OpBuilder &modBuilder,
41464212
fir::FirOpBuilder &builder,
@@ -4317,6 +4383,66 @@ genGlobalCtorsWithModifier(Fortran::lower::AbstractConverter &converter,
43174383
dataClause);
43184384
}
43194385

4386+
static fir::GlobalOp
4387+
lookupGlobalBySymbolOrEquivalence(Fortran::lower::AbstractConverter &converter,
4388+
fir::FirOpBuilder &builder,
4389+
const Fortran::semantics::Symbol &sym) {
4390+
const Fortran::semantics::Symbol *commonBlock =
4391+
Fortran::semantics::FindCommonBlockContaining(sym);
4392+
std::string globalName = commonBlock ? converter.mangleName(*commonBlock)
4393+
: converter.mangleName(sym);
4394+
if (fir::GlobalOp g = builder.getNamedGlobal(globalName)) {
4395+
return g;
4396+
}
4397+
// Not found: if not a COMMON member, try equivalence members
4398+
if (!commonBlock) {
4399+
if (const Fortran::semantics::EquivalenceSet *eqSet =
4400+
Fortran::semantics::FindEquivalenceSet(sym)) {
4401+
for (const Fortran::semantics::EquivalenceObject &eqObj : *eqSet) {
4402+
std::string eqName = converter.mangleName(eqObj.symbol);
4403+
if (fir::GlobalOp g = builder.getNamedGlobal(eqName))
4404+
return g;
4405+
}
4406+
}
4407+
}
4408+
return {};
4409+
}
4410+
4411+
template <typename EmitterFn>
4412+
static void emitCommonGlobal(Fortran::lower::AbstractConverter &converter,
4413+
fir::FirOpBuilder &builder,
4414+
const Fortran::parser::AccObject &obj,
4415+
mlir::acc::DataClause clause,
4416+
EmitterFn &&emitCtorDtor) {
4417+
Fortran::semantics::Symbol &sym = getSymbolFromAccObject(obj);
4418+
if (!(sym.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
4419+
Fortran::semantics::FindCommonBlockContaining(sym)))
4420+
return;
4421+
4422+
fir::GlobalOp globalOp =
4423+
lookupGlobalBySymbolOrEquivalence(converter, builder, sym);
4424+
if (!globalOp)
4425+
llvm::report_fatal_error("could not retrieve global symbol");
4426+
4427+
std::stringstream ctorName;
4428+
ctorName << globalOp.getSymName().str() << "_acc_ctor";
4429+
if (builder.getModule().lookupSymbol<mlir::acc::GlobalConstructorOp>(
4430+
ctorName.str()))
4431+
return;
4432+
4433+
mlir::Location operandLocation = genOperandLocation(converter, obj);
4434+
addDeclareAttr(builder, globalOp.getOperation(), clause);
4435+
mlir::OpBuilder modBuilder(builder.getModule().getBodyRegion());
4436+
modBuilder.setInsertionPointAfter(globalOp);
4437+
std::stringstream asFortran;
4438+
asFortran << sym.name().ToString();
4439+
4440+
auto savedIP = builder.saveInsertionPoint();
4441+
emitCtorDtor(modBuilder, operandLocation, globalOp, clause, asFortran,
4442+
ctorName.str());
4443+
builder.restoreInsertionPoint(savedIP);
4444+
}
4445+
43204446
static void
43214447
genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
43224448
Fortran::semantics::SemanticsContext &semanticsContext,
@@ -4342,11 +4468,9 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
43424468
dataClauseOperands.end());
43434469
} else if (const auto *createClause =
43444470
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
4345-
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
4346-
createClause->v;
4347-
const auto &accObjectList =
4348-
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
43494471
auto crtDataStart = dataClauseOperands.size();
4472+
const auto &accObjectList =
4473+
std::get<Fortran::parser::AccObjectList>(createClause->v.t);
43504474
genDeclareDataOperandOperations<mlir::acc::CreateOp, mlir::acc::DeleteOp>(
43514475
accObjectList, converter, semanticsContext, stmtCtx,
43524476
dataClauseOperands, mlir::acc::DataClause::acc_create,
@@ -4378,11 +4502,9 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
43784502
} else if (const auto *copyoutClause =
43794503
std::get_if<Fortran::parser::AccClause::Copyout>(
43804504
&clause.u)) {
4381-
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
4382-
copyoutClause->v;
4383-
const auto &accObjectList =
4384-
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
43854505
auto crtDataStart = dataClauseOperands.size();
4506+
const auto &accObjectList =
4507+
std::get<Fortran::parser::AccObjectList>(copyoutClause->v.t);
43864508
genDeclareDataOperandOperations<mlir::acc::CreateOp,
43874509
mlir::acc::CopyoutOp>(
43884510
accObjectList, converter, semanticsContext, stmtCtx,
@@ -4423,6 +4545,11 @@ genDeclareInFunction(Fortran::lower::AbstractConverter &converter,
44234545
}
44244546
}
44254547

4548+
// If no structured operands were generated (all objects were COMMON),
4549+
// do not create a declare region.
4550+
if (dataClauseOperands.empty())
4551+
return;
4552+
44264553
mlir::func::FuncOp funcOp = builder.getFunction();
44274554
auto ops = funcOp.getOps<mlir::acc::DeclareEnterOp>();
44284555
mlir::Value declareToken;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
2+
3+
! Verify that a COMMON block declared with OpenACC declare inside a function
4+
! is lowered as a global declare (acc.global_ctor/dtor) rather than a
5+
! structured declare.
6+
7+
program p
8+
implicit none
9+
real :: pi
10+
integer :: i
11+
common /COM/ pi
12+
!$acc declare copyin(/COM/)
13+
data pi/0.0/
14+
15+
! CHECK-DAG: acc.global_ctor @{{.*}}_acc_ctor {
16+
! CHECK-DAG: %[[ADDR0:.*]] = fir.address_of(@{{.*}}) {acc.declare = #acc.declare<dataClause = acc_copyin>} : {{.*}}
17+
! CHECK-DAG: acc.declare_enter dataOperands(%{{.*}} : {{.*}})
18+
! CHECK-DAG: acc.terminator
19+
! CHECK-DAG: }
20+
21+
! CHECK-DAG: acc.global_dtor @{{.*}}_acc_dtor {
22+
! CHECK-DAG: %[[ADDR1:.*]] = fir.address_of(@{{.*}}) {acc.declare = #acc.declare<dataClause = acc_copyin>} : !fir.ref<tuple<f32>>
23+
! CHECK-DAG: %[[GDP:.*]] = acc.getdeviceptr varPtr(%[[ADDR1]] : !fir.ref<tuple<f32>>) -> !fir.ref<tuple<f32>> {dataClause = #acc<data_clause acc_copyin>, {{.*}}}
24+
! CHECK-DAG: acc.declare_exit dataOperands(%[[GDP]] : !fir.ref<tuple<f32>>)
25+
! CHECK-DAG: acc.delete accPtr(%[[GDP]] : !fir.ref<tuple<f32>>) {dataClause = #acc<data_clause acc_copyin>{{.*}}}
26+
! CHECK-DAG: acc.terminator
27+
! CHECK-DAG: }
28+
29+
contains
30+
31+
subroutine s()
32+
implicit none
33+
real :: pi
34+
common /COM/ pi
35+
!$acc declare copyin(/COM/)
36+
end subroutine s
37+
38+
end program p
39+
40+

0 commit comments

Comments
 (0)