xref: /llvm-project/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp (revision a0406ce823e8f1c1993b565d08b045c0104c3a5a)
1 //===-- DataSharingProcessor.cpp --------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "DataSharingProcessor.h"
14 
15 #include "Utils.h"
16 #include "flang/Lower/ConvertVariable.h"
17 #include "flang/Lower/PFTBuilder.h"
18 #include "flang/Lower/SymbolMap.h"
19 #include "flang/Optimizer/Builder/HLFIRTools.h"
20 #include "flang/Optimizer/Builder/Todo.h"
21 #include "flang/Optimizer/HLFIR/HLFIROps.h"
22 #include "flang/Semantics/tools.h"
23 
24 namespace Fortran {
25 namespace lower {
26 namespace omp {
27 bool DataSharingProcessor::OMPConstructSymbolVisitor::isSymbolDefineBy(
28     const semantics::Symbol *symbol, lower::pft::Evaluation &eval) const {
29   return eval.visit(
30       common::visitors{[&](const parser::OpenMPConstruct &functionParserNode) {
31                          return symDefMap.count(symbol) &&
32                                 symDefMap.at(symbol) == &functionParserNode;
33                        },
34                        [](const auto &functionParserNode) { return false; }});
35 }
36 
37 DataSharingProcessor::DataSharingProcessor(
38     lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
39     const List<Clause> &clauses, lower::pft::Evaluation &eval,
40     bool shouldCollectPreDeterminedSymbols, bool useDelayedPrivatization,
41     lower::SymMap &symTable)
42     : converter(converter), semaCtx(semaCtx),
43       firOpBuilder(converter.getFirOpBuilder()), clauses(clauses), eval(eval),
44       shouldCollectPreDeterminedSymbols(shouldCollectPreDeterminedSymbols),
45       useDelayedPrivatization(useDelayedPrivatization), symTable(symTable),
46       visitor() {
47   eval.visit([&](const auto &functionParserNode) {
48     parser::Walk(functionParserNode, visitor);
49   });
50 }
51 
52 void DataSharingProcessor::processStep1(
53     mlir::omp::PrivateClauseOps *clauseOps) {
54   collectSymbolsForPrivatization();
55   collectDefaultSymbols();
56   collectImplicitSymbols();
57   collectPreDeterminedSymbols();
58 
59   privatize(clauseOps);
60 
61   insertBarrier();
62 }
63 
64 void DataSharingProcessor::processStep2(mlir::Operation *op, bool isLoop) {
65   // 'sections' lastprivate is handled by genOMP()
66   if (!mlir::isa<mlir::omp::SectionsOp>(op)) {
67     mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
68     copyLastPrivatize(op);
69   }
70 
71   if (isLoop) {
72     // push deallocs out of the loop
73     firOpBuilder.setInsertionPointAfter(op);
74     insertDeallocs();
75   } else {
76     mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
77     insertDeallocs();
78   }
79 }
80 
81 void DataSharingProcessor::insertDeallocs() {
82   for (const semantics::Symbol *sym : allPrivatizedSymbols)
83     if (semantics::IsAllocatable(sym->GetUltimate())) {
84       if (!useDelayedPrivatization) {
85         converter.createHostAssociateVarCloneDealloc(*sym);
86         continue;
87       }
88 
89       lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym);
90       assert(hsb && "Host symbol box not found");
91       mlir::Type symType = hsb.getAddr().getType();
92       mlir::Location symLoc = hsb.getAddr().getLoc();
93       fir::ExtendedValue symExV = converter.getSymbolExtendedValue(*sym);
94       mlir::omp::PrivateClauseOp privatizer = symToPrivatizer.at(sym);
95 
96       lower::SymMapScope scope(symTable);
97       mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
98 
99       mlir::Region &deallocRegion = privatizer.getDeallocRegion();
100       fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
101       mlir::Block *deallocEntryBlock = firOpBuilder.createBlock(
102           &deallocRegion, /*insertPt=*/{}, symType, symLoc);
103 
104       firOpBuilder.setInsertionPointToEnd(deallocEntryBlock);
105       symTable.addSymbol(*sym,
106                          fir::substBase(symExV, deallocRegion.getArgument(0)));
107 
108       converter.createHostAssociateVarCloneDealloc(*sym);
109       firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc());
110     }
111 }
112 
113 void DataSharingProcessor::cloneSymbol(const semantics::Symbol *sym) {
114   bool isFirstPrivate = sym->test(semantics::Symbol::Flag::OmpFirstPrivate);
115   bool success = converter.createHostAssociateVarClone(
116       *sym, /*skipDefaultInit=*/isFirstPrivate);
117   (void)success;
118   assert(success && "Privatization failed due to existing binding");
119 
120   // Initialize clone from original object if it has any allocatable member.
121   auto needInitClone = [&] {
122     if (isFirstPrivate)
123       return false;
124 
125     SymbolBox sb = symTable.lookupSymbol(sym);
126     assert(sb);
127     mlir::Value addr = sb.getAddr();
128     assert(addr);
129     return !fir::isPointerType(addr.getType()) &&
130            hlfir::mayHaveAllocatableComponent(addr.getType());
131   };
132 
133   if (needInitClone()) {
134     Fortran::lower::initializeCloneAtRuntime(converter, *sym, symTable);
135     callsInitClone = true;
136   }
137 }
138 
139 void DataSharingProcessor::copyFirstPrivateSymbol(
140     const semantics::Symbol *sym, mlir::OpBuilder::InsertPoint *copyAssignIP) {
141   if (sym->test(semantics::Symbol::Flag::OmpFirstPrivate))
142     converter.copyHostAssociateVar(*sym, copyAssignIP);
143 }
144 
145 void DataSharingProcessor::copyLastPrivateSymbol(
146     const semantics::Symbol *sym, mlir::OpBuilder::InsertPoint *lastPrivIP) {
147   if (sym->test(semantics::Symbol::Flag::OmpLastPrivate))
148     converter.copyHostAssociateVar(*sym, lastPrivIP, /*hostIsSource=*/false);
149 }
150 
151 void DataSharingProcessor::collectOmpObjectListSymbol(
152     const omp::ObjectList &objects,
153     llvm::SetVector<const semantics::Symbol *> &symbolSet) {
154   for (const omp::Object &object : objects)
155     symbolSet.insert(object.sym());
156 }
157 
158 void DataSharingProcessor::collectSymbolsForPrivatization() {
159   for (const omp::Clause &clause : clauses) {
160     if (const auto &privateClause =
161             std::get_if<omp::clause::Private>(&clause.u)) {
162       collectOmpObjectListSymbol(privateClause->v, explicitlyPrivatizedSymbols);
163     } else if (const auto &firstPrivateClause =
164                    std::get_if<omp::clause::Firstprivate>(&clause.u)) {
165       collectOmpObjectListSymbol(firstPrivateClause->v,
166                                  explicitlyPrivatizedSymbols);
167     } else if (const auto &lastPrivateClause =
168                    std::get_if<omp::clause::Lastprivate>(&clause.u)) {
169       lastprivateModifierNotSupported(*lastPrivateClause,
170                                       converter.getCurrentLocation());
171       const ObjectList &objects = std::get<ObjectList>(lastPrivateClause->t);
172       collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);
173     }
174   }
175 
176   for (auto *sym : explicitlyPrivatizedSymbols)
177     allPrivatizedSymbols.insert(sym);
178 }
179 
180 bool DataSharingProcessor::needBarrier() {
181   // Emit implicit barrier to synchronize threads and avoid data races on
182   // initialization of firstprivate variables and post-update of lastprivate
183   // variables.
184   // Emit implicit barrier for linear clause. Maybe on somewhere else.
185   for (const semantics::Symbol *sym : allPrivatizedSymbols) {
186     if (sym->test(semantics::Symbol::Flag::OmpLastPrivate) &&
187         (sym->test(semantics::Symbol::Flag::OmpFirstPrivate) || callsInitClone))
188       return true;
189   }
190   return false;
191 }
192 
193 void DataSharingProcessor::insertBarrier() {
194   if (needBarrier())
195     firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
196 }
197 
198 void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
199   mlir::omp::LoopNestOp loopOp;
200   if (auto wrapper = mlir::dyn_cast<mlir::omp::LoopWrapperInterface>(op))
201     loopOp = mlir::cast<mlir::omp::LoopNestOp>(wrapper.getWrappedLoop());
202 
203   bool cmpCreated = false;
204   mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
205   for (const omp::Clause &clause : clauses) {
206     if (clause.id != llvm::omp::OMPC_lastprivate)
207       continue;
208     if (mlir::isa<mlir::omp::WsloopOp>(op) ||
209         mlir::isa<mlir::omp::SimdOp>(op)) {
210       // Update the original variable just before exiting the worksharing
211       // loop. Conversion as follows:
212       //
213       // omp.wsloop / omp.simd {    omp.wsloop / omp.simd {
214       //   omp.loop_nest {            omp.loop_nest {
215       //     ...                        ...
216       //     store          ===>        store
217       //     omp.yield                  %v = arith.addi %iv, %step
218       //   }                            %cmp = %step < 0 ? %v < %ub : %v > %ub
219       // }                              fir.if %cmp {
220       //                                  fir.store %v to %loopIV
221       //                                  ^%lpv_update_blk:
222       //                                }
223       //                                omp.yield
224       //                              }
225       //                            }
226 
227       // Only generate the compare once in presence of multiple LastPrivate
228       // clauses.
229       if (cmpCreated)
230         continue;
231       cmpCreated = true;
232 
233       mlir::Location loc = loopOp.getLoc();
234       mlir::Operation *lastOper = loopOp.getRegion().back().getTerminator();
235       firOpBuilder.setInsertionPoint(lastOper);
236 
237       mlir::Value cmpOp;
238       llvm::SmallVector<mlir::Value> vs;
239       vs.reserve(loopOp.getIVs().size());
240       for (auto [iv, ub, step] :
241            llvm::zip_equal(loopOp.getIVs(), loopOp.getLoopUpperBounds(),
242                            loopOp.getLoopSteps())) {
243         // v = iv + step
244         // cmp = step < 0 ? v < ub : v > ub
245         mlir::Value v = firOpBuilder.create<mlir::arith::AddIOp>(loc, iv, step);
246         vs.push_back(v);
247         mlir::Value zero =
248             firOpBuilder.createIntegerConstant(loc, step.getType(), 0);
249         mlir::Value negativeStep = firOpBuilder.create<mlir::arith::CmpIOp>(
250             loc, mlir::arith::CmpIPredicate::slt, step, zero);
251         mlir::Value vLT = firOpBuilder.create<mlir::arith::CmpIOp>(
252             loc, mlir::arith::CmpIPredicate::slt, v, ub);
253         mlir::Value vGT = firOpBuilder.create<mlir::arith::CmpIOp>(
254             loc, mlir::arith::CmpIPredicate::sgt, v, ub);
255         mlir::Value icmpOp = firOpBuilder.create<mlir::arith::SelectOp>(
256             loc, negativeStep, vLT, vGT);
257 
258         if (cmpOp) {
259           cmpOp = firOpBuilder.create<mlir::arith::AndIOp>(loc, cmpOp, icmpOp);
260         } else {
261           cmpOp = icmpOp;
262         }
263       }
264 
265       auto ifOp = firOpBuilder.create<fir::IfOp>(loc, cmpOp, /*else*/ false);
266       firOpBuilder.setInsertionPointToStart(&ifOp.getThenRegion().front());
267       for (auto [v, loopIV] : llvm::zip_equal(vs, loopIVs)) {
268         assert(loopIV && "loopIV was not set");
269         firOpBuilder.createStoreWithConvert(loc, v, loopIV);
270       }
271       lastPrivIP = firOpBuilder.saveInsertionPoint();
272     } else if (mlir::isa<mlir::omp::SectionsOp>(op)) {
273       // Already handled by genOMP()
274     } else {
275       TODO(converter.getCurrentLocation(),
276            "lastprivate clause in constructs other than "
277            "simd/worksharing-loop");
278     }
279   }
280 }
281 
282 static const parser::CharBlock *
283 getSource(const semantics::SemanticsContext &semaCtx,
284           const lower::pft::Evaluation &eval) {
285   const parser::CharBlock *source = nullptr;
286 
287   auto ompConsVisit = [&](const parser::OpenMPConstruct &x) {
288     std::visit(common::visitors{
289                    [&](const parser::OpenMPSectionsConstruct &x) {
290                      source = &std::get<0>(x.t).source;
291                    },
292                    [&](const parser::OpenMPLoopConstruct &x) {
293                      source = &std::get<0>(x.t).source;
294                    },
295                    [&](const parser::OpenMPBlockConstruct &x) {
296                      source = &std::get<0>(x.t).source;
297                    },
298                    [&](const parser::OpenMPCriticalConstruct &x) {
299                      source = &std::get<0>(x.t).source;
300                    },
301                    [&](const parser::OpenMPAtomicConstruct &x) {
302                      std::visit([&](const auto &x) { source = &x.source; },
303                                 x.u);
304                    },
305                    [&](const auto &x) { source = &x.source; },
306                },
307                x.u);
308   };
309 
310   eval.visit(common::visitors{
311       [&](const parser::OpenMPConstruct &x) { ompConsVisit(x); },
312       [&](const parser::OpenMPDeclarativeConstruct &x) { source = &x.source; },
313       [&](const parser::OmpEndLoopDirective &x) { source = &x.source; },
314       [&](const auto &x) {},
315   });
316 
317   return source;
318 }
319 
320 void DataSharingProcessor::collectSymbolsInNestedRegions(
321     lower::pft::Evaluation &eval, semantics::Symbol::Flag flag,
322     llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions) {
323   for (lower::pft::Evaluation &nestedEval : eval.getNestedEvaluations()) {
324     if (nestedEval.hasNestedEvaluations()) {
325       if (nestedEval.isConstruct())
326         // Recursively look for OpenMP constructs within `nestedEval`'s region
327         collectSymbolsInNestedRegions(nestedEval, flag, symbolsInNestedRegions);
328       else {
329         converter.collectSymbolSet(nestedEval, symbolsInNestedRegions, flag,
330                                    /*collectSymbols=*/true,
331                                    /*collectHostAssociatedSymbols=*/false);
332       }
333     }
334   }
335 }
336 
337 // Collect symbols to be default privatized in two steps.
338 // In step 1, collect all symbols in `eval` that match `flag` into
339 // `defaultSymbols`. In step 2, for nested constructs (if any), if and only if
340 // the nested construct is an OpenMP construct, collect those nested
341 // symbols skipping host associated symbols into `symbolsInNestedRegions`.
342 // Later, in current context, all symbols in the set
343 // `defaultSymbols` - `symbolsInNestedRegions` will be privatized.
344 void DataSharingProcessor::collectSymbols(
345     semantics::Symbol::Flag flag,
346     llvm::SetVector<const semantics::Symbol *> &symbols) {
347   // Collect all scopes associated with 'eval'.
348   llvm::SetVector<const semantics::Scope *> clauseScopes;
349   std::function<void(const semantics::Scope *)> collectScopes =
350       [&](const semantics::Scope *scope) {
351         clauseScopes.insert(scope);
352         for (const semantics::Scope &child : scope->children())
353           collectScopes(&child);
354       };
355   const parser::CharBlock *source =
356       clauses.empty() ? getSource(semaCtx, eval) : &clauses.front().source;
357   const semantics::Scope *curScope = nullptr;
358   if (source && !source->empty()) {
359     curScope = &semaCtx.FindScope(*source);
360     collectScopes(curScope);
361   }
362   // Collect all symbols referenced in the evaluation being processed,
363   // that matches 'flag'.
364   llvm::SetVector<const semantics::Symbol *> allSymbols;
365   converter.collectSymbolSet(eval, allSymbols, flag,
366                              /*collectSymbols=*/true,
367                              /*collectHostAssociatedSymbols=*/true);
368 
369   llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;
370   collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
371 
372   for (auto *symbol : allSymbols)
373     if (visitor.isSymbolDefineBy(symbol, eval))
374       symbolsInNestedRegions.remove(symbol);
375 
376   // Filter-out symbols that must not be privatized.
377   bool collectImplicit = flag == semantics::Symbol::Flag::OmpImplicit;
378   bool collectPreDetermined = flag == semantics::Symbol::Flag::OmpPreDetermined;
379 
380   auto isPrivatizable = [](const semantics::Symbol &sym) -> bool {
381     return !semantics::IsProcedure(sym) &&
382            !sym.GetUltimate().has<semantics::DerivedTypeDetails>() &&
383            !sym.GetUltimate().has<semantics::NamelistDetails>() &&
384            !semantics::IsImpliedDoIndex(sym.GetUltimate()) &&
385            !semantics::IsStmtFunction(sym);
386   };
387 
388   auto shouldCollectSymbol = [&](const semantics::Symbol *sym) {
389     if (collectImplicit)
390       return sym->test(semantics::Symbol::Flag::OmpImplicit);
391 
392     if (collectPreDetermined)
393       return sym->test(semantics::Symbol::Flag::OmpPreDetermined);
394 
395     return !sym->test(semantics::Symbol::Flag::OmpImplicit) &&
396            !sym->test(semantics::Symbol::Flag::OmpPreDetermined);
397   };
398 
399   for (const auto *sym : allSymbols) {
400     assert(curScope && "couldn't find current scope");
401     if (isPrivatizable(*sym) && !symbolsInNestedRegions.contains(sym) &&
402         !explicitlyPrivatizedSymbols.contains(sym) &&
403         shouldCollectSymbol(sym) && clauseScopes.contains(&sym->owner())) {
404       allPrivatizedSymbols.insert(sym);
405       symbols.insert(sym);
406     }
407   }
408 }
409 
410 void DataSharingProcessor::collectDefaultSymbols() {
411   using DataSharingAttribute = omp::clause::Default::DataSharingAttribute;
412   for (const omp::Clause &clause : clauses) {
413     if (const auto *defaultClause =
414             std::get_if<omp::clause::Default>(&clause.u)) {
415       if (defaultClause->v == DataSharingAttribute::Private)
416         collectSymbols(semantics::Symbol::Flag::OmpPrivate, defaultSymbols);
417       else if (defaultClause->v == DataSharingAttribute::Firstprivate)
418         collectSymbols(semantics::Symbol::Flag::OmpFirstPrivate,
419                        defaultSymbols);
420     }
421   }
422 }
423 
424 void DataSharingProcessor::collectImplicitSymbols() {
425   // There will be no implicit symbols when a default clause is present.
426   if (defaultSymbols.empty())
427     collectSymbols(semantics::Symbol::Flag::OmpImplicit, implicitSymbols);
428 }
429 
430 void DataSharingProcessor::collectPreDeterminedSymbols() {
431   if (shouldCollectPreDeterminedSymbols)
432     collectSymbols(semantics::Symbol::Flag::OmpPreDetermined,
433                    preDeterminedSymbols);
434 }
435 
436 void DataSharingProcessor::privatize(mlir::omp::PrivateClauseOps *clauseOps) {
437   for (const semantics::Symbol *sym : allPrivatizedSymbols) {
438     if (const auto *commonDet =
439             sym->detailsIf<semantics::CommonBlockDetails>()) {
440       for (const auto &mem : commonDet->objects())
441         doPrivatize(&*mem, clauseOps);
442     } else
443       doPrivatize(sym, clauseOps);
444   }
445 }
446 
447 void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) {
448   insertLastPrivateCompare(op);
449   for (const semantics::Symbol *sym : allPrivatizedSymbols)
450     if (const auto *commonDet =
451             sym->detailsIf<semantics::CommonBlockDetails>()) {
452       for (const auto &mem : commonDet->objects()) {
453         copyLastPrivateSymbol(&*mem, &lastPrivIP);
454       }
455     } else {
456       copyLastPrivateSymbol(sym, &lastPrivIP);
457     }
458 }
459 
460 void DataSharingProcessor::doPrivatize(const semantics::Symbol *sym,
461                                        mlir::omp::PrivateClauseOps *clauseOps) {
462   if (!useDelayedPrivatization) {
463     cloneSymbol(sym);
464     copyFirstPrivateSymbol(sym);
465     return;
466   }
467 
468   lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym);
469   assert(hsb && "Host symbol box not found");
470 
471   mlir::Type symType = hsb.getAddr().getType();
472   mlir::Location symLoc = hsb.getAddr().getLoc();
473   std::string privatizerName = sym->name().ToString() + ".privatizer";
474   bool isFirstPrivate = sym->test(semantics::Symbol::Flag::OmpFirstPrivate);
475 
476   mlir::omp::PrivateClauseOp privatizerOp = [&]() {
477     auto moduleOp = firOpBuilder.getModule();
478     auto uniquePrivatizerName = fir::getTypeAsString(
479         symType, converter.getKindMap(),
480         converter.mangleName(*sym) +
481             (isFirstPrivate ? "_firstprivate" : "_private"));
482 
483     if (auto existingPrivatizer =
484             moduleOp.lookupSymbol<mlir::omp::PrivateClauseOp>(
485                 uniquePrivatizerName))
486       return existingPrivatizer;
487 
488     mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
489     firOpBuilder.setInsertionPointToStart(moduleOp.getBody());
490     auto result = firOpBuilder.create<mlir::omp::PrivateClauseOp>(
491         symLoc, uniquePrivatizerName, symType,
492         isFirstPrivate ? mlir::omp::DataSharingClauseType::FirstPrivate
493                        : mlir::omp::DataSharingClauseType::Private);
494     fir::ExtendedValue symExV = converter.getSymbolExtendedValue(*sym);
495     lower::SymMapScope outerScope(symTable);
496 
497     // Populate the `alloc` region.
498     {
499       mlir::Region &allocRegion = result.getAllocRegion();
500       mlir::Block *allocEntryBlock = firOpBuilder.createBlock(
501           &allocRegion, /*insertPt=*/{}, symType, symLoc);
502 
503       firOpBuilder.setInsertionPointToEnd(allocEntryBlock);
504 
505       fir::ExtendedValue localExV =
506           hlfir::translateToExtendedValue(
507               symLoc, firOpBuilder, hlfir::Entity{allocRegion.getArgument(0)},
508               /*contiguousHint=*/
509               evaluate::IsSimplyContiguous(*sym, converter.getFoldingContext()))
510               .first;
511 
512       symTable.addSymbol(*sym, localExV);
513       lower::SymMapScope innerScope(symTable);
514       cloneSymbol(sym);
515       mlir::Value cloneAddr = symTable.shallowLookupSymbol(*sym).getAddr();
516       mlir::Type cloneType = cloneAddr.getType();
517 
518       // A `convert` op is required for variables that are storage associated
519       // via `equivalence`. The problem is that these variables are declared as
520       // `fir.ptr`s while their privatized storage is declared as `fir.ref`,
521       // therefore we convert to proper symbol type.
522       mlir::Value yieldedValue =
523           (symType == cloneType) ? cloneAddr
524                                  : firOpBuilder.createConvert(
525                                        cloneAddr.getLoc(), symType, cloneAddr);
526 
527       firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc(),
528                                               yieldedValue);
529     }
530 
531     // Populate the `copy` region if this is a `firstprivate`.
532     if (isFirstPrivate) {
533       mlir::Region &copyRegion = result.getCopyRegion();
534       // First block argument corresponding to the original/host value while
535       // second block argument corresponding to the privatized value.
536       mlir::Block *copyEntryBlock = firOpBuilder.createBlock(
537           &copyRegion, /*insertPt=*/{}, {symType, symType}, {symLoc, symLoc});
538       firOpBuilder.setInsertionPointToEnd(copyEntryBlock);
539 
540       auto addSymbol = [&](unsigned argIdx, bool force = false) {
541         symExV.match(
542             [&](const fir::MutableBoxValue &box) {
543               symTable.addSymbol(
544                   *sym, fir::substBase(box, copyRegion.getArgument(argIdx)),
545                   force);
546             },
547             [&](const auto &box) {
548               symTable.addSymbol(*sym, copyRegion.getArgument(argIdx), force);
549             });
550       };
551 
552       addSymbol(0, true);
553       lower::SymMapScope innerScope(symTable);
554       addSymbol(1);
555 
556       auto ip = firOpBuilder.saveInsertionPoint();
557       copyFirstPrivateSymbol(sym, &ip);
558 
559       firOpBuilder.create<mlir::omp::YieldOp>(
560           hsb.getAddr().getLoc(), symTable.shallowLookupSymbol(*sym).getAddr());
561     }
562 
563     return result;
564   }();
565 
566   if (clauseOps) {
567     clauseOps->privateSyms.push_back(mlir::SymbolRefAttr::get(privatizerOp));
568     clauseOps->privateVars.push_back(hsb.getAddr());
569   }
570 
571   symToPrivatizer[sym] = privatizerOp;
572 }
573 
574 } // namespace omp
575 } // namespace lower
576 } // namespace Fortran
577