diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 2f531efaf09aa..a03f0cc73dc2b 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -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 +static std::string +getMapperIdentifier(lower::AbstractConverter &converter, + const std::optional &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, @@ -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(clause.t), mapTypeBits, @@ -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, 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(callbackFn); diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 310f7ab97e9a0..0d6aab1c1a48c 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -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); @@ -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, @@ -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(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()}; - auto *md{ultimate.detailsIf()}; - 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(mods)}) { + ResolveMapperModifier(*mapper); + } + return true; +} + +bool OmpVisitor::Pre(const parser::OmpClause::From &x) { + auto &mods{OmpGetModifiers(x.v)}; + if (auto *mapper{OmpGetUniqueModifier(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()}; + auto *md{ultimate.detailsIf()}; + 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, @@ -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()) { @@ -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()}) - if (IsAllocatable(symbol) && !object->cudaDataAttr()) - object->set_cudaDataAttr(common::CUDADataAttr::Managed); } currScope().InstantiateDerivedTypes(); for (const auto &decl : decls) { diff --git a/flang/test/Lower/OpenMP/declare-mapper.f90 b/flang/test/Lower/OpenMP/declare-mapper.f90 index 7eda1a4c497be..18de556e2ce7d 100644 --- a/flang/test/Lower/OpenMP/declare-mapper.f90 +++ b/flang/test/Lower/OpenMP/declare-mapper.f90 @@ -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 @@ -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 diff --git a/flang/test/Semantics/OpenMP/target-update-mapper.f90 b/flang/test/Semantics/OpenMP/target-update-mapper.f90 new file mode 100644 index 0000000000000..f37d288670594 --- /dev/null +++ b/flang/test/Semantics/OpenMP/target-update-mapper.f90 @@ -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 diff --git a/offload/test/offloading/fortran/target-update-custom-mapper.f90 b/offload/test/offloading/fortran/target-update-custom-mapper.f90 new file mode 100644 index 0000000000000..e3384c2911d47 --- /dev/null +++ b/offload/test/offloading/fortran/target-update-custom-mapper.f90 @@ -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