[Flang][OpenMP] Update declare mapper lookup via use-module (#167903)

This commit is contained in:
Akash Banerjee
2025-11-13 19:21:34 +00:00
committed by GitHub
parent d4c8cfeac0
commit 833ffa54f2
13 changed files with 199 additions and 37 deletions

View File

@@ -41,6 +41,7 @@ struct OmpClauseList;
namespace semantics {
class Symbol;
class Scope;
class SemanticsContext;
} // namespace semantics
@@ -97,6 +98,13 @@ bool markOpenMPDeferredDeclareTargetFunctions(
AbstractConverter &);
void genOpenMPRequires(mlir::Operation *, const Fortran::semantics::Symbol *);
// Materialize omp.declare_mapper ops for mapper declarations found in
// imported modules. If \p scope is null, materialize for the whole
// semantics global scope; otherwise, operate recursively starting at \p scope.
void materializeOpenMPDeclareMappers(
Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
const Fortran::semantics::Scope *scope = nullptr);
} // namespace lower
} // namespace Fortran

View File

@@ -777,6 +777,24 @@ private:
DeclVector declList_;
};
// Used for OpenMP DECLARE MAPPER, it holds the declaration constructs
// so they can be serialized into module files and later re-parsed when
// USE-associated.
class MapperDetails {
public:
using DeclVector = std::vector<const parser::OpenMPDeclarativeConstruct *>;
MapperDetails() = default;
void AddDecl(const parser::OpenMPDeclarativeConstruct *decl) {
declList_.emplace_back(decl);
}
const DeclVector &GetDeclList() const { return declList_; }
private:
DeclVector declList_;
};
class UnknownDetails {};
using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails,
@@ -784,7 +802,7 @@ using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails,
ObjectEntityDetails, ProcEntityDetails, AssocEntityDetails,
DerivedTypeDetails, UseDetails, UseErrorDetails, HostAssocDetails,
GenericDetails, ProcBindingDetails, NamelistDetails, CommonBlockDetails,
TypeParamDetails, MiscDetails, UserReductionDetails>;
TypeParamDetails, MiscDetails, UserReductionDetails, MapperDetails>;
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Details &);
std::string DetailsToString(const Details &);

View File

@@ -448,6 +448,13 @@ public:
}
});
// Ensure imported OpenMP declare mappers are materialized at module
// scope before lowering any constructs that may reference them.
createBuilderOutsideOfFuncOpAndDo([&]() {
Fortran::lower::materializeOpenMPDeclareMappers(
*this, bridge.getSemanticsContext());
});
// Create definitions of intrinsic module constants.
createBuilderOutsideOfFuncOpAndDo(
[&]() { createIntrinsicModuleDefinitions(pft); });

View File

