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 ©Region = 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 ©Region, /*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