Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions flang/include/flang/Lower/OpenMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,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

Expand Down
20 changes: 19 additions & 1 deletion flang/include/flang/Semantics/symbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -777,14 +777,32 @@ class UserReductionDetails {
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,
SubprogramDetails, SubprogramNameDetails, EntityDetails,
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 &);

Expand Down
7 changes: 7 additions & 0 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
});

// 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); });
Expand Down
12 changes: 8 additions & 4 deletions flang/lib/Lower/OpenMP/ClauseProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1402,10 +1402,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,
Expand Down
63 changes: 58 additions & 5 deletions flang/lib/Lower/OpenMP/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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),
// do not recreate it.
Comment on lines +3582 to +3583
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states the mapper op might already exist from 'regular lowering', but this check occurs in the implementation function that is called both by regular lowering and by materialization of imported mappers. The comment should clarify that this deduplication applies to both paths, not just one scenario.

Suggested change
// If the mapper op already exists (e.g., created by regular lowering),
// do not recreate it.
// If the mapper op already exists (e.g., created by regular lowering or by
// materialization of imported mappers), do not recreate it.

Copilot uses AI. Check for mistakes.
if (converter.getModuleOp().lookupSymbol(mapperNameStr))
return;

// Save current insertion point before moving to the module scope to create
// the DeclareMapperOp
Expand All @@ -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,
Expand Down Expand Up @@ -4239,3 +4255,40 @@ 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 (const semantics::Symbol *modSym = root.symbol()) {
if (!modSym->test(semantics::Symbol::Flag::ModFile))
return;
} else {
return;
}
Comment on lines +4275 to +4280
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested conditions can be simplified using early returns. Consider restructuring as: if (!root.symbol() || !root.symbol()->test(semantics::Symbol::Flag::ModFile)) return; to reduce nesting and improve readability.

Suggested change
if (const semantics::Symbol *modSym = root.symbol()) {
if (!modSym->test(semantics::Symbol::Flag::ModFile))
return;
} else {
return;
}
if (!root.symbol() || !root.symbol()->test(semantics::Symbol::Flag::ModFile))
return;

Copilot uses AI. Check for mistakes.

// 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);
}
}
}
}
}
12 changes: 12 additions & 0 deletions flang/lib/Semantics/mod-file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &);
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -1098,6 +1100,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());
}
}

Comment on lines +1103 to +1112
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test that checks the presence of the declare mapper in the module file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

void PutInit(llvm::raw_ostream &os, const Symbol &symbol, const MaybeExpr &init,
const parser::Expr *unanalyzed, SemanticsContext &context) {
if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
Expand Down
47 changes: 34 additions & 13 deletions flang/lib/Semantics/resolve-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1824,21 +1824,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;
Expand All @@ -1852,8 +1856,15 @@ 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.
Symbol &mapperSym{MakeSymbol(parser::CharBlock(mapperName), Attrs{})};
if (auto *md{mapperSym.detailsIf<MapperDetails>()}) {
md->AddDecl(declaratives_.back());
} else if (mapperSym.has<UnknownDetails>() || mapperSym.has<MiscDetails>()) {
mapperSym.set_details(MapperDetails{});
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)};
Expand Down Expand Up @@ -3614,10 +3625,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);
Expand Down
6 changes: 5 additions & 1 deletion flang/lib/Semantics/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
26 changes: 25 additions & 1 deletion flang/test/Lower/OpenMP/declare-mapper.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
7 changes: 3 additions & 4 deletions flang/test/Parser/OpenMP/map-modifiers.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -329,15 +329,15 @@ subroutine f21(x, y)
!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

!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'

Expand Down Expand Up @@ -375,4 +375,3 @@ subroutine f22(x)
!PARSE-TREE: | | SectionSubscript -> Integer -> Expr = 'i'
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | bool = 'true'

14 changes: 14 additions & 0 deletions flang/test/Semantics/OpenMP/declare-mapper-modfile.f90
Original file line number Diff line number Diff line change
@@ -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)
Loading
Loading