@@ -1397,10 +1397,14 @@ bool ClauseProcessor::processMap(
}
if (mappers) {
assert(mappers->size() == 1 && "more than one mapper");
mapperIdName = mappers->front().v.id().symbol->name().ToString();
if (mapperIdName != "default")
mapperIdName = converter.mangleName(
mapperIdName, mappers->front().v.id().symbol->owner());
const semantics::Symbol *mapperSym = mappers->front().v.id().symbol;
mapperIdName = mapperSym->name().ToString();
if (mapperIdName != "default") {
// Mangle with the ultimate owner so that use-associated mapper
// identifiers resolve to the same symbol as their defining scope.
const semantics::Symbol &ultimate = mapperSym->GetUltimate();
mapperIdName = converter.mangleName(mapperIdName, ultimate.owner());
}
}
processMapObjects(stmtCtx, clauseLocation,

View File

@@ -3553,10 +3553,10 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OpenMPDeclareMapperConstruct &construct) {
static void genOpenMPDeclareMapperImpl(
lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
const parser::OpenMPDeclareMapperConstruct &construct,
const semantics::Symbol *mapperSymOpt = nullptr) {
mlir::Location loc = converter.genLocation(construct.source);
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
const parser::OmpArgumentList &args = construct.v.Arguments();
@@ -3572,8 +3572,17 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
"Expected derived type");
std::string mapperNameStr = mapperName;
if (auto *sym = converter.getCurrentScope().FindSymbol(mapperNameStr))
if (mapperSymOpt && mapperNameStr != "default") {
mapperNameStr = converter.mangleName(mapperNameStr, mapperSymOpt->owner());
} else if (auto *sym =
converter.getCurrentScope().FindSymbol(mapperNameStr)) {
mapperNameStr = converter.mangleName(mapperNameStr, sym->owner());
}
// If the mapper op already exists (e.g., created by regular lowering or by
// materialization of imported mappers), do not recreate it.
if (converter.getModuleOp().lookupSymbol(mapperNameStr))
return;
// Save current insertion point before moving to the module scope to create
// the DeclareMapperOp
@@ -3596,6 +3605,13 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseOps.mapVars);
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OpenMPDeclareMapperConstruct &construct) {
genOpenMPDeclareMapperImpl(converter, semaCtx, construct);
}
static void
genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -4231,3 +4247,36 @@ void Fortran::lower::genOpenMPRequires(mlir::Operation *mod,
offloadMod.setRequires(mlirFlags);
}
}
// Walk scopes and materialize omp.declare_mapper ops for mapper declarations
// found in imported modules. If \p scope is null, start from the global scope.
void Fortran::lower::materializeOpenMPDeclareMappers(
Fortran::lower::AbstractConverter &converter,
semantics::SemanticsContext &semaCtx, const semantics::Scope *scope) {
const semantics::Scope &root = scope ? *scope : semaCtx.globalScope();
// Recurse into child scopes first (modules, submodules, etc.).
for (const semantics::Scope &child : root.children())
materializeOpenMPDeclareMappers(converter, semaCtx, &child);
// Only consider module scopes to avoid duplicating local constructs.
if (!root.IsModule())
return;
// Only materialize for modules coming from mod files to avoid duplicates.
if (!root.symbol() || !root.symbol()->test(semantics::Symbol::Flag::ModFile))
return;
// Scan symbols in this module scope for MapperDetails.
for (auto &it : root) {
const semantics::Symbol &sym = *it.second;
if (auto *md = sym.detailsIf<semantics::MapperDetails>()) {
for (const auto *decl : md->GetDeclList()) {
if (const auto *mapperDecl =
std::get_if<parser::OpenMPDeclareMapperConstruct>(&decl->u)) {
genOpenMPDeclareMapperImpl(converter, semaCtx, *mapperDecl, &sym);
}
}
}
}
}

View File

