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
55 changes: 36 additions & 19 deletions flang/lib/Lower/OpenMP/ClauseProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,32 @@ void ClauseProcessor::processMapObjects(
}
}

/// Extract and mangle the mapper identifier name from a mapper clause.
/// Returns "__implicit_mapper" if no mapper is specified, or "default" if
/// the default mapper is specified, otherwise returns the mangled mapper name.
/// This handles both the Map clause (which uses a vector of mappers) and
/// To/From clauses (which use a DefinedOperator).
template <typename MapperType>
static std::string
getMapperIdentifier(lower::AbstractConverter &converter,
const std::optional<MapperType> &mapper) {
if (!mapper)
return "__implicit_mapper";

// Handle mapper types (both have the same structure)
assert(mapper->size() == 1 && "more than one mapper");
const semantics::Symbol *mapperSym = mapper->front().v.id().symbol;

std::string 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());
}
return mapperIdName;
}

bool ClauseProcessor::processMap(
mlir::Location currentLocation, lower::StatementContext &stmtCtx,
mlir::omp::MapClauseOps &result, llvm::omp::Directive directive,
Expand Down Expand Up @@ -1511,17 +1537,7 @@ bool ClauseProcessor::processMap(
TODO(currentLocation,
"Support for iterator modifiers is not implemented yet");
}
if (mappers) {
assert(mappers->size() == 1 && "more than one mapper");
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());
}
}
mapperIdName = getMapperIdentifier(converter, mappers);

processMapObjects(stmtCtx, clauseLocation,
std::get<omp::ObjectList>(clause.t), mapTypeBits,
Expand All @@ -1545,21 +1561,22 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
mlir::Location clauseLocation = converter.genLocation(source);
const auto &[expectation, mapper, iterator, objects] = clause.t;

// TODO Support motion modifiers: mapper, iterator.
if (mapper) {
TODO(clauseLocation, "Mapper modifier is not supported yet");
} else if (iterator) {
TODO(clauseLocation, "Iterator modifier is not supported yet");
}

mlir::omp::ClauseMapFlags mapTypeBits =
std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To>
? mlir::omp::ClauseMapFlags::to
: mlir::omp::ClauseMapFlags::from;
if (expectation && *expectation == omp::clause::To::Expectation::Present)
mapTypeBits |= mlir::omp::ClauseMapFlags::present;

// Support motion modifiers: mapper, iterator.
std::string mapperIdName = getMapperIdentifier(converter, mapper);
if (iterator) {
TODO(clauseLocation, "Iterator modifier is not supported yet");
}

processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
parentMemberIndices, result.mapVars, mapSymbols);
parentMemberIndices, result.mapVars, mapSymbols,
mapperIdName);
};

bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn);
Expand Down
76 changes: 43 additions & 33 deletions flang/lib/Semantics/resolve-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,8 @@ class OmpVisitor : public virtual DeclarationVisitor {
return true;
}
bool Pre(const parser::OmpMapClause &);
bool Pre(const parser::OmpClause::To &);
bool Pre(const parser::OmpClause::From &);

bool Pre(const parser::OpenMPSectionsConstruct &) {
PushScope(Scope::Kind::OtherConstruct, nullptr);
Expand Down Expand Up @@ -1780,6 +1782,7 @@ class OmpVisitor : public virtual DeclarationVisitor {
}

private:
void ResolveMapperModifier(const parser::OmpMapper &mapper);
void ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
const parser::OmpClauseList &clauses);
void ProcessReductionSpecifier(const parser::OmpReductionSpecifier &spec,
Expand Down Expand Up @@ -1851,32 +1854,49 @@ void OmpVisitor::Post(const parser::OmpStylizedInstance &x) { //
bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
auto &mods{OmpGetModifiers(x)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
// 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 &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;
ResolveMapperModifier(*mapper);
}
return true;
}

bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
ResolveMapperModifier(*mapper);
}
return true;
}

bool OmpVisitor::Pre(const parser::OmpClause::From &x) {
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
ResolveMapperModifier(*mapper);
}
return true;
}

void OmpVisitor::ResolveMapperModifier(const parser::OmpMapper &mapper) {
if (auto *symbol{FindSymbol(currScope(), mapper.v)}) {
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 {
// 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 {
// 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);
}
context().Say(
mapper.v.source, "'%s' not declared"_err_en_US, mapper.v.source);
}
}
return true;
}

