//===-- ClauseProcessor.cpp -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ // //===----------------------------------------------------------------------===// #include "ClauseProcessor.h" #include "Clauses.h" #include "flang/Lower/PFTBuilder.h" #include "flang/Parser/tools.h" #include "flang/Semantics/tools.h" namespace Fortran { namespace lower { namespace omp { /// Check for unsupported map operand types. static void checkMapType(mlir::Location location, mlir::Type type) { if (auto refType = type.dyn_cast()) type = refType.getElementType(); if (auto boxType = type.dyn_cast_or_null()) if (!boxType.getElementType().isa()) TODO(location, "OMPD_target_data MapOperand BoxType"); } static mlir::omp::ScheduleModifier translateScheduleModifier(const omp::clause::Schedule::ModType &m) { switch (m) { case omp::clause::Schedule::ModType::Monotonic: return mlir::omp::ScheduleModifier::monotonic; case omp::clause::Schedule::ModType::Nonmonotonic: return mlir::omp::ScheduleModifier::nonmonotonic; case omp::clause::Schedule::ModType::Simd: return mlir::omp::ScheduleModifier::simd; } return mlir::omp::ScheduleModifier::none; } static mlir::omp::ScheduleModifier getScheduleModifier(const omp::clause::Schedule &clause) { using ScheduleModifier = omp::clause::Schedule::ScheduleModifier; const auto &modifier = std::get>(clause.t); // The input may have the modifier any order, so we look for one that isn't // SIMD. If modifier is not set at all, fall down to the bottom and return // "none". if (modifier) { using ModType = omp::clause::Schedule::ModType; const auto &modType1 = std::get(modifier->t); if (modType1 == ModType::Simd) { const auto &modType2 = std::get>(modifier->t); if (modType2 && *modType2 != ModType::Simd) return translateScheduleModifier(*modType2); return mlir::omp::ScheduleModifier::none; } return translateScheduleModifier(modType1); } return mlir::omp::ScheduleModifier::none; } static mlir::omp::ScheduleModifier getSimdModifier(const omp::clause::Schedule &clause) { using ScheduleModifier = omp::clause::Schedule::ScheduleModifier; const auto &modifier = std::get>(clause.t); // Either of the two possible modifiers in the input can be the SIMD modifier, // so look in either one, and return simd if we find one. Not found = return // "none". if (modifier) { using ModType = omp::clause::Schedule::ModType; const auto &modType1 = std::get(modifier->t); if (modType1 == ModType::Simd) return mlir::omp::ScheduleModifier::simd; const auto &modType2 = std::get>(modifier->t); if (modType2 && *modType2 == ModType::Simd) return mlir::omp::ScheduleModifier::simd; } return mlir::omp::ScheduleModifier::none; } static void genAllocateClause(Fortran::lower::AbstractConverter &converter, const omp::clause::Allocate &clause, llvm::SmallVectorImpl &allocatorOperands, llvm::SmallVectorImpl &allocateOperands) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::Location currentLocation = converter.getCurrentLocation(); Fortran::lower::StatementContext stmtCtx; const omp::ObjectList &objectList = std::get(clause.t); const auto &modifier = std::get>(clause.t); // If the allocate modifier is present, check if we only use the allocator // submodifier. ALIGN in this context is unimplemented const bool onlyAllocator = modifier && std::holds_alternative( modifier->u); if (modifier && !onlyAllocator) { TODO(currentLocation, "OmpAllocateClause ALIGN modifier"); } // Check if allocate clause has allocator specified. If so, add it // to list of allocators, otherwise, add default allocator to // list of allocators. if (onlyAllocator) { const auto &value = std::get(modifier->u); mlir::Value operand = fir::getBase(converter.genExprValue(value.v, stmtCtx)); allocatorOperands.append(objectList.size(), operand); } else { mlir::Value operand = firOpBuilder.createIntegerConstant( currentLocation, firOpBuilder.getI32Type(), 1); allocatorOperands.append(objectList.size(), operand); } genObjectList(objectList, converter, allocateOperands); } static mlir::omp::ClauseProcBindKindAttr genProcBindKindAttr(fir::FirOpBuilder &firOpBuilder, const omp::clause::ProcBind &clause) { mlir::omp::ClauseProcBindKind procBindKind; switch (clause.v) { case omp::clause::ProcBind::Type::Master: procBindKind = mlir::omp::ClauseProcBindKind::Master; break; case omp::clause::ProcBind::Type::Close: procBindKind = mlir::omp::ClauseProcBindKind::Close; break; case omp::clause::ProcBind::Type::Spread: procBindKind = mlir::omp::ClauseProcBindKind::Spread; break; case omp::clause::ProcBind::Type::Primary: procBindKind = mlir::omp::ClauseProcBindKind::Primary; break; } return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), procBindKind); } static mlir::omp::ClauseTaskDependAttr genDependKindAttr(fir::FirOpBuilder &firOpBuilder, const omp::clause::Depend &clause) { mlir::omp::ClauseTaskDepend pbKind; const auto &inOut = std::get(clause.u); switch (std::get(inOut.t)) { case omp::clause::Depend::Type::In: pbKind = mlir::omp::ClauseTaskDepend::taskdependin; break; case omp::clause::Depend::Type::Out: pbKind = mlir::omp::ClauseTaskDepend::taskdependout; break; case omp::clause::Depend::Type::Inout: pbKind = mlir::omp::ClauseTaskDepend::taskdependinout; break; default: llvm_unreachable("unknown parser task dependence type"); break; } return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(), pbKind); } static mlir::Value getIfClauseOperand(Fortran::lower::AbstractConverter &converter, const omp::clause::If &clause, omp::clause::If::DirectiveNameModifier directiveName, mlir::Location clauseLocation) { // Only consider the clause if it's intended for the given directive. auto &directive = std::get>(clause.t); if (directive && directive.value() != directiveName) return nullptr; Fortran::lower::StatementContext stmtCtx; fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::Value ifVal = fir::getBase( converter.genExprValue(std::get(clause.t), stmtCtx)); return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(), ifVal); } static void addUseDeviceClause(Fortran::lower::AbstractConverter &converter, const omp::ObjectList &objects, llvm::SmallVectorImpl &operands, llvm::SmallVectorImpl &useDeviceTypes, llvm::SmallVectorImpl &useDeviceLocs, llvm::SmallVectorImpl &useDeviceSymbols) { genObjectList(objects, converter, operands); for (mlir::Value &operand : operands) { checkMapType(operand.getLoc(), operand.getType()); useDeviceTypes.push_back(operand.getType()); useDeviceLocs.push_back(operand.getLoc()); } for (const omp::Object &object : objects) useDeviceSymbols.push_back(object.id()); } //===----------------------------------------------------------------------===// // ClauseProcessor unique clauses //===----------------------------------------------------------------------===// bool ClauseProcessor::processCollapse( mlir::Location currentLocation, Fortran::lower::pft::Evaluation &eval, llvm::SmallVectorImpl &lowerBound, llvm::SmallVectorImpl &upperBound, llvm::SmallVectorImpl &step, llvm::SmallVectorImpl &iv, std::size_t &loopVarTypeSize) const { bool found = false; fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); // Collect the loops to collapse. Fortran::lower::pft::Evaluation *doConstructEval = &eval.getFirstNestedEvaluation(); if (doConstructEval->getIf() ->IsDoConcurrent()) { TODO(currentLocation, "Do Concurrent in Worksharing loop construct"); } std::int64_t collapseValue = 1l; if (auto *clause = findUniqueClause()) { collapseValue = Fortran::evaluate::ToInt64(clause->v).value(); found = true; } loopVarTypeSize = 0; do { Fortran::lower::pft::Evaluation *doLoop = &doConstructEval->getFirstNestedEvaluation(); auto *doStmt = doLoop->getIf(); assert(doStmt && "Expected do loop to be in the nested evaluation"); const auto &loopControl = std::get>(doStmt->t); const Fortran::parser::LoopControl::Bounds *bounds = std::get_if(&loopControl->u); assert(bounds && "Expected bounds for worksharing do loop"); Fortran::lower::StatementContext stmtCtx; lowerBound.push_back(fir::getBase(converter.genExprValue( *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); upperBound.push_back(fir::getBase(converter.genExprValue( *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); if (bounds->step) { step.push_back(fir::getBase(converter.genExprValue( *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); } else { // If `step` is not present, assume it as `1`. step.push_back(firOpBuilder.createIntegerConstant( currentLocation, firOpBuilder.getIntegerType(32), 1)); } iv.push_back(bounds->name.thing.symbol); loopVarTypeSize = std::max(loopVarTypeSize, bounds->name.thing.symbol->GetUltimate().size()); collapseValue--; doConstructEval = &*std::next(doConstructEval->getNestedEvaluations().begin()); } while (collapseValue > 0); return found; } bool ClauseProcessor::processDefault() const { if (auto *clause = findUniqueClause()) { // Private, Firstprivate, Shared, None switch (clause->v) { case omp::clause::Default::Type::Shared: case omp::clause::Default::Type::None: // Default clause with shared or none do not require any handling since // Shared is the default behavior in the IR and None is only required // for semantic checks. break; case omp::clause::Default::Type::Private: // TODO Support default(private) break; case omp::clause::Default::Type::Firstprivate: // TODO Support default(firstprivate) break; } return true; } return false; } bool ClauseProcessor::processDevice(Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { const Fortran::parser::CharBlock *source = nullptr; if (auto *clause = findUniqueClause(&source)) { mlir::Location clauseLocation = converter.genLocation(*source); if (auto deviceModifier = std::get>( clause->t)) { if (deviceModifier == omp::clause::Device::DeviceModifier::Ancestor) { TODO(clauseLocation, "OMPD_target Device Modifier Ancestor"); } } const auto &deviceExpr = std::get(clause->t); result = fir::getBase(converter.genExprValue(deviceExpr, stmtCtx)); return true; } return false; } bool ClauseProcessor::processDeviceType( mlir::omp::DeclareTargetDeviceType &result) const { if (auto *clause = findUniqueClause()) { // Case: declare target ... device_type(any | host | nohost) switch (clause->v) { case omp::clause::DeviceType::Type::Nohost: result = mlir::omp::DeclareTargetDeviceType::nohost; break; case omp::clause::DeviceType::Type::Host: result = mlir::omp::DeclareTargetDeviceType::host; break; case omp::clause::DeviceType::Type::Any: result = mlir::omp::DeclareTargetDeviceType::any; break; } return true; } return false; } bool ClauseProcessor::processFinal(Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { const Fortran::parser::CharBlock *source = nullptr; if (auto *clause = findUniqueClause(&source)) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::Location clauseLocation = converter.genLocation(*source); mlir::Value finalVal = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); result = firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(), finalVal); return true; } return false; } bool ClauseProcessor::processHint(mlir::IntegerAttr &result) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); int64_t hintValue = *Fortran::evaluate::ToInt64(clause->v); result = firOpBuilder.getI64IntegerAttr(hintValue); return true; } return false; } bool ClauseProcessor::processMergeable(mlir::UnitAttr &result) const { return markClauseOccurrence(result); } bool ClauseProcessor::processNowait(mlir::UnitAttr &result) const { return markClauseOccurrence(result); } bool ClauseProcessor::processNumTeams(Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { // TODO Get lower and upper bounds for num_teams when parser is updated to // accept both. if (auto *clause = findUniqueClause()) { result = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); return true; } return false; } bool ClauseProcessor::processNumThreads( Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { if (auto *clause = findUniqueClause()) { // OMPIRBuilder expects `NUM_THREADS` clause as a `Value`. result = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); return true; } return false; } bool ClauseProcessor::processOrdered(mlir::IntegerAttr &result) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); int64_t orderedClauseValue = 0l; if (clause->v.has_value()) orderedClauseValue = *Fortran::evaluate::ToInt64(*clause->v); result = firOpBuilder.getI64IntegerAttr(orderedClauseValue); return true; } return false; } bool ClauseProcessor::processPriority(Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { if (auto *clause = findUniqueClause()) { result = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); return true; } return false; } bool ClauseProcessor::processProcBind( mlir::omp::ClauseProcBindKindAttr &result) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); result = genProcBindKindAttr(firOpBuilder, *clause); return true; } return false; } bool ClauseProcessor::processSafelen(mlir::IntegerAttr &result) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); const std::optional safelenVal = Fortran::evaluate::ToInt64(clause->v); result = firOpBuilder.getI64IntegerAttr(*safelenVal); return true; } return false; } bool ClauseProcessor::processSchedule( mlir::omp::ClauseScheduleKindAttr &valAttr, mlir::omp::ScheduleModifierAttr &modifierAttr, mlir::UnitAttr &simdModifierAttr) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::MLIRContext *context = firOpBuilder.getContext(); const auto &scheduleType = std::get(clause->t); mlir::omp::ClauseScheduleKind scheduleKind; switch (scheduleType) { case omp::clause::Schedule::ScheduleType::Static: scheduleKind = mlir::omp::ClauseScheduleKind::Static; break; case omp::clause::Schedule::ScheduleType::Dynamic: scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic; break; case omp::clause::Schedule::ScheduleType::Guided: scheduleKind = mlir::omp::ClauseScheduleKind::Guided; break; case omp::clause::Schedule::ScheduleType::Auto: scheduleKind = mlir::omp::ClauseScheduleKind::Auto; break; case omp::clause::Schedule::ScheduleType::Runtime: scheduleKind = mlir::omp::ClauseScheduleKind::Runtime; break; } mlir::omp::ScheduleModifier scheduleModifier = getScheduleModifier(*clause); if (scheduleModifier != mlir::omp::ScheduleModifier::none) modifierAttr = mlir::omp::ScheduleModifierAttr::get(context, scheduleModifier); if (getSimdModifier(*clause) != mlir::omp::ScheduleModifier::none) simdModifierAttr = firOpBuilder.getUnitAttr(); valAttr = mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind); return true; } return false; } bool ClauseProcessor::processScheduleChunk( Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { if (auto *clause = findUniqueClause()) { if (const auto &chunkExpr = std::get(clause->t)) result = fir::getBase(converter.genExprValue(*chunkExpr, stmtCtx)); return true; } return false; } bool ClauseProcessor::processSimdlen(mlir::IntegerAttr &result) const { if (auto *clause = findUniqueClause()) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); const std::optional simdlenVal = Fortran::evaluate::ToInt64(clause->v); result = firOpBuilder.getI64IntegerAttr(*simdlenVal); return true; } return false; } bool ClauseProcessor::processThreadLimit( Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const { if (auto *clause = findUniqueClause()) { result = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); return true; } return false; } bool ClauseProcessor::processUntied(mlir::UnitAttr &result) const { return markClauseOccurrence(result); } //===----------------------------------------------------------------------===// // ClauseProcessor repeatable clauses //===----------------------------------------------------------------------===// bool ClauseProcessor::processAllocate( llvm::SmallVectorImpl &allocatorOperands, llvm::SmallVectorImpl &allocateOperands) const { return findRepeatableClause( [&](const omp::clause::Allocate &clause, const Fortran::parser::CharBlock &) { genAllocateClause(converter, clause, allocatorOperands, allocateOperands); }); } bool ClauseProcessor::processCopyin() const { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); auto checkAndCopyHostAssociateVar = [&](Fortran::semantics::Symbol *sym, mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) { assert(sym->has() && "No host-association found"); if (converter.isPresentShallowLookup(*sym)) converter.copyHostAssociateVar(*sym, copyAssignIP); }; bool hasCopyin = findRepeatableClause( [&](const omp::clause::Copyin &clause, const Fortran::parser::CharBlock &) { for (const omp::Object &object : clause.v) { Fortran::semantics::Symbol *sym = object.id(); assert(sym && "Expecting symbol"); if (const auto *commonDetails = sym->detailsIf()) { for (const auto &mem : commonDetails->objects()) checkAndCopyHostAssociateVar(&*mem, &insPt); break; } if (Fortran::semantics::IsAllocatableOrObjectPointer( &sym->GetUltimate())) TODO(converter.getCurrentLocation(), "pointer or allocatable variables in Copyin clause"); assert(sym->has() && "No host-association found"); checkAndCopyHostAssociateVar(sym); } }); // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to // the execution of the associated structured block. Emit implicit barrier to // synchronize threads and avoid data races on propagation master's thread // values of threadprivate variables to local instances of that variables of // all other implicit threads. if (hasCopyin) firOpBuilder.create(converter.getCurrentLocation()); firOpBuilder.restoreInsertionPoint(insPt); return hasCopyin; } /// Class that extracts information from the specified type. class TypeInfo { public: TypeInfo(mlir::Type ty) { typeScan(ty); } // Returns the length of character types. std::optional getCharLength() const { return charLen; } // Returns the shape of array types. llvm::ArrayRef getShape() const { return shape; } // Is the type inside a box? bool isBox() const { return inBox; } private: void typeScan(mlir::Type type); std::optional charLen; llvm::SmallVector shape; bool inBox = false; }; void TypeInfo::typeScan(mlir::Type ty) { if (auto sty = mlir::dyn_cast(ty)) { assert(shape.empty() && !sty.getShape().empty()); shape = llvm::SmallVector(sty.getShape()); typeScan(sty.getEleTy()); } else if (auto bty = mlir::dyn_cast(ty)) { inBox = true; typeScan(bty.getEleTy()); } else if (auto cty = mlir::dyn_cast(ty)) { charLen = cty.getLen(); } else if (auto hty = mlir::dyn_cast(ty)) { typeScan(hty.getEleTy()); } else if (auto pty = mlir::dyn_cast(ty)) { typeScan(pty.getEleTy()); } else { // The scan ends when reaching any built-in or record type. assert(ty.isIntOrIndexOrFloat() || mlir::isa(ty) || mlir::isa(ty) || mlir::isa(ty)); } } // Create a function that performs a copy between two variables, compatible // with their types and attributes. static mlir::func::FuncOp createCopyFunc(mlir::Location loc, Fortran::lower::AbstractConverter &converter, mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); mlir::ModuleOp module = builder.getModule(); mlir::Type eleTy = mlir::cast(varType).getEleTy(); TypeInfo typeInfo(eleTy); std::string copyFuncName = fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy"); if (auto decl = module.lookupSymbol(copyFuncName)) return decl; // create function mlir::OpBuilder::InsertionGuard guard(builder); mlir::OpBuilder modBuilder(module.getBodyRegion()); llvm::SmallVector argsTy = {varType, varType}; auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); mlir::func::FuncOp funcOp = modBuilder.create(loc, copyFuncName, funcType); funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, {loc, loc}); builder.setInsertionPointToStart(&funcOp.getRegion().back()); // generate body fir::FortranVariableFlagsAttr attrs; if (varAttrs != fir::FortranVariableFlagsEnum::None) attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs); llvm::SmallVector typeparams; if (typeInfo.getCharLength().has_value()) { mlir::Value charLen = builder.createIntegerConstant( loc, builder.getCharacterLengthType(), *typeInfo.getCharLength()); typeparams.push_back(charLen); } mlir::Value shape; if (!typeInfo.isBox() && !typeInfo.getShape().empty()) { llvm::SmallVector extents; for (auto extent : typeInfo.getShape()) extents.push_back( builder.createIntegerConstant(loc, builder.getIndexType(), extent)); shape = builder.create(loc, extents); } auto declDst = builder.create(loc, funcOp.getArgument(0), copyFuncName + "_dst", shape, typeparams, attrs); auto declSrc = builder.create(loc, funcOp.getArgument(1), copyFuncName + "_src", shape, typeparams, attrs); converter.copyVar(loc, declDst.getBase(), declSrc.getBase()); builder.create(loc); return funcOp; } bool ClauseProcessor::processCopyPrivate( mlir::Location currentLocation, llvm::SmallVectorImpl ©PrivateVars, llvm::SmallVectorImpl ©PrivateFuncs) const { auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) { mlir::Value symVal = converter.getSymbolAddress(*sym); auto declOp = symVal.getDefiningOp(); if (!declOp) fir::emitFatalError(currentLocation, "COPYPRIVATE is supported only in HLFIR mode"); symVal = declOp.getBase(); mlir::Type symType = symVal.getType(); fir::FortranVariableFlagsEnum attrs = declOp.getFortranAttrs().has_value() ? *declOp.getFortranAttrs() : fir::FortranVariableFlagsEnum::None; mlir::Value cpVar = symVal; // CopyPrivate variables must be passed by reference. However, in the case // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box. // In these cases to retrieve the appropriate !fir.ref> to // access the data we need we must perform an alloca and then store to it // and retrieve the data from the new alloca. if (mlir::isa(symType)) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); auto alloca = builder.create(currentLocation, symType); builder.create(currentLocation, symVal, alloca); cpVar = alloca; } copyPrivateVars.push_back(cpVar); mlir::func::FuncOp funcOp = createCopyFunc(currentLocation, converter, cpVar.getType(), attrs); copyPrivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp)); }; bool hasCopyPrivate = findRepeatableClause( [&](const clause::Copyprivate &clause, const Fortran::parser::CharBlock &) { for (const Object &object : clause.v) { Fortran::semantics::Symbol *sym = object.id(); if (const auto *commonDetails = sym->detailsIf()) { for (const auto &mem : commonDetails->objects()) addCopyPrivateVar(&*mem); break; } addCopyPrivateVar(sym); } }); return hasCopyPrivate; } bool ClauseProcessor::processDepend( llvm::SmallVectorImpl &dependTypeOperands, llvm::SmallVectorImpl &dependOperands) const { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); return findRepeatableClause( [&](const omp::clause::Depend &clause, const Fortran::parser::CharBlock &) { assert(std::holds_alternative(clause.u) && "Only InOut is handled at the moment"); const auto &inOut = std::get(clause.u); const auto &objects = std::get(inOut.t); mlir::omp::ClauseTaskDependAttr dependTypeOperand = genDependKindAttr(firOpBuilder, clause); dependTypeOperands.append(objects.size(), dependTypeOperand); for (const omp::Object &object : objects) { assert(object.ref() && "Expecting designator"); if (Fortran::evaluate::ExtractSubstring(*object.ref())) { TODO(converter.getCurrentLocation(), "substring not supported for task depend"); } else if (Fortran::evaluate::IsArrayElement(*object.ref())) { TODO(converter.getCurrentLocation(), "array sections not supported for task depend"); } Fortran::semantics::Symbol *sym = object.id(); const mlir::Value variable = converter.getSymbolAddress(*sym); dependOperands.push_back(variable); } }); } bool ClauseProcessor::processIf( omp::clause::If::DirectiveNameModifier directiveName, mlir::Value &result) const { bool found = false; findRepeatableClause( [&](const omp::clause::If &clause, const Fortran::parser::CharBlock &source) { mlir::Location clauseLocation = converter.genLocation(source); mlir::Value operand = getIfClauseOperand(converter, clause, directiveName, clauseLocation); // Assume that, at most, a single 'if' clause will be applicable to the // given directive. if (operand) { result = operand; found = true; } }); return found; } bool ClauseProcessor::processLink( llvm::SmallVectorImpl &result) const { return findRepeatableClause( [&](const omp::clause::Link &clause, const Fortran::parser::CharBlock &) { // Case: declare target link(var1, var2)... gatherFuncAndVarSyms( clause.v, mlir::omp::DeclareTargetCaptureClause::link, result); }); } mlir::omp::MapInfoOp createMapInfoOp(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value baseAddr, mlir::Value varPtrPtr, std::string name, llvm::ArrayRef bounds, llvm::ArrayRef members, uint64_t mapType, mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, bool isVal) { if (auto boxTy = baseAddr.getType().dyn_cast()) { baseAddr = builder.create(loc, baseAddr); retTy = baseAddr.getType(); } mlir::TypeAttr varType = mlir::TypeAttr::get( llvm::cast(retTy).getElementType()); mlir::omp::MapInfoOp op = builder.create( loc, retTy, baseAddr, varType, varPtrPtr, members, bounds, builder.getIntegerAttr(builder.getIntegerType(64, false), mapType), builder.getAttr(mapCaptureType), builder.getStringAttr(name)); return op; } bool ClauseProcessor::processMap( mlir::Location currentLocation, const llvm::omp::Directive &directive, Fortran::lower::StatementContext &stmtCtx, llvm::SmallVectorImpl &mapOperands, llvm::SmallVectorImpl *mapSymTypes, llvm::SmallVectorImpl *mapSymLocs, llvm::SmallVectorImpl *mapSymbols) const { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); return findRepeatableClause( [&](const omp::clause::Map &clause, const Fortran::parser::CharBlock &source) { using Map = omp::clause::Map; mlir::Location clauseLocation = converter.genLocation(source); const auto &oMapType = std::get>(clause.t); llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE; // If the map type is specified, then process it else Tofrom is the // default. if (oMapType) { const Map::MapType::Type &mapType = std::get(oMapType->t); switch (mapType) { case Map::MapType::Type::To: mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO; break; case Map::MapType::Type::From: mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; break; case Map::MapType::Type::Tofrom: mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; break; case Map::MapType::Type::Alloc: case Map::MapType::Type::Release: // alloc and release is the default map_type for the Target Data // Ops, i.e. if no bits for map_type is supplied then alloc/release // is implicitly assumed based on the target directive. Default // value for Target Data and Enter Data is alloc and for Exit Data // it is release. break; case Map::MapType::Type::Delete: mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE; } if (std::get>(oMapType->t)) mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS; } else { mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; } for (const omp::Object &object : std::get(clause.t)) { llvm::SmallVector bounds; std::stringstream asFortran; Fortran::lower::AddrAndBoundsInfo info = Fortran::lower::gatherDataOperandAddrAndBounds< mlir::omp::MapBoundsOp, mlir::omp::MapBoundsType>( converter, firOpBuilder, semaCtx, stmtCtx, *object.id(), object.ref(), clauseLocation, asFortran, bounds, treatIndexAsSection); auto origSymbol = converter.getSymbolAddress(*object.id()); mlir::Value symAddr = info.addr; if (origSymbol && fir::isTypeWithDescriptor(origSymbol.getType())) symAddr = origSymbol; // Explicit map captures are captured ByRef by default, // optimisation passes may alter this to ByCopy or other capture // types to optimise mlir::Value mapOp = createMapInfoOp( firOpBuilder, clauseLocation, symAddr, mlir::Value{}, asFortran.str(), bounds, {}, static_cast< std::underlying_type_t>( mapTypeBits), mlir::omp::VariableCaptureKind::ByRef, symAddr.getType()); mapOperands.push_back(mapOp); if (mapSymTypes) mapSymTypes->push_back(symAddr.getType()); if (mapSymLocs) mapSymLocs->push_back(symAddr.getLoc()); if (mapSymbols) mapSymbols->push_back(object.id()); } }); } bool ClauseProcessor::processReduction( mlir::Location currentLocation, llvm::SmallVectorImpl &reductionVars, llvm::SmallVectorImpl &reductionDeclSymbols, llvm::SmallVectorImpl *reductionSymbols) const { return findRepeatableClause( [&](const omp::clause::Reduction &clause, const Fortran::parser::CharBlock &) { ReductionProcessor rp; rp.addDeclareReduction(currentLocation, converter, clause, reductionVars, reductionDeclSymbols, reductionSymbols); }); } bool ClauseProcessor::processSectionsReduction( mlir::Location currentLocation) const { return findRepeatableClause( [&](const omp::clause::Reduction &, const Fortran::parser::CharBlock &) { TODO(currentLocation, "OMPC_Reduction"); }); } bool ClauseProcessor::processTo( llvm::SmallVectorImpl &result) const { return findRepeatableClause( [&](const omp::clause::To &clause, const Fortran::parser::CharBlock &) { // Case: declare target to(func, var1, var2)... gatherFuncAndVarSyms(clause.v, mlir::omp::DeclareTargetCaptureClause::to, result); }); } bool ClauseProcessor::processEnter( llvm::SmallVectorImpl &result) const { return findRepeatableClause( [&](const omp::clause::Enter &clause, const Fortran::parser::CharBlock &) { // Case: declare target enter(func, var1, var2)... gatherFuncAndVarSyms( clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result); }); } bool ClauseProcessor::processUseDeviceAddr( llvm::SmallVectorImpl &operands, llvm::SmallVectorImpl &useDeviceTypes, llvm::SmallVectorImpl &useDeviceLocs, llvm::SmallVectorImpl &useDeviceSymbols) const { return findRepeatableClause( [&](const omp::clause::UseDeviceAddr &clause, const Fortran::parser::CharBlock &) { addUseDeviceClause(converter, clause.v, operands, useDeviceTypes, useDeviceLocs, useDeviceSymbols); }); } bool ClauseProcessor::processUseDevicePtr( llvm::SmallVectorImpl &operands, llvm::SmallVectorImpl &useDeviceTypes, llvm::SmallVectorImpl &useDeviceLocs, llvm::SmallVectorImpl &useDeviceSymbols) const { return findRepeatableClause( [&](const omp::clause::UseDevicePtr &clause, const Fortran::parser::CharBlock &) { addUseDeviceClause(converter, clause.v, operands, useDeviceTypes, useDeviceLocs, useDeviceSymbols); }); } } // namespace omp } // namespace lower } // namespace Fortran