diff options
Diffstat (limited to 'flang/lib/Lower')
24 files changed, 434 insertions, 536 deletions
diff --git a/flang/lib/Lower/Allocatable.cpp b/flang/lib/Lower/Allocatable.cpp index ce9d894..444b5b6 100644 --- a/flang/lib/Lower/Allocatable.cpp +++ b/flang/lib/Lower/Allocatable.cpp @@ -490,6 +490,16 @@ private: return; } + // Preserve characters' dynamic length. + if (lenParams.empty() && box.isCharacter() && + !box.hasNonDeferredLenParams()) { + auto charTy = mlir::dyn_cast<fir::CharacterType>(box.getEleTy()); + if (charTy && charTy.hasDynamicLen()) { + fir::ExtendedValue exv{box}; + lenParams.push_back(fir::factory::readCharLen(builder, loc, exv)); + } + } + // Generate a sequence of runtime calls. errorManager.genStatCheck(builder, loc); genAllocateObjectInit(box, allocatorIdx); diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 7f0907a..c003a5b 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -475,7 +475,9 @@ public: fir::runtime::genMain(*builder, toLocation(), bridge.getEnvironmentDefaults(), getFoldingContext().languageFeatures().IsEnabled( - Fortran::common::LanguageFeature::CUDA)); + Fortran::common::LanguageFeature::CUDA), + getFoldingContext().languageFeatures().IsEnabled( + Fortran::common::LanguageFeature::Coarray)); }); finalizeOpenMPLowering(globalOmpRequiresSymbol); @@ -1400,21 +1402,23 @@ private: mlir::Value genLoopVariableAddress(mlir::Location loc, const Fortran::semantics::Symbol &sym, bool isUnordered) { - if (isUnordered || sym.has<Fortran::semantics::HostAssocDetails>() || - sym.has<Fortran::semantics::UseDetails>()) { - if (!shallowLookupSymbol(sym) && - !GetSymbolDSA(sym).test( - Fortran::semantics::Symbol::Flag::OmpShared)) { - // Do concurrent loop variables are not mapped yet since they are local - // to the Do concurrent scope (same for OpenMP loops). - mlir::OpBuilder::InsertPoint insPt = builder->saveInsertionPoint(); - builder->setInsertionPointToStart(builder->getAllocaBlock()); - mlir::Type tempTy = genType(sym); - mlir::Value temp = - builder->createTemporaryAlloc(loc, tempTy, toStringRef(sym.name())); - bindIfNewSymbol(sym, temp); - builder->restoreInsertionPoint(insPt); - } + if (!shallowLookupSymbol(sym) && + (isUnordered || + GetSymbolDSA(sym).test(Fortran::semantics::Symbol::Flag::OmpPrivate) || + GetSymbolDSA(sym).test( + Fortran::semantics::Symbol::Flag::OmpFirstPrivate) || + GetSymbolDSA(sym).test( + Fortran::semantics::Symbol::Flag::OmpLastPrivate) || + GetSymbolDSA(sym).test(Fortran::semantics::Symbol::Flag::OmpLinear))) { + // Do concurrent loop variables are not mapped yet since they are + // local to the Do concurrent scope (same for OpenMP loops). + mlir::OpBuilder::InsertPoint insPt = builder->saveInsertionPoint(); + builder->setInsertionPointToStart(builder->getAllocaBlock()); + mlir::Type tempTy = genType(sym); + mlir::Value temp = + builder->createTemporaryAlloc(loc, tempTy, toStringRef(sym.name())); + bindIfNewSymbol(sym, temp); + builder->restoreInsertionPoint(insPt); } auto entry = lookupSymbol(sym); (void)entry; @@ -2060,10 +2064,10 @@ private: // TODO Promote to using `enableDelayedPrivatization` (which is enabled by // default unlike the staging flag) once the implementation of this is more // complete. - bool useDelayedPriv = - enableDelayedPrivatizationStaging && doConcurrentLoopOp; + bool useDelayedPriv = enableDelayedPrivatization && doConcurrentLoopOp; llvm::SetVector<const Fortran::semantics::Symbol *> allPrivatizedSymbols; - llvm::SmallSet<const Fortran::semantics::Symbol *, 16> mightHaveReadHostSym; + llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 16> + mightHaveReadHostSym; for (const Fortran::semantics::Symbol *symToPrivatize : info.localSymList) { if (useDelayedPriv) { @@ -2122,6 +2126,9 @@ private: } } + if (!doConcurrentLoopOp) + return; + llvm::SmallVector<bool> reduceVarByRef; llvm::SmallVector<mlir::Attribute> reductionDeclSymbols; llvm::SmallVector<mlir::Attribute> nestReduceAttrs; @@ -4824,7 +4831,9 @@ private: void genCUDADataTransfer(fir::FirOpBuilder &builder, mlir::Location loc, const Fortran::evaluate::Assignment &assign, - hlfir::Entity &lhs, hlfir::Entity &rhs) { + hlfir::Entity &lhs, hlfir::Entity &rhs, + bool isWholeAllocatableAssignment, + bool keepLhsLengthInAllocatableAssignment) { bool lhsIsDevice = Fortran::evaluate::HasCUDADeviceAttrs(assign.lhs); bool rhsIsDevice = Fortran::evaluate::HasCUDADeviceAttrs(assign.rhs); @@ -4889,6 +4898,28 @@ private: // host = device if (!lhsIsDevice && rhsIsDevice) { + if (Fortran::lower::isTransferWithConversion(rhs)) { + mlir::OpBuilder::InsertionGuard insertionGuard(builder); + auto elementalOp = + mlir::dyn_cast<hlfir::ElementalOp>(rhs.getDefiningOp()); + assert(elementalOp && "expect elemental op"); + auto designateOp = + *elementalOp.getBody()->getOps<hlfir::DesignateOp>().begin(); + builder.setInsertionPoint(elementalOp); + // Create a temp to transfer the rhs before applying the conversion. + hlfir::Entity entity{designateOp.getMemref()}; + auto [temp, cleanup] = hlfir::createTempFromMold(loc, builder, entity); + auto transferKindAttr = cuf::DataTransferKindAttr::get( + builder.getContext(), cuf::DataTransferKind::DeviceHost); + cuf::DataTransferOp::create(builder, loc, designateOp.getMemref(), temp, + /*shape=*/mlir::Value{}, transferKindAttr); + designateOp.getMemrefMutable().assign(temp); + builder.setInsertionPointAfter(elementalOp); + hlfir::AssignOp::create(builder, loc, elementalOp, lhs, + isWholeAllocatableAssignment, + keepLhsLengthInAllocatableAssignment); + return; + } auto transferKindAttr = cuf::DataTransferKindAttr::get( builder.getContext(), cuf::DataTransferKind::DeviceHost); cuf::DataTransferOp::create(builder, loc, rhsVal, lhsVal, shape, @@ -5036,7 +5067,9 @@ private: hlfir::Entity rhs = evaluateRhs(localStmtCtx); hlfir::Entity lhs = evaluateLhs(localStmtCtx); if (isCUDATransfer && !hasCUDAImplicitTransfer) - genCUDADataTransfer(builder, loc, assign, lhs, rhs); + genCUDADataTransfer(builder, loc, assign, lhs, rhs, + isWholeAllocatableAssignment, + keepLhsLengthInAllocatableAssignment); else hlfir::AssignOp::create(builder, loc, rhs, lhs, isWholeAllocatableAssignment, diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt index 1d1c7ddd..eb4d57d 100644 --- a/flang/lib/Lower/CMakeLists.txt +++ b/flang/lib/Lower/CMakeLists.txt @@ -60,6 +60,7 @@ add_flang_library(FortranLower FortranParser FortranEvaluate FortranSemantics + FortranUtils LINK_COMPONENTS Support diff --git a/flang/lib/Lower/CUDA.cpp b/flang/lib/Lower/CUDA.cpp index f6d0078..1293d2c 100644 --- a/flang/lib/Lower/CUDA.cpp +++ b/flang/lib/Lower/CUDA.cpp @@ -13,6 +13,7 @@ #include "flang/Lower/CUDA.h" #include "flang/Lower/AbstractConverter.h" #include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/HLFIR/HLFIROps.h" #define DEBUG_TYPE "flang-lower-cuda" @@ -155,3 +156,12 @@ cuf::DataAttributeAttr Fortran::lower::translateSymbolCUFDataAttribute( Fortran::semantics::GetCUDADataAttr(&sym.GetUltimate()); return cuf::getDataAttribute(mlirContext, cudaAttr); } + +bool Fortran::lower::isTransferWithConversion(mlir::Value rhs) { + if (auto elOp = mlir::dyn_cast<hlfir::ElementalOp>(rhs.getDefiningOp())) + if (llvm::hasSingleElement(elOp.getBody()->getOps<hlfir::DesignateOp>()) && + llvm::hasSingleElement(elOp.getBody()->getOps<fir::LoadOp>()) == 1 && + llvm::hasSingleElement(elOp.getBody()->getOps<fir::ConvertOp>()) == 1) + return true; + return false; +} diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp index bf713f5..04dcc92 100644 --- a/flang/lib/Lower/ConvertCall.cpp +++ b/flang/lib/Lower/ConvertCall.cpp @@ -880,9 +880,10 @@ struct CallContext { std::optional<mlir::Type> resultType, mlir::Location loc, Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symMap, - Fortran::lower::StatementContext &stmtCtx) + Fortran::lower::StatementContext &stmtCtx, bool doCopyIn = true) : procRef{procRef}, converter{converter}, symMap{symMap}, - stmtCtx{stmtCtx}, resultType{resultType}, loc{loc} {} + stmtCtx{stmtCtx}, resultType{resultType}, loc{loc}, doCopyIn{doCopyIn} { + } fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); } @@ -924,6 +925,7 @@ struct CallContext { Fortran::lower::StatementContext &stmtCtx; std::optional<mlir::Type> resultType; mlir::Location loc; + bool doCopyIn; }; using ExvAndCleanup = @@ -1161,18 +1163,6 @@ mlir::Value static getZeroLowerBounds(mlir::Location loc, return builder.genShift(loc, lowerBounds); } -static bool -isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg, - Fortran::evaluate::FoldingContext &foldingContext) { - if (const auto *expr = arg.UnwrapExpr()) - return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext); - const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy(); - assert(sym && - "expect ActualArguments to be expression or assumed-type symbols"); - return sym->Rank() == 0 || - Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext); -} - static bool isParameterObjectOrSubObject(hlfir::Entity entity) { mlir::Value base = entity; bool foundParameter = false; @@ -1204,6 +1194,10 @@ static bool isParameterObjectOrSubObject(hlfir::Entity entity) { /// fir.box_char...). /// This function should only be called with an actual that is present. /// The optional aspects must be handled by this function user. +/// +/// Note: while Fortran::lower::CallerInterface::PassedEntity (the type of arg) +/// is technically a template type, in the prepare*ActualArgument() calls +/// it resolves to Fortran::evaluate::ActualArgument * static PreparedDummyArgument preparePresentUserCallActualArgument( mlir::Location loc, fir::FirOpBuilder &builder, const Fortran::lower::PreparedActualArgument &preparedActual, @@ -1211,9 +1205,6 @@ static PreparedDummyArgument preparePresentUserCallActualArgument( const Fortran::lower::CallerInterface::PassedEntity &arg, CallContext &callContext) { - Fortran::evaluate::FoldingContext &foldingContext = - callContext.converter.getFoldingContext(); - // Step 1: get the actual argument, which includes addressing the // element if this is an array in an elemental call. hlfir::Entity actual = preparedActual.getActual(loc, builder); @@ -1254,13 +1245,20 @@ static PreparedDummyArgument preparePresentUserCallActualArgument( passingPolymorphicToNonPolymorphic && (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType)); - // The simple contiguity of the actual is "lost" when passing a polymorphic - // to a non polymorphic entity because the dummy dynamic type matters for - // the contiguity. - const bool mustDoCopyInOut = - actual.isArray() && arg.mustBeMadeContiguous() && - (passingPolymorphicToNonPolymorphic || - !isSimplyContiguous(*arg.entity, foldingContext)); + bool mustDoCopyIn{false}; + bool mustDoCopyOut{false}; + + if (callContext.doCopyIn) { + Fortran::evaluate::FoldingContext &foldingContext{ + callContext.converter.getFoldingContext()}; + + bool suggestCopyIn = Fortran::evaluate::MayNeedCopy( + arg.entity, arg.characteristics, foldingContext, /*forCopyOut=*/false); + bool suggestCopyOut = Fortran::evaluate::MayNeedCopy( + arg.entity, arg.characteristics, foldingContext, /*forCopyOut=*/true); + mustDoCopyIn = actual.isArray() && suggestCopyIn; + mustDoCopyOut = actual.isArray() && suggestCopyOut; + } const bool actualIsAssumedRank = actual.isAssumedRank(); // Create dummy type with actual argument rank when the dummy is an assumed @@ -1370,8 +1368,14 @@ static PreparedDummyArgument preparePresentUserCallActualArgument( entity = hlfir::Entity{associate.getBase()}; // Register the temporary destruction after the call. preparedDummy.pushExprAssociateCleanUp(associate); - } else if (mustDoCopyInOut) { + } else if (mustDoCopyIn || mustDoCopyOut) { // Copy-in non contiguous variables. + // + // TODO: copy-in and copy-out are now determined separately, in order + // to allow more fine grained copying. While currently both copy-in + // and copy-out are must be done together, these copy operations could + // be separated in the future. (This is related to TODO comment below.) + // // TODO: for non-finalizable monomorphic derived type actual // arguments associated with INTENT(OUT) dummy arguments // we may avoid doing the copy and only allocate the temporary. @@ -1379,7 +1383,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument( // allocation for the temp in this case. We can communicate // this to the codegen via some CopyInOp flag. // This is a performance concern. - entity = genCopyIn(entity, arg.mayBeModifiedByCall()); + entity = genCopyIn(entity, mustDoCopyOut); } } else { const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr(); @@ -2966,8 +2970,11 @@ void Fortran::lower::convertUserDefinedAssignmentToHLFIR( const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs, Fortran::lower::SymMap &symMap) { Fortran::lower::StatementContext definedAssignmentContext; + // For defined assignment, don't use regular copy-in/copy-out mechanism: + // defined assignment generates hlfir.region_assign construct, and this + // construct automatically handles any copy-in. CallContext callContext(procRef, /*resultType=*/std::nullopt, loc, converter, - symMap, definedAssignmentContext); + symMap, definedAssignmentContext, /*doCopyIn=*/false); Fortran::lower::CallerInterface caller(procRef, converter); mlir::FunctionType callSiteType = caller.genFunctionType(); PreparedActualArgument preparedLhs{lhs, /*isPresent=*/std::nullopt}; diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp index 5588f62..d7f94e1 100644 --- a/flang/lib/Lower/ConvertExpr.cpp +++ b/flang/lib/Lower/ConvertExpr.cpp @@ -2750,7 +2750,7 @@ public: fir::unwrapSequenceType(fir::unwrapPassByRefType(argTy)))) TODO(loc, "passing to an OPTIONAL CONTIGUOUS derived type argument " "with length parameters"); - if (Fortran::evaluate::IsAssumedRank(*expr)) + if (Fortran::semantics::IsAssumedRank(*expr)) TODO(loc, "passing an assumed rank entity to an OPTIONAL " "CONTIGUOUS argument"); // Assumed shape VALUE are currently TODO in the call interface diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp index 9930dd6..81e09a1 100644 --- a/flang/lib/Lower/ConvertExprToHLFIR.cpp +++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp @@ -26,7 +26,6 @@ #include "flang/Optimizer/Builder/Complex.h" #include "flang/Optimizer/Builder/IntrinsicCall.h" #include "flang/Optimizer/Builder/MutableBox.h" -#include "flang/Optimizer/Builder/Runtime/Character.h" #include "flang/Optimizer/Builder/Runtime/Derived.h" #include "flang/Optimizer/Builder/Runtime/Pointer.h" #include "flang/Optimizer/Builder/Todo.h" @@ -1286,16 +1285,8 @@ struct BinaryOp<Fortran::evaluate::Relational< fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { - auto [lhsExv, lhsCleanUp] = - hlfir::translateToExtendedValue(loc, builder, lhs); - auto [rhsExv, rhsCleanUp] = - hlfir::translateToExtendedValue(loc, builder, rhs); - auto cmp = fir::runtime::genCharCompare( - builder, loc, translateSignedRelational(op.opr), lhsExv, rhsExv); - if (lhsCleanUp) - (*lhsCleanUp)(); - if (rhsCleanUp) - (*rhsCleanUp)(); + auto cmp = hlfir::CmpCharOp::create( + builder, loc, translateSignedRelational(op.opr), lhs, rhs); return hlfir::EntityWithAttributes{cmp}; } }; diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index fd66592..80af7f4 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -1720,7 +1720,7 @@ static bool lowerToBoxValue(const Fortran::semantics::Symbol &sym, return true; // Assumed rank and optional fir.box cannot yet be read while lowering the // specifications. - if (Fortran::evaluate::IsAssumedRank(sym) || + if (Fortran::semantics::IsAssumedRank(sym) || Fortran::semantics::IsOptional(sym)) return true; // Polymorphic entity should be tracked through a fir.box that has the @@ -2172,7 +2172,7 @@ void Fortran::lower::mapSymbolAttributes( return; } - const bool isAssumedRank = Fortran::evaluate::IsAssumedRank(sym); + const bool isAssumedRank = Fortran::semantics::IsAssumedRank(sym); if (isAssumedRank && !allowAssumedRank) TODO(loc, "assumed-rank variable in procedure implemented in Fortran"); diff --git a/flang/lib/Lower/HlfirIntrinsics.cpp b/flang/lib/Lower/HlfirIntrinsics.cpp index 6e1d06a..39595d6 100644 --- a/flang/lib/Lower/HlfirIntrinsics.cpp +++ b/flang/lib/Lower/HlfirIntrinsics.cpp @@ -170,6 +170,17 @@ protected: mlir::Type stmtResultType) override; }; +class HlfirEOShiftLowering : public HlfirTransformationalIntrinsic { +public: + using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic; + +protected: + mlir::Value + lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals, + const fir::IntrinsicArgumentLoweringRules *argLowering, + mlir::Type stmtResultType) override; +}; + class HlfirReshapeLowering : public HlfirTransformationalIntrinsic { public: using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic; @@ -430,6 +441,46 @@ mlir::Value HlfirCShiftLowering::lowerImpl( return createOp<hlfir::CShiftOp>(resultType, operands); } +mlir::Value HlfirEOShiftLowering::lowerImpl( + const Fortran::lower::PreparedActualArguments &loweredActuals, + const fir::IntrinsicArgumentLoweringRules *argLowering, + mlir::Type stmtResultType) { + auto operands = getOperandVector(loweredActuals, argLowering); + assert(operands.size() == 4); + mlir::Value array = operands[0]; + mlir::Value shift = operands[1]; + mlir::Value boundary = operands[2]; + mlir::Value dim = operands[3]; + // If DIM is present, then dereference it if it is a ref. + if (dim) + dim = hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{dim}); + + mlir::Type resultType = computeResultType(array, stmtResultType); + + if (boundary && fir::isa_trivial(boundary.getType())) { + mlir::Type elementType = hlfir::getFortranElementType(resultType); + if (auto logicalTy = mlir::dyn_cast<fir::LogicalType>(elementType)) { + // Scalar logical constant boundary might be represented using i1, i2, ... + // type. We need to cast it to fir.logical type of the ARRAY/result. + if (boundary.getType() != logicalTy) + boundary = builder.createConvert(loc, logicalTy, boundary); + } else { + // When the boundary is a constant like '1u', the lowering converts + // it into a signless arith.constant value (which is a requirement + // of the Arith dialect). If the ARRAY/RESULT is also UNSIGNED, + // we have to cast the boundary to the same unsigned type. + auto resultIntTy = mlir::dyn_cast<mlir::IntegerType>(elementType); + auto boundaryIntTy = + mlir::dyn_cast<mlir::IntegerType>(boundary.getType()); + if (resultIntTy && boundaryIntTy && + resultIntTy.getSignedness() != boundaryIntTy.getSignedness()) + boundary = builder.createConvert(loc, resultIntTy, boundary); + } + } + + return createOp<hlfir::EOShiftOp>(resultType, array, shift, boundary, dim); +} + mlir::Value HlfirReshapeLowering::lowerImpl( const Fortran::lower::PreparedActualArguments &loweredActuals, const fir::IntrinsicArgumentLoweringRules *argLowering, @@ -489,6 +540,9 @@ std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic( if (name == "cshift") return HlfirCShiftLowering{builder, loc}.lower(loweredActuals, argLowering, stmtResultType); + if (name == "eoshift") + return HlfirEOShiftLowering{builder, loc}.lower(loweredActuals, argLowering, + stmtResultType); if (name == "reshape") return HlfirReshapeLowering{builder, loc}.lower(loweredActuals, argLowering, stmtResultType); diff --git a/flang/lib/Lower/HostAssociations.cpp b/flang/lib/Lower/HostAssociations.cpp index 2a330cc..ad6aba1 100644 --- a/flang/lib/Lower/HostAssociations.cpp +++ b/flang/lib/Lower/HostAssociations.cpp @@ -431,7 +431,7 @@ public: mlir::Value box = args.valueInTuple; mlir::IndexType idxTy = builder.getIndexType(); llvm::SmallVector<mlir::Value> lbounds; - if (!ba.lboundIsAllOnes() && !Fortran::evaluate::IsAssumedRank(sym)) { + if (!ba.lboundIsAllOnes() && !Fortran::semantics::IsAssumedRank(sym)) { if (ba.isStaticArray()) { for (std::int64_t lb : ba.staticLBound()) lbounds.emplace_back(builder.createIntegerConstant(loc, idxTy, lb)); @@ -490,7 +490,7 @@ private: bool isPolymorphic = type && type->IsPolymorphic(); return isScalarOrContiguous && !isPolymorphic && !isDerivedWithLenParameters(sym) && - !Fortran::evaluate::IsAssumedRank(sym); + !Fortran::semantics::IsAssumedRank(sym); } }; } // namespace diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp index 35edcb0..7a84b21 100644 --- a/flang/lib/Lower/OpenACC.cpp +++ b/flang/lib/Lower/OpenACC.cpp @@ -1575,7 +1575,7 @@ static void genCombiner(fir::FirOpBuilder &builder, mlir::Location loc, if (bounds.empty()) { llvm::SmallVector<mlir::Value> extents; mlir::Type idxTy = builder.getIndexType(); - for (auto extent : seqTy.getShape()) { + for (auto extent : llvm::reverse(seqTy.getShape())) { mlir::Value lb = mlir::arith::ConstantOp::create( builder, loc, idxTy, builder.getIntegerAttr(idxTy, 0)); mlir::Value ub = mlir::arith::ConstantOp::create( @@ -1607,12 +1607,11 @@ static void genCombiner(fir::FirOpBuilder &builder, mlir::Location loc, } } else { // Lowerbound, upperbound and step are passed as block arguments. - [[maybe_unused]] unsigned nbRangeArgs = + unsigned nbRangeArgs = recipe.getCombinerRegion().getArguments().size() - 2; assert((nbRangeArgs / 3 == seqTy.getDimension()) && "Expect 3 block arguments per dimension"); - for (unsigned i = 2; i < recipe.getCombinerRegion().getArguments().size(); - i += 3) { + for (int i = nbRangeArgs - 1; i >= 2; i -= 3) { mlir::Value lb = recipe.getCombinerRegion().getArgument(i); mlir::Value ub = recipe.getCombinerRegion().getArgument(i + 1); mlir::Value step = recipe.getCombinerRegion().getArgument(i + 2); @@ -1623,8 +1622,11 @@ static void genCombiner(fir::FirOpBuilder &builder, mlir::Location loc, ivs.push_back(loop.getInductionVar()); } } - auto addr1 = fir::CoordinateOp::create(builder, loc, refTy, value1, ivs); - auto addr2 = fir::CoordinateOp::create(builder, loc, refTy, value2, ivs); + llvm::SmallVector<mlir::Value> reversedIvs(ivs.rbegin(), ivs.rend()); + auto addr1 = + fir::CoordinateOp::create(builder, loc, refTy, value1, reversedIvs); + auto addr2 = + fir::CoordinateOp::create(builder, loc, refTy, value2, reversedIvs); auto load1 = fir::LoadOp::create(builder, loc, addr1); auto load2 = fir::LoadOp::create(builder, loc, addr2); mlir::Value res = diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp index ed0bff0..ff82a36 100644 --- a/flang/lib/Lower/OpenMP/Atomic.cpp +++ b/flang/lib/Lower/OpenMP/Atomic.cpp @@ -43,179 +43,6 @@ namespace omp { using namespace Fortran::lower::omp; } -namespace { -// An example of a type that can be used to get the return value from -// the visitor: -// visitor(type_identity<Xyz>) -> result_type -using SomeArgType = evaluate::Type<common::TypeCategory::Integer, 4>; - -struct GetProc - : public evaluate::Traverse<GetProc, const evaluate::ProcedureDesignator *, - false> { - using Result = const evaluate::ProcedureDesignator *; - using Base = evaluate::Traverse<GetProc, Result, false>; - GetProc() : Base(*this) {} - - using Base::operator(); - - static Result Default() { return nullptr; } - - Result operator()(const evaluate::ProcedureDesignator &p) const { return &p; } - static Result Combine(Result a, Result b) { return a != nullptr ? a : b; } -}; - -struct WithType { - WithType(const evaluate::DynamicType &t) : type(t) { - assert(type.category() != common::TypeCategory::Derived && - "Type cannot be a derived type"); - } - - template <typename VisitorTy> // - auto visit(VisitorTy &&visitor) const - -> std::invoke_result_t<VisitorTy, SomeArgType> { - switch (type.category()) { - case common::TypeCategory::Integer: - switch (type.kind()) { - case 1: - return visitor(llvm::type_identity<evaluate::Type<Integer, 1>>{}); - case 2: - return visitor(llvm::type_identity<evaluate::Type<Integer, 2>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Integer, 4>>{}); - case 8: - return visitor(llvm::type_identity<evaluate::Type<Integer, 8>>{}); - case 16: - return visitor(llvm::type_identity<evaluate::Type<Integer, 16>>{}); - } - break; - case common::TypeCategory::Unsigned: - switch (type.kind()) { - case 1: - return visitor(llvm::type_identity<evaluate::Type<Unsigned, 1>>{}); - case 2: - return visitor(llvm::type_identity<evaluate::Type<Unsigned, 2>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Unsigned, 4>>{}); - case 8: - return visitor(llvm::type_identity<evaluate::Type<Unsigned, 8>>{}); - case 16: - return visitor(llvm::type_identity<evaluate::Type<Unsigned, 16>>{}); - } - break; - case common::TypeCategory::Real: - switch (type.kind()) { - case 2: - return visitor(llvm::type_identity<evaluate::Type<Real, 2>>{}); - case 3: - return visitor(llvm::type_identity<evaluate::Type<Real, 3>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Real, 4>>{}); - case 8: - return visitor(llvm::type_identity<evaluate::Type<Real, 8>>{}); - case 10: - return visitor(llvm::type_identity<evaluate::Type<Real, 10>>{}); - case 16: - return visitor(llvm::type_identity<evaluate::Type<Real, 16>>{}); - } - break; - case common::TypeCategory::Complex: - switch (type.kind()) { - case 2: - return visitor(llvm::type_identity<evaluate::Type<Complex, 2>>{}); - case 3: - return visitor(llvm::type_identity<evaluate::Type<Complex, 3>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Complex, 4>>{}); - case 8: - return visitor(llvm::type_identity<evaluate::Type<Complex, 8>>{}); - case 10: - return visitor(llvm::type_identity<evaluate::Type<Complex, 10>>{}); - case 16: - return visitor(llvm::type_identity<evaluate::Type<Complex, 16>>{}); - } - break; - case common::TypeCategory::Logical: - switch (type.kind()) { - case 1: - return visitor(llvm::type_identity<evaluate::Type<Logical, 1>>{}); - case 2: - return visitor(llvm::type_identity<evaluate::Type<Logical, 2>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Logical, 4>>{}); - case 8: - return visitor(llvm::type_identity<evaluate::Type<Logical, 8>>{}); - } - break; - case common::TypeCategory::Character: - switch (type.kind()) { - case 1: - return visitor(llvm::type_identity<evaluate::Type<Character, 1>>{}); - case 2: - return visitor(llvm::type_identity<evaluate::Type<Character, 2>>{}); - case 4: - return visitor(llvm::type_identity<evaluate::Type<Character, 4>>{}); - } - break; - case common::TypeCategory::Derived: - (void)Derived; - break; - } - llvm_unreachable("Unhandled type"); - } - - const evaluate::DynamicType &type; - -private: - // Shorter names. - static constexpr auto Character = common::TypeCategory::Character; - static constexpr auto Complex = common::TypeCategory::Complex; - static constexpr auto Derived = common::TypeCategory::Derived; - static constexpr auto Integer = common::TypeCategory::Integer; - static constexpr auto Logical = common::TypeCategory::Logical; - static constexpr auto Real = common::TypeCategory::Real; - static constexpr auto Unsigned = common::TypeCategory::Unsigned; -}; - -template <typename T, typename U = std::remove_const_t<T>> -U AsRvalue(T &t) { - U copy{t}; - return std::move(copy); -} - -template <typename T> -T &&AsRvalue(T &&t) { - return std::move(t); -} - -struct ArgumentReplacer - : public evaluate::Traverse<ArgumentReplacer, bool, false> { - using Base = evaluate::Traverse<ArgumentReplacer, bool, false>; - using Result = bool; - - Result Default() const { return false; } - - ArgumentReplacer(evaluate::ActualArguments &&newArgs) - : Base(*this), args_(std::move(newArgs)) {} - - using Base::operator(); - - template <typename T> - Result operator()(const evaluate::FunctionRef<T> &x) { - assert(!done_); - auto &mut = const_cast<evaluate::FunctionRef<T> &>(x); - mut.arguments() = args_; - done_ = true; - return true; - } - - Result Combine(Result &&a, Result &&b) { return a || b; } - -private: - bool done_{false}; - evaluate::ActualArguments &&args_; -}; -} // namespace - [[maybe_unused]] static void dumpAtomicAnalysis(const parser::OpenMPAtomicConstruct::Analysis &analysis) { auto whatStr = [](int k) { @@ -412,85 +239,6 @@ makeMemOrderAttr(lower::AbstractConverter &converter, return nullptr; } -static bool replaceArgs(semantics::SomeExpr &expr, - evaluate::ActualArguments &&newArgs) { - return ArgumentReplacer(std::move(newArgs))(expr); -} - -static semantics::SomeExpr makeCall(const evaluate::DynamicType &type, - const evaluate::ProcedureDesignator &proc, - const evaluate::ActualArguments &args) { - return WithType(type).visit([&](auto &&s) -> semantics::SomeExpr { - using Type = typename llvm::remove_cvref_t<decltype(s)>::type; - return evaluate::AsGenericExpr( - evaluate::FunctionRef<Type>(AsRvalue(proc), AsRvalue(args))); - }); -} - -static const evaluate::ProcedureDesignator & -getProcedureDesignator(const semantics::SomeExpr &call) { - const evaluate::ProcedureDesignator *proc = GetProc{}(call); - assert(proc && "Call has no procedure designator"); - return *proc; -} - -static semantics::SomeExpr // -genReducedMinMax(const semantics::SomeExpr &orig, - const semantics::SomeExpr *atomArg, - const std::vector<semantics::SomeExpr> &args) { - // Take a list of arguments to a min/max operation, e.g. [a0, a1, ...] - // One of the a_i's, say a_t, must be atomArg. - // Generate tmp = min/max(a0, a1, ... [except a_t]). Then generate - // call = min/max(a_t, tmp). - // Return "call". - - // The min/max intrinsics have 2 mandatory arguments, the rest is optional. - // Make sure that the "tmp = min/max(...)" doesn't promote an optional - // argument to a non-optional position. This could happen if a_t is at - // position 0 or 1. - if (args.size() <= 2) - return orig; - - evaluate::ActualArguments nonAtoms; - - auto AsActual = [](const semantics::SomeExpr &x) { - semantics::SomeExpr copy = x; - return evaluate::ActualArgument(std::move(copy)); - }; - // Semantic checks guarantee that the "atom" shows exactly once in the - // argument list (with potential conversions around it). - // For the first two (non-optional) arguments, if "atom" is among them, - // replace it with another occurrence of the other non-optional argument. - if (atomArg == &args[0]) { - // (atom, x, y...) -> (x, x, y...) - nonAtoms.push_back(AsActual(args[1])); - nonAtoms.push_back(AsActual(args[1])); - } else if (atomArg == &args[1]) { - // (x, atom, y...) -> (x, x, y...) - nonAtoms.push_back(AsActual(args[0])); - nonAtoms.push_back(AsActual(args[0])); - } else { - // (x, y, z...) -> unchanged - nonAtoms.push_back(AsActual(args[0])); - nonAtoms.push_back(AsActual(args[1])); - } - - // The rest of arguments are optional, so we can just skip "atom". - for (size_t i = 2, e = args.size(); i != e; ++i) { - if (atomArg != &args[i]) - nonAtoms.push_back(AsActual(args[i])); - } - - // The type of the intermediate min/max is the same as the type of its - // arguments, which may be different from the type of the original - // expression. The original expression may have additional coverts. - auto tmp = - makeCall(*atomArg->GetType(), getProcedureDesignator(orig), nonAtoms); - semantics::SomeExpr call = orig; - replaceArgs(call, {AsActual(*atomArg), AsActual(tmp)}); - return call; -} - static mlir::Operation * // genAtomicRead(lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx, mlir::Location loc, @@ -610,25 +358,6 @@ genAtomicUpdate(lower::AbstractConverter &converter, auto [opcode, args] = evaluate::GetTopLevelOperationIgnoreResizing(input); assert(!args.empty() && "Update operation without arguments"); - // Pass args as an argument to avoid capturing a structured binding. - const semantics::SomeExpr *atomArg = [&](auto &args) { - for (const semantics::SomeExpr &e : args) { - if (evaluate::IsSameOrConvertOf(e, atom)) - return &e; - } - llvm_unreachable("Atomic variable not in argument list"); - }(args); - - if (opcode == evaluate::operation::Operator::Min || - opcode == evaluate::operation::Operator::Max) { - // Min and max operations are expanded inline, so reduce them to - // operations with exactly two (non-optional) arguments. - rhs = genReducedMinMax(rhs, atomArg, args); - input = *evaluate::GetConvertInput(rhs); - std::tie(opcode, args) = - evaluate::GetTopLevelOperationIgnoreResizing(input); - atomArg = nullptr; // No longer valid. - } for (auto &arg : args) { if (!evaluate::IsSameOrConvertOf(arg, atom)) { mlir::Value val = fir::getBase(converter.genExprValue(arg, naCtx, &loc)); diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index b98ad3c..6b9bd66 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -19,6 +19,7 @@ #include "flang/Lower/Support/ReductionProcessor.h" #include "flang/Parser/tools.h" #include "flang/Semantics/tools.h" +#include "flang/Utils/OpenMP.h" #include "llvm/Frontend/OpenMP/OMP.h.inc" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" @@ -647,10 +648,8 @@ addAlignedClause(lower::AbstractConverter &converter, // The default alignment for some targets is equal to 0. // Do not generate alignment assumption if alignment is less than or equal to - // 0. - if (alignment > 0) { - // alignment value must be power of 2 - assert((alignment & (alignment - 1)) == 0 && "alignment is not power of 2"); + // 0 or not a power of two + if (alignment > 0 && ((alignment & (alignment - 1)) == 0)) { auto &objects = std::get<omp::ObjectList>(clause.t); if (!objects.empty()) genObjectList(objects, converter, alignedVars); @@ -1179,12 +1178,13 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result) const { } bool ClauseProcessor::processLink( - llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { return findRepeatableClause<omp::clause::Link>( [&](const omp::clause::Link &clause, const parser::CharBlock &) { // Case: declare target link(var1, var2)... gatherFuncAndVarSyms( - clause.v, mlir::omp::DeclareTargetCaptureClause::link, result); + clause.v, mlir::omp::DeclareTargetCaptureClause::link, result, + /*automap=*/false); }); } @@ -1280,7 +1280,7 @@ void ClauseProcessor::processMapObjects( auto location = mlir::NameLoc::get( mlir::StringAttr::get(firOpBuilder.getContext(), asFortran.str()), baseOp.getLoc()); - mlir::omp::MapInfoOp mapOp = createMapInfoOp( + mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp( firOpBuilder, location, baseOp, /*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds, /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{}, @@ -1507,26 +1507,27 @@ bool ClauseProcessor::processTaskReduction( } bool ClauseProcessor::processTo( - llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { return findRepeatableClause<omp::clause::To>( [&](const omp::clause::To &clause, const parser::CharBlock &) { // Case: declare target to(func, var1, var2)... gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), - mlir::omp::DeclareTargetCaptureClause::to, result); + mlir::omp::DeclareTargetCaptureClause::to, result, + /*automap=*/false); }); } bool ClauseProcessor::processEnter( - llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { return findRepeatableClause<omp::clause::Enter>( [&](const omp::clause::Enter &clause, const parser::CharBlock &source) { - mlir::Location currentLocation = converter.genLocation(source); - if (std::get<std::optional<omp::clause::Enter::Modifier>>(clause.t)) - TODO(currentLocation, "Declare target enter AUTOMAP modifier"); + bool automap = + std::get<std::optional<omp::clause::Enter::Modifier>>(clause.t) + .has_value(); // Case: declare target enter(func, var1, var2)... gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), mlir::omp::DeclareTargetCaptureClause::enter, - result); + result, automap); }); } diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h index f8a1f79..c46bdb3 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.h +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h @@ -118,7 +118,7 @@ public: bool processDepend(lower::SymMap &symMap, lower::StatementContext &stmtCtx, mlir::omp::DependClauseOps &result) const; bool - processEnter(llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const; + processEnter(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const; bool processIf(omp::clause::If::DirectiveNameModifier directiveName, mlir::omp::IfClauseOps &result) const; bool processInReduction( @@ -129,7 +129,7 @@ public: llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const; bool processLinear(mlir::omp::LinearClauseOps &result) const; bool - processLink(llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const; + processLink(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const; // This method is used to process a map clause. // The optional parameter mapSyms is used to store the original Fortran symbol @@ -150,7 +150,7 @@ public: bool processTaskReduction( mlir::Location currentLocation, mlir::omp::TaskReductionClauseOps &result, llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const; - bool processTo(llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const; + bool processTo(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const; bool processUseDeviceAddr( lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result, @@ -208,11 +208,15 @@ void ClauseProcessor::processTODO(mlir::Location currentLocation, if (!x) return; unsigned version = semaCtx.langOptions().OpenMPVersion; - TODO(currentLocation, - "Unhandled clause " + llvm::omp::getOpenMPClauseName(id).upper() + - " in " + - llvm::omp::getOpenMPDirectiveName(directive, version).upper() + - " construct"); + bool isSimdDirective = llvm::omp::getOpenMPDirectiveName(directive, version) + .upper() + .find("SIMD") != llvm::StringRef::npos; + if (!semaCtx.langOptions().OpenMPSimd || isSimdDirective) + TODO(currentLocation, + "Unhandled clause " + llvm::omp::getOpenMPClauseName(id).upper() + + " in " + + llvm::omp::getOpenMPDirectiveName(directive, version).upper() + + " construct"); }; for (ClauseIterator it = clauses.begin(); it != clauses.end(); ++it) diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index 7f75aae..1a16e1c 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -396,6 +396,8 @@ makePrescriptiveness(parser::OmpPrescriptiveness::Value v) { switch (v) { case parser::OmpPrescriptiveness::Value::Strict: return clause::Prescriptiveness::Strict; + case parser::OmpPrescriptiveness::Value::Fallback: + return clause::Prescriptiveness::Fallback; } llvm_unreachable("Unexpected prescriptiveness"); } @@ -770,6 +772,27 @@ Doacross make(const parser::OmpClause::Doacross &inp, // DynamicAllocators: empty +DynGroupprivate make(const parser::OmpClause::DynGroupprivate &inp, + semantics::SemanticsContext &semaCtx) { + // imp.v -> OmpDyngroupprivateClause + CLAUSET_ENUM_CONVERT( // + convert, parser::OmpAccessGroup::Value, DynGroupprivate::AccessGroup, + // clang-format off + MS(Cgroup, Cgroup) + // clang-format on + ); + + auto &mods = semantics::OmpGetModifiers(inp.v); + auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpAccessGroup>(mods); + auto *m1 = semantics::OmpGetUniqueModifier<parser::OmpPrescriptiveness>(mods); + auto &size = std::get<parser::ScalarIntExpr>(inp.v.t); + + return DynGroupprivate{ + {/*AccessGroup=*/maybeApplyToV(convert, m0), + /*Prescriptiveness=*/maybeApplyToV(makePrescriptiveness, m1), + /*Size=*/makeExpr(size, semaCtx)}}; +} + Enter make(const parser::OmpClause::Enter &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpEnterClause diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp index 67a9a46..146a252 100644 --- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp +++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp @@ -30,18 +30,27 @@ #include "flang/Semantics/tools.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/Frontend/OpenMP/OMP.h" +#include <variant> namespace Fortran { namespace lower { namespace omp { bool DataSharingProcessor::OMPConstructSymbolVisitor::isSymbolDefineBy( const semantics::Symbol *symbol, lower::pft::Evaluation &eval) const { - return eval.visit( - common::visitors{[&](const parser::OpenMPConstruct &functionParserNode) { - return symDefMap.count(symbol) && - symDefMap.at(symbol) == &functionParserNode; - }, - [](const auto &functionParserNode) { return false; }}); + return eval.visit(common::visitors{ + [&](const parser::OpenMPConstruct &functionParserNode) { + return symDefMap.count(symbol) && + symDefMap.at(symbol) == ConstructPtr(&functionParserNode); + }, + [](const auto &functionParserNode) { return false; }}); +} + +bool DataSharingProcessor::OMPConstructSymbolVisitor:: + isSymbolDefineByNestedDeclaration(const semantics::Symbol *symbol) const { + return symDefMap.count(symbol) && + std::holds_alternative<const parser::DeclarationConstruct *>( + symDefMap.at(symbol)); } static bool isConstructWithTopLevelTarget(lower::pft::Evaluation &eval) { @@ -81,13 +90,14 @@ DataSharingProcessor::DataSharingProcessor(lower::AbstractConverter &converter, isTargetPrivatization) {} void DataSharingProcessor::processStep1( - mlir::omp::PrivateClauseOps *clauseOps) { + mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir) { collectSymbolsForPrivatization(); collectDefaultSymbols(); collectImplicitSymbols(); collectPreDeterminedSymbols(); - privatize(clauseOps); + privatize(clauseOps, dir); insertBarrier(clauseOps); } @@ -414,47 +424,10 @@ static parser::CharBlock getSource(const semantics::SemanticsContext &semaCtx, }); } -static void collectPrivatizingConstructs( - llvm::SmallSet<llvm::omp::Directive, 16> &constructs, unsigned version) { - using Clause = llvm::omp::Clause; - using Directive = llvm::omp::Directive; - - static const Clause privatizingClauses[] = { - Clause::OMPC_private, - Clause::OMPC_lastprivate, - Clause::OMPC_firstprivate, - Clause::OMPC_in_reduction, - Clause::OMPC_reduction, - Clause::OMPC_linear, - // TODO: Clause::OMPC_induction, - Clause::OMPC_task_reduction, - Clause::OMPC_detach, - Clause::OMPC_use_device_ptr, - Clause::OMPC_is_device_ptr, - }; - - for (auto dir : llvm::enum_seq_inclusive<Directive>(Directive::First_, - Directive::Last_)) { - bool allowsPrivatizing = llvm::any_of(privatizingClauses, [&](Clause cls) { - return llvm::omp::isAllowedClauseForDirective(dir, cls, version); - }); - if (allowsPrivatizing) - constructs.insert(dir); - } -} - bool DataSharingProcessor::isOpenMPPrivatizingConstruct( const parser::OpenMPConstruct &omp, unsigned version) { - static llvm::SmallSet<llvm::omp::Directive, 16> privatizing; - [[maybe_unused]] static bool init = - (collectPrivatizingConstructs(privatizing, version), true); - - // As of OpenMP 6.0, privatizing constructs (with the test being if they - // allow a privatizing clause) are: dispatch, distribute, do, for, loop, - // parallel, scope, sections, simd, single, target, target_data, task, - // taskgroup, taskloop, and teams. - return llvm::is_contained(privatizing, - parser::omp::GetOmpDirectiveName(omp).v); + return llvm::omp::isPrivatizingConstruct( + parser::omp::GetOmpDirectiveName(omp).v, version); } bool DataSharingProcessor::isOpenMPPrivatizingEvaluation( @@ -550,11 +523,23 @@ void DataSharingProcessor::collectSymbols( return false; } - return sym->test(semantics::Symbol::Flag::OmpImplicit); + // Collect implicit symbols only if they are not defined by a nested + // `DeclarationConstruct`. If `sym` is not defined by the current OpenMP + // evaluation then it is defined by a block nested within the OpenMP + // construct. This, in turn, means that the private allocation for the + // symbol will be emitted as part of the nested block and there is no need + // to privatize it within the OpenMP construct. + return !visitor.isSymbolDefineByNestedDeclaration(sym) && + sym->test(semantics::Symbol::Flag::OmpImplicit); } - if (collectPreDetermined) - return sym->test(semantics::Symbol::Flag::OmpPreDetermined); + if (collectPreDetermined) { + // Similar to implicit symbols, collect pre-determined symbols only if + // they are not defined by a nested `DeclarationConstruct` + return visitor.isSymbolDefineBy(sym, eval) && + !visitor.isSymbolDefineByNestedDeclaration(sym) && + sym->test(semantics::Symbol::Flag::OmpPreDetermined); + } return !sym->test(semantics::Symbol::Flag::OmpImplicit) && !sym->test(semantics::Symbol::Flag::OmpPreDetermined); @@ -597,14 +582,15 @@ void DataSharingProcessor::collectPreDeterminedSymbols() { preDeterminedSymbols); } -void DataSharingProcessor::privatize(mlir::omp::PrivateClauseOps *clauseOps) { +void DataSharingProcessor::privatize(mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir) { for (const semantics::Symbol *sym : allPrivatizedSymbols) { if (const auto *commonDet = sym->detailsIf<semantics::CommonBlockDetails>()) { for (const auto &mem : commonDet->objects()) - privatizeSymbol(&*mem, clauseOps); + privatizeSymbol(&*mem, clauseOps, dir); } else - privatizeSymbol(sym, clauseOps); + privatizeSymbol(sym, clauseOps, dir); } } @@ -623,7 +609,8 @@ void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) { void DataSharingProcessor::privatizeSymbol( const semantics::Symbol *symToPrivatize, - mlir::omp::PrivateClauseOps *clauseOps) { + mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir) { if (!useDelayedPrivatization) { cloneSymbol(symToPrivatize); copyFirstPrivateSymbol(symToPrivatize); @@ -633,7 +620,7 @@ void DataSharingProcessor::privatizeSymbol( Fortran::lower::privatizeSymbol<mlir::omp::PrivateClauseOp, mlir::omp::PrivateClauseOps>( converter, firOpBuilder, symTable, allPrivatizedSymbols, - mightHaveReadHostSym, symToPrivatize, clauseOps); + mightHaveReadHostSym, symToPrivatize, clauseOps, dir); } } // namespace omp } // namespace lower diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h index 96e7fa6..f6aa865 100644 --- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h +++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h @@ -19,6 +19,7 @@ #include "flang/Parser/parse-tree.h" #include "flang/Semantics/symbol.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" +#include <variant> namespace mlir { namespace omp { @@ -58,20 +59,35 @@ private: } void Post(const parser::Name &name) { - auto *current = !constructs.empty() ? constructs.back() : nullptr; + auto current = !constructs.empty() ? constructs.back() : ConstructPtr(); symDefMap.try_emplace(name.symbol, current); } - llvm::SmallVector<const parser::OpenMPConstruct *> constructs; - llvm::DenseMap<semantics::Symbol *, const parser::OpenMPConstruct *> - symDefMap; + bool Pre(const parser::DeclarationConstruct &decl) { + constructs.push_back(&decl); + return true; + } + + void Post(const parser::DeclarationConstruct &decl) { + constructs.pop_back(); + } /// Given a \p symbol and an \p eval, returns true if eval is the OMP /// construct that defines symbol. bool isSymbolDefineBy(const semantics::Symbol *symbol, lower::pft::Evaluation &eval) const; + // Given a \p symbol, returns true if it is defined by a nested + // `DeclarationConstruct`. + bool + isSymbolDefineByNestedDeclaration(const semantics::Symbol *symbol) const; + private: + using ConstructPtr = std::variant<const parser::OpenMPConstruct *, + const parser::DeclarationConstruct *>; + llvm::SmallVector<ConstructPtr> constructs; + llvm::DenseMap<semantics::Symbol *, ConstructPtr> symDefMap; + unsigned version; }; @@ -91,7 +107,7 @@ private: lower::pft::Evaluation &eval; bool shouldCollectPreDeterminedSymbols; bool useDelayedPrivatization; - llvm::SmallSet<const semantics::Symbol *, 16> mightHaveReadHostSym; + llvm::SmallPtrSet<const semantics::Symbol *, 16> mightHaveReadHostSym; lower::SymMap &symTable; bool isTargetPrivatization; OMPConstructSymbolVisitor visitor; @@ -110,7 +126,8 @@ private: void collectDefaultSymbols(); void collectImplicitSymbols(); void collectPreDeterminedSymbols(); - void privatize(mlir::omp::PrivateClauseOps *clauseOps); + void privatize(mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir = std::nullopt); void copyLastPrivatize(mlir::Operation *op); void insertLastPrivateCompare(mlir::Operation *op); void cloneSymbol(const semantics::Symbol *sym); @@ -151,7 +168,8 @@ public: // Step2 performs the copying for lastprivates and requires knowledge of the // MLIR operation to insert the last private update. Step2 adds // dealocation code as well. - void processStep1(mlir::omp::PrivateClauseOps *clauseOps = nullptr); + void processStep1(mlir::omp::PrivateClauseOps *clauseOps = nullptr, + std::optional<llvm::omp::Directive> dir = std::nullopt); void processStep2(mlir::Operation *op, bool isLoop); void pushLoopIV(mlir::Value iv) { loopIVs.push_back(iv); } @@ -168,7 +186,8 @@ public: } void privatizeSymbol(const semantics::Symbol *symToPrivatize, - mlir::omp::PrivateClauseOps *clauseOps); + mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir = std::nullopt); }; } // namespace omp diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 4ce9a0e..574c322 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -38,6 +38,7 @@ #include "flang/Semantics/tools.h" #include "flang/Support/Flags.h" #include "flang/Support/OpenMP-utils.h" +#include "flang/Utils/OpenMP.h" #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/Support/StateStack.h" @@ -47,6 +48,7 @@ using namespace Fortran::lower::omp; using namespace Fortran::common::openmp; +using namespace Fortran::utils::openmp; //===----------------------------------------------------------------------===// // Code generation helper functions @@ -407,7 +409,7 @@ static void processHostEvalClauses(lower::AbstractConverter &converter, const parser::OmpClauseList *endClauseList = nullptr; common::visit( common::visitors{ - [&](const parser::OpenMPBlockConstruct &ompConstruct) { + [&](const parser::OmpBlockConstruct &ompConstruct) { beginClauseList = &ompConstruct.BeginDir().Clauses(); if (auto &endSpec = ompConstruct.EndDir()) endClauseList = &endSpec->Clauses(); @@ -534,6 +536,13 @@ static void processHostEvalClauses(lower::AbstractConverter &converter, cp.processCollapse(loc, eval, hostInfo->ops, hostInfo->iv); break; + case OMPD_teams_workdistribute: + cp.processThreadLimit(stmtCtx, hostInfo->ops); + [[fallthrough]]; + case OMPD_target_teams_workdistribute: + cp.processNumTeams(stmtCtx, hostInfo->ops); + break; + // Standalone 'target' case. case OMPD_target: { processSingleNestedIf( @@ -765,14 +774,14 @@ static void getDeclareTargetInfo( lower::pft::Evaluation &eval, const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct, mlir::omp::DeclareTargetOperands &clauseOps, - llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause) { + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause) { const auto &spec = std::get<parser::OmpDeclareTargetSpecifier>(declareTargetConstruct.t); if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) { ObjectList objects{makeObjects(*objectList, semaCtx)}; // Case: declare target(func, var1, var2) gatherFuncAndVarSyms(objects, mlir::omp::DeclareTargetCaptureClause::to, - symbolAndClause); + symbolAndClause, /*automap=*/false); } else if (const auto *clauseList{ parser::Unwrap<parser::OmpClauseList>(spec.u)}) { List<Clause> clauses = makeClauses(*clauseList, semaCtx); @@ -805,21 +814,20 @@ static void collectDeferredDeclareTargets( llvm::SmallVectorImpl<lower::OMPDeferredDeclareTargetInfo> &deferredDeclareTarget) { mlir::omp::DeclareTargetOperands clauseOps; - llvm::SmallVector<DeclareTargetCapturePair> symbolAndClause; + llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause; getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct, clauseOps, symbolAndClause); // Return the device type only if at least one of the targets for the // directive is a function or subroutine mlir::ModuleOp mod = converter.getFirOpBuilder().getModule(); - for (const DeclareTargetCapturePair &symClause : symbolAndClause) { - mlir::Operation *op = mod.lookupSymbol( - converter.mangleName(std::get<const semantics::Symbol &>(symClause))); + for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) { + mlir::Operation *op = + mod.lookupSymbol(converter.mangleName(symClause.symbol)); if (!op) { - deferredDeclareTarget.push_back({std::get<0>(symClause), - clauseOps.deviceType, - std::get<1>(symClause)}); + deferredDeclareTarget.push_back({symClause.clause, clauseOps.deviceType, + symClause.automap, symClause.symbol}); } } } @@ -830,16 +838,16 @@ getDeclareTargetFunctionDevice( lower::pft::Evaluation &eval, const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct) { mlir::omp::DeclareTargetOperands clauseOps; - llvm::SmallVector<DeclareTargetCapturePair> symbolAndClause; + llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause; getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct, clauseOps, symbolAndClause); // Return the device type only if at least one of the targets for the // directive is a function or subroutine mlir::ModuleOp mod = converter.getFirOpBuilder().getModule(); - for (const DeclareTargetCapturePair &symClause : symbolAndClause) { - mlir::Operation *op = mod.lookupSymbol( - converter.mangleName(std::get<const semantics::Symbol &>(symClause))); + for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) { + mlir::Operation *op = + mod.lookupSymbol(converter.mangleName(symClause.symbol)); if (mlir::isa_and_nonnull<mlir::func::FuncOp>(op)) return clauseOps.deviceType; @@ -1056,7 +1064,7 @@ getImplicitMapTypeAndKind(fir::FirOpBuilder &firOpBuilder, static void markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter, mlir::omp::DeclareTargetCaptureClause captureClause, - mlir::omp::DeclareTargetDeviceType deviceType) { + mlir::omp::DeclareTargetDeviceType deviceType, bool automap) { // TODO: Add support for program local variables with declare target applied auto declareTargetOp = llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(op); if (!declareTargetOp) @@ -1071,11 +1079,11 @@ markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter, if (declareTargetOp.isDeclareTarget()) { if (declareTargetOp.getDeclareTargetDeviceType() != deviceType) declareTargetOp.setDeclareTarget(mlir::omp::DeclareTargetDeviceType::any, - captureClause); + captureClause, automap); return; } - declareTargetOp.setDeclareTarget(deviceType, captureClause); + declareTargetOp.setDeclareTarget(deviceType, captureClause, automap); } //===----------------------------------------------------------------------===// @@ -2263,7 +2271,8 @@ genOrderedOp(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, mlir::Location loc, const ConstructQueue &queue, ConstructQueue::const_iterator item) { - TODO(loc, "OMPD_ordered"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(loc, "OMPD_ordered"); return nullptr; } @@ -2450,7 +2459,8 @@ genScopeOp(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, mlir::Location loc, const ConstructQueue &queue, ConstructQueue::const_iterator item) { - TODO(loc, "Scope construct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(loc, "Scope construct"); return nullptr; } @@ -2819,6 +2829,17 @@ genTeamsOp(lower::AbstractConverter &converter, lower::SymMap &symTable, queue, item, clauseOps); } +static mlir::omp::WorkdistributeOp genWorkdistributeOp( + lower::AbstractConverter &converter, lower::SymMap &symTable, + semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, + mlir::Location loc, const ConstructQueue &queue, + ConstructQueue::const_iterator item) { + return genOpWithBody<mlir::omp::WorkdistributeOp>( + OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, + llvm::omp::Directive::OMPD_workdistribute), + queue, item); +} + //===----------------------------------------------------------------------===// // Code generation functions for the standalone version of constructs that can // also be a leaf of a composite construct @@ -3236,7 +3257,7 @@ static mlir::omp::WsloopOp genCompositeDoSimd( DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval, /*shouldCollectPreDeterminedSymbols=*/true, /*useDelayedPrivatization=*/true, symTable); - simdItemDSP.processStep1(&simdClauseOps); + simdItemDSP.processStep1(&simdClauseOps, simdItem->id); // Pass the innermost leaf construct's clauses because that's where COLLAPSE // is placed by construct decomposition. @@ -3277,7 +3298,8 @@ static mlir::omp::TaskloopOp genCompositeTaskloopSimd( lower::pft::Evaluation &eval, mlir::Location loc, const ConstructQueue &queue, ConstructQueue::const_iterator item) { assert(std::distance(item, queue.end()) == 2 && "Invalid leaf constructs"); - TODO(loc, "Composite TASKLOOP SIMD"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(loc, "Composite TASKLOOP SIMD"); return nullptr; } @@ -3449,13 +3471,18 @@ static void genOMPDispatch(lower::AbstractConverter &converter, break; case llvm::omp::Directive::OMPD_tile: { unsigned version = semaCtx.langOptions().OpenMPVersion; - TODO(loc, "Unhandled loop directive (" + - llvm::omp::getOpenMPDirectiveName(dir, version) + ")"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(loc, "Unhandled loop directive (" + + llvm::omp::getOpenMPDirectiveName(dir, version) + ")"); + break; } case llvm::omp::Directive::OMPD_unroll: genUnrollOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item); break; - // case llvm::omp::Directive::OMPD_workdistribute: + case llvm::omp::Directive::OMPD_workdistribute: + newOp = genWorkdistributeOp(converter, symTable, semaCtx, eval, loc, queue, + item); + break; case llvm::omp::Directive::OMPD_workshare: newOp = genWorkshareOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item); @@ -3485,35 +3512,40 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDeclarativeAllocate &declarativeAllocate) { - TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDeclarativeAssumes &assumesConstruct) { - TODO(converter.getCurrentLocation(), "OpenMP ASSUMES declaration"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMP ASSUMES declaration"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OmpDeclareVariantDirective &declareVariantDirective) { - TODO(converter.getCurrentLocation(), "OmpDeclareVariantDirective"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OmpDeclareVariantDirective"); } static void genOMP( lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDeclareReductionConstruct &declareReductionConstruct) { - TODO(converter.getCurrentLocation(), "OpenMPDeclareReductionConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPDeclareReductionConstruct"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDeclareSimdConstruct &declareSimdConstruct) { - TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); } static void @@ -3564,14 +3596,14 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct) { mlir::omp::DeclareTargetOperands clauseOps; - llvm::SmallVector<DeclareTargetCapturePair> symbolAndClause; + llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause; mlir::ModuleOp mod = converter.getFirOpBuilder().getModule(); getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct, clauseOps, symbolAndClause); - for (const DeclareTargetCapturePair &symClause : symbolAndClause) { - mlir::Operation *op = mod.lookupSymbol( - converter.mangleName(std::get<const semantics::Symbol &>(symClause))); + for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) { + mlir::Operation *op = + mod.lookupSymbol(converter.mangleName(symClause.symbol)); // Some symbols are deferred until later in the module, these are handled // upon finalization of the module for OpenMP inside of Bridge, so we simply @@ -3579,16 +3611,21 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, if (!op) continue; - markDeclareTarget( - op, converter, - std::get<mlir::omp::DeclareTargetCaptureClause>(symClause), - clauseOps.deviceType); + markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType, + symClause.automap); } } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, + const parser::OpenMPGroupprivate &directive) { + TODO(converter.getCurrentLocation(), "GROUPPRIVATE"); +} + +static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, + semantics::SemanticsContext &semaCtx, + lower::pft::Evaluation &eval, const parser::OpenMPRequiresConstruct &requiresConstruct) { // Requires directives are gathered and processed in semantics and // then combined in the lowering bridge before triggering codegen @@ -3709,14 +3746,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, (void)objects; (void)clauses; - TODO(converter.getCurrentLocation(), "OpenMPDepobjConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPDepobjConstruct"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPInteropConstruct &interopConstruct) { - TODO(converter.getCurrentLocation(), "OpenMPInteropConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPInteropConstruct"); } static void @@ -3732,7 +3771,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPAllocatorsConstruct &allocsConstruct) { - TODO(converter.getCurrentLocation(), "OpenMPAllocatorsConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPAllocatorsConstruct"); } //===----------------------------------------------------------------------===// @@ -3749,7 +3789,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, - const parser::OpenMPBlockConstruct &blockConstruct) { + const parser::OmpBlockConstruct &blockConstruct) { const parser::OmpDirectiveSpecification &beginSpec = blockConstruct.BeginDir(); List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx); @@ -3798,7 +3838,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, !std::holds_alternative<clause::Detach>(clause.u)) { std::string name = parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(clause.id)); - TODO(clauseLocation, name + " clause is not implemented yet"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(clauseLocation, name + " clause is not implemented yet"); } } @@ -3814,7 +3855,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, lower::pft::Evaluation &eval, const parser::OpenMPAssumeConstruct &assumeConstruct) { mlir::Location clauseLocation = converter.genLocation(assumeConstruct.source); - TODO(clauseLocation, "OpenMP ASSUME construct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(clauseLocation, "OpenMP ASSUME construct"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, @@ -3850,21 +3892,24 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPUtilityConstruct &) { - TODO(converter.getCurrentLocation(), "OpenMPUtilityConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPUtilityConstruct"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPDispatchConstruct &) { - TODO(converter.getCurrentLocation(), "OpenMPDispatchConstruct"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPDispatchConstruct"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPExecutableAllocate &execAllocConstruct) { - TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); + if (!semaCtx.langOptions().OpenMPSimd) + TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); } static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, @@ -3936,9 +3981,12 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, List<Clause> clauses = makeClauses( std::get<parser::OmpClauseList>(beginSectionsDirective.t), semaCtx); const auto &endSectionsDirective = - std::get<parser::OmpEndSectionsDirective>(sectionsConstruct.t); + std::get<std::optional<parser::OmpEndSectionsDirective>>( + sectionsConstruct.t); + assert(endSectionsDirective && + "Missing end section directive should have been handled in semantics"); clauses.append(makeClauses( - std::get<parser::OmpClauseList>(endSectionsDirective.t), semaCtx)); + std::get<parser::OmpClauseList>(endSectionsDirective->t), semaCtx)); mlir::Location currentLocation = converter.getCurrentLocation(); llvm::omp::Directive directive = @@ -4102,7 +4150,7 @@ void Fortran::lower::genDeclareTargetIntGlobal( bool Fortran::lower::isOpenMPTargetConstruct( const parser::OpenMPConstruct &omp) { llvm::omp::Directive dir = llvm::omp::Directive::OMPD_unknown; - if (const auto *block = std::get_if<parser::OpenMPBlockConstruct>(&omp.u)) { + if (const auto *block = std::get_if<parser::OmpBlockConstruct>(&omp.u)) { dir = block->BeginDir().DirId(); } else if (const auto *loop = std::get_if<parser::OpenMPLoopConstruct>(&omp.u)) { @@ -4176,7 +4224,7 @@ bool Fortran::lower::markOpenMPDeferredDeclareTargetFunctions( deviceCodeFound = true; markDeclareTarget(op, converter, declTar.declareTargetCaptureClause, - devType); + devType, declTar.automap); } return deviceCodeFound; diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp index 13fda97..cb6dd57 100644 --- a/flang/lib/Lower/OpenMP/Utils.cpp +++ b/flang/lib/Lower/OpenMP/Utils.cpp @@ -24,6 +24,7 @@ #include <flang/Parser/parse-tree.h> #include <flang/Parser/tools.h> #include <flang/Semantics/tools.h> +#include <flang/Utils/OpenMP.h> #include <llvm/Support/CommandLine.h> #include <iterator> @@ -102,41 +103,10 @@ getIterationVariableSymbol(const lower::pft::Evaluation &eval) { void gatherFuncAndVarSyms( const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause, - llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause) { + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause, + bool automap) { for (const Object &object : objects) - symbolAndClause.emplace_back(clause, *object.sym()); -} - -mlir::omp::MapInfoOp -createMapInfoOp(fir::FirOpBuilder &builder, mlir::Location loc, - mlir::Value baseAddr, mlir::Value varPtrPtr, - llvm::StringRef name, llvm::ArrayRef<mlir::Value> bounds, - llvm::ArrayRef<mlir::Value> members, - mlir::ArrayAttr membersIndex, uint64_t mapType, - mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, - bool partialMap, mlir::FlatSymbolRefAttr mapperId) { - if (auto boxTy = llvm::dyn_cast<fir::BaseBoxType>(baseAddr.getType())) { - baseAddr = fir::BoxAddrOp::create(builder, loc, baseAddr); - retTy = baseAddr.getType(); - } - - mlir::TypeAttr varType = mlir::TypeAttr::get( - llvm::cast<mlir::omp::PointerLikeType>(retTy).getElementType()); - - // For types with unknown extents such as <2x?xi32> we discard the incomplete - // type info and only retain the base type. The correct dimensions are later - // recovered through the bounds info. - if (auto seqType = llvm::dyn_cast<fir::SequenceType>(varType.getValue())) - if (seqType.hasDynamicExtents()) - varType = mlir::TypeAttr::get(seqType.getEleTy()); - - mlir::omp::MapInfoOp op = mlir::omp::MapInfoOp::create( - builder, loc, retTy, baseAddr, varType, - builder.getIntegerAttr(builder.getIntegerType(64, false), mapType), - builder.getAttr<mlir::omp::VariableCaptureKindAttr>(mapCaptureType), - varPtrPtr, members, membersIndex, bounds, mapperId, - builder.getStringAttr(name), builder.getBoolAttr(partialMap)); - return op; + symbolAndClause.emplace_back(clause, *object.sym(), automap); } // This function gathers the individual omp::Object's that make up a @@ -402,7 +372,7 @@ mlir::Value createParentSymAndGenIntermediateMaps( // Create a map for the intermediate member and insert it and it's // indices into the parentMemberIndices list to track it. - mlir::omp::MapInfoOp mapOp = createMapInfoOp( + mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp( firOpBuilder, clauseLocation, curValue, /*varPtrPtr=*/mlir::Value{}, asFortran, /*bounds=*/interimBounds, @@ -562,7 +532,7 @@ void insertChildMapInfoIntoParent( converter.getCurrentLocation(), asFortran, bounds, treatIndexAsSection); - mlir::omp::MapInfoOp mapOp = createMapInfoOp( + mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp( firOpBuilder, info.rawInput.getLoc(), info.rawInput, /*varPtrPtr=*/mlir::Value(), asFortran.str(), bounds, members, firOpBuilder.create2DI64ArrayAttr( diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h index 11641ba..88371ab 100644 --- a/flang/lib/Lower/OpenMP/Utils.h +++ b/flang/lib/Lower/OpenMP/Utils.h @@ -42,8 +42,15 @@ class AbstractConverter; namespace omp { -using DeclareTargetCapturePair = - std::pair<mlir::omp::DeclareTargetCaptureClause, const semantics::Symbol &>; +struct DeclareTargetCaptureInfo { + mlir::omp::DeclareTargetCaptureClause clause; + bool automap = false; + const semantics::Symbol &symbol; + + DeclareTargetCaptureInfo(mlir::omp::DeclareTargetCaptureClause c, + const semantics::Symbol &s, bool a = false) + : clause(c), automap(a), symbol(s) {} +}; // A small helper structure for keeping track of a component members MapInfoOp // and index data when lowering OpenMP map clauses. Keeps track of the @@ -107,16 +114,6 @@ struct OmpMapParentAndMemberData { semantics::SemanticsContext &semaCtx); }; -mlir::omp::MapInfoOp -createMapInfoOp(fir::FirOpBuilder &builder, mlir::Location loc, - mlir::Value baseAddr, mlir::Value varPtrPtr, - llvm::StringRef name, llvm::ArrayRef<mlir::Value> bounds, - llvm::ArrayRef<mlir::Value> members, - mlir::ArrayAttr membersIndex, uint64_t mapType, - mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, - bool partialMap = false, - mlir::FlatSymbolRefAttr mapperId = mlir::FlatSymbolRefAttr()); - void insertChildMapInfoIntoParent( Fortran::lower::AbstractConverter &converter, Fortran::semantics::SemanticsContext &semaCtx, @@ -150,7 +147,8 @@ getIterationVariableSymbol(const lower::pft::Evaluation &eval); void gatherFuncAndVarSyms( const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause, - llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause); + llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause, + bool automap = false); int64_t getCollapseValue(const List<Clause> &clauses); diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp index a28cc01..80f31c2 100644 --- a/flang/lib/Lower/PFTBuilder.cpp +++ b/flang/lib/Lower/PFTBuilder.cpp @@ -1742,11 +1742,11 @@ private: layeredVarList[i].end()); } - llvm::SmallSet<const semantics::Symbol *, 32> seen; + llvm::SmallPtrSet<const semantics::Symbol *, 32> seen; std::vector<Fortran::lower::pft::VariableList> layeredVarList; - llvm::SmallSet<const semantics::Symbol *, 32> aliasSyms; + llvm::SmallPtrSet<const semantics::Symbol *, 32> aliasSyms; /// Set of scopes that have been analyzed for aliases. - llvm::SmallSet<const semantics::Scope *, 4> analyzedScopes; + llvm::SmallPtrSet<const semantics::Scope *, 4> analyzedScopes; std::vector<Fortran::lower::pft::Variable::AggregateStore> stores; }; } // namespace diff --git a/flang/lib/Lower/Runtime.cpp b/flang/lib/Lower/Runtime.cpp index fc59a24..494dd49 100644 --- a/flang/lib/Lower/Runtime.cpp +++ b/flang/lib/Lower/Runtime.cpp @@ -39,8 +39,7 @@ static void genUnreachable(fir::FirOpBuilder &builder, mlir::Location loc) { if (parentOp->getDialect()->getNamespace() == mlir::omp::OpenMPDialect::getDialectNamespace()) Fortran::lower::genOpenMPTerminator(builder, parentOp, loc); - else if (parentOp->getDialect()->getNamespace() == - mlir::acc::OpenACCDialect::getDialectNamespace()) + else if (Fortran::lower::isInsideOpenACCComputeConstruct(builder)) Fortran::lower::genOpenACCTerminator(builder, parentOp, loc); else fir::UnreachableOp::create(builder, loc); diff --git a/flang/lib/Lower/Support/PrivateReductionUtils.cpp b/flang/lib/Lower/Support/PrivateReductionUtils.cpp index fff060b..1b09801 100644 --- a/flang/lib/Lower/Support/PrivateReductionUtils.cpp +++ b/flang/lib/Lower/Support/PrivateReductionUtils.cpp @@ -616,6 +616,8 @@ void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() { assert(sym && "Symbol information is required to privatize derived types"); assert(!scalarInitValue && "ScalarInitvalue is unused for privatization"); } + if (hlfir::Entity{moldArg}.isAssumedRank()) + TODO(loc, "Privatization of assumed rank variable"); mlir::Type valTy = fir::unwrapRefType(argType); if (fir::isa_trivial(valTy)) { diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp index 881401e..1b4d37e 100644 --- a/flang/lib/Lower/Support/Utils.cpp +++ b/flang/lib/Lower/Support/Utils.cpp @@ -654,8 +654,9 @@ void privatizeSymbol( lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, lower::SymMap &symTable, llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, - llvm::SmallSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, - const semantics::Symbol *symToPrivatize, OperandsStructType *clauseOps) { + llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, + const semantics::Symbol *symToPrivatize, OperandsStructType *clauseOps, + std::optional<llvm::omp::Directive> dir) { constexpr bool isDoConcurrent = std::is_same_v<OpType, fir::LocalitySpecifierOp>; mlir::OpBuilder::InsertPoint dcIP; @@ -676,6 +677,13 @@ void privatizeSymbol( bool emitCopyRegion = symToPrivatize->test(semantics::Symbol::Flag::OmpFirstPrivate) || symToPrivatize->test(semantics::Symbol::Flag::LocalityLocalInit); + // A symbol attached to the simd directive can have the firstprivate flag set + // on it when it is also used in a non-firstprivate privatization clause. + // For instance: $omp do simd lastprivate(a) firstprivate(a) + // We cannot apply the firstprivate privatizer to simd, so make sure we do + // not emit the copy region when dealing with the SIMD directive. + if (dir && dir == llvm::omp::Directive::OMPD_simd) + emitCopyRegion = false; mlir::Value privVal = hsb.getAddr(); mlir::Type allocType = privVal.getType(); @@ -846,17 +854,19 @@ privatizeSymbol<mlir::omp::PrivateClauseOp, mlir::omp::PrivateClauseOps>( lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, lower::SymMap &symTable, llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, - llvm::SmallSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, + llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, const semantics::Symbol *symToPrivatize, - mlir::omp::PrivateClauseOps *clauseOps); + mlir::omp::PrivateClauseOps *clauseOps, + std::optional<llvm::omp::Directive> dir); template void privatizeSymbol<fir::LocalitySpecifierOp, fir::LocalitySpecifierOperands>( lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, lower::SymMap &symTable, llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, - llvm::SmallSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, + llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, const semantics::Symbol *symToPrivatize, - fir::LocalitySpecifierOperands *clauseOps); + fir::LocalitySpecifierOperands *clauseOps, + std::optional<llvm::omp::Directive> dir); } // end namespace Fortran::lower |