@@ -692,6 +692,91 @@ static void threadPrivatizeVars(lower::AbstractConverter &converter,
692692 }
693693}
694694
695+ static void groupprivatizeVars (lower::AbstractConverter &converter,
696+ lower::pft::Evaluation &eval) {
697+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder ();
698+ mlir::Location currentLocation = converter.getCurrentLocation ();
699+ mlir::OpBuilder::InsertionGuard guard (firOpBuilder);
700+ firOpBuilder.setInsertionPointToStart (firOpBuilder.getAllocaBlock ());
701+
702+ auto module = converter.getModuleOp ();
703+
704+ // Create a groupprivate operation for the symbol.
705+ // TODO: Extract device_type from the groupprivate directive.
706+ auto genGroupprivateOp = [&](const semantics::Symbol &sym) -> mlir::Value {
707+ std::string globalName = converter.mangleName (sym);
708+ fir::GlobalOp global = module .lookupSymbol <fir::GlobalOp>(globalName);
709+ if (!global) {
710+ return mlir::Value ();
711+ }
712+
713+ // Generate fir.address_of to get the global address
714+ mlir::Value symAddr = fir::AddrOfOp::create (
715+ firOpBuilder, currentLocation, global.resultType (), global.getSymbol ());
716+
717+ mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
718+ mlir::omp::DeclareTargetDeviceType::any;
719+ mlir::omp::DeclareTargetDeviceTypeAttr deviceTypeAttr =
720+ mlir::omp::DeclareTargetDeviceTypeAttr::get (firOpBuilder.getContext (),
721+ deviceTypeEnum);
722+
723+ return mlir::omp::GroupprivateOp::create (firOpBuilder, currentLocation,
724+ symAddr.getType (), symAddr,
725+ deviceTypeAttr);
726+ };
727+
728+ llvm::SetVector<const semantics::Symbol *> groupprivateSyms;
729+ converter.collectSymbolSet (eval, groupprivateSyms,
730+ semantics::Symbol::Flag::OmpGroupPrivate,
731+ /* collectSymbols=*/ true ,
732+ /* collectHostAssociatedSymbols=*/ true );
733+ std::set<semantics::SourceName> groupprivateSymNames;
734+
735+ // For a COMMON block, the GroupprivateOp is generated for the block itself
736+ // instead of its members.
737+ llvm::SetVector<const semantics::Symbol *> commonSyms;
738+
739+ for (std::size_t i = 0 ; i < groupprivateSyms.size (); i++) {
740+ const semantics::Symbol *sym = groupprivateSyms[i];
741+ mlir::Value symGroupprivateValue;
742+ // The variable may be used more than once, and each reference has one
743+ // symbol with the same name. Only do once for references of one variable.
744+ if (groupprivateSymNames.find (sym->name ()) != groupprivateSymNames.end ())
745+ continue ;
746+ groupprivateSymNames.insert (sym->name ());
747+
748+ if (const semantics::Symbol *common =
749+ semantics::FindCommonBlockContaining (sym->GetUltimate ())) {
750+ // Handle common block members: create groupprivate op for the entire
751+ // common block, then compute member offset.
752+ mlir::Value commonGroupprivateValue;
753+ if (commonSyms.contains (common)) {
754+ commonGroupprivateValue = converter.getSymbolAddress (*common);
755+ } else {
756+ commonGroupprivateValue = genGroupprivateOp (*common);
757+ if (!commonGroupprivateValue)
758+ continue ;
759+ converter.bindSymbol (*common, commonGroupprivateValue);
760+ commonSyms.insert (common);
761+ }
762+ symGroupprivateValue = lower::genCommonBlockMember (
763+ converter, currentLocation, sym->GetUltimate (),
764+ commonGroupprivateValue, common->size ());
765+ } else {
766+ symGroupprivateValue = genGroupprivateOp (*sym);
767+ }
768+
769+ if (!symGroupprivateValue) {
770+ continue ;
771+ }
772+
773+ fir::ExtendedValue sexv = converter.getSymbolExtendedValue (*sym);
774+ fir::ExtendedValue symGroupprivateExv =
775+ getExtendedValue (sexv, symGroupprivateValue);
776+ converter.bindSymbol (*sym, symGroupprivateExv);
777+ }
778+ }
779+
695780static mlir::Operation *setLoopVar (lower::AbstractConverter &converter,
696781 mlir::Location loc, mlir::Value indexVal,
697782 const semantics::Symbol *sym) {
@@ -1230,6 +1315,10 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
12301315 }
12311316 }
12321317
1318+ if (info.dir == llvm::omp::Directive::OMPD_teams) {
1319+ groupprivatizeVars (info.converter , info.eval );
1320+ }
1321+
12331322 if (!info.genSkeletonOnly ) {
12341323 if (ConstructQueue::const_iterator next = std::next (item);
12351324 next != queue.end ()) {
@@ -2736,6 +2825,11 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
27362825 !symbolsWithDynamicSubstring.contains (&sym.GetUltimate ()))
27372826 return ;
27382827
2828+ // Skip groupprivate symbols - they don't need to be mapped because
2829+ // groupprivate creates its own LDS storage.
2830+ if (sym.GetUltimate ().test (semantics::Symbol::Flag::OmpGroupPrivate))
2831+ return ;
2832+
27392833 if (!isDuplicateMappedSymbol (sym, dsp.getAllSymbolsToPrivatize (),
27402834 hasDeviceAddrSyms, mapSyms, isDevicePtrSyms)) {
27412835 if (const auto *details =
@@ -4015,7 +4109,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
40154109 semantics::SemanticsContext &semaCtx,
40164110 lower::pft::Evaluation &eval,
40174111 const parser::OpenMPGroupprivate &directive) {
4018- TODO (converter.getCurrentLocation (), " GROUPPRIVATE" );
4112+ // The groupprivate directive is lowered when the variable is referenced
4113+ // inside target/teams regions.
40194114}
40204115
40214116static void genOMP (lower::AbstractConverter &converter, lower::SymMap &symTable,
@@ -4419,6 +4514,9 @@ void Fortran::lower::genOpenMPSymbolProperties(
44194514
44204515 if (sym.test (semantics::Symbol::Flag::OmpDeclareTarget))
44214516 lower::genDeclareTargetIntGlobal (converter, var);
4517+
4518+ if (sym.test (semantics::Symbol::Flag::OmpGroupPrivate))
4519+ lower::genGroupprivateOp (converter, var);
44224520}
44234521
44244522void Fortran::lower::genThreadprivateOp (lower::AbstractConverter &converter,
@@ -4487,6 +4585,42 @@ void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
44874585 converter.bindSymbol (sym, symThreadprivateExv);
44884586}
44894587
4588+ void Fortran::lower::genGroupprivateOp (lower::AbstractConverter &converter,
4589+ const lower::pft::Variable &var) {
4590+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder ();
4591+ mlir::Location currentLocation = converter.getCurrentLocation ();
4592+
4593+ const semantics::Symbol &sym = var.getSymbol ();
4594+
4595+ // For common block members, the groupprivate op is generated for the entire
4596+ // common block in groupprivatizeVars, not for individual members here.
4597+ // The common block already has a global, so nothing to do here.
4598+ if (semantics::FindCommonBlockContaining (sym.GetUltimate ())) {
4599+ return ;
4600+ }
4601+
4602+ fir::GlobalOp global;
4603+ auto module = converter.getModuleOp ();
4604+ std::string globalName = converter.mangleName (sym);
4605+
4606+ // Handle non-global variables - create a GlobalOp for them.
4607+ // Local variables with SAVE attribute can be groupprivate.
4608+ // Promote them to fir.global so that omp.groupprivate
4609+ // can reference them via fir.address_of.
4610+ if (!var.isGlobal ()) {
4611+ if (module .lookupSymbol <fir::GlobalOp>(globalName))
4612+ global = module .lookupSymbol <fir::GlobalOp>(globalName);
4613+ else
4614+ global = globalInitialization (converter, firOpBuilder, sym, var,
4615+ currentLocation);
4616+ } else {
4617+ global = module .lookupSymbol <fir::GlobalOp>(globalName);
4618+ }
4619+
4620+ // The actual omp.groupprivate operation is created by groupprivatizeVars
4621+ // when entering a teams region.
4622+ }
4623+
44904624// This function replicates threadprivate's behaviour of generating
44914625// an internal fir.GlobalOp for non-global variables in the main program
44924626// that have the implicit SAVE attribute, to simplifiy LLVM-IR and MLIR
0 commit comments