@@ -59,6 +59,7 @@ static void PutBound(llvm::raw_ostream &, const Bound &);
static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
static void PutShape(
llvm::raw_ostream &, const ArraySpec &, char open, char close);
static void PutMapper(llvm::raw_ostream &, const Symbol &, SemanticsContext &);
static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
@@ -938,6 +939,7 @@ void ModFileWriter::PutEntity(llvm::raw_ostream &os, const Symbol &symbol) {
[&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
[&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
[&](const UserReductionDetails &) { PutUserReduction(os, symbol); },
[&](const MapperDetails &) { PutMapper(decls_, symbol, context_); },
[&](const auto &) {
common::die("PutEntity: unexpected details: %s",
DetailsToString(symbol.details()).c_str());
@@ -1101,6 +1103,16 @@ void ModFileWriter::PutUserReduction(
}
}
static void PutMapper(
llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
const auto &details{symbol.get<MapperDetails>()};
// Emit each saved DECLARE MAPPER construct as-is, so that consumers of the
// module can reparse it and recreate the mapper symbol and semantics state.
for (const auto *decl : details.GetDeclList()) {
Unparse(os, *decl, context.langOptions());
}
}
void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
const parser::Expr *unanalyzed, SemanticsContext &context) {
if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {

View File

@@ -1852,21 +1852,25 @@ bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
// TODO: Do we need a specific flag or type here, to distinghuish against
// other ConstructName things? Leaving this for the full implementation
// of mapper lowering.
auto *misc{symbol->detailsIf<MiscDetails>()};
if (!misc || misc->kind() != MiscDetails::Kind::ConstructName)
auto &ultimate{symbol->GetUltimate()};
auto *misc{ultimate.detailsIf<MiscDetails>()};
auto *md{ultimate.detailsIf<MapperDetails>()};
if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
context().Say(mapper->v.source,
"Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
else
mapper->v.symbol = symbol;
} else {
mapper->v.symbol =
&MakeSymbol(mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
// TODO: When completing the implementation, we probably want to error if
// the symbol is not declared, but right now, testing that the TODO for
// OmpMapClause happens is obscured by the TODO for declare mapper, so
// leaving this out. Remove the above line once the declare mapper is
// implemented. context().Say(mapper->v.source, "'%s' not
// declared"_err_en_US, mapper->v.source);
// Allow the special 'default' mapper identifier without prior
// declaration so lowering can recognize and handle it. Emit an
// error for any other missing mapper identifier.
if (mapper->v.source.ToString() == "default") {
mapper->v.symbol = &MakeSymbol(
mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
context().Say(
mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
}
}
}
return true;
@@ -1880,8 +1884,16 @@ void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
// the type has been fully processed.
BeginDeclTypeSpec();
auto &mapperName{std::get<std::string>(spec.t)};
MakeSymbol(parser::CharBlock(mapperName), Attrs{},
MiscDetails{MiscDetails::Kind::ConstructName});
// Create or update the mapper symbol with MapperDetails and
// keep track of the declarative construct for module emission.
SourceName mapperSource{context().SaveTempName(std::string{mapperName})};
Symbol &mapperSym{MakeSymbol(mapperSource, Attrs{})};
if (!mapperSym.detailsIf<MapperDetails>()) {
mapperSym.set_details(MapperDetails{});
}
if (!context().langOptions().OpenMPSimd) {
mapperSym.get<MapperDetails>().AddDecl(declaratives_.back());
}
PushScope(Scope::Kind::OtherConstruct, nullptr);
Walk(std::get<parser::TypeSpec>(spec.t));
auto &varName{std::get<parser::Name>(spec.t)};
@@ -3611,10 +3623,20 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
rename.u);
}
for (const auto &[name, symbol] : *useModuleScope_) {
// Default USE imports public names, excluding intrinsic-only and most
// miscellaneous details. Allow OpenMP mapper identifiers represented
// as MapperDetails, and also legacy MiscDetails::ConstructName.
bool isMapper{symbol->has<MapperDetails>()};
if (!isMapper) {
if (const auto *misc{symbol->detailsIf<MiscDetails>()}) {
isMapper = misc->kind() == MiscDetails::Kind::ConstructName;
}
}
if (symbol->attrs().test(Attr::PUBLIC) && !IsUseRenamed(symbol->name()) &&
(!symbol->implicitAttrs().test(Attr::INTRINSIC) ||
symbol->has<UseDetails>()) &&
!symbol->has<MiscDetails>() && useNames.count(name) == 0) {
(!symbol->has<MiscDetails>() || isMapper) &&
useNames.count(name) == 0) {
SourceName location{x.moduleName.source};
if (auto *localSymbol{FindInScope(name)}) {
DoAddUse(location, localSymbol->name(), *localSymbol, *symbol);

View File

@@ -338,7 +338,8 @@ std::string DetailsToString(const Details &details) {
[](const TypeParamDetails &) { return "TypeParam"; },
[](const MiscDetails &) { return "Misc"; },
[](const AssocEntityDetails &) { return "AssocEntity"; },
[](const UserReductionDetails &) { return "UserReductionDetails"; }},
[](const UserReductionDetails &) { return "UserReductionDetails"; },
[](const MapperDetails &) { return "MapperDetails"; }},
details);
}
@@ -379,6 +380,7 @@ bool Symbol::CanReplaceDetails(const Details &details) const {
[&](const UserReductionDetails &) {
return has<UserReductionDetails>();
},
[&](const MapperDetails &) { return has<MapperDetails>(); },
[](const auto &) { return false; },
},
details);
@@ -685,6 +687,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
DumpType(os, type);
}
},
// Avoid recursive streaming for MapperDetails; nothing more to dump
[&](const MapperDetails &) {},
[&](const auto &x) { os << x; },
},
details);

View File