void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
Expand Down Expand Up @@ -9876,9 +9896,6 @@ void ResolveNamesVisitor::FinishSpecificationPart(
misparsedStmtFuncFound_ = false;
funcResultStack().CompleteFunctionResultType();
CheckImports();
if (inInterfaceBlock()) {
FinishNamelists(); // NAMELIST is useless in an interface, but allowed
}
for (auto &pair : currScope()) {
auto &symbol{*pair.second};
if (inInterfaceBlock()) {
Expand Down Expand Up @@ -9913,13 +9930,6 @@ void ResolveNamesVisitor::FinishSpecificationPart(
SetBindNameOn(symbol);
}
}
// Implicitly treat allocatable arrays as managed when feature is enabled.
// This is done after all explicit CUDA attributes have been processed.
if (context().languageFeatures().IsEnabled(
common::LanguageFeature::CudaManaged))
if (auto *object{symbol.detailsIf<ObjectEntityDetails>()})
if (IsAllocatable(symbol) && !object->cudaDataAttr())
object->set_cudaDataAttr(common::CUDADataAttr::Managed);
}
currScope().InstantiateDerivedTypes();
for (const auto &decl : decls) {
Expand Down
34 changes: 34 additions & 0 deletions flang/test/Lower/OpenMP/declare-mapper.f90
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
! 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
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -module-dir %t %t/omp-declare-mapper-8.mod.f90 -o - >/dev/null
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -J %t %t/omp-declare-mapper-8.use.f90 -o - | FileCheck %t/omp-declare-mapper-8.use.f90
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %t/omp-declare-mapper-9.f90 -o - | FileCheck %t/omp-declare-mapper-9.f90

!--- omp-declare-mapper-1.f90
subroutine declare_mapper_1
Expand Down Expand Up @@ -360,3 +361,36 @@ program use_module_default_mapper
a%x = 8
!$omp end target
end program use_module_default_mapper

!--- omp-declare-mapper-9.f90
! Test mapper usage in target update to/from clauses
program target_update_mapper
type :: typ
integer :: a
integer :: b
end type typ

!CHECK: omp.declare_mapper @_QQFcustom : !fir.type<_QFTtyp{a:i32,b:i32}>
!CHECK: omp.declare_mapper @_QQFtyp_omp_default_mapper : !fir.type<_QFTtyp{a:i32,b:i32}>

!$omp declare mapper(typ :: t) map(t%a, t%b)
!$omp declare mapper(custom: typ :: t) map(t%a)

type(typ) :: t

! Test target update to with custom mapper
!CHECK: %[[MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO]] : {{.*}})
!$omp target update to(mapper(custom): t)

! Test target update from with custom mapper
!CHECK: %[[MAP_INFO2:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(from) capture(ByRef) mapper(@_QQFcustom) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO2]] : {{.*}})
!$omp target update from(mapper(custom): t)

! Test target update to with default mapper
!CHECK: %[[MAP_INFO3:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}, {{.*}}) map_clauses(to) capture(ByRef) mapper(@_QQFtyp_omp_default_mapper) -> {{.*}}
!CHECK: omp.target_update map_entries(%[[MAP_INFO3]] : {{.*}})
!$omp target update to(mapper(default): t)

end program target_update_mapper
30 changes: 30 additions & 0 deletions flang/test/Semantics/OpenMP/target-update-mapper.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52

! Test mapper name resolution in target update to/from clauses

program target_update_mapper
type :: typ
integer :: a
end type typ

!$omp declare mapper(custom: typ :: t) map(t%a)

type(typ) :: t
integer :: not_a_mapper

! Valid: using custom mapper
!$omp target update to(mapper(custom): t)
!$omp target update from(mapper(custom): t)

! Valid: using default mapper
!$omp target update to(mapper(default): t)

! Error: undefined mapper
!ERROR: 'undefined_mapper' not declared
!$omp target update to(mapper(undefined_mapper): t)

! Error: wrong kind of symbol
!ERROR: Name 'not_a_mapper' should be a mapper name
!$omp target update from(mapper(not_a_mapper): t)

end program target_update_mapper
85 changes: 85 additions & 0 deletions offload/test/offloading/fortran/target-update-custom-mapper.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
! Test custom mappers with target update to/from clauses
! REQUIRES: flang, amdgpu

! RUN: %libomptarget-compile-fortran-run-and-check-generic

program target_update_mapper_test
implicit none
integer, parameter :: n = 100

type :: my_type
integer :: a(n)
integer :: b(n)
end type my_type

! Declare custom mapper that only maps field 'a'
!$omp declare mapper(custom : my_type :: t) map(t%a)

! Declare default mapper that maps both fields
!$omp declare mapper(my_type :: t) map(t%a, t%b)

type(my_type) :: obj1, obj2
integer :: i, sum_a, sum_b

! ========== Test 1: Custom mapper (field 'a' only) ==========

! Initialize data on host
do i = 1, n
obj1%a(i) = i
obj1%b(i) = i * 2
end do

! Allocate and update using custom mapper (only 'a')
!$omp target enter data map(mapper(custom), alloc: obj1)

obj1%a = 10
!$omp target update to(mapper(custom): obj1)

obj1%a = 0
!$omp target update from(mapper(custom): obj1)

sum_a = sum(obj1%a)
sum_b = sum(obj1%b)

! CHECK: Sum of a (custom mapper): 1000
print *, "Sum of a (custom mapper):", sum_a

! Field 'b' was never mapped with custom mapper
! CHECK: Sum of b (never mapped): 10100
print *, "Sum of b (never mapped):", sum_b

!$omp target exit data map(mapper(custom), delete: obj1)

! ========== Test 2: Default mapper (both fields) ==========

! Initialize separate object for default mapper test
do i = 1, n
obj2%a(i) = 20
obj2%b(i) = 30
end do

! Allocate and update using default mapper (both 'a' and 'b')
!$omp target enter data map(mapper(default), alloc: obj2)

!$omp target update to(mapper(default): obj2)

obj2%a = 0
obj2%b = 0

!$omp target update from(mapper(default): obj2)

sum_a = sum(obj2%a)
sum_b = sum(obj2%b)

! CHECK: Sum of a (default mapper): 2000
print *, "Sum of a (default mapper):", sum_a

! CHECK: Sum of b (default mapper): 3000
print *, "Sum of b (default mapper):", sum_b

!$omp target exit data map(mapper(default), delete: obj2)

! CHECK: Test passed!
print *, "Test passed!"

end program target_update_mapper_test
Loading