brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · 83c2eda Raw
629 lines · cpp
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-exception6//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/Support/PrivateReductionUtils.h"19#include "flang/Lower/Support/Utils.h"20#include "flang/Lower/SymbolMap.h"21#include "flang/Optimizer/Builder/BoxValue.h"22#include "flang/Optimizer/Builder/HLFIRTools.h"23#include "flang/Optimizer/Builder/Todo.h"24#include "flang/Optimizer/Dialect/FIROps.h"25#include "flang/Optimizer/HLFIR/HLFIRDialect.h"26#include "flang/Optimizer/HLFIR/HLFIROps.h"27#include "flang/Parser/openmp-utils.h"28#include "flang/Semantics/attr.h"29#include "flang/Semantics/openmp-directive-sets.h"30#include "flang/Semantics/tools.h"31#include "llvm/ADT/Sequence.h"32#include "llvm/ADT/SmallSet.h"33#include "llvm/Frontend/OpenMP/OMP.h"34#include <variant>35 36namespace Fortran {37namespace lower {38namespace omp {39bool DataSharingProcessor::OMPConstructSymbolVisitor::isSymbolDefineBy(40    const semantics::Symbol *symbol, lower::pft::Evaluation &eval) const {41  return eval.visit(common::visitors{42      [&](const parser::OpenMPConstruct &functionParserNode) {43        return symDefMap.count(symbol) &&44               symDefMap.at(symbol) == ConstructPtr(&functionParserNode);45      },46      [](const auto &functionParserNode) { return false; }});47}48 49bool DataSharingProcessor::OMPConstructSymbolVisitor::50    isSymbolDefineByNestedDeclaration(const semantics::Symbol *symbol) const {51  return symDefMap.count(symbol) &&52         std::holds_alternative<const parser::DeclarationConstruct *>(53             symDefMap.at(symbol));54}55 56static bool isConstructWithTopLevelTarget(lower::pft::Evaluation &eval) {57  const auto *ompEval = eval.getIf<parser::OpenMPConstruct>();58  if (ompEval) {59    auto dir = parser::omp::GetOmpDirectiveName(*ompEval).v;60    if (llvm::omp::topTargetSet.test(dir))61      return true;62  }63  return false;64}65 66DataSharingProcessor::DataSharingProcessor(67    lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,68    const List<Clause> &clauses, lower::pft::Evaluation &eval,69    bool shouldCollectPreDeterminedSymbols, bool useDelayedPrivatization,70    lower::SymMap &symTable, bool isTargetPrivatization)71    : converter(converter), semaCtx(semaCtx),72      firOpBuilder(converter.getFirOpBuilder()), clauses(clauses), eval(eval),73      shouldCollectPreDeterminedSymbols(shouldCollectPreDeterminedSymbols),74      useDelayedPrivatization(useDelayedPrivatization), symTable(symTable),75      isTargetPrivatization(isTargetPrivatization), visitor(semaCtx) {76  eval.visit([&](const auto &functionParserNode) {77    parser::Walk(functionParserNode, visitor);78  });79}80 81DataSharingProcessor::DataSharingProcessor(lower::AbstractConverter &converter,82                                           semantics::SemanticsContext &semaCtx,83                                           lower::pft::Evaluation &eval,84                                           bool useDelayedPrivatization,85                                           lower::SymMap &symTable,86                                           bool isTargetPrivatization)87    : DataSharingProcessor(converter, semaCtx, {}, eval,88                           /*shouldCollectPreDeterminedSymols=*/false,89                           useDelayedPrivatization, symTable,90                           isTargetPrivatization) {}91 92void DataSharingProcessor::processStep1(93    mlir::omp::PrivateClauseOps *clauseOps,94    std::optional<llvm::omp::Directive> dir) {95  collectSymbolsForPrivatization();96  collectDefaultSymbols();97  collectImplicitSymbols();98  collectPreDeterminedSymbols();99 100  privatize(clauseOps, dir);101 102  insertBarrier(clauseOps);103}104 105void DataSharingProcessor::processStep2(mlir::Operation *op, bool isLoop) {106  // 'sections' lastprivate is handled by genOMP()107  if (mlir::isa<mlir::omp::SectionOp>(op))108    return;109  if (!mlir::isa<mlir::omp::SectionsOp>(op)) {110    mlir::OpBuilder::InsertionGuard guard(firOpBuilder);111    copyLastPrivatize(op);112  }113 114  if (isLoop) {115    // push deallocs out of the loop116    firOpBuilder.setInsertionPointAfter(op);117    insertDeallocs();118  } else {119    mlir::OpBuilder::InsertionGuard guard(firOpBuilder);120    insertDeallocs();121  }122}123 124void DataSharingProcessor::insertDeallocs() {125  for (const semantics::Symbol *sym : allPrivatizedSymbols)126    if (semantics::IsAllocatable(sym->GetUltimate())) {127      if (!useDelayedPrivatization) {128        converter.createHostAssociateVarCloneDealloc(*sym);129        continue;130      }131      // For delayed privatization deallocs are created by132      // populateByRefInitAndCleanupRegions133    }134}135 136void DataSharingProcessor::cloneSymbol(const semantics::Symbol *sym) {137  bool isFirstPrivate = sym->test(semantics::Symbol::Flag::OmpFirstPrivate);138 139  // If we are doing eager-privatization on a symbol created using delayed140  // privatization there could be incompatible types here e.g.141  // fir.ref<fir.box<fir.array<>>>142  bool success = [&]() -> bool {143    const auto *details =144        sym->detailsIf<Fortran::semantics::HostAssocDetails>();145    assert(details && "No host-association found");146    const Fortran::semantics::Symbol &hsym = details->symbol();147    mlir::Value addr = converter.getSymbolAddress(hsym);148 149    if (auto refTy = mlir::dyn_cast<fir::ReferenceType>(addr.getType())) {150      if (auto boxTy = mlir::dyn_cast<fir::BoxType>(refTy.getElementType())) {151        if (auto arrayTy =152                mlir::dyn_cast<fir::SequenceType>(boxTy.getElementType())) {153          // FirConverter/fir::ExtendedValue considers all references to boxes154          // as mutable boxes. Outside of OpenMP it doesn't make sense to have a155          // mutable box of an array. Work around this here by loading the156          // reference so it is a normal boxed array.157          fir::FirOpBuilder &builder = converter.getFirOpBuilder();158          mlir::Location loc = converter.genLocation(hsym.name());159          fir::ExtendedValue hexv = converter.getSymbolExtendedValue(hsym);160 161          llvm::SmallVector<mlir::Value> extents =162              fir::factory::getExtents(loc, builder, hexv);163 164          // TODO: uniqName, name165          mlir::Value allocVal =166              builder.allocateLocal(loc, arrayTy, /*uniqName=*/"",167                                    /*name=*/"", extents, /*typeParams=*/{},168                                    sym->GetUltimate().attrs().test(169                                        Fortran::semantics::Attr::TARGET));170          mlir::Value shape = builder.genShape(loc, extents);171          mlir::Value box = builder.createBox(loc, boxTy, allocVal, shape,172                                              nullptr, {}, nullptr);173 174          // This can't be a CharArrayBoxValue because otherwise175          // boxTy.getElementType() would be a character type.176          // Assume the array element type isn't polymorphic because we are177          // privatizing.178          fir::ExtendedValue newExv = fir::ArrayBoxValue{box, extents};179 180          converter.bindSymbol(*sym, newExv);181          return true;182        }183      }184    }185 186    // Normal case:187    return converter.createHostAssociateVarClone(188        *sym, /*skipDefaultInit=*/isFirstPrivate);189  }();190  (void)success;191  assert(success && "Privatization failed due to existing binding");192 193  // Initialize clone from original object if it has any allocatable member.194  auto needInitClone = [&] {195    if (isFirstPrivate)196      return false;197 198    SymbolBox sb = symTable.lookupSymbol(sym);199    assert(sb);200    mlir::Value addr = sb.getAddr();201    assert(addr);202    return !fir::isPointerType(addr.getType()) &&203           hlfir::mayHaveAllocatableComponent(addr.getType());204  };205 206  if (needInitClone()) {207    Fortran::lower::initializeCloneAtRuntime(converter, *sym, symTable);208    mightHaveReadHostSym.insert(sym);209  }210}211 212void DataSharingProcessor::copyFirstPrivateSymbol(213    const semantics::Symbol *sym, mlir::OpBuilder::InsertPoint *copyAssignIP) {214  if (sym->test(semantics::Symbol::Flag::OmpFirstPrivate) ||215      sym->test(semantics::Symbol::Flag::LocalityLocalInit))216    converter.copyHostAssociateVar(*sym, copyAssignIP);217}218 219void DataSharingProcessor::copyLastPrivateSymbol(220    const semantics::Symbol *sym, mlir::OpBuilder::InsertPoint *lastPrivIP) {221  if (sym->test(semantics::Symbol::Flag::OmpLastPrivate))222    converter.copyHostAssociateVar(*sym, lastPrivIP, /*hostIsSource=*/false);223}224 225void DataSharingProcessor::collectOmpObjectListSymbol(226    const omp::ObjectList &objects,227    llvm::SetVector<const semantics::Symbol *> &symbolSet) {228  for (const omp::Object &object : objects)229    symbolSet.insert(object.sym());230}231 232void DataSharingProcessor::collectSymbolsForPrivatization() {233  // Add checks here for exceptional cases where privatization is not234  // needed and be deferred to a later phase (like OpenMP IRBuilder).235  // Such cases are suggested to be clearly documented and explained236  // instead of being silently skipped237  auto isException = [&](const Fortran::semantics::Symbol *sym) -> bool {238    // `OmpPreDetermined` symbols cannot be exceptions since239    // their privatized symbols are heavily used in FIR.240    if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))241      return false;242 243    // The handling of linear clause is deferred to the OpenMP244    // IRBuilder which is responsible for all its aspects,245    // including privatization. Privatizing linear variables at this point would246    // cause the following structure:247    //248    // omp.op linear(%linear = %step : !fir.ref<type>) {249    //	Use %linear in this BB250    // }251    //252    // to be changed to the following:253    //254    // omp. op linear(%linear = %step : !fir.ref<type>)255    // 	private(%linear -> %arg0 : !fir.ref<i32>) {256    //	Declare and use %arg0 in this BB257    // }258    //259    // The OpenMP IRBuilder needs to map the linear MLIR value260    // (i.e. %linear) to its `uses` in the BB to correctly261    // implement the functionalities of linear clause. However,262    // privatizing here disallows the IRBuilder to263    // draw a relation between %linear and %arg0. Hence skip.264    if (sym->test(Fortran::semantics::Symbol::Flag::OmpLinear))265      return true;266    return false;267  };268 269  for (const omp::Clause &clause : clauses) {270    if (const auto &privateClause =271            std::get_if<omp::clause::Private>(&clause.u)) {272      collectOmpObjectListSymbol(privateClause->v, explicitlyPrivatizedSymbols);273    } else if (const auto &firstPrivateClause =274                   std::get_if<omp::clause::Firstprivate>(&clause.u)) {275      collectOmpObjectListSymbol(firstPrivateClause->v,276                                 explicitlyPrivatizedSymbols);277    } else if (const auto &lastPrivateClause =278                   std::get_if<omp::clause::Lastprivate>(&clause.u)) {279      lastprivateModifierNotSupported(*lastPrivateClause,280                                      converter.getCurrentLocation());281      const ObjectList &objects = std::get<ObjectList>(lastPrivateClause->t);282      collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);283    }284  }285 286  // TODO For common blocks, add the underlying objects within the block. Doing287  // so, we won't need to explicitly handle block objects (or forget to do288  // so).289  for (auto *sym : explicitlyPrivatizedSymbols)290    if (!isException(sym))291      allPrivatizedSymbols.insert(sym);292}293 294bool DataSharingProcessor::needBarrier() {295  // Emit implicit barrier to synchronize threads and avoid data races on296  // initialization of firstprivate variables and post-update of lastprivate297  // variables.298  // Emit implicit barrier for linear clause in the OpenMPIRBuilder.299  for (const semantics::Symbol *sym : allPrivatizedSymbols) {300    if (sym->test(semantics::Symbol::Flag::OmpLastPrivate) &&301        (sym->test(semantics::Symbol::Flag::OmpFirstPrivate) ||302         mightHaveReadHostSym.contains(sym)))303      return true;304  }305  return false;306}307 308void DataSharingProcessor::insertBarrier(309    mlir::omp::PrivateClauseOps *clauseOps) {310  if (!needBarrier())311    return;312 313  if (useDelayedPrivatization) {314    if (clauseOps)315      clauseOps->privateNeedsBarrier =316          mlir::UnitAttr::get(&converter.getMLIRContext());317  } else {318    mlir::omp::BarrierOp::create(firOpBuilder, converter.getCurrentLocation());319  }320}321 322void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {323  mlir::omp::LoopNestOp loopOp;324  if (auto wrapper = mlir::dyn_cast<mlir::omp::LoopWrapperInterface>(op))325    loopOp = mlir::cast<mlir::omp::LoopNestOp>(wrapper.getWrappedLoop());326 327  mlir::OpBuilder::InsertionGuard guard(firOpBuilder);328  bool hasLastPrivate = [&]() {329    for (const semantics::Symbol *sym : allPrivatizedSymbols) {330      if (const auto *commonDet =331              sym->detailsIf<semantics::CommonBlockDetails>()) {332        for (const auto &mem : commonDet->objects())333          if (mem->test(semantics::Symbol::Flag::OmpLastPrivate))334            return true;335      } else if (sym->test(semantics::Symbol::Flag::OmpLastPrivate))336        return true;337    }338 339    return false;340  }();341 342  if (!hasLastPrivate)343    return;344 345  if (mlir::isa<mlir::omp::WsloopOp>(op) || mlir::isa<mlir::omp::SimdOp>(op) ||346      mlir::isa<mlir::omp::TaskloopOp>(op)) {347    mlir::omp::LoopRelatedClauseOps result;348    llvm::SmallVector<const semantics::Symbol *> iv;349    collectLoopRelatedInfo(converter, converter.getCurrentLocation(), eval,350                           clauses, result, iv);351 352    // Update the original variable just before exiting the worksharing353    // loop. Conversion as follows:354    //355    // omp.wsloop / omp.simd {    omp.wsloop / omp.simd {356    //   omp.loop_nest {            omp.loop_nest {357    //     ...                        ...358    //     store          ===>        store359    //     omp.yield                  %v = arith.addi %iv, %step360    //   }                            %cmp = %step < 0 ? %v < %ub : %v > %ub361    // }                              fir.if %cmp {362    //                                  fir.store %v to %loopIV363    //                                  ^%lpv_update_blk:364    //                                }365    //                                omp.yield366    //                              }367    //                            }368    mlir::Location loc = loopOp.getLoc();369    mlir::Operation *lastOper = loopOp.getRegion().back().getTerminator();370    firOpBuilder.setInsertionPoint(lastOper);371 372    mlir::Value cmpOp;373    llvm::SmallVector<mlir::Value> vs;374    vs.reserve(loopOp.getIVs().size());375    for (auto [iv, ub, step] : llvm::zip_equal(376             loopOp.getIVs(), result.loopUpperBounds, result.loopSteps)) {377      // v = iv + step378      // cmp = step < 0 ? v < ub : v > ub379      mlir::Value v = mlir::arith::AddIOp::create(firOpBuilder, loc, iv, step);380      vs.push_back(v);381      mlir::Value zero =382          firOpBuilder.createIntegerConstant(loc, step.getType(), 0);383      mlir::Value negativeStep = mlir::arith::CmpIOp::create(384          firOpBuilder, loc, mlir::arith::CmpIPredicate::slt, step, zero);385      mlir::Value vLT = mlir::arith::CmpIOp::create(386          firOpBuilder, loc, mlir::arith::CmpIPredicate::slt, v, ub);387      mlir::Value vGT = mlir::arith::CmpIOp::create(388          firOpBuilder, loc, mlir::arith::CmpIPredicate::sgt, v, ub);389      mlir::Value icmpOp = mlir::arith::SelectOp::create(390          firOpBuilder, loc, negativeStep, vLT, vGT);391 392      if (cmpOp)393        cmpOp = mlir::arith::AndIOp::create(firOpBuilder, loc, cmpOp, icmpOp);394      else395        cmpOp = icmpOp;396    }397 398    auto ifOp = fir::IfOp::create(firOpBuilder, loc, cmpOp, /*else*/ false);399    firOpBuilder.setInsertionPointToStart(&ifOp.getThenRegion().front());400    for (auto [v, loopIV] : llvm::zip_equal(vs, loopIVs)) {401      hlfir::Entity loopIVEntity{loopIV};402      loopIVEntity =403          hlfir::derefPointersAndAllocatables(loc, firOpBuilder, loopIVEntity);404      hlfir::AssignOp::create(firOpBuilder, loc, v, loopIVEntity);405    }406    lastPrivIP = firOpBuilder.saveInsertionPoint();407  } else if (mlir::isa<mlir::omp::SectionsOp>(op)) {408    // Already handled by genOMP()409  } else {410    TODO(converter.getCurrentLocation(),411         "lastprivate clause in constructs other than "412         "simd/worksharing-loop/taskloop");413  }414}415 416static parser::CharBlock getSource(const semantics::SemanticsContext &semaCtx,417                                   const lower::pft::Evaluation &eval) {418  return eval.visit(common::visitors{419      [&](const parser::OpenMPConstruct &x) {420        return parser::omp::GetOmpDirectiveName(x).source;421      },422      [&](const parser::OpenMPDeclarativeConstruct &x) { return x.source; },423      [&](const parser::OmpEndLoopDirective &x) { return x.source; },424      [&](const auto &x) { return parser::CharBlock{}; },425  });426}427 428bool DataSharingProcessor::isOpenMPPrivatizingConstruct(429    const parser::OpenMPConstruct &omp, unsigned version) {430  return llvm::omp::isPrivatizingConstruct(431      parser::omp::GetOmpDirectiveName(omp).v, version);432}433 434bool DataSharingProcessor::isOpenMPPrivatizingEvaluation(435    const pft::Evaluation &eval) const {436  unsigned version = semaCtx.langOptions().OpenMPVersion;437  return eval.visit([=](auto &&s) {438    using BareS = llvm::remove_cvref_t<decltype(s)>;439    if constexpr (std::is_same_v<BareS, parser::OpenMPConstruct>) {440      return isOpenMPPrivatizingConstruct(s, version);441    } else {442      return false;443    }444  });445}446 447void DataSharingProcessor::collectSymbolsInNestedRegions(448    lower::pft::Evaluation &eval, semantics::Symbol::Flag flag,449    llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions) {450  if (!eval.hasNestedEvaluations())451    return;452  for (pft::Evaluation &nestedEval : eval.getNestedEvaluations()) {453    if (isOpenMPPrivatizingEvaluation(nestedEval)) {454      converter.collectSymbolSet(nestedEval, symbolsInNestedRegions, flag,455                                 /*collectSymbols=*/true,456                                 /*collectHostAssociatedSymbols=*/false);457    } else {458      // Recursively look for OpenMP constructs within `nestedEval`'s region459      collectSymbolsInNestedRegions(nestedEval, flag, symbolsInNestedRegions);460    }461  }462}463 464// Collect symbols to be default privatized in two steps.465// In step 1, collect all symbols in `eval` that match `flag` into466// `defaultSymbols`. In step 2, for nested constructs (if any), if and only if467// the nested construct is an OpenMP construct, collect those nested468// symbols skipping host associated symbols into `symbolsInNestedRegions`.469// Later, in current context, all symbols in the set470// `defaultSymbols` - `symbolsInNestedRegions` will be privatized.471void DataSharingProcessor::collectSymbols(472    semantics::Symbol::Flag flag,473    llvm::SetVector<const semantics::Symbol *> &symbols) {474  // Collect all scopes associated with 'eval'.475  llvm::SetVector<const semantics::Scope *> clauseScopes;476  std::function<void(const semantics::Scope *)> collectScopes =477      [&](const semantics::Scope *scope) {478        clauseScopes.insert(scope);479        for (const semantics::Scope &child : scope->children())480          collectScopes(&child);481      };482  parser::CharBlock source =483      clauses.empty() ? getSource(semaCtx, eval) : clauses.front().source;484  const semantics::Scope *curScope = nullptr;485  if (!source.empty()) {486    curScope = &semaCtx.FindScope(source);487    collectScopes(curScope);488  }489  // Collect all symbols referenced in the evaluation being processed,490  // that matches 'flag'.491  llvm::SetVector<const semantics::Symbol *> allSymbols;492  converter.collectSymbolSet(eval, allSymbols, flag,493                             /*collectSymbols=*/true,494                             /*collectHostAssociatedSymbols=*/true);495 496  llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;497  collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);498 499  for (auto *symbol : allSymbols)500    if (visitor.isSymbolDefineBy(symbol, eval))501      symbolsInNestedRegions.remove(symbol);502 503  // Filter-out symbols that must not be privatized.504  bool collectImplicit = flag == semantics::Symbol::Flag::OmpImplicit;505  bool collectPreDetermined = flag == semantics::Symbol::Flag::OmpPreDetermined;506 507  auto isPrivatizable = [](const semantics::Symbol &sym) -> bool {508    return !semantics::IsProcedure(sym) &&509           !sym.GetUltimate().has<semantics::DerivedTypeDetails>() &&510           !sym.GetUltimate().has<semantics::NamelistDetails>() &&511           !semantics::IsImpliedDoIndex(sym.GetUltimate()) &&512           !semantics::IsStmtFunction(sym);513  };514 515  auto shouldCollectSymbol = [&](const semantics::Symbol *sym) {516    if (collectImplicit) {517      // If we're a combined construct with a target region, implicit518      // firstprivate captures, should only belong to the target region519      // and not be added/captured by later directives. Parallel regions520      // will likely want the same captures to be shared and for SIMD it's521      // illegal to have firstprivate clauses.522      if (isConstructWithTopLevelTarget(eval) && !isTargetPrivatization &&523          sym->test(semantics::Symbol::Flag::OmpFirstPrivate)) {524        return false;525      }526 527      // Collect implicit symbols only if they are not defined by a nested528      // `DeclarationConstruct`. If `sym` is not defined by the current OpenMP529      // evaluation then it is defined by a block nested within the OpenMP530      // construct. This, in turn, means that the private allocation for the531      // symbol will be emitted as part of the nested block and there is no need532      // to privatize it within the OpenMP construct.533      return !visitor.isSymbolDefineByNestedDeclaration(sym) &&534             sym->test(semantics::Symbol::Flag::OmpImplicit);535    }536 537    if (collectPreDetermined) {538      // Similar to implicit symbols, collect pre-determined symbols only if539      // they are not defined by a nested `DeclarationConstruct`540      return visitor.isSymbolDefineBy(sym, eval) &&541             !visitor.isSymbolDefineByNestedDeclaration(sym) &&542             sym->test(semantics::Symbol::Flag::OmpPreDetermined);543    }544 545    return !sym->test(semantics::Symbol::Flag::OmpImplicit) &&546           !sym->test(semantics::Symbol::Flag::OmpPreDetermined);547  };548 549  for (const auto *sym : allSymbols) {550    assert(curScope && "couldn't find current scope");551    if (isPrivatizable(*sym) && !symbolsInNestedRegions.contains(sym) &&552        !explicitlyPrivatizedSymbols.contains(sym) &&553        shouldCollectSymbol(sym) && clauseScopes.contains(&sym->owner())) {554      allPrivatizedSymbols.insert(sym);555      symbols.insert(sym);556    }557  }558}559 560void DataSharingProcessor::collectDefaultSymbols() {561  using DataSharingAttribute = omp::clause::Default::DataSharingAttribute;562  for (const omp::Clause &clause : clauses) {563    if (const auto *defaultClause =564            std::get_if<omp::clause::Default>(&clause.u)) {565      if (defaultClause->v == DataSharingAttribute::Private)566        collectSymbols(semantics::Symbol::Flag::OmpPrivate, defaultSymbols);567      else if (defaultClause->v == DataSharingAttribute::Firstprivate)568        collectSymbols(semantics::Symbol::Flag::OmpFirstPrivate,569                       defaultSymbols);570    }571  }572}573 574void DataSharingProcessor::collectImplicitSymbols() {575  // There will be no implicit symbols when a default clause is present.576  if (defaultSymbols.empty())577    collectSymbols(semantics::Symbol::Flag::OmpImplicit, implicitSymbols);578}579 580void DataSharingProcessor::collectPreDeterminedSymbols() {581  if (shouldCollectPreDeterminedSymbols)582    collectSymbols(semantics::Symbol::Flag::OmpPreDetermined,583                   preDeterminedSymbols);584}585 586void DataSharingProcessor::privatize(mlir::omp::PrivateClauseOps *clauseOps,587                                     std::optional<llvm::omp::Directive> dir) {588  for (const semantics::Symbol *sym : allPrivatizedSymbols) {589    if (const auto *commonDet =590            sym->detailsIf<semantics::CommonBlockDetails>()) {591      for (const auto &mem : commonDet->objects())592        privatizeSymbol(&*mem, clauseOps, dir);593    } else594      privatizeSymbol(sym, clauseOps, dir);595  }596}597 598void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) {599  insertLastPrivateCompare(op);600  for (const semantics::Symbol *sym : allPrivatizedSymbols)601    if (const auto *commonDet =602            sym->detailsIf<semantics::CommonBlockDetails>()) {603      for (const auto &mem : commonDet->objects()) {604        copyLastPrivateSymbol(&*mem, &lastPrivIP);605      }606    } else {607      copyLastPrivateSymbol(sym, &lastPrivIP);608    }609}610 611void DataSharingProcessor::privatizeSymbol(612    const semantics::Symbol *symToPrivatize,613    mlir::omp::PrivateClauseOps *clauseOps,614    std::optional<llvm::omp::Directive> dir) {615  if (!useDelayedPrivatization) {616    cloneSymbol(symToPrivatize);617    copyFirstPrivateSymbol(symToPrivatize);618    return;619  }620 621  Fortran::lower::privatizeSymbol<mlir::omp::PrivateClauseOp,622                                  mlir::omp::PrivateClauseOps>(623      converter, firOpBuilder, symTable, allPrivatizedSymbols,624      mightHaveReadHostSym, symToPrivatize, clauseOps, dir);625}626} // namespace omp627} // namespace lower628} // namespace Fortran629