@@ -6,7 +6,9 @@
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/omp-declare-mapper-3.f90 -o - | FileCheck %t/omp-declare-mapper-3.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/omp-declare-mapper-4.f90 -o - | FileCheck %t/omp-declare-mapper-4.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/omp-declare-mapper-5.f90 -o - | FileCheck %t/omp-declare-mapper-5.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 %t/omp-declare-mapper-6.f90 -o - | FileCheck %t/omp-declare-mapper-6.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/omp-declare-mapper-6.f90 -o - | FileCheck %t/omp-declare-mapper-6.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -module-dir %t %t/omp-declare-mapper-7.mod.f90 -o - >/dev/null
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -J %t %t/omp-declare-mapper-7.use.f90 -o - | FileCheck %t/omp-declare-mapper-7.use.f90
!--- omp-declare-mapper-1.f90
subroutine declare_mapper_1
@@ -301,3 +303,25 @@ subroutine declare_mapper_nested_parent
r%real_arr = r%base_arr(1) + r%inner%deep_arr(1)
!$omp end target
end subroutine declare_mapper_nested_parent
!--- omp-declare-mapper-7.mod.f90
! Module with DECLARE MAPPER to be compiled separately
module m_mod
implicit none
type :: mty
integer :: x
end type mty
!$omp declare mapper(mymap : mty :: v) map(tofrom: v%x)
end module m_mod
!--- omp-declare-mapper-7.use.f90
! Consumer program that USEs the module and applies the mapper by name.
! CHECK: %{{.*}} = omp.map.info {{.*}} mapper(@{{.*mymap}}) {{.*}} {name = "a"}
program use_module_mapper
use m_mod
implicit none
type(mty) :: a
!$omp target map(mapper(mymap) : a)
a%x = 42
!$omp end target
end program use_module_mapper

View File

@@ -320,7 +320,7 @@ subroutine f21(x, y)
integer :: x(10)
integer :: y
integer, parameter :: p = 23
!$omp target map(mapper(xx), from: x)
!$omp target map(mapper(default), from: x)
x = x + 1
!$omp end target
end
@@ -329,7 +329,7 @@ end
!UNPARSE: INTEGER x(10_4)
!UNPARSE: INTEGER y
!UNPARSE: INTEGER, PARAMETER :: p = 23_4
!UNPARSE: !$OMP TARGET MAP(MAPPER(XX), FROM: X)
!UNPARSE: !$OMP TARGET MAP(MAPPER(DEFAULT), FROM: X)
!UNPARSE: x=x+1_4
!UNPARSE: !$OMP END TARGET
!UNPARSE: END SUBROUTINE
@@ -337,7 +337,7 @@ end
!PARSE-TREE: OmpBeginDirective
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | Modifier -> OmpMapper -> Name = 'xx'
!PARSE-TREE: | | Modifier -> OmpMapper -> Name = 'default'
!PARSE-TREE: | | Modifier -> OmpMapType -> Value = From
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
@@ -375,4 +375,3 @@ end
!PARSE-TREE: | | SectionSubscript -> Integer -> Expr = 'i'
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | bool = 'true'

View File

@@ -0,0 +1,14 @@
! RUN: split-file %s %t
! RUN: %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=50 -module-dir %t %t/m.f90
! RUN: cat %t/m.mod | FileCheck --ignore-case %s
!--- m.f90
module m
implicit none
type :: t
integer :: x
end type t
!$omp declare mapper(mymap : t :: v) map(v%x)
end module m
!CHECK: !$OMP DECLARE MAPPER(mymap:t::v) MAP(v%x)

View File

@@ -11,9 +11,9 @@ program main
!$omp declare mapper(ty :: maptwo) map(maptwo, maptwo%x)
!! Note, symbols come out in their respective scope, but not in declaration order.
!CHECK: mymapper: Misc ConstructName
!CHECK: mymapper: MapperDetails
!CHECK: ty: DerivedType components: x
!CHECK: ty.omp.default.mapper: Misc ConstructName
!CHECK: ty.omp.default.mapper: MapperDetails
!CHECK: DerivedType scope: ty
!CHECK: OtherConstruct scope:
!CHECK: mapped (OmpMapToFrom) {{.*}} ObjectEntity type: TYPE(ty)
@@ -21,4 +21,3 @@ program main
!CHECK: maptwo (OmpMapToFrom) {{.*}} ObjectEntity type: TYPE(ty)
end program main

View File

@@ -1,14 +1,16 @@
! RUN: %flang_fc1 -fdebug-dump-symbols -fopenmp -fopenmp-version=50 %s | FileCheck %s
program main
!CHECK-LABEL: MainProgram scope: MAIN
type ty
real(4) :: x
end type ty
!$omp declare mapper(xx : ty :: v) map(v)
integer, parameter :: n = 256
real(8) :: a(256)
type(ty) :: a(256)
!$omp target map(mapper(xx), from:a)
do i=1,n
a(i) = 4.2
a(i)%x = 4.2
end do
!$omp end target
!CHECK: OtherConstruct scope: size=0 alignment=1 sourceRange=74 bytes
!CHECK: OtherClause scope: size=0 alignment=1 sourceRange=0 bytes
!CHECK: xx: Misc ConstructName
!CHECK: xx: MapperDetails
end program main