diff options
Diffstat (limited to 'flang/lib')
31 files changed, 1228 insertions, 664 deletions
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp index 21e6b3c..9c059b0 100644 --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -1693,17 +1693,17 @@ struct ArgumentExtractor // to int(kind=4) for example. return (*this)(x.template operand<0>()); } else { - return std::make_pair(operation::OperationCode(x), + return std::make_pair(operation::OperationCode(x.derived()), OperationArgs(x, std::index_sequence_for<Os...>{})); } } template <typename T> Result operator()(const Designator<T> &x) const { - return {operation::Operator::Identity, {AsSomeExpr(x)}}; + return {operation::OperationCode(x), {AsSomeExpr(x)}}; } template <typename T> Result operator()(const Constant<T> &x) const { - return {operation::Operator::Identity, {AsSomeExpr(x)}}; + return {operation::OperationCode(x), {AsSomeExpr(x)}}; } template <typename... Rs> @@ -1793,6 +1793,10 @@ std::string operation::ToString(operation::Operator op) { llvm_unreachable("Unhandler operator"); } +operation::Operator operation::OperationCode(const Relational<SomeType> &op) { + return common::visit([](auto &&s) { return OperationCode(s); }, op.u); +} + operation::Operator operation::OperationCode(const ProcedureDesignator &proc) { Operator code{llvm::StringSwitch<Operator>(proc.GetName()) .Case("associated", Operator::Associated) @@ -1809,10 +1813,15 @@ operation::Operator operation::OperationCode(const ProcedureDesignator &proc) { } std::pair<operation::Operator, std::vector<Expr<SomeType>>> -GetTopLevelOperation(const Expr<SomeType> &expr) { +GetTopLevelOperationIgnoreResizing(const Expr<SomeType> &expr) { return operation::ArgumentExtractor<true>{}(expr); } +std::pair<operation::Operator, std::vector<Expr<SomeType>>> +GetTopLevelOperation(const Expr<SomeType> &expr) { + return operation::ArgumentExtractor<false>{}(expr); +} + namespace operation { struct ConvertCollector : public Traverse<ConvertCollector, @@ -1936,6 +1945,33 @@ bool IsSameOrConvertOf(const Expr<SomeType> &expr, const Expr<SomeType> &x) { return false; } } + +struct VariableFinder : public evaluate::AnyTraverse<VariableFinder> { + using Base = evaluate::AnyTraverse<VariableFinder>; + using SomeExpr = Expr<SomeType>; + VariableFinder(const SomeExpr &v) : Base(*this), var(v) {} + + using Base::operator(); + + template <typename T> + bool operator()(const evaluate::Designator<T> &x) const { + return evaluate::AsGenericExpr(common::Clone(x)) == var; + } + + template <typename T> + bool operator()(const evaluate::FunctionRef<T> &x) const { + return evaluate::AsGenericExpr(common::Clone(x)) == var; + } + +private: + const SomeExpr &var; +}; + +bool IsVarSubexpressionOf( + const Expr<SomeType> &sub, const Expr<SomeType> &super) { + return VariableFinder{sub}(super); +} + } // namespace Fortran::evaluate namespace Fortran::semantics { diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp index 2e0f91f..cd8ddda 100644 --- a/flang/lib/Frontend/CompilerInstance.cpp +++ b/flang/lib/Frontend/CompilerInstance.cpp @@ -233,10 +233,8 @@ clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> CompilerInstance::createDiagnostics(clang::DiagnosticOptions &opts, clang::DiagnosticConsumer *client, bool shouldOwnClient) { - clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( - new clang::DiagnosticIDs()); - clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( - new clang::DiagnosticsEngine(diagID, opts)); + auto diags = llvm::makeIntrusiveRefCnt<clang::DiagnosticsEngine>( + clang::DiagnosticIDs::create(), opts); // Create the diagnostic client for reporting errors or for // implementing -verify. diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index f55d866..111c5aa4 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -512,6 +512,16 @@ static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) { args.getLastArg(clang::driver::options::OPT_triple)) opts.triple = a->getValue(); + opts.atomicIgnoreDenormalMode = args.hasFlag( + clang::driver::options::OPT_fatomic_ignore_denormal_mode, + clang::driver::options::OPT_fno_atomic_ignore_denormal_mode, false); + opts.atomicFineGrainedMemory = args.hasFlag( + clang::driver::options::OPT_fatomic_fine_grained_memory, + clang::driver::options::OPT_fno_atomic_fine_grained_memory, false); + opts.atomicRemoteMemory = + args.hasFlag(clang::driver::options::OPT_fatomic_remote_memory, + clang::driver::options::OPT_fno_atomic_remote_memory, false); + if (const llvm::opt::Arg *a = args.getLastArg(clang::driver::options::OPT_target_cpu)) opts.cpu = a->getValue(); diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 92aae79..75048c1 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -2167,10 +2167,35 @@ private: /// - structured and unstructured concurrent loops void genFIR(const Fortran::parser::DoConstruct &doConstruct) { setCurrentPositionAt(doConstruct); - // Collect loop nest information. - // Generate begin loop code directly for infinite and while loops. Fortran::lower::pft::Evaluation &eval = getEval(); bool unstructuredContext = eval.lowerAsUnstructured(); + + // Loops with induction variables inside OpenACC compute constructs + // need special handling to ensure that the IVs are privatized. + if (Fortran::lower::isInsideOpenACCComputeConstruct(*builder)) { + mlir::Operation *loopOp = Fortran::lower::genOpenACCLoopFromDoConstruct( + *this, bridge.getSemanticsContext(), localSymbols, doConstruct, eval); + bool success = loopOp != nullptr; + if (success) { + // Sanity check that the builder insertion point is inside the newly + // generated loop. + assert( + loopOp->getRegion(0).isAncestor( + builder->getInsertionPoint()->getBlock()->getParent()) && + "builder insertion point is not inside the newly generated loop"); + + // Loop body code. + auto iter = eval.getNestedEvaluations().begin(); + for (auto end = --eval.getNestedEvaluations().end(); iter != end; + ++iter) + genFIR(*iter, unstructuredContext); + return; + } + // Fall back to normal loop handling. + } + + // Collect loop nest information. + // Generate begin loop code directly for infinite and while loops. Fortran::lower::pft::Evaluation &doStmtEval = eval.getFirstNestedEvaluation(); auto *doStmt = doStmtEval.getIf<Fortran::parser::NonLabelDoStmt>(); @@ -3124,7 +3149,7 @@ private: Fortran::lower::pft::Evaluation *curEval = &getEval(); if (accLoop || accCombined) { - int64_t loopCount; + uint64_t loopCount; if (accLoop) { const Fortran::parser::AccBeginLoopDirective &beginLoopDir = std::get<Fortran::parser::AccBeginLoopDirective>(accLoop->t); @@ -3142,7 +3167,7 @@ private: if (curEval->lowerAsStructured()) { curEval = &curEval->getFirstNestedEvaluation(); - for (int64_t i = 1; i < loopCount; i++) + for (uint64_t i = 1; i < loopCount; i++) curEval = &*std::next(curEval->getNestedEvaluations().begin()); } } @@ -6682,27 +6707,30 @@ Fortran::lower::LoweringBridge::LoweringBridge( loweringOptions{loweringOptions}, envDefaults{envDefaults}, languageFeatures{languageFeatures} { // Register the diagnostic handler. - context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) { - llvm::raw_ostream &os = llvm::errs(); - switch (diag.getSeverity()) { - case mlir::DiagnosticSeverity::Error: - os << "error: "; - break; - case mlir::DiagnosticSeverity::Remark: - os << "info: "; - break; - case mlir::DiagnosticSeverity::Warning: - os << "warning: "; - break; - default: - break; - } - if (!mlir::isa<mlir::UnknownLoc>(diag.getLocation())) - os << diag.getLocation() << ": "; - os << diag << '\n'; - os.flush(); - return mlir::success(); - }); + if (loweringOptions.getRegisterMLIRDiagnosticsHandler()) { + diagHandlerID = + context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) { + llvm::raw_ostream &os = llvm::errs(); + switch (diag.getSeverity()) { + case mlir::DiagnosticSeverity::Error: + os << "error: "; + break; + case mlir::DiagnosticSeverity::Remark: + os << "info: "; + break; + case mlir::DiagnosticSeverity::Warning: + os << "warning: "; + break; + default: + break; + } + if (!mlir::isa<mlir::UnknownLoc>(diag.getLocation())) + os << diag.getLocation() << ": "; + os << diag << '\n'; + os.flush(); + return mlir::success(); + }); + } auto getPathLocation = [&semanticsContext, &context]() -> mlir::Location { std::optional<std::string> path; @@ -6733,6 +6761,10 @@ Fortran::lower::LoweringBridge::LoweringBridge( fir::setKindMapping(*module, kindMap); fir::setTargetCPU(*module, targetMachine.getTargetCPU()); fir::setTuneCPU(*module, targetOpts.cpuToTuneFor); + fir::setAtomicIgnoreDenormalMode(*module, + targetOpts.atomicIgnoreDenormalMode); + fir::setAtomicFineGrainedMemory(*module, targetOpts.atomicFineGrainedMemory); + fir::setAtomicRemoteMemory(*module, targetOpts.atomicRemoteMemory); fir::setTargetFeatures(*module, targetMachine.getTargetFeatureString()); fir::support::setMLIRDataLayout(*module, targetMachine.createDataLayout()); fir::setIdent(*module, Fortran::common::getFlangFullVersion()); @@ -6740,6 +6772,11 @@ Fortran::lower::LoweringBridge::LoweringBridge( fir::setCommandline(*module, *cgOpts.RecordCommandLine); } +Fortran::lower::LoweringBridge::~LoweringBridge() { + if (diagHandlerID) + context.getDiagEngine().eraseHandler(*diagHandlerID); +} + void Fortran::lower::genCleanUpInRegionIfAny( mlir::Location loc, fir::FirOpBuilder &builder, mlir::Region ®ion, Fortran::lower::StatementContext &context) { diff --git a/flang/lib/Lower/Mangler.cpp b/flang/lib/Lower/Mangler.cpp index 1333e3fe..e1ae86a 100644 --- a/flang/lib/Lower/Mangler.cpp +++ b/flang/lib/Lower/Mangler.cpp @@ -224,8 +224,18 @@ std::string Fortran::lower::mangle::mangleName( assert(paramExpr && "derived type kind param not explicit"); std::optional<int64_t> init = Fortran::evaluate::ToInt64(paramValue->GetExplicit()); - assert(init && "derived type kind param is not constant"); - kinds.emplace_back(*init); + // TODO: put the assertion check back when parametrized derived types + // are supported: + // assert(init && "derived type kind param is not constant"); + // + // The init parameter above will require a FoldingContext for proper + // expression evaluation to an integer constant, otherwise the + // compiler may crash here (see example in issue #127424). + if (!init) { + TODO_NOLOC("parameterized derived types"); + } else { + kinds.emplace_back(*init); + } } } return fir::NameUniquer::doType(modules, procs, blockId, symbolName, kinds); diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp index 471f368..57ce1d3 100644 --- a/flang/lib/Lower/OpenACC.cpp +++ b/flang/lib/Lower/OpenACC.cpp @@ -36,6 +36,7 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/Support/LLVM.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Frontend/OpenACC/ACC.h.inc" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -2142,6 +2143,168 @@ static void determineDefaultLoopParMode( } } +// Extract loop bounds, steps, induction variables, and privatization info +// for both DO CONCURRENT and regular do loops +static void processDoLoopBounds( + Fortran::lower::AbstractConverter &converter, + mlir::Location currentLocation, Fortran::lower::StatementContext &stmtCtx, + fir::FirOpBuilder &builder, + const Fortran::parser::DoConstruct &outerDoConstruct, + Fortran::lower::pft::Evaluation &eval, + llvm::SmallVector<mlir::Value> &lowerbounds, + llvm::SmallVector<mlir::Value> &upperbounds, + llvm::SmallVector<mlir::Value> &steps, + llvm::SmallVector<mlir::Value> &privateOperands, + llvm::SmallVector<mlir::Value> &ivPrivate, + llvm::SmallVector<mlir::Attribute> &privatizationRecipes, + llvm::SmallVector<mlir::Type> &ivTypes, + llvm::SmallVector<mlir::Location> &ivLocs, + llvm::SmallVector<bool> &inclusiveBounds, + llvm::SmallVector<mlir::Location> &locs, uint64_t loopsToProcess) { + assert(loopsToProcess > 0 && "expect at least one loop"); + locs.push_back(currentLocation); // Location of the directive + Fortran::lower::pft::Evaluation *crtEval = &eval.getFirstNestedEvaluation(); + bool isDoConcurrent = outerDoConstruct.IsDoConcurrent(); + + if (isDoConcurrent) { + locs.push_back(converter.genLocation( + Fortran::parser::FindSourceLocation(outerDoConstruct))); + const Fortran::parser::LoopControl *loopControl = + &*outerDoConstruct.GetLoopControl(); + const auto &concurrent = + std::get<Fortran::parser::LoopControl::Concurrent>(loopControl->u); + if (!std::get<std::list<Fortran::parser::LocalitySpec>>(concurrent.t) + .empty()) + TODO(currentLocation, "DO CONCURRENT with locality spec inside ACC"); + + const auto &concurrentHeader = + std::get<Fortran::parser::ConcurrentHeader>(concurrent.t); + const auto &controls = + std::get<std::list<Fortran::parser::ConcurrentControl>>( + concurrentHeader.t); + for (const auto &control : controls) { + lowerbounds.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(std::get<1>(control.t)), stmtCtx))); + upperbounds.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(std::get<2>(control.t)), stmtCtx))); + if (const auto &expr = + std::get<std::optional<Fortran::parser::ScalarIntExpr>>( + control.t)) + steps.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(*expr), stmtCtx))); + else // If `step` is not present, assume it is `1`. + steps.push_back(builder.createIntegerConstant( + currentLocation, upperbounds[upperbounds.size() - 1].getType(), 1)); + + const auto &name = std::get<Fortran::parser::Name>(control.t); + privatizeIv(converter, *name.symbol, currentLocation, ivTypes, ivLocs, + privateOperands, ivPrivate, privatizationRecipes, + isDoConcurrent); + + inclusiveBounds.push_back(true); + } + } else { + for (uint64_t i = 0; i < loopsToProcess; ++i) { + const Fortran::parser::LoopControl *loopControl; + if (i == 0) { + loopControl = &*outerDoConstruct.GetLoopControl(); + locs.push_back(converter.genLocation( + Fortran::parser::FindSourceLocation(outerDoConstruct))); + } else { + auto *doCons = crtEval->getIf<Fortran::parser::DoConstruct>(); + assert(doCons && "expect do construct"); + loopControl = &*doCons->GetLoopControl(); + locs.push_back(converter.genLocation( + Fortran::parser::FindSourceLocation(*doCons))); + } + + const Fortran::parser::LoopControl::Bounds *bounds = + std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); + assert(bounds && "Expected bounds on the loop construct"); + lowerbounds.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); + upperbounds.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); + if (bounds->step) + steps.push_back(fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); + else // If `step` is not present, assume it is `1`. + steps.push_back(builder.createIntegerConstant( + currentLocation, upperbounds[upperbounds.size() - 1].getType(), 1)); + + Fortran::semantics::Symbol &ivSym = + bounds->name.thing.symbol->GetUltimate(); + privatizeIv(converter, ivSym, currentLocation, ivTypes, ivLocs, + privateOperands, ivPrivate, privatizationRecipes); + + inclusiveBounds.push_back(true); + + if (i < loopsToProcess - 1) + crtEval = &*std::next(crtEval->getNestedEvaluations().begin()); + } + } +} + +static mlir::acc::LoopOp +buildACCLoopOp(Fortran::lower::AbstractConverter &converter, + mlir::Location currentLocation, + Fortran::semantics::SemanticsContext &semanticsContext, + Fortran::lower::StatementContext &stmtCtx, + const Fortran::parser::DoConstruct &outerDoConstruct, + Fortran::lower::pft::Evaluation &eval, + llvm::SmallVector<mlir::Value> &privateOperands, + llvm::SmallVector<mlir::Attribute> &privatizationRecipes, + llvm::SmallVector<mlir::Value> &gangOperands, + llvm::SmallVector<mlir::Value> &workerNumOperands, + llvm::SmallVector<mlir::Value> &vectorOperands, + llvm::SmallVector<mlir::Value> &tileOperands, + llvm::SmallVector<mlir::Value> &cacheOperands, + llvm::SmallVector<mlir::Value> &reductionOperands, + llvm::SmallVector<mlir::Type> &retTy, mlir::Value yieldValue, + uint64_t loopsToProcess) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + + llvm::SmallVector<mlir::Value> ivPrivate; + llvm::SmallVector<mlir::Type> ivTypes; + llvm::SmallVector<mlir::Location> ivLocs; + llvm::SmallVector<bool> inclusiveBounds; + llvm::SmallVector<mlir::Location> locs; + llvm::SmallVector<mlir::Value> lowerbounds, upperbounds, steps; + + // Look at the do/do concurrent loops to extract bounds information. + processDoLoopBounds(converter, currentLocation, stmtCtx, builder, + outerDoConstruct, eval, lowerbounds, upperbounds, steps, + privateOperands, ivPrivate, privatizationRecipes, ivTypes, + ivLocs, inclusiveBounds, locs, loopsToProcess); + + // Prepare the operand segment size attribute and the operands value range. + llvm::SmallVector<mlir::Value> operands; + llvm::SmallVector<int32_t> operandSegments; + addOperands(operands, operandSegments, lowerbounds); + addOperands(operands, operandSegments, upperbounds); + addOperands(operands, operandSegments, steps); + addOperands(operands, operandSegments, gangOperands); + addOperands(operands, operandSegments, workerNumOperands); + addOperands(operands, operandSegments, vectorOperands); + addOperands(operands, operandSegments, tileOperands); + addOperands(operands, operandSegments, cacheOperands); + addOperands(operands, operandSegments, privateOperands); + addOperands(operands, operandSegments, reductionOperands); + + auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>( + builder, builder.getFusedLoc(locs), currentLocation, eval, operands, + operandSegments, /*outerCombined=*/false, retTy, yieldValue, ivTypes, + ivLocs); + + for (auto [arg, value] : llvm::zip( + loopOp.getLoopRegions().front()->front().getArguments(), ivPrivate)) + fir::StoreOp::create(builder, currentLocation, arg, value); + + loopOp.setInclusiveUpperbound(inclusiveBounds); + + return loopOp; +} + static mlir::acc::LoopOp createLoopOp( Fortran::lower::AbstractConverter &converter, mlir::Location currentLocation, @@ -2154,9 +2317,9 @@ static mlir::acc::LoopOp createLoopOp( std::nullopt, bool needEarlyReturnHandling = false) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); - llvm::SmallVector<mlir::Value> tileOperands, privateOperands, ivPrivate, + llvm::SmallVector<mlir::Value> tileOperands, privateOperands, reductionOperands, cacheOperands, vectorOperands, workerNumOperands, - gangOperands, lowerbounds, upperbounds, steps; + gangOperands; llvm::SmallVector<mlir::Attribute> privatizationRecipes, reductionRecipes; llvm::SmallVector<int32_t> tileOperandsSegments, gangOperandsSegments; llvm::SmallVector<int64_t> collapseValues; @@ -2325,107 +2488,6 @@ static mlir::acc::LoopOp createLoopOp( } } - llvm::SmallVector<mlir::Type> ivTypes; - llvm::SmallVector<mlir::Location> ivLocs; - llvm::SmallVector<bool> inclusiveBounds; - llvm::SmallVector<mlir::Location> locs; - locs.push_back(currentLocation); // Location of the directive - Fortran::lower::pft::Evaluation *crtEval = &eval.getFirstNestedEvaluation(); - bool isDoConcurrent = outerDoConstruct.IsDoConcurrent(); - if (isDoConcurrent) { - locs.push_back(converter.genLocation( - Fortran::parser::FindSourceLocation(outerDoConstruct))); - const Fortran::parser::LoopControl *loopControl = - &*outerDoConstruct.GetLoopControl(); - const auto &concurrent = - std::get<Fortran::parser::LoopControl::Concurrent>(loopControl->u); - if (!std::get<std::list<Fortran::parser::LocalitySpec>>(concurrent.t) - .empty()) - TODO(currentLocation, "DO CONCURRENT with locality spec"); - - const auto &concurrentHeader = - std::get<Fortran::parser::ConcurrentHeader>(concurrent.t); - const auto &controls = - std::get<std::list<Fortran::parser::ConcurrentControl>>( - concurrentHeader.t); - for (const auto &control : controls) { - lowerbounds.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(std::get<1>(control.t)), stmtCtx))); - upperbounds.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(std::get<2>(control.t)), stmtCtx))); - if (const auto &expr = - std::get<std::optional<Fortran::parser::ScalarIntExpr>>( - control.t)) - steps.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(*expr), stmtCtx))); - else // If `step` is not present, assume it is `1`. - steps.push_back(builder.createIntegerConstant( - currentLocation, upperbounds[upperbounds.size() - 1].getType(), 1)); - - const auto &name = std::get<Fortran::parser::Name>(control.t); - privatizeIv(converter, *name.symbol, currentLocation, ivTypes, ivLocs, - privateOperands, ivPrivate, privatizationRecipes, - isDoConcurrent); - - inclusiveBounds.push_back(true); - } - } else { - int64_t loopCount = - Fortran::lower::getLoopCountForCollapseAndTile(accClauseList); - for (unsigned i = 0; i < loopCount; ++i) { - const Fortran::parser::LoopControl *loopControl; - if (i == 0) { - loopControl = &*outerDoConstruct.GetLoopControl(); - locs.push_back(converter.genLocation( - Fortran::parser::FindSourceLocation(outerDoConstruct))); - } else { - auto *doCons = crtEval->getIf<Fortran::parser::DoConstruct>(); - assert(doCons && "expect do construct"); - loopControl = &*doCons->GetLoopControl(); - locs.push_back(converter.genLocation( - Fortran::parser::FindSourceLocation(*doCons))); - } - - const Fortran::parser::LoopControl::Bounds *bounds = - std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); - assert(bounds && "Expected bounds on the loop construct"); - lowerbounds.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); - upperbounds.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); - if (bounds->step) - steps.push_back(fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); - else // If `step` is not present, assume it is `1`. - steps.push_back(builder.createIntegerConstant( - currentLocation, upperbounds[upperbounds.size() - 1].getType(), 1)); - - Fortran::semantics::Symbol &ivSym = - bounds->name.thing.symbol->GetUltimate(); - privatizeIv(converter, ivSym, currentLocation, ivTypes, ivLocs, - privateOperands, ivPrivate, privatizationRecipes); - - inclusiveBounds.push_back(true); - - if (i < loopCount - 1) - crtEval = &*std::next(crtEval->getNestedEvaluations().begin()); - } - } - - // Prepare the operand segment size attribute and the operands value range. - llvm::SmallVector<mlir::Value> operands; - llvm::SmallVector<int32_t> operandSegments; - addOperands(operands, operandSegments, lowerbounds); - addOperands(operands, operandSegments, upperbounds); - addOperands(operands, operandSegments, steps); - addOperands(operands, operandSegments, gangOperands); - addOperands(operands, operandSegments, workerNumOperands); - addOperands(operands, operandSegments, vectorOperands); - addOperands(operands, operandSegments, tileOperands); - addOperands(operands, operandSegments, cacheOperands); - addOperands(operands, operandSegments, privateOperands); - addOperands(operands, operandSegments, reductionOperands); - llvm::SmallVector<mlir::Type> retTy; mlir::Value yieldValue; if (needEarlyReturnHandling) { @@ -2434,16 +2496,13 @@ static mlir::acc::LoopOp createLoopOp( retTy.push_back(i1Ty); } - auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>( - builder, builder.getFusedLoc(locs), currentLocation, eval, operands, - operandSegments, /*outerCombined=*/false, retTy, yieldValue, ivTypes, - ivLocs); - - for (auto [arg, value] : llvm::zip( - loopOp.getLoopRegions().front()->front().getArguments(), ivPrivate)) - fir::StoreOp::create(builder, currentLocation, arg, value); - - loopOp.setInclusiveUpperbound(inclusiveBounds); + uint64_t loopsToProcess = + Fortran::lower::getLoopCountForCollapseAndTile(accClauseList); + auto loopOp = buildACCLoopOp( + converter, currentLocation, semanticsContext, stmtCtx, outerDoConstruct, + eval, privateOperands, privatizationRecipes, gangOperands, + workerNumOperands, vectorOperands, tileOperands, cacheOperands, + reductionOperands, retTy, yieldValue, loopsToProcess); if (!gangDeviceTypes.empty()) loopOp.setGangAttr(builder.getArrayAttr(gangDeviceTypes)); @@ -4899,6 +4958,12 @@ bool Fortran::lower::isInOpenACCLoop(fir::FirOpBuilder &builder) { return false; } +bool Fortran::lower::isInsideOpenACCComputeConstruct( + fir::FirOpBuilder &builder) { + return mlir::isa_and_nonnull<ACC_COMPUTE_CONSTRUCT_OPS>( + mlir::acc::getEnclosingComputeOp(builder.getRegion())); +} + void Fortran::lower::setInsertionPointAfterOpenACCLoopIfInside( fir::FirOpBuilder &builder) { if (auto loopOp = @@ -4913,10 +4978,10 @@ void Fortran::lower::genEarlyReturnInOpenACCLoop(fir::FirOpBuilder &builder, mlir::acc::YieldOp::create(builder, loc, yieldValue); } -int64_t Fortran::lower::getLoopCountForCollapseAndTile( +uint64_t Fortran::lower::getLoopCountForCollapseAndTile( const Fortran::parser::AccClauseList &clauseList) { - int64_t collapseLoopCount = 1; - int64_t tileLoopCount = 1; + uint64_t collapseLoopCount = 1; + uint64_t tileLoopCount = 1; for (const Fortran::parser::AccClause &clause : clauseList.v) { if (const auto *collapseClause = std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) { @@ -4935,3 +5000,101 @@ int64_t Fortran::lower::getLoopCountForCollapseAndTile( return tileLoopCount; return collapseLoopCount; } + +/// Create an ACC loop operation for a DO construct when inside ACC compute +/// constructs This serves as a bridge between regular DO construct handling and +/// ACC loop creation +mlir::Operation *Fortran::lower::genOpenACCLoopFromDoConstruct( + AbstractConverter &converter, + Fortran::semantics::SemanticsContext &semanticsContext, + Fortran::lower::SymMap &localSymbols, + const Fortran::parser::DoConstruct &doConstruct, pft::Evaluation &eval) { + // Only convert loops which have induction variables that need privatized. + if (!doConstruct.IsDoNormal() && !doConstruct.IsDoConcurrent()) + return nullptr; + + // If the evaluation is unstructured, then we cannot convert the loop + // because acc loop does not have an unstructured form. + // TODO: There may be other strategies that can be employed such + // as generating acc.private for the loop variables without attaching + // them to acc.loop. + // For now - generate a not-yet-implemented message because without + // privatizing the induction variable, the loop may not execute correctly. + // Only do this for `acc kernels` because in `acc parallel`, scalars end + // up as implicitly firstprivate. + if (eval.lowerAsUnstructured()) { + if (mlir::isa_and_present<mlir::acc::KernelsOp>( + mlir::acc::getEnclosingComputeOp( + converter.getFirOpBuilder().getRegion()))) + TODO(converter.getCurrentLocation(), + "unstructured do loop in acc kernels"); + return nullptr; + } + + // Open up a new scope for the loop variables. + localSymbols.pushScope(); + auto scopeGuard = llvm::make_scope_exit([&]() { localSymbols.popScope(); }); + + // Prepare empty operand vectors since there are no associated `acc loop` + // clauses with the Fortran do loops being handled here. + llvm::SmallVector<mlir::Value> privateOperands, gangOperands, + workerNumOperands, vectorOperands, tileOperands, cacheOperands, + reductionOperands; + llvm::SmallVector<mlir::Attribute> privatizationRecipes; + llvm::SmallVector<mlir::Type> retTy; + mlir::Value yieldValue; + uint64_t loopsToProcess = 1; // Single loop construct + + // Use same mechanism that handles `acc loop` contained do loops to handle + // the implicit loop case. + Fortran::lower::StatementContext stmtCtx; + auto loopOp = buildACCLoopOp( + converter, converter.getCurrentLocation(), semanticsContext, stmtCtx, + doConstruct, eval, privateOperands, privatizationRecipes, gangOperands, + workerNumOperands, vectorOperands, tileOperands, cacheOperands, + reductionOperands, retTy, yieldValue, loopsToProcess); + + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + if (!privatizationRecipes.empty()) + loopOp.setPrivatizationRecipesAttr(mlir::ArrayAttr::get( + converter.getFirOpBuilder().getContext(), privatizationRecipes)); + + // Normal do loops which are not annotated with `acc loop` should be + // left for analysis by marking with `auto`. This is the case even in the case + // of `acc parallel` region because the normal rules of applying `independent` + // is only for loops marked with `acc loop`. + // For do concurrent loops, the spec says in section 2.17.2: + // "When do concurrent appears without a loop construct in a kernels construct + // it is treated as if it is annotated with loop auto. If it appears in a + // parallel construct or an accelerator routine then it is treated as if it is + // annotated with loop independent." + // So this means that in all cases we mark with `auto` unless it is a + // `do concurrent` in an `acc parallel` construct or it must be `seq` because + // it is in an `acc serial` construct. + mlir::Operation *accRegionOp = + mlir::acc::getEnclosingComputeOp(converter.getFirOpBuilder().getRegion()); + mlir::acc::LoopParMode parMode = + mlir::isa_and_present<mlir::acc::ParallelOp>(accRegionOp) && + doConstruct.IsDoConcurrent() + ? mlir::acc::LoopParMode::loop_independent + : mlir::isa_and_present<mlir::acc::SerialOp>(accRegionOp) + ? mlir::acc::LoopParMode::loop_seq + : mlir::acc::LoopParMode::loop_auto; + + // Set the parallel mode based on the computed parMode + auto deviceNoneAttr = mlir::acc::DeviceTypeAttr::get( + builder.getContext(), mlir::acc::DeviceType::None); + auto arrOfDeviceNone = + mlir::ArrayAttr::get(builder.getContext(), deviceNoneAttr); + if (parMode == mlir::acc::LoopParMode::loop_independent) { + loopOp.setIndependentAttr(arrOfDeviceNone); + } else if (parMode == mlir::acc::LoopParMode::loop_seq) { + loopOp.setSeqAttr(arrOfDeviceNone); + } else if (parMode == mlir::acc::LoopParMode::loop_auto) { + loopOp.setAuto_Attr(arrOfDeviceNone); + } else { + llvm_unreachable("Unexpected loop par mode"); + } + + return loopOp; +} diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp index 9a233d2..ed0bff0 100644 --- a/flang/lib/Lower/OpenMP/Atomic.cpp +++ b/flang/lib/Lower/OpenMP/Atomic.cpp @@ -607,7 +607,7 @@ genAtomicUpdate(lower::AbstractConverter &converter, // This must exist by now. semantics::SomeExpr rhs = assign.rhs; semantics::SomeExpr input = *evaluate::GetConvertInput(rhs); - auto [opcode, args] = evaluate::GetTopLevelOperation(input); + auto [opcode, args] = evaluate::GetTopLevelOperationIgnoreResizing(input); assert(!args.empty() && "Update operation without arguments"); // Pass args as an argument to avoid capturing a structured binding. @@ -625,7 +625,8 @@ genAtomicUpdate(lower::AbstractConverter &converter, // operations with exactly two (non-optional) arguments. rhs = genReducedMinMax(rhs, atomArg, args); input = *evaluate::GetConvertInput(rhs); - std::tie(opcode, args) = evaluate::GetTopLevelOperation(input); + std::tie(opcode, args) = + evaluate::GetTopLevelOperationIgnoreResizing(input); atomArg = nullptr; // No longer valid. } for (auto &arg : args) { @@ -635,9 +636,16 @@ genAtomicUpdate(lower::AbstractConverter &converter, } } + mlir::ModuleOp module = builder.getModule(); + mlir::omp::AtomicControlAttr atomicControlAttr = + mlir::omp::AtomicControlAttr::get( + builder.getContext(), fir::getAtomicIgnoreDenormalMode(module), + fir::getAtomicFineGrainedMemory(module), + fir::getAtomicRemoteMemory(module)); builder.restoreInsertionPoint(atomicAt); auto updateOp = mlir::omp::AtomicUpdateOp::create( - builder, loc, atomAddr, hint, makeMemOrderAttr(converter, memOrder)); + builder, loc, atomAddr, atomicControlAttr, hint, + makeMemOrderAttr(converter, memOrder)); mlir::Region ®ion = updateOp->getRegion(0); mlir::Block *block = builder.createBlock(®ion, {}, {atomType}, {loc}); @@ -699,7 +707,7 @@ void Fortran::lower::omp::lowerAtomic( }; fir::FirOpBuilder &builder = converter.getFirOpBuilder(); - auto &dirSpec = std::get<parser::OmpDirectiveSpecification>(construct.t); + const parser::OmpDirectiveSpecification &dirSpec = construct.BeginDir(); omp::List<omp::Clause> clauses = makeClauses(dirSpec.Clauses(), semaCtx); lower::StatementContext stmtCtx; diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 594f95e..8eabf4f 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -1519,10 +1519,14 @@ bool ClauseProcessor::processTo( bool ClauseProcessor::processEnter( llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { return findRepeatableClause<omp::clause::Enter>( - [&](const omp::clause::Enter &clause, const parser::CharBlock &) { + [&](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"); // Case: declare target enter(func, var1, var2)... - gatherFuncAndVarSyms( - clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result); + gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), + mlir::omp::DeclareTargetCaptureClause::enter, + result); }); } diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index 686fba0..7f75aae 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -772,8 +772,19 @@ Doacross make(const parser::OmpClause::Doacross &inp, Enter make(const parser::OmpClause::Enter &inp, semantics::SemanticsContext &semaCtx) { - // inp.v -> parser::OmpObjectList - return Enter{makeObjects(/*List=*/inp.v, semaCtx)}; + // inp.v -> parser::OmpEnterClause + CLAUSET_ENUM_CONVERT( // + convert, parser::OmpAutomapModifier::Value, Enter::Modifier, + // clang-format off + MS(Automap, Automap) + // clang-format on + ); + auto &mods = semantics::OmpGetModifiers(inp.v); + auto *mod = semantics::OmpGetUniqueModifier<parser::OmpAutomapModifier>(mods); + auto &objList = std::get<parser::OmpObjectList>(inp.v.t); + + return Enter{{/*Modifier=*/maybeApplyToV(convert, mod), + /*List=*/makeObjects(objList, semaCtx)}}; } Exclusive make(const parser::OmpClause::Exclusive &inp, diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp index 2ac4d95..2c0cbb2 100644 --- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp +++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp @@ -389,42 +389,16 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) { } } -static const parser::CharBlock * -getSource(const semantics::SemanticsContext &semaCtx, - const lower::pft::Evaluation &eval) { - const parser::CharBlock *source = nullptr; - - auto ompConsVisit = [&](const parser::OpenMPConstruct &x) { - std::visit( - common::visitors{ - [&](const parser::OpenMPSectionsConstruct &x) { - source = &std::get<0>(x.t).source; - }, - [&](const parser::OpenMPLoopConstruct &x) { - source = &std::get<0>(x.t).source; - }, - [&](const parser::OpenMPBlockConstruct &x) { - source = &std::get<0>(x.t).source; - }, - [&](const parser::OpenMPCriticalConstruct &x) { - source = &std::get<0>(x.t).source; - }, - [&](const parser::OpenMPAtomicConstruct &x) { - source = &std::get<parser::OmpDirectiveSpecification>(x.t).source; - }, - [&](const auto &x) { source = &x.source; }, - }, - x.u); - }; - - eval.visit(common::visitors{ - [&](const parser::OpenMPConstruct &x) { ompConsVisit(x); }, - [&](const parser::OpenMPDeclarativeConstruct &x) { source = &x.source; }, - [&](const parser::OmpEndLoopDirective &x) { source = &x.source; }, - [&](const auto &x) {}, +static parser::CharBlock getSource(const semantics::SemanticsContext &semaCtx, + const lower::pft::Evaluation &eval) { + return eval.visit(common::visitors{ + [&](const parser::OpenMPConstruct &x) { + return parser::omp::GetOmpDirectiveName(x).source; + }, + [&](const parser::OpenMPDeclarativeConstruct &x) { return x.source; }, + [&](const parser::OmpEndLoopDirective &x) { return x.source; }, + [&](const auto &x) { return parser::CharBlock{}; }, }); - - return source; } static void collectPrivatizingConstructs( @@ -518,11 +492,11 @@ void DataSharingProcessor::collectSymbols( for (const semantics::Scope &child : scope->children()) collectScopes(&child); }; - const parser::CharBlock *source = - clauses.empty() ? getSource(semaCtx, eval) : &clauses.front().source; + parser::CharBlock source = + clauses.empty() ? getSource(semaCtx, eval) : clauses.front().source; const semantics::Scope *curScope = nullptr; - if (source && !source->empty()) { - curScope = &semaCtx.FindScope(*source); + if (!source.empty()) { + curScope = &semaCtx.FindScope(source); collectScopes(curScope); } // Collect all symbols referenced in the evaluation being processed, diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 12089d6..d1efd8e 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -407,16 +407,9 @@ static void processHostEvalClauses(lower::AbstractConverter &converter, common::visit( common::visitors{ [&](const parser::OpenMPBlockConstruct &ompConstruct) { - const auto &beginDirective = - std::get<parser::OmpBeginBlockDirective>(ompConstruct.t); - beginClauseList = - &std::get<parser::OmpClauseList>(beginDirective.t); - if (auto &endDirective = - std::get<std::optional<parser::OmpEndBlockDirective>>( - ompConstruct.t)) { - endClauseList = - &std::get<parser::OmpClauseList>(endDirective->t); - } + beginClauseList = &ompConstruct.BeginDir().Clauses(); + if (auto &endSpec = ompConstruct.EndDir()) + endClauseList = &endSpec->Clauses(); }, [&](const parser::OpenMPLoopConstruct &ompConstruct) { const auto &beginDirective = @@ -697,20 +690,16 @@ static void threadPrivatizeVars(lower::AbstractConverter &converter, } } -static mlir::Operation * -createAndSetPrivatizedLoopVar(lower::AbstractConverter &converter, - mlir::Location loc, mlir::Value indexVal, - const semantics::Symbol *sym) { +static mlir::Operation *setLoopVar(lower::AbstractConverter &converter, + mlir::Location loc, mlir::Value indexVal, + const semantics::Symbol *sym) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); + mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); - mlir::Type tempTy = converter.genType(*sym); - - assert(converter.isPresentShallowLookup(*sym) && - "Expected symbol to be in symbol table."); - firOpBuilder.restoreInsertionPoint(insPt); + mlir::Value cvtVal = firOpBuilder.createConvert(loc, tempTy, indexVal); hlfir::Entity lhs{converter.getSymbolAddress(*sym)}; @@ -721,6 +710,15 @@ createAndSetPrivatizedLoopVar(lower::AbstractConverter &converter, return storeOp; } +static mlir::Operation * +createAndSetPrivatizedLoopVar(lower::AbstractConverter &converter, + mlir::Location loc, mlir::Value indexVal, + const semantics::Symbol *sym) { + assert(converter.isPresentShallowLookup(*sym) && + "Expected symbol to be in symbol table."); + return setLoopVar(converter, loc, indexVal, sym); +} + // This helper function implements the functionality of "promoting" non-CPTR // arguments of use_device_ptr to use_device_addr arguments (automagic // conversion of use_device_ptr -> use_device_addr in these cases). The way we @@ -1123,6 +1121,11 @@ struct OpWithBodyGenInfo { return *this; } + OpWithBodyGenInfo &setPrivatize(bool value) { + privatize = value; + return *this; + } + /// [inout] converter to use for the clauses. lower::AbstractConverter &converter; /// [in] Symbol table @@ -1149,6 +1152,8 @@ struct OpWithBodyGenInfo { /// [in] if set to `true`, skip generating nested evaluations and dispatching /// any further leaf constructs. bool genSkeletonOnly = false; + /// [in] enables handling of privatized variable unless set to `false`. + bool privatize = true; }; /// Create the body (block) for an OpenMP Operation. @@ -1209,7 +1214,7 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info, // code will use the right symbols. bool isLoop = llvm::omp::getDirectiveAssociation(info.dir) == llvm::omp::Association::Loop; - bool privatize = info.clauses; + bool privatize = info.clauses && info.privatize; firOpBuilder.setInsertionPoint(marker); std::optional<DataSharingProcessor> tempDsp; @@ -2083,7 +2088,7 @@ genCanonicalLoopOp(lower::AbstractConverter &converter, lower::SymMap &symTable, const ConstructQueue &queue, ConstructQueue::const_iterator item, llvm::ArrayRef<const semantics::Symbol *> ivs, - llvm::omp::Directive directive, DataSharingProcessor &dsp) { + llvm::omp::Directive directive) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); assert(ivs.size() == 1 && "Nested loops not yet implemented"); @@ -2176,10 +2181,8 @@ genCanonicalLoopOp(lower::AbstractConverter &converter, lower::SymMap &symTable, mlir::Value userVal = firOpBuilder.create<mlir::arith::AddIOp>(loc, loopLBVar, scaled); - // The argument is not currently in memory, so make a temporary for the - // argument, and store it there, then bind that location to the argument. - mlir::Operation *storeOp = - createAndSetPrivatizedLoopVar(converter, loc, userVal, iv); + // Write loop value to loop variable + mlir::Operation *storeOp = setLoopVar(converter, loc, userVal, iv); firOpBuilder.setInsertionPointAfter(storeOp); return {iv}; @@ -2190,7 +2193,7 @@ genCanonicalLoopOp(lower::AbstractConverter &converter, lower::SymMap &symTable, OpWithBodyGenInfo(converter, symTable, semaCtx, loc, nestedEval, directive) .setClauses(&item->clauses) - .setDataSharingProcessor(&dsp) + .setPrivatize(false) .setGenRegionEntryCb(ivCallback), queue, item, tripcount, cli); @@ -2216,17 +2219,10 @@ static void genUnrollOp(Fortran::lower::AbstractConverter &converter, cp.processTODO<clause::Partial, clause::Full>( loc, llvm::omp::Directive::OMPD_unroll); - // Even though unroll does not support data-sharing clauses, but this is - // required to fill the symbol table. - DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval, - /*shouldCollectPreDeterminedSymbols=*/true, - /*useDelayedPrivatization=*/false, symTable); - dsp.processStep1(); - // Emit the associated loop auto canonLoop = genCanonicalLoopOp(converter, symTable, semaCtx, eval, loc, queue, item, - iv, llvm::omp::Directive::OMPD_unroll, dsp); + iv, llvm::omp::Directive::OMPD_unroll); // Apply unrolling to it auto cli = canonLoop.getCli(); @@ -2329,7 +2325,7 @@ genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable, assert(sectionsConstruct && "Missing additional parsing information"); const auto §ionBlocks = - std::get<parser::OmpSectionBlocks>(sectionsConstruct->t); + std::get<std::list<parser::OpenMPConstruct>>(sectionsConstruct->t); mlir::omp::SectionsOperands clauseOps; llvm::SmallVector<const semantics::Symbol *> reductionSyms; genSectionsClauses(converter, semaCtx, item->clauses, loc, clauseOps, @@ -2382,7 +2378,7 @@ genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable, // because we need to run genReductionVars on each omp.section so that the // reduction variable gets mapped to the private version for (auto [construct, nestedEval] : - llvm::zip(sectionBlocks.v, eval.getNestedEvaluations())) { + llvm::zip(sectionBlocks, eval.getNestedEvaluations())) { const auto *sectionConstruct = std::get_if<parser::OpenMPSectionConstruct>(&construct.u); if (!sectionConstruct) { @@ -3145,11 +3141,16 @@ static mlir::omp::DistributeOp genCompositeDistributeSimd( genSimdClauses(converter, semaCtx, simdItem->clauses, loc, simdClauseOps, simdReductionSyms); - // TODO: Support delayed privatization. - DataSharingProcessor dsp(converter, semaCtx, simdItem->clauses, eval, - /*shouldCollectPreDeterminedSymbols=*/true, - /*useDelayedPrivatization=*/false, symTable); - dsp.processStep1(); + DataSharingProcessor distributeItemDSP( + converter, semaCtx, distributeItem->clauses, eval, + /*shouldCollectPreDeterminedSymbols=*/false, + /*useDelayedPrivatization=*/true, symTable); + distributeItemDSP.processStep1(&distributeClauseOps); + + DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval, + /*shouldCollectPreDeterminedSymbols=*/true, + /*useDelayedPrivatization=*/true, symTable); + simdItemDSP.processStep1(&simdClauseOps); // Pass the innermost leaf construct's clauses because that's where COLLAPSE // is placed by construct decomposition. @@ -3160,13 +3161,15 @@ static mlir::omp::DistributeOp genCompositeDistributeSimd( // Operation creation. EntryBlockArgs distributeArgs; - // TODO: Add private syms and vars. + distributeArgs.priv.syms = distributeItemDSP.getDelayedPrivSymbols(); + distributeArgs.priv.vars = distributeClauseOps.privateVars; auto distributeOp = genWrapperOp<mlir::omp::DistributeOp>( converter, loc, distributeClauseOps, distributeArgs); distributeOp.setComposite(/*val=*/true); EntryBlockArgs simdArgs; - // TODO: Add private syms and vars. + simdArgs.priv.syms = simdItemDSP.getDelayedPrivSymbols(); + simdArgs.priv.vars = simdClauseOps.privateVars; simdArgs.reduction.syms = simdReductionSyms; simdArgs.reduction.vars = simdClauseOps.reductionVars; auto simdOp = @@ -3176,7 +3179,7 @@ static mlir::omp::DistributeOp genCompositeDistributeSimd( genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, simdItem, loopNestClauseOps, iv, {{distributeOp, distributeArgs}, {simdOp, simdArgs}}, - llvm::omp::Directive::OMPD_distribute_simd, dsp); + llvm::omp::Directive::OMPD_distribute_simd, simdItemDSP); return distributeOp; } @@ -3200,11 +3203,16 @@ static mlir::omp::WsloopOp genCompositeDoSimd( genSimdClauses(converter, semaCtx, simdItem->clauses, loc, simdClauseOps, simdReductionSyms); - // TODO: Support delayed privatization. - DataSharingProcessor dsp(converter, semaCtx, simdItem->clauses, eval, - /*shouldCollectPreDeterminedSymbols=*/true, - /*useDelayedPrivatization=*/false, symTable); - dsp.processStep1(); + DataSharingProcessor wsloopItemDSP( + converter, semaCtx, doItem->clauses, eval, + /*shouldCollectPreDeterminedSymbols=*/false, + /*useDelayedPrivatization=*/true, symTable); + wsloopItemDSP.processStep1(&wsloopClauseOps); + + DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval, + /*shouldCollectPreDeterminedSymbols=*/true, + /*useDelayedPrivatization=*/true, symTable); + simdItemDSP.processStep1(&simdClauseOps); // Pass the innermost leaf construct's clauses because that's where COLLAPSE // is placed by construct decomposition. @@ -3215,7 +3223,8 @@ static mlir::omp::WsloopOp genCompositeDoSimd( // Operation creation. EntryBlockArgs wsloopArgs; - // TODO: Add private syms and vars. + wsloopArgs.priv.syms = wsloopItemDSP.getDelayedPrivSymbols(); + wsloopArgs.priv.vars = wsloopClauseOps.privateVars; wsloopArgs.reduction.syms = wsloopReductionSyms; wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars; auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>( @@ -3223,7 +3232,8 @@ static mlir::omp::WsloopOp genCompositeDoSimd( wsloopOp.setComposite(/*val=*/true); EntryBlockArgs simdArgs; - // TODO: Add private syms and vars. + simdArgs.priv.syms = simdItemDSP.getDelayedPrivSymbols(); + simdArgs.priv.vars = simdClauseOps.privateVars; simdArgs.reduction.syms = simdReductionSyms; simdArgs.reduction.vars = simdClauseOps.reductionVars; auto simdOp = @@ -3233,7 +3243,7 @@ static mlir::omp::WsloopOp genCompositeDoSimd( genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, simdItem, loopNestClauseOps, iv, {{wsloopOp, wsloopArgs}, {simdOp, simdArgs}}, - llvm::omp::Directive::OMPD_do_simd, dsp); + llvm::omp::Directive::OMPD_do_simd, simdItemDSP); return wsloopOp; } @@ -3716,25 +3726,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval, const parser::OpenMPBlockConstruct &blockConstruct) { - const auto &beginBlockDirective = - std::get<parser::OmpBeginBlockDirective>(blockConstruct.t); - mlir::Location currentLocation = - converter.genLocation(beginBlockDirective.source); - const auto origDirective = - std::get<parser::OmpBlockDirective>(beginBlockDirective.t).v; - List<Clause> clauses = makeClauses( - std::get<parser::OmpClauseList>(beginBlockDirective.t), semaCtx); - - if (const auto &endBlockDirective = - std::get<std::optional<parser::OmpEndBlockDirective>>( - blockConstruct.t)) { - clauses.append(makeClauses( - std::get<parser::OmpClauseList>(endBlockDirective->t), semaCtx)); - } - - assert(llvm::omp::blockConstructSet.test(origDirective) && + const parser::OmpDirectiveSpecification &beginSpec = + blockConstruct.BeginDir(); + List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx); + if (auto &endSpec = blockConstruct.EndDir()) + clauses.append(makeClauses(endSpec->Clauses(), semaCtx)); + + llvm::omp::Directive directive = beginSpec.DirId(); + assert(llvm::omp::blockConstructSet.test(directive) && "Expected block construct"); - (void)origDirective; + mlir::Location currentLocation = converter.genLocation(beginSpec.source); for (const Clause &clause : clauses) { mlir::Location clauseLocation = converter.genLocation(clause.source); @@ -3777,13 +3778,9 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, } } - llvm::omp::Directive directive = - std::get<parser::OmpBlockDirective>(beginBlockDirective.t).v; - const parser::CharBlock &source = - std::get<parser::OmpBlockDirective>(beginBlockDirective.t).source; ConstructQueue queue{ buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx, - eval, source, directive, clauses)}; + eval, beginSpec.source, directive, clauses)}; genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue, queue.begin()); } @@ -4071,8 +4068,7 @@ 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)) { - const auto &begin = std::get<parser::OmpBeginBlockDirective>(block->t); - dir = std::get<parser::OmpBlockDirective>(begin.t).v; + dir = block->BeginDir().DirId(); } else if (const auto *loop = std::get_if<parser::OpenMPLoopConstruct>(&omp.u)) { const auto &begin = std::get<parser::OmpBeginLoopDirective>(loop->t); diff --git a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp index 01c0be6..c2e0afe1 100644 --- a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp +++ b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp @@ -88,6 +88,57 @@ void fir::setTuneCPU(mlir::ModuleOp mod, llvm::StringRef cpu) { mod->setAttr(tuneCpuName, mlir::StringAttr::get(ctx, cpu)); } +static constexpr const char *atomicIgnoreDenormalModeName = + "fir.atomic_ignore_denormal_mode"; + +void fir::setAtomicIgnoreDenormalMode(mlir::ModuleOp mod, bool value) { + if (value) { + auto *ctx = mod.getContext(); + mod->setAttr(atomicIgnoreDenormalModeName, mlir::UnitAttr::get(ctx)); + } else { + if (mod->hasAttr(atomicIgnoreDenormalModeName)) + mod->removeAttr(atomicIgnoreDenormalModeName); + } +} + +bool fir::getAtomicIgnoreDenormalMode(mlir::ModuleOp mod) { + return mod->hasAttr(atomicIgnoreDenormalModeName); +} + +static constexpr const char *atomicFineGrainedMemoryName = + "fir.atomic_fine_grained_memory"; + +void fir::setAtomicFineGrainedMemory(mlir::ModuleOp mod, bool value) { + if (value) { + auto *ctx = mod.getContext(); + mod->setAttr(atomicFineGrainedMemoryName, mlir::UnitAttr::get(ctx)); + } else { + if (mod->hasAttr(atomicFineGrainedMemoryName)) + mod->removeAttr(atomicFineGrainedMemoryName); + } +} + +bool fir::getAtomicFineGrainedMemory(mlir::ModuleOp mod) { + return mod->hasAttr(atomicFineGrainedMemoryName); +} + +static constexpr const char *atomicRemoteMemoryName = + "fir.atomic_remote_memory"; + +void fir::setAtomicRemoteMemory(mlir::ModuleOp mod, bool value) { + if (value) { + auto *ctx = mod.getContext(); + mod->setAttr(atomicRemoteMemoryName, mlir::UnitAttr::get(ctx)); + } else { + if (mod->hasAttr(atomicRemoteMemoryName)) + mod->removeAttr(atomicRemoteMemoryName); + } +} + +bool fir::getAtomicRemoteMemory(mlir::ModuleOp mod) { + return mod->hasAttr(atomicRemoteMemoryName); +} + llvm::StringRef fir::getTuneCPU(mlir::ModuleOp mod) { if (auto attr = mod->getAttrOfType<mlir::StringAttr>(tuneCpuName)) return attr.getValue(); diff --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt index 7ccdd4f..38038e1 100644 --- a/flang/lib/Optimizer/Support/CMakeLists.txt +++ b/flang/lib/Optimizer/Support/CMakeLists.txt @@ -1,6 +1,3 @@ -get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) - add_flang_library(FIRSupport DataLayout.cpp InitFIR.cpp @@ -23,12 +20,12 @@ add_flang_library(FIRSupport ${extension_libs} MLIR_LIBS - ${dialect_libs} - ${extension_libs} MLIRBuiltinToLLVMIRTranslation + MLIRLLVMToLLVMIRTranslation MLIROpenACCToLLVMIRTranslation MLIROpenMPToLLVMIRTranslation - MLIRLLVMToLLVMIRTranslation + MLIRRegisterAllDialects + MLIRRegisterAllExtensions MLIRTargetLLVMIRExport MLIRTargetLLVMIRImport ) diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 1c62614..7daba33 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -34,6 +34,39 @@ namespace Fortran::parser { constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok; constexpr auto endOmpLine = space >> endOfLine; +// Given a parser for a single element, and a parser for a list of elements +// of the same type, create a parser that constructs the entire list by having +// the single element be the head of the list, and the rest be the tail. +template <typename ParserH, typename ParserT> struct ConsParser { + static_assert(std::is_same_v<std::list<typename ParserH::resultType>, + typename ParserT::resultType>); + + using resultType = typename ParserT::resultType; + constexpr ConsParser(ParserH h, ParserT t) : head_(h), tail_(t) {} + + std::optional<resultType> Parse(ParseState &state) const { + if (auto &&first{head_.Parse(state)}) { + if (auto rest{tail_.Parse(state)}) { + rest->push_front(std::move(*first)); + return std::move(*rest); + } + } + return std::nullopt; + } + +private: + const ParserH head_; + const ParserT tail_; +}; + +template <typename ParserH, typename ParserT, + typename ValueH = typename ParserH::resultType, + typename ValueT = typename ParserT::resultType, + typename = std::enable_if_t<std::is_same_v<std::list<ValueH>, ValueT>>> +constexpr auto cons(ParserH head, ParserT tail) { + return ConsParser<ParserH, ParserT>(head, tail); +} + // Given a parser P for a wrapper class, invoke P, and if it succeeds return // the wrapped object. template <typename Parser> struct UnwrapParser { @@ -449,6 +482,9 @@ TYPE_PARSER(construct<OmpAllocatorSimpleModifier>(scalarIntExpr)) TYPE_PARSER(construct<OmpAlwaysModifier>( // "ALWAYS" >> pure(OmpAlwaysModifier::Value::Always))) +TYPE_PARSER(construct<OmpAutomapModifier>( + "AUTOMAP" >> pure(OmpAutomapModifier::Value::Automap))) + TYPE_PARSER(construct<OmpChunkModifier>( // "SIMD" >> pure(OmpChunkModifier::Value::Simd))) @@ -466,6 +502,8 @@ TYPE_PARSER(construct<OmpDeviceModifier>( "ANCESTOR" >> pure(OmpDeviceModifier::Value::Ancestor) || "DEVICE_NUM" >> pure(OmpDeviceModifier::Value::Device_Num))) +TYPE_PARSER(construct<OmpDirectiveNameModifier>(OmpDirectiveNameParser{})) + TYPE_PARSER(construct<OmpExpectation>( // "PRESENT" >> pure(OmpExpectation::Value::Present))) @@ -601,6 +639,9 @@ TYPE_PARSER(sourced(construct<OmpDependClause::TaskDep::Modifier>(sourced( TYPE_PARSER( sourced(construct<OmpDeviceClause::Modifier>(Parser<OmpDeviceModifier>{}))) +TYPE_PARSER( + sourced(construct<OmpEnterClause::Modifier>(Parser<OmpAutomapModifier>{}))) + TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>( sourced(construct<OmpFromClause::Modifier>(Parser<OmpExpectation>{}) || construct<OmpFromClause::Modifier>(Parser<OmpMapper>{}) || @@ -609,7 +650,8 @@ TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>( TYPE_PARSER(sourced( construct<OmpGrainsizeClause::Modifier>(Parser<OmpPrescriptiveness>{}))) -TYPE_PARSER(sourced(construct<OmpIfClause::Modifier>(OmpDirectiveNameParser{}))) +TYPE_PARSER(sourced( + construct<OmpIfClause::Modifier>(Parser<OmpDirectiveNameModifier>{}))) TYPE_PARSER(sourced( construct<OmpInitClause::Modifier>( @@ -735,6 +777,10 @@ TYPE_PARSER(construct<OmpDefaultClause>( Parser<OmpDefaultClause::DataSharingAttribute>{}) || construct<OmpDefaultClause>(indirect(Parser<OmpDirectiveSpecification>{})))) +TYPE_PARSER(construct<OmpEnterClause>( + maybe(nonemptyList(Parser<OmpEnterClause::Modifier>{}) / ":"), + Parser<OmpObjectList>{})) + TYPE_PARSER(construct<OmpFailClause>( "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) || "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) || @@ -1023,7 +1069,7 @@ TYPE_PARSER( // "DYNAMIC_ALLOCATORS" >> construct<OmpClause>(construct<OmpClause::DynamicAllocators>()) || "ENTER" >> construct<OmpClause>(construct<OmpClause::Enter>( - parenthesized(Parser<OmpObjectList>{}))) || + parenthesized(Parser<OmpEnterClause>{}))) || "EXCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Exclusive>( parenthesized(Parser<OmpObjectList>{}))) || "FAIL" >> construct<OmpClause>(construct<OmpClause::Fail>( @@ -1335,16 +1381,41 @@ TYPE_PARSER(sourced(construct<OmpLoopDirective>(first( TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>( sourced(Parser<OmpLoopDirective>{}), Parser<OmpClauseList>{}))) +static inline constexpr auto IsDirective(llvm::omp::Directive dir) { + return [dir](const OmpDirectiveName &name) -> bool { return dir == name.v; }; +} + +struct OmpBeginDirectiveParser { + using resultType = OmpDirectiveSpecification; + + constexpr OmpBeginDirectiveParser(llvm::omp::Directive dir) : dir_(dir) {} + + std::optional<resultType> Parse(ParseState &state) const { + auto &&p{predicated(Parser<OmpDirectiveName>{}, IsDirective(dir_)) >= + Parser<OmpDirectiveSpecification>{}}; + return p.Parse(state); + } + +private: + llvm::omp::Directive dir_; +}; + struct OmpEndDirectiveParser { using resultType = OmpDirectiveSpecification; constexpr OmpEndDirectiveParser(llvm::omp::Directive dir) : dir_(dir) {} std::optional<resultType> Parse(ParseState &state) const { - if ((startOmpLine >> "END"_sptok).Parse(state)) { - auto &&dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)}; - if (dirSpec && dirSpec->DirId() == dir_) { - return std::move(dirSpec); + if (startOmpLine.Parse(state)) { + if (auto endToken{verbatim("END"_sptok).Parse(state)}) { + if (auto &&dirSpec{OmpBeginDirectiveParser(dir_).Parse(state)}) { + // Extend the "source" on both the OmpDirectiveName and the + // OmpDirectiveNameSpecification. + CharBlock &nameSource{std::get<OmpDirectiveName>(dirSpec->t).source}; + nameSource.ExtendToCover(endToken->source); + dirSpec->source.ExtendToCover(endToken->source); + return std::move(*dirSpec); + } } } return std::nullopt; @@ -1354,57 +1425,67 @@ private: llvm::omp::Directive dir_; }; -struct OmpAllocatorsConstructParser { - using resultType = OpenMPAllocatorsConstruct; +struct OmpStatementConstructParser { + using resultType = OmpBlockConstruct; + + constexpr OmpStatementConstructParser(llvm::omp::Directive dir) : dir_(dir) {} std::optional<resultType> Parse(ParseState &state) const { - auto dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)}; - if (!dirSpec || dirSpec->DirId() != llvm::omp::Directive::OMPD_allocators) { - return std::nullopt; - } + if (auto begin{OmpBeginDirectiveParser(dir_).Parse(state)}) { + Block body; + if (auto stmt{attempt(Parser<ExecutionPartConstruct>{}).Parse(state)}) { + body.emplace_back(std::move(*stmt)); + } + // Allow empty block. Check for this in semantics. - // This should be an allocate-stmt. That will be checked in semantics. - Block block; - if (auto stmt{attempt(Parser<ExecutionPartConstruct>{}).Parse(state)}) { - block.emplace_back(std::move(*stmt)); + auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)}; + return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)), + std::move(body), + llvm::transformOptional(std::move(*end), + [](auto &&s) { return OmpEndDirective(std::move(s)); })}; } - // Allow empty block. Check for this in semantics. - - auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_allocators}}; - return OpenMPAllocatorsConstruct{ - std::move(*dirSpec), std::move(block), *maybe(end).Parse(state)}; + return std::nullopt; } + +private: + llvm::omp::Directive dir_; }; -TYPE_PARSER(sourced( // - construct<OpenMPAllocatorsConstruct>( - "ALLOCATORS"_tok >= OmpAllocatorsConstructParser{}))) +struct OmpBlockConstructParser { + using resultType = OmpBlockConstruct; -struct OmpDispatchConstructParser { - using resultType = OpenMPDispatchConstruct; + constexpr OmpBlockConstructParser(llvm::omp::Directive dir) : dir_(dir) {} std::optional<resultType> Parse(ParseState &state) const { - auto dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)}; - if (!dirSpec || dirSpec->DirId() != llvm::omp::Directive::OMPD_dispatch) { - return std::nullopt; - } - - // This should be a function call. That will be checked in semantics. - Block block; - if (auto stmt{attempt(Parser<ExecutionPartConstruct>{}).Parse(state)}) { - block.emplace_back(std::move(*stmt)); + if (auto &&begin{OmpBeginDirectiveParser(dir_).Parse(state)}) { + if (auto &&body{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) { + // Try strictly-structured block with an optional end-directive + auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)}; + return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)), + std::move(*body), + llvm::transformOptional(std::move(*end), + [](auto &&s) { return OmpEndDirective(std::move(s)); })}; + } else if (auto &&body{ + attempt(LooselyStructuredBlockParser{}).Parse(state)}) { + // Try loosely-structured block with a mandatory end-directive + if (auto end{OmpEndDirectiveParser{dir_}.Parse(state)}) { + return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)), + std::move(*body), OmpEndDirective{std::move(*end)}}; + } + } } - // Allow empty block. Check for this in semantics. - - auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_dispatch}}; - return OpenMPDispatchConstruct{ - std::move(*dirSpec), std::move(block), *maybe(end).Parse(state)}; + return std::nullopt; } + +private: + llvm::omp::Directive dir_; }; -TYPE_PARSER(sourced( // - construct<OpenMPDispatchConstruct>( - "DISPATCH"_tok >= OmpDispatchConstructParser{}))) +TYPE_PARSER(sourced(construct<OpenMPAllocatorsConstruct>( + OmpStatementConstructParser{llvm::omp::Directive::OMPD_allocators}))) + +TYPE_PARSER(sourced(construct<OpenMPDispatchConstruct>( + OmpStatementConstructParser{llvm::omp::Directive::OMPD_dispatch}))) // Parser for an arbitrary OpenMP ATOMIC construct. // @@ -1469,8 +1550,10 @@ struct OmpAtomicConstructParser { } } recursing_ = false; - return OpenMPAtomicConstruct{ - std::move(*dirSpec), std::move(tail.first), std::move(tail.second)}; + return OpenMPAtomicConstruct{OmpBeginDirective(std::move(*dirSpec)), + std::move(tail.first), + llvm::transformOptional(std::move(tail.second), + [](auto &&s) { return OmpEndDirective(std::move(s)); })}; } recursing_ = false; @@ -1571,10 +1654,6 @@ TYPE_PARSER(sourced( // predicated(OmpDirectiveNameParser{}, IsSimpleStandalone) >= Parser<OmpDirectiveSpecification>{}))) -static inline constexpr auto IsDirective(llvm::omp::Directive dir) { - return [dir](const OmpDirectiveName &name) -> bool { return dir == name.v; }; -} - TYPE_PARSER(sourced( // construct<OpenMPFlushConstruct>( predicated(OmpDirectiveNameParser{}, @@ -1625,40 +1704,6 @@ TYPE_PARSER( Parser<OpenMPInteropConstruct>{})) / endOfLine) -// Directive names (of non-block constructs) whose prefix is a name of -// a block-associated construct. We need to exclude them from the block -// directive parser below to avoid parsing parts of them. -static constexpr auto StandaloneDirectiveLookahead{// - "TARGET ENTER DATA"_sptok || "TARGET_ENTER_DATA"_sptok || // - "TARGET EXIT DATA"_sptok || "TARGET_EXIT"_sptok || // - "TARGET UPDATE"_sptok || "TARGET_UPDATE"_sptok}; - -// Directives enclosing structured-block -TYPE_PARSER((!StandaloneDirectiveLookahead) >= - construct<OmpBlockDirective>(first( - "MASKED" >> pure(llvm::omp::Directive::OMPD_masked), - "MASTER" >> pure(llvm::omp::Directive::OMPD_master), - "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered), - "PARALLEL MASKED" >> pure(llvm::omp::Directive::OMPD_parallel_masked), - "PARALLEL MASTER" >> pure(llvm::omp::Directive::OMPD_parallel_master), - "PARALLEL WORKSHARE" >> - pure(llvm::omp::Directive::OMPD_parallel_workshare), - "PARALLEL" >> pure(llvm::omp::Directive::OMPD_parallel), - "SCOPE" >> pure(llvm::omp::Directive::OMPD_scope), - "SINGLE" >> pure(llvm::omp::Directive::OMPD_single), - "TARGET DATA" >> pure(llvm::omp::Directive::OMPD_target_data), - "TARGET_DATA" >> pure(llvm::omp::Directive::OMPD_target_data), - "TARGET PARALLEL" >> pure(llvm::omp::Directive::OMPD_target_parallel), - "TARGET TEAMS" >> pure(llvm::omp::Directive::OMPD_target_teams), - "TARGET" >> pure(llvm::omp::Directive::OMPD_target), - "TASK"_id >> pure(llvm::omp::Directive::OMPD_task), - "TASKGROUP" >> pure(llvm::omp::Directive::OMPD_taskgroup), - "TEAMS" >> pure(llvm::omp::Directive::OMPD_teams), - "WORKSHARE" >> pure(llvm::omp::Directive::OMPD_workshare)))) - -TYPE_PARSER(sourced(construct<OmpBeginBlockDirective>( - sourced(Parser<OmpBlockDirective>{}), Parser<OmpClauseList>{}))) - TYPE_PARSER(construct<OmpInitializerProc>(Parser<ProcedureDesignator>{}, parenthesized(many(maybe(","_tok) >> Parser<ActualArgSpec>{})))) @@ -1808,12 +1853,27 @@ TYPE_PARSER(sourced( block, maybe(Parser<OmpEndAssumeDirective>{} / endOmpLine)))) // Block Construct +#define MakeBlockConstruct(dir) \ + construct<OpenMPBlockConstruct>(OmpBlockConstructParser{dir}) TYPE_PARSER( // - construct<OpenMPBlockConstruct>(Parser<OmpBeginBlockDirective>{}, - StrictlyStructuredBlockParser{}, - maybe(Parser<OmpEndBlockDirective>{})) || - construct<OpenMPBlockConstruct>(Parser<OmpBeginBlockDirective>{}, - LooselyStructuredBlockParser{}, Parser<OmpEndBlockDirective>{})) + MakeBlockConstruct(llvm::omp::Directive::OMPD_masked) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_master) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_ordered) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_masked) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_master) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_workshare) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_scope) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_single) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_target_data) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_target_parallel) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_target_teams) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_target) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_task) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_taskgroup) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_teams) || + MakeBlockConstruct(llvm::omp::Directive::OMPD_workshare)) +#undef MakeBlockConstruct // OMP SECTIONS Directive TYPE_PARSER(construct<OmpSectionsDirective>(first( @@ -1828,19 +1888,20 @@ TYPE_PARSER( sourced("END"_tok >> Parser<OmpSectionsDirective>{}), Parser<OmpClauseList>{}))) -// OMP SECTION-BLOCK - -TYPE_PARSER(construct<OpenMPSectionConstruct>(block)) - -TYPE_PARSER(maybe(startOmpLine >> "SECTION"_tok / endOmpLine) >> - construct<OmpSectionBlocks>(nonemptySeparated( - construct<OpenMPConstruct>(sourced(Parser<OpenMPSectionConstruct>{})), - startOmpLine >> "SECTION"_tok / endOmpLine))) +static constexpr auto sectionDir{ + startOmpLine >> (predicated(OmpDirectiveNameParser{}, + IsDirective(llvm::omp::Directive::OMPD_section)) >= + Parser<OmpDirectiveSpecification>{})}; // OMP SECTIONS (OpenMP 5.0 - 2.8.1), PARALLEL SECTIONS (OpenMP 5.0 - 2.13.3) -TYPE_PARSER(construct<OpenMPSectionsConstruct>( +TYPE_PARSER(sourced(construct<OpenMPSectionsConstruct>( Parser<OmpBeginSectionsDirective>{} / endOmpLine, - Parser<OmpSectionBlocks>{}, Parser<OmpEndSectionsDirective>{} / endOmpLine)) + cons( // + construct<OpenMPConstruct>(sourced( + construct<OpenMPSectionConstruct>(maybe(sectionDir), block))), + many(construct<OpenMPConstruct>( + sourced(construct<OpenMPSectionConstruct>(sectionDir, block))))), + Parser<OmpEndSectionsDirective>{} / endOmpLine))) static bool IsExecutionPart(const OmpDirectiveName &name) { return name.IsExecutionPart(); @@ -1867,12 +1928,6 @@ TYPE_CONTEXT_PARSER("OpenMP construct"_en_US, construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}), construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{})))) -// END OMP Block directives -TYPE_PARSER( - startOmpLine >> sourced(construct<OmpEndBlockDirective>( - sourced("END"_tok >> Parser<OmpBlockDirective>{}), - Parser<OmpClauseList>{}))) - // END OMP Loop directives TYPE_PARSER( startOmpLine >> sourced(construct<OmpEndLoopDirective>( diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp index 7b46c9f4..cb30939 100644 --- a/flang/lib/Parser/parse-tree.cpp +++ b/flang/lib/Parser/parse-tree.cpp @@ -322,7 +322,7 @@ std::string OmpTraitSetSelectorName::ToString() const { } llvm::omp::Clause OpenMPAtomicConstruct::GetKind() const { - auto &dirSpec{std::get<OmpDirectiveSpecification>(t)}; + const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; for (auto &clause : dirSpec.Clauses().v) { switch (clause.Id()) { case llvm::omp::Clause::OMPC_read: @@ -337,14 +337,14 @@ llvm::omp::Clause OpenMPAtomicConstruct::GetKind() const { } bool OpenMPAtomicConstruct::IsCapture() const { - auto &dirSpec{std::get<OmpDirectiveSpecification>(t)}; + const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; return llvm::any_of(dirSpec.Clauses().v, [](auto &clause) { return clause.Id() == llvm::omp::Clause::OMPC_capture; }); } bool OpenMPAtomicConstruct::IsCompare() const { - auto &dirSpec{std::get<OmpDirectiveSpecification>(t)}; + const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; return llvm::any_of(dirSpec.Clauses().v, [](auto &clause) { return clause.Id() == llvm::omp::Clause::OMPC_compare; }); diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index fc15d46..44f31ca 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2250,6 +2250,11 @@ public: Walk(std::get<OmpObjectList>(x.t)); Walk(": ", std::get<std::optional<std::list<Modifier>>>(x.t)); } + void Unparse(const OmpEnterClause &x) { + using Modifier = OmpEnterClause::Modifier; + Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": "); + Walk(std::get<OmpObjectList>(x.t)); + } void Unparse(const OmpFromClause &x) { using Modifier = OmpFromClause::Modifier; Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": "); @@ -2508,87 +2513,39 @@ public: } } void Unparse(const OmpObjectList &x) { Walk(x.v, ","); } - void Unparse(const OmpBlockDirective &x) { - switch (x.v) { - case llvm::omp::Directive::OMPD_masked: - Word("MASKED"); - break; - case llvm::omp::Directive::OMPD_master: - Word("MASTER"); - break; - case llvm::omp::Directive::OMPD_ordered: - Word("ORDERED "); - break; - case llvm::omp::Directive::OMPD_parallel_masked: - Word("PARALLEL MASKED"); - break; - case llvm::omp::Directive::OMPD_parallel_master: - Word("PARALLEL MASTER"); - break; - case llvm::omp::Directive::OMPD_parallel_workshare: - Word("PARALLEL WORKSHARE "); - break; - case llvm::omp::Directive::OMPD_parallel: - Word("PARALLEL "); - break; - case llvm::omp::Directive::OMPD_scope: - Word("SCOPE "); - break; - case llvm::omp::Directive::OMPD_single: - Word("SINGLE "); - break; - case llvm::omp::Directive::OMPD_target_data: - Word("TARGET DATA "); - break; - case llvm::omp::Directive::OMPD_target_parallel: - Word("TARGET PARALLEL "); - break; - case llvm::omp::Directive::OMPD_target_teams: - Word("TARGET TEAMS "); - break; - case llvm::omp::Directive::OMPD_target: - Word("TARGET "); - break; - case llvm::omp::Directive::OMPD_taskgroup: - Word("TASKGROUP "); - break; - case llvm::omp::Directive::OMPD_task: - Word("TASK "); - break; - case llvm::omp::Directive::OMPD_teams: - Word("TEAMS "); - break; - case llvm::omp::Directive::OMPD_workshare: - Word("WORKSHARE "); - break; - default: - // Nothing to be done - break; - } - } void Unparse(const common::OmpMemoryOrderType &x) { Word(ToUpperCaseLetters(common::EnumToString(x))); } - template <typename Construct> void UnparseBlockConstruct(const Construct &x) { + void Unparse(const OmpBeginDirective &x) { BeginOpenMP(); Word("!$OMP "); - Walk(std::get<OmpDirectiveSpecification>(x.t)); + Walk(static_cast<const OmpDirectiveSpecification &>(x)); + Put("\n"); + EndOpenMP(); + } + + void Unparse(const OmpEndDirective &x) { + BeginOpenMP(); + Word("!$OMP END "); + Walk(static_cast<const OmpDirectiveSpecification &>(x)); Put("\n"); EndOpenMP(); + } + + void Unparse(const OmpBlockConstruct &x) { + Walk(std::get<OmpBeginDirective>(x.t)); Walk(std::get<Block>(x.t), ""); - if (auto &end{std::get<std::optional<OmpDirectiveSpecification>>(x.t)}) { - BeginOpenMP(); - Word("!$OMP END "); + if (auto &end{std::get<std::optional<OmpEndDirective>>(x.t)}) { Walk(*end); + } else { Put("\n"); - EndOpenMP(); } } void Unparse(const OpenMPAtomicConstruct &x) { // - UnparseBlockConstruct(x); + Unparse(static_cast<const OmpBlockConstruct &>(x)); } void Unparse(const OpenMPExecutableAllocate &x) { @@ -2619,7 +2576,7 @@ public: EndOpenMP(); } void Unparse(const OpenMPAllocatorsConstruct &x) { // - UnparseBlockConstruct(x); + Unparse(static_cast<const OmpBlockConstruct &>(x)); } void Unparse(const OmpAssumeDirective &x) { BeginOpenMP(); @@ -2759,7 +2716,7 @@ public: EndOpenMP(); } void Unparse(const OpenMPDispatchConstruct &x) { // - UnparseBlockConstruct(x); + Unparse(static_cast<const OmpBlockConstruct &>(x)); } void Unparse(const OpenMPRequiresConstruct &y) { BeginOpenMP(); @@ -2801,16 +2758,16 @@ public: break; } } - void Unparse(const OmpSectionBlocks &x) { - for (const auto &y : x.v) { + void Unparse(const OpenMPSectionConstruct &x) { + if (auto &&dirSpec{ + std::get<std::optional<OmpDirectiveSpecification>>(x.t)}) { BeginOpenMP(); - Word("!$OMP SECTION"); + Word("!$OMP "); + Walk(*dirSpec); Put("\n"); EndOpenMP(); - // y.u is an OpenMPSectionConstruct - // (y.u).v is Block - Walk(std::get<OpenMPSectionConstruct>(y.u).v, ""); } + Walk(std::get<Block>(x.t), ""); } void Unparse(const OpenMPSectionsConstruct &x) { BeginOpenMP(); @@ -2818,7 +2775,7 @@ public: Walk(std::get<OmpBeginSectionsDirective>(x.t)); Put("\n"); EndOpenMP(); - Walk(std::get<OmpSectionBlocks>(x.t)); + Walk(std::get<std::list<OpenMPConstruct>>(x.t), ""); BeginOpenMP(); Word("!$OMP END "); Walk(std::get<OmpEndSectionsDirective>(x.t)); @@ -2892,19 +2849,7 @@ public: EndOpenMP(); } void Unparse(const OpenMPBlockConstruct &x) { - BeginOpenMP(); - Word("!$OMP "); - Walk(std::get<OmpBeginBlockDirective>(x.t)); - Put("\n"); - EndOpenMP(); - Walk(std::get<Block>(x.t), ""); - if (auto &&end{std::get<std::optional<OmpEndBlockDirective>>(x.t)}) { - BeginOpenMP(); - Word("!$OMP END "); - Walk(*end); - Put("\n"); - EndOpenMP(); - } + Unparse(static_cast<const OmpBlockConstruct &>(x)); } void Unparse(const OpenMPLoopConstruct &x) { BeginOpenMP(); @@ -2986,6 +2931,7 @@ public: WALK_NESTED_ENUM(UseStmt, ModuleNature) // R1410 WALK_NESTED_ENUM(OmpAdjustArgsClause::OmpAdjustOp, Value) // OMP adjustop WALK_NESTED_ENUM(OmpAtClause, ActionTime) // OMP at + WALK_NESTED_ENUM(OmpAutomapModifier, Value) // OMP automap-modifier WALK_NESTED_ENUM(OmpBindClause, Binding) // OMP bind WALK_NESTED_ENUM(OmpProcBindClause, AffinityPolicy) // OMP proc_bind WALK_NESTED_ENUM(OmpDefaultClause, DataSharingAttribute) // OMP default diff --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp index 9cbea97..77e2b01 100644 --- a/flang/lib/Semantics/check-acc-structure.cpp +++ b/flang/lib/Semantics/check-acc-structure.cpp @@ -7,8 +7,15 @@ //===----------------------------------------------------------------------===// #include "check-acc-structure.h" #include "flang/Common/enum-set.h" +#include "flang/Evaluate/tools.h" #include "flang/Parser/parse-tree.h" +#include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" +#include "flang/Semantics/type.h" +#include "flang/Support/Fortran.h" +#include "llvm/Support/AtomicOrdering.h" + +#include <optional> #define CHECK_SIMPLE_CLAUSE(X, Y) \ void AccStructureChecker::Enter(const parser::AccClause::X &) { \ @@ -342,20 +349,219 @@ void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) { dirContext_.pop_back(); } -void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) { - const parser::AssignmentStmt &assignment{ - std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement}; - const auto &var{std::get<parser::Variable>(assignment.t)}; - const auto &expr{std::get<parser::Expr>(assignment.t)}; +void AccStructureChecker::CheckAtomicStmt( + const parser::AssignmentStmt &assign, const std::string &construct) { + const auto &var{std::get<parser::Variable>(assign.t)}; + const auto &expr{std::get<parser::Expr>(assign.t)}; const auto *rhs{GetExpr(context_, expr)}; const auto *lhs{GetExpr(context_, var)}; - if (lhs && rhs) { - if (lhs->Rank() != 0) + + if (lhs) { + if (lhs->Rank() != 0) { context_.Say(expr.source, - "LHS of atomic update statement must be scalar"_err_en_US); - if (rhs->Rank() != 0) + "LHS of atomic %s statement must be scalar"_err_en_US, construct); + } + // TODO: Check if lhs is intrinsic type. + } + if (rhs) { + if (rhs->Rank() != 0) { context_.Say(var.GetSource(), - "RHS of atomic update statement must be scalar"_err_en_US); + "RHS of atomic %s statement must be scalar"_err_en_US, construct); + } + // TODO: Check if rhs is intrinsic type. + } +} + +static constexpr evaluate::operation::OperatorSet validAccAtomicUpdateOperators{ + evaluate::operation::Operator::Add, evaluate::operation::Operator::Mul, + evaluate::operation::Operator::Sub, evaluate::operation::Operator::Div, + evaluate::operation::Operator::And, evaluate::operation::Operator::Or, + evaluate::operation::Operator::Eqv, evaluate::operation::Operator::Neqv, + evaluate::operation::Operator::Max, evaluate::operation::Operator::Min}; + +static bool IsValidAtomicUpdateOperation( + const evaluate::operation::Operator &op) { + return validAccAtomicUpdateOperators.test(op); +} + +// Couldn't reproduce this behavior with evaluate::UnwrapConvertedExpr which +// is similar but only works within a single type category. +static SomeExpr GetExprModuloConversion(const SomeExpr &expr) { + const auto [op, args]{evaluate::GetTopLevelOperation(expr)}; + // Check: if it is a conversion then it must have at least one argument. + CHECK(((op != evaluate::operation::Operator::Convert && + op != evaluate::operation::Operator::Resize) || + args.size() >= 1) && + "Invalid conversion operation"); + if ((op == evaluate::operation::Operator::Convert || + op == evaluate::operation::Operator::Resize) && + args.size() >= 1) { + return args[0]; + } + return expr; +} + +void AccStructureChecker::CheckAtomicUpdateStmt( + const parser::AssignmentStmt &assign, const SomeExpr &updateVar, + const SomeExpr *captureVar) { + CheckAtomicStmt(assign, "update"); + const auto &expr{std::get<parser::Expr>(assign.t)}; + const auto *rhs{GetExpr(context_, expr)}; + if (rhs) { + const auto [op, args]{ + evaluate::GetTopLevelOperation(GetExprModuloConversion(*rhs))}; + if (!IsValidAtomicUpdateOperation(op)) { + context_.Say(expr.source, + "Invalid atomic update operation, can only use: *, +, -, *, /, and, or, eqv, neqv, max, min, iand, ior, ieor"_err_en_US); + } else { + bool foundUpdateVar{false}; + for (const auto &arg : args) { + if (updateVar == GetExprModuloConversion(arg)) { + if (foundUpdateVar) { + context_.Say(expr.source, + "The updated variable, %s, cannot appear more than once in the atomic update operation"_err_en_US, + updateVar.AsFortran()); + } else { + foundUpdateVar = true; + } + } else if (evaluate::IsVarSubexpressionOf(updateVar, arg)) { + // TODO: Get the source location of arg and point to the individual + // argument. + context_.Say(expr.source, + "Arguments to the atomic update operation cannot reference the updated variable, %s, as a subexpression"_err_en_US, + updateVar.AsFortran()); + } + } + if (!foundUpdateVar) { + context_.Say(expr.source, + "The RHS of this atomic update statement must reference the updated variable: %s"_err_en_US, + updateVar.AsFortran()); + } + } + } +} + +void AccStructureChecker::CheckAtomicWriteStmt( + const parser::AssignmentStmt &assign, const SomeExpr &updateVar, + const SomeExpr *captureVar) { + CheckAtomicStmt(assign, "write"); + const auto &expr{std::get<parser::Expr>(assign.t)}; + const auto *rhs{GetExpr(context_, expr)}; + if (rhs) { + if (evaluate::IsVarSubexpressionOf(updateVar, *rhs)) { + context_.Say(expr.source, + "The RHS of this atomic write statement cannot reference the atomic variable: %s"_err_en_US, + updateVar.AsFortran()); + } + } +} + +void AccStructureChecker::CheckAtomicCaptureStmt( + const parser::AssignmentStmt &assign, const SomeExpr *updateVar, + const SomeExpr &captureVar) { + CheckAtomicStmt(assign, "capture"); +} + +void AccStructureChecker::Enter(const parser::AccAtomicCapture &capture) { + const Fortran::parser::AssignmentStmt &stmt1{ + std::get<Fortran::parser::AccAtomicCapture::Stmt1>(capture.t) + .v.statement}; + const Fortran::parser::AssignmentStmt &stmt2{ + std::get<Fortran::parser::AccAtomicCapture::Stmt2>(capture.t) + .v.statement}; + const auto &var1{std::get<parser::Variable>(stmt1.t)}; + const auto &var2{std::get<parser::Variable>(stmt2.t)}; + const auto *lhs1{GetExpr(context_, var1)}; + const auto *lhs2{GetExpr(context_, var2)}; + if (!lhs1 || !lhs2) { + // Not enough information to check. + return; + } + if (*lhs1 == *lhs2) { + context_.Say(std::get<parser::Verbatim>(capture.t).source, + "The variables assigned in this atomic capture construct must be distinct"_err_en_US); + return; + } + const auto &expr1{std::get<parser::Expr>(stmt1.t)}; + const auto &expr2{std::get<parser::Expr>(stmt2.t)}; + const auto *rhs1{GetExpr(context_, expr1)}; + const auto *rhs2{GetExpr(context_, expr2)}; + if (!rhs1 || !rhs2) { + return; + } + bool stmt1CapturesLhs2{*lhs2 == GetExprModuloConversion(*rhs1)}; + bool stmt2CapturesLhs1{*lhs1 == GetExprModuloConversion(*rhs2)}; + if (stmt1CapturesLhs2 && !stmt2CapturesLhs1) { + if (*lhs2 == GetExprModuloConversion(*rhs2)) { + // a = b; b = b: Doesn't fit the spec. + context_.Say(std::get<parser::Verbatim>(capture.t).source, + "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US); + // TODO: Add attatchment that a = b seems to be a capture, + // but b = b is not a valid update or write. + } else if (evaluate::IsVarSubexpressionOf(*lhs2, *rhs2)) { + // Take v = x; x = <expr w/ x> as capture; update + const auto &updateVar{*lhs2}; + const auto &captureVar{*lhs1}; + CheckAtomicCaptureStmt(stmt1, &updateVar, captureVar); + CheckAtomicUpdateStmt(stmt2, updateVar, &captureVar); + } else { + // Take v = x; x = <expr w/o x> as capture; write + const auto &updateVar{*lhs2}; + const auto &captureVar{*lhs1}; + CheckAtomicCaptureStmt(stmt1, &updateVar, captureVar); + CheckAtomicWriteStmt(stmt2, updateVar, &captureVar); + } + } else if (stmt2CapturesLhs1 && !stmt1CapturesLhs2) { + if (*lhs1 == GetExprModuloConversion(*rhs1)) { + // Error a = a; b = a; + context_.Say(var1.GetSource(), + "The first assignment in this atomic capture construct doesn't perform a valid update"_err_en_US); + // Add attatchment that a = a is not considered an update, + // but b = a seems to be a capture. + } else { + // Take x = <expr>; v = x: as update; capture + const auto &updateVar{*lhs1}; + const auto &captureVar{*lhs2}; + CheckAtomicUpdateStmt(stmt1, updateVar, &captureVar); + CheckAtomicCaptureStmt(stmt2, &updateVar, captureVar); + } + } else if (stmt1CapturesLhs2 && stmt2CapturesLhs1) { + // x1 = x2; x2 = x1; Doesn't fit the spec. + context_.Say(std::get<parser::Verbatim>(capture.t).source, + "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US); + // TODO: Add attatchment that both assignments seem to be captures. + } else { // !stmt1CapturesLhs2 && !stmt2CapturesLhs1 + // a = <expr != b>; b = <expr != a>; Doesn't fit the spec + context_.Say(std::get<parser::Verbatim>(capture.t).source, + "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US); + // TODO: Add attatchment that neither assignment seems to be a capture. + } +} + +void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) { + const auto &assign{ + std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement}; + const auto &var{std::get<parser::Variable>(assign.t)}; + if (const auto *updateVar{GetExpr(context_, var)}) { + CheckAtomicUpdateStmt(assign, *updateVar, /*captureVar=*/nullptr); + } +} + +void AccStructureChecker::Enter(const parser::AccAtomicWrite &x) { + const auto &assign{ + std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement}; + const auto &var{std::get<parser::Variable>(assign.t)}; + if (const auto *updateVar{GetExpr(context_, var)}) { + CheckAtomicWriteStmt(assign, *updateVar, /*captureVar=*/nullptr); + } +} + +void AccStructureChecker::Enter(const parser::AccAtomicRead &x) { + const auto &assign{ + std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement}; + const auto &var{std::get<parser::Variable>(assign.t)}; + if (const auto *captureVar{GetExpr(context_, var)}) { + CheckAtomicCaptureStmt(assign, /*updateVar=*/nullptr, *captureVar); } } diff --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h index 6a9aa01..359f155 100644 --- a/flang/lib/Semantics/check-acc-structure.h +++ b/flang/lib/Semantics/check-acc-structure.h @@ -63,6 +63,9 @@ public: void Enter(const parser::OpenACCCacheConstruct &); void Leave(const parser::OpenACCCacheConstruct &); void Enter(const parser::AccAtomicUpdate &); + void Enter(const parser::AccAtomicCapture &); + void Enter(const parser::AccAtomicWrite &); + void Enter(const parser::AccAtomicRead &); void Enter(const parser::OpenACCEndConstruct &); // Clauses @@ -80,6 +83,19 @@ public: #include "llvm/Frontend/OpenACC/ACC.inc" private: + void CheckAtomicStmt( + const parser::AssignmentStmt &assign, const std::string &construct); + void CheckAtomicUpdateStmt(const parser::AssignmentStmt &assign, + const SomeExpr &updateVar, const SomeExpr *captureVar); + void CheckAtomicCaptureStmt(const parser::AssignmentStmt &assign, + const SomeExpr *updateVar, const SomeExpr &captureVar); + void CheckAtomicWriteStmt(const parser::AssignmentStmt &assign, + const SomeExpr &updateVar, const SomeExpr *captureVar); + void CheckAtomicUpdateVariable( + const parser::Variable &updateVar, const parser::Variable &captureVar); + void CheckAtomicCaptureVariable( + const parser::Variable &captureVar, const parser::Variable &updateVar); + bool CheckAllowedModifier(llvm::acc::Clause clause); bool IsComputeConstruct(llvm::acc::Directive directive) const; bool IsInsideComputeConstruct() const; diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp index b011476..9b48432 100644 --- a/flang/lib/Semantics/check-cuda.cpp +++ b/flang/lib/Semantics/check-cuda.cpp @@ -761,14 +761,13 @@ void CUDAChecker::Enter(const parser::AssignmentStmt &x) { // legal. if (nbLhs == 0 && nbRhs > 1) { context_.Say(lhsLoc, - "More than one reference to a CUDA object on the right hand side of the assigment"_err_en_US); + "More than one reference to a CUDA object on the right hand side of the assignment"_err_en_US); } - if (Fortran::evaluate::HasCUDADeviceAttrs(assign->lhs) && - Fortran::evaluate::HasCUDAImplicitTransfer(assign->rhs)) { + if (evaluate::HasCUDADeviceAttrs(assign->lhs) && + evaluate::HasCUDAImplicitTransfer(assign->rhs)) { if (GetNbOfCUDAManagedOrUnifiedSymbols(assign->lhs) == 1 && - GetNbOfCUDAManagedOrUnifiedSymbols(assign->rhs) == 1 && - GetNbOfCUDADeviceSymbols(assign->rhs) == 1) { + GetNbOfCUDAManagedOrUnifiedSymbols(assign->rhs) == 1 && nbRhs == 1) { return; // This is a special case handled on the host. } context_.Say(lhsLoc, "Unsupported CUDA data transfer"_err_en_US); diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index a2f2906..d769f22 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -2081,7 +2081,7 @@ static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) { } static bool ConflictsWithIntrinsicOperator( - const GenericKind &kind, const Procedure &proc) { + const GenericKind &kind, const Procedure &proc, SemanticsContext &context) { if (!kind.IsIntrinsicOperator()) { return false; } @@ -2167,7 +2167,7 @@ bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind, } } else if (!checkDefinedOperatorArgs(opName, specific, proc)) { return false; // error was reported - } else if (ConflictsWithIntrinsicOperator(kind, proc)) { + } else if (ConflictsWithIntrinsicOperator(kind, proc, context_)) { msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US; } if (msg) { diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp index c5ed879..eca50c1 100644 --- a/flang/lib/Semantics/check-omp-atomic.cpp +++ b/flang/lib/Semantics/check-omp-atomic.cpp @@ -197,7 +197,8 @@ static std::pair<parser::CharBlock, parser::CharBlock> SplitAssignmentSource( } static bool IsCheckForAssociated(const SomeExpr &cond) { - return GetTopLevelOperation(cond).first == operation::Operator::Associated; + return GetTopLevelOperationIgnoreResizing(cond).first == + operation::Operator::Associated; } static bool IsMaybeAtomicWrite(const evaluate::Assignment &assign) { @@ -399,8 +400,8 @@ OmpStructureChecker::CheckUpdateCapture( // subexpression of the right-hand side. // 2. An assignment could be a capture (cbc) if the right-hand side is // a variable (or a function ref), with potential type conversions. - bool cbu1{IsSubexpressionOf(as1.lhs, as1.rhs)}; // Can as1 be an update? - bool cbu2{IsSubexpressionOf(as2.lhs, as2.rhs)}; // Can as2 be an update? + bool cbu1{IsVarSubexpressionOf(as1.lhs, as1.rhs)}; // Can as1 be an update? + bool cbu2{IsVarSubexpressionOf(as2.lhs, as2.rhs)}; // Can as2 be an update? bool cbc1{IsVarOrFunctionRef(GetConvertInput(as1.rhs))}; // Can 1 be capture? bool cbc2{IsVarOrFunctionRef(GetConvertInput(as2.rhs))}; // Can 2 be capture? @@ -607,7 +608,7 @@ void OmpStructureChecker::CheckAtomicUpdateAssignment( std::pair<operation::Operator, std::vector<SomeExpr>> top{ operation::Operator::Unknown, {}}; if (auto &&maybeInput{GetConvertInput(update.rhs)}) { - top = GetTopLevelOperation(*maybeInput); + top = GetTopLevelOperationIgnoreResizing(*maybeInput); } switch (top.first) { case operation::Operator::Add: @@ -657,7 +658,7 @@ void OmpStructureChecker::CheckAtomicUpdateAssignment( if (IsSameOrConvertOf(arg, atom)) { ++count; } else { - if (!subExpr && IsSubexpressionOf(atom, arg)) { + if (!subExpr && evaluate::IsVarSubexpressionOf(atom, arg)) { subExpr = arg; } nonAtom.push_back(arg); @@ -715,7 +716,7 @@ void OmpStructureChecker::CheckAtomicConditionalUpdateAssignment( CheckAtomicVariable(atom, alsrc); - auto top{GetTopLevelOperation(cond)}; + auto top{GetTopLevelOperationIgnoreResizing(cond)}; // Missing arguments to operations would have been diagnosed by now. switch (top.first) { @@ -1105,12 +1106,11 @@ void OmpStructureChecker::CheckAtomicRead( // of the following forms: // v = x // v => x - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; auto &block{std::get<parser::Block>(x.t)}; // Read cannot be conditional or have a capture statement. if (x.IsCompare() || x.IsCapture()) { - context_.Say(dirSpec.source, + context_.Say(x.BeginDir().source, "ATOMIC READ cannot have COMPARE or CAPTURE clauses"_err_en_US); return; } @@ -1141,12 +1141,11 @@ void OmpStructureChecker::CheckAtomicRead( void OmpStructureChecker::CheckAtomicWrite( const parser::OpenMPAtomicConstruct &x) { - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; auto &block{std::get<parser::Block>(x.t)}; // Write cannot be conditional or have a capture statement. if (x.IsCompare() || x.IsCapture()) { - context_.Say(dirSpec.source, + context_.Say(x.BeginDir().source, "ATOMIC WRITE cannot have COMPARE or CAPTURE clauses"_err_en_US); return; } @@ -1234,7 +1233,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { } }}; - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; auto &dir{std::get<parser::OmpDirectiveName>(dirSpec.t)}; PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_atomic); llvm::omp::Clause kind{x.GetKind()}; diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp index b82e2f7..59d57a2 100644 --- a/flang/lib/Semantics/check-omp-loop.cpp +++ b/flang/lib/Semantics/check-omp-loop.cpp @@ -18,6 +18,7 @@ #include "flang/Common/idioms.h" #include "flang/Common/visit.h" #include "flang/Parser/char-block.h" +#include "flang/Parser/openmp-utils.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Parser/parse-tree.h" #include "flang/Parser/tools.h" @@ -196,14 +197,9 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { common::visitors{ // Allow `!$OMP ORDERED SIMD` [&](const parser::OpenMPBlockConstruct &c) { - const auto &beginBlockDir{ - std::get<parser::OmpBeginBlockDirective>(c.t)}; - const auto &beginDir{ - std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { - const auto &clauses{ - std::get<parser::OmpClauseList>(beginBlockDir.t)}; - for (const auto &clause : clauses.v) { + const parser::OmpDirectiveSpecification &beginSpec{c.BeginDir()}; + if (beginSpec.DirId() == llvm::omp::Directive::OMPD_ordered) { + for (const auto &clause : beginSpec.Clauses().v) { if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { eligibleSIMD = true; break; @@ -247,7 +243,7 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { }, c.u); if (!eligibleSIMD) { - context_.Say(parser::FindSourceLocation(c), + context_.Say(parser::omp::GetOmpDirectiveName(c).source, "The only OpenMP constructs that can be encountered during execution " "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, " "the `SIMD` construct, the `SCAN` construct and the `ORDERED` " diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index d214d22..a9c56c3 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -498,7 +498,7 @@ template <typename Checker> struct DirectiveSpellingVisitor { template <typename... Ts> static const parser::OmpDirectiveName &GetDirName( const std::tuple<Ts...> &t) { - return std::get<parser::OmpDirectiveSpecification>(t).DirName(); + return std::get<parser::OmpBeginDirective>(t).DirName(); } bool Pre(const parser::OmpSectionsDirective &x) { @@ -588,12 +588,14 @@ template <typename Checker> struct DirectiveSpellingVisitor { checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_requires); return false; } - - bool Pre(const parser::OmpBlockDirective &x) { - checker_(x.source, x.v); + bool Pre(const parser::OmpBeginDirective &x) { + checker_(x.DirName().source, x.DirId()); + return false; + } + bool Pre(const parser::OmpEndDirective &x) { + checker_(x.DirName().source, x.DirId()); return false; } - bool Pre(const parser::OmpLoopDirective &x) { checker_(x.source, x.v); return false; @@ -726,22 +728,22 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { // 2.12.5 Target Construct Restriction bool eligibleTarget{true}; llvm::omp::Directive ineligibleTargetDir; + parser::CharBlock source; common::visit( common::visitors{ [&](const parser::OpenMPBlockConstruct &c) { - const auto &beginBlockDir{ - std::get<parser::OmpBeginBlockDirective>(c.t)}; - const auto &beginDir{ - std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - if (beginDir.v == llvm::omp::Directive::OMPD_target_data) { + const parser::OmpDirectiveSpecification &beginSpec{c.BeginDir()}; + source = beginSpec.DirName().source; + if (beginSpec.DirId() == llvm::omp::Directive::OMPD_target_data) { eligibleTarget = false; - ineligibleTargetDir = beginDir.v; + ineligibleTargetDir = beginSpec.DirId(); } }, [&](const parser::OpenMPStandaloneConstruct &c) { common::visit( common::visitors{ [&](const parser::OpenMPSimpleStandaloneConstruct &c) { + source = c.v.DirName().source; switch (llvm::omp::Directive dirId{c.v.DirId()}) { case llvm::omp::Directive::OMPD_target_update: case llvm::omp::Directive::OMPD_target_enter_data: @@ -762,6 +764,7 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { std::get<parser::OmpBeginLoopDirective>(c.t)}; const auto &beginDir{ std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; + source = beginLoopDir.source; if (llvm::omp::allTargetSet.test(beginDir.v)) { eligibleTarget = false; ineligibleTargetDir = beginDir.v; @@ -771,8 +774,7 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { }, c.u); if (!eligibleTarget) { - context_.Warn(common::UsageWarning::OpenMPUsage, - parser::FindSourceLocation(c), + context_.Warn(common::UsageWarning::OpenMPUsage, source, "If %s directive is nested inside TARGET region, the behaviour is unspecified"_port_en_US, parser::ToUpperCaseLetters( getDirectiveName(ineligibleTargetDir).str())); @@ -780,25 +782,18 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { } void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { - const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; - const auto &endBlockDir{ - std::get<std::optional<parser::OmpEndBlockDirective>>(x.t)}; - const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; + const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()}; + const std::optional<parser::OmpEndDirective> &endSpec{x.EndDir()}; const parser::Block &block{std::get<parser::Block>(x.t)}; - if (endBlockDir) { - const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir->t)}; - CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); - } - - PushContextAndClauseSets(beginDir.source, beginDir.v); + PushContextAndClauseSets(beginSpec.DirName().source, beginSpec.DirId()); if (llvm::omp::allTargetSet.test(GetContext().directive)) { EnterDirectiveNest(TargetNest); } if (CurrentDirectiveIsNested()) { if (llvm::omp::bottomTeamsSet.test(GetContextParent().directive)) { - HasInvalidTeamsNesting(beginDir.v, beginDir.source); + HasInvalidTeamsNesting(beginSpec.DirId(), beginSpec.source); } if (GetContext().directive == llvm::omp::Directive::OMPD_master) { CheckMasterNesting(x); @@ -807,7 +802,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { // region or a target region. if (GetContext().directive == llvm::omp::Directive::OMPD_teams && GetContextParent().directive != llvm::omp::Directive::OMPD_target) { - context_.Say(parser::FindSourceLocation(x), + context_.Say(x.BeginDir().DirName().source, "%s region can only be strictly nested within the implicit parallel " "region or TARGET region"_err_en_US, ContextDirectiveAsFortran()); @@ -824,12 +819,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { } } - CheckNoBranching(block, beginDir.v, beginDir.source); + CheckNoBranching(block, beginSpec.DirId(), beginSpec.source); // Target block constructs are target device constructs. Keep track of // whether any such construct has been visited to later check that REQUIRES // directives for target-related options don't appear after them. - if (llvm::omp::allTargetSet.test(beginDir.v)) { + if (llvm::omp::allTargetSet.test(beginSpec.DirId())) { deviceConstructFound_ = true; } @@ -839,8 +834,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { bool foundNowait{false}; parser::CharBlock NowaitSource; - auto catchCopyPrivateNowaitClauses = [&](const auto &dir, bool isEnd) { - for (auto &clause : std::get<parser::OmpClauseList>(dir.t).v) { + auto catchCopyPrivateNowaitClauses = [&](const auto &dirSpec, bool isEnd) { + for (auto &clause : dirSpec.Clauses().v) { if (clause.Id() == llvm::omp::Clause::OMPC_copyprivate) { for (const auto &ompObject : GetOmpObjectList(clause)->v) { const auto *name{parser::Unwrap<parser::Name>(ompObject)}; @@ -881,9 +876,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { } } }; - catchCopyPrivateNowaitClauses(beginBlockDir, false); - if (endBlockDir) { - catchCopyPrivateNowaitClauses(*endBlockDir, true); + catchCopyPrivateNowaitClauses(beginSpec, false); + if (endSpec) { + catchCopyPrivateNowaitClauses(*endSpec, true); } unsigned version{context_.langOptions().OpenMPVersion}; if (version <= 52 && NowaitSource.ToString().size() && @@ -893,7 +888,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { } } - switch (beginDir.v) { + switch (beginSpec.DirId()) { case llvm::omp::Directive::OMPD_target: if (CheckTargetBlockOnlyTeams(block)) { EnterDirectiveNest(TargetBlockOnlyTeams); @@ -901,27 +896,25 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { break; case llvm::omp::OMPD_workshare: case llvm::omp::OMPD_parallel_workshare: - CheckWorkshareBlockStmts(block, beginDir.source); + CheckWorkshareBlockStmts(block, beginSpec.source); HasInvalidWorksharingNesting( - beginDir.source, llvm::omp::nestedWorkshareErrSet); + beginSpec.source, llvm::omp::nestedWorkshareErrSet); break; case llvm::omp::Directive::OMPD_scope: case llvm::omp::Directive::OMPD_single: // TODO: This check needs to be extended while implementing nesting of // regions checks. HasInvalidWorksharingNesting( - beginDir.source, llvm::omp::nestedWorkshareErrSet); + beginSpec.source, llvm::omp::nestedWorkshareErrSet); break; - case llvm::omp::Directive::OMPD_task: { - const auto &clauses{std::get<parser::OmpClauseList>(beginBlockDir.t)}; - for (const auto &clause : clauses.v) { + case llvm::omp::Directive::OMPD_task: + for (const auto &clause : beginSpec.Clauses().v) { if (std::get_if<parser::OmpClause::Untied>(&clause.u)) { OmpUnitedTaskDesignatorChecker check{context_}; parser::Walk(block, check); } } break; - } default: break; } @@ -934,7 +927,7 @@ void OmpStructureChecker::CheckMasterNesting( // TODO: Expand the check to include `LOOP` construct as well when it is // supported. if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet)) { - context_.Say(parser::FindSourceLocation(x), + context_.Say(x.BeginDir().source, "`MASTER` region may not be closely nested inside of `WORKSHARING`, " "`LOOP`, `TASK`, `TASKLOOP`," " or `ATOMIC` region."_err_en_US); @@ -1034,7 +1027,7 @@ void OmpStructureChecker::ChecksOnOrderedAsBlock() { } } -void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective &) { +void OmpStructureChecker::Leave(const parser::OmpBeginDirective &) { switch (GetContext().directive) { case llvm::omp::Directive::OMPD_ordered: // [5.1] 2.19.9 Ordered Construct Restriction @@ -1057,10 +1050,11 @@ void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { PushContextAndClauseSets(beginDir.source, beginDir.v); AddEndDirectiveClauses(std::get<parser::OmpClauseList>(endSectionsDir.t)); - const auto §ionBlocks{std::get<parser::OmpSectionBlocks>(x.t)}; - for (const parser::OpenMPConstruct &block : sectionBlocks.v) { - CheckNoBranching(std::get<parser::OpenMPSectionConstruct>(block.u).v, - beginDir.v, beginDir.source); + const auto §ionBlocks{std::get<std::list<parser::OpenMPConstruct>>(x.t)}; + for (const parser::OpenMPConstruct &construct : sectionBlocks) { + auto §ion{std::get<parser::OpenMPSectionConstruct>(construct.u)}; + CheckNoBranching( + std::get<parser::Block>(section.t), beginDir.v, beginDir.source); } HasInvalidWorksharingNesting( beginDir.source, llvm::omp::nestedWorkshareErrSet); @@ -1568,9 +1562,10 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) { }, [&](const parser::OmpClause::Enter &enterClause) { enterClauseFound = true; - CheckSymbolNames(dir.source, enterClause.v); - CheckVarIsNotPartOfAnotherVar(dir.source, enterClause.v); - CheckThreadprivateOrDeclareTargetVar(enterClause.v); + auto &objList{std::get<parser::OmpObjectList>(enterClause.v.t)}; + CheckSymbolNames(dir.source, objList); + CheckVarIsNotPartOfAnotherVar(dir.source, objList); + CheckThreadprivateOrDeclareTargetVar(objList); }, [&](const parser::OmpClause::DeviceType &deviceTypeClause) { deviceTypeClauseFound = true; @@ -1599,7 +1594,7 @@ void OmpStructureChecker::Enter(const parser::OmpErrorDirective &x) { } void OmpStructureChecker::Enter(const parser::OpenMPDispatchConstruct &x) { - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; const auto &block{std::get<parser::Block>(x.t)}; PushContextAndClauseSets( dirSpec.DirName().source, llvm::omp::Directive::OMPD_dispatch); @@ -1670,7 +1665,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { isPredefinedAllocator = true; - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; auto &block{std::get<parser::Block>(x.t)}; PushContextAndClauseSets( dirSpec.DirName().source, llvm::omp::Directive::OMPD_allocators); @@ -1701,7 +1696,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) { } void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) { - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; for (const auto &clause : dirSpec.Clauses().v) { if (const auto *allocClause{ @@ -1735,7 +1730,7 @@ void OmpStructureChecker::CheckBarrierNesting( // TODO: Expand the check to include `LOOP` construct as well when it is // supported. if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { - context_.Say(parser::FindSourceLocation(x), + context_.Say(x.v.DirName().source, "`BARRIER` region may not be closely nested inside of `WORKSHARING`, " "`LOOP`, `TASK`, `TASKLOOP`," "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); @@ -2275,22 +2270,21 @@ void OmpStructureChecker::CheckCancellationNest( } } -void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { - const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; - ResetPartialContext(dir.source); - switch (dir.v) { +void OmpStructureChecker::Enter(const parser::OmpEndDirective &x) { + parser::CharBlock source{x.DirName().source}; + ResetPartialContext(source); + switch (x.DirId()) { case llvm::omp::Directive::OMPD_scope: - PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_scope); + PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_scope); break; // 2.7.3 end-single-clause -> copyprivate-clause | // nowait-clause case llvm::omp::Directive::OMPD_single: - PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single); + PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_single); break; // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] case llvm::omp::Directive::OMPD_workshare: - PushContextAndClauseSets( - dir.source, llvm::omp::Directive::OMPD_end_workshare); + PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_workshare); break; default: // no clauses are allowed @@ -2303,7 +2297,7 @@ void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { // constructs unless a nowait clause is specified. Only OMPD_end_single and // end_workshareare popped as they are pushed while entering the // EndBlockDirective. -void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) { +void OmpStructureChecker::Leave(const parser::OmpEndDirective &x) { if ((GetContext().directive == llvm::omp::Directive::OMPD_end_scope) || (GetContext().directive == llvm::omp::Directive::OMPD_end_single) || (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) { @@ -4028,7 +4022,11 @@ void OmpStructureChecker::Enter(const parser::OmpClause::HasDeviceAddr &x) { void OmpStructureChecker::Enter(const parser::OmpClause::Enter &x) { CheckAllowedClause(llvm::omp::Clause::OMPC_enter); - const parser::OmpObjectList &objList{x.v}; + if (!OmpVerifyModifiers( + x.v, llvm::omp::OMPC_enter, GetContext().clauseSource, context_)) { + return; + } + const parser::OmpObjectList &objList{std::get<parser::OmpObjectList>(x.v.t)}; SymbolSourceMap symbols; GetSymbolsInObjectList(objList, symbols); for (const auto &[symbol, source] : symbols) { @@ -4352,11 +4350,8 @@ bool OmpStructureChecker::CheckTargetBlockOnlyTeams( parser::Unwrap<parser::OpenMPConstruct>(*it)}) { if (const auto *ompBlockConstruct{ std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { - const auto &beginBlockDir{ - std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; - const auto &beginDir{ - std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - if (beginDir.v == llvm::omp::Directive::OMPD_teams) { + llvm::omp::Directive dirId{ompBlockConstruct->BeginDir().DirId()}; + if (dirId == llvm::omp::Directive::OMPD_teams) { nestedTeams = true; } } @@ -4402,11 +4397,7 @@ void OmpStructureChecker::CheckWorkshareBlockStmts( auto currentDir{llvm::omp::Directive::OMPD_unknown}; if (const auto *ompBlockConstruct{ std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { - const auto &beginBlockDir{ - std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; - const auto &beginDir{ - std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - currentDir = beginDir.v; + currentDir = ompBlockConstruct->BeginDir().DirId(); } else if (const auto *ompLoopConstruct{ std::get_if<parser::OpenMPLoopConstruct>( &ompConstruct->u)}) { @@ -4488,17 +4479,18 @@ const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList( const parser::OmpClause &clause) { // Clauses with OmpObjectList as its data member - using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate, - parser::OmpClause::Copyin, parser::OmpClause::Enter, - parser::OmpClause::Firstprivate, parser::OmpClause::Link, - parser::OmpClause::Private, parser::OmpClause::Shared, - parser::OmpClause::UseDevicePtr, parser::OmpClause::UseDeviceAddr>; + using MemberObjectListClauses = + std::tuple<parser::OmpClause::Copyprivate, parser::OmpClause::Copyin, + parser::OmpClause::Firstprivate, parser::OmpClause::Link, + parser::OmpClause::Private, parser::OmpClause::Shared, + parser::OmpClause::UseDevicePtr, parser::OmpClause::UseDeviceAddr>; // Clauses with OmpObjectList in the tuple - using TupleObjectListClauses = std::tuple<parser::OmpClause::Aligned, - parser::OmpClause::Allocate, parser::OmpClause::From, - parser::OmpClause::Lastprivate, parser::OmpClause::Map, - parser::OmpClause::Reduction, parser::OmpClause::To>; + using TupleObjectListClauses = + std::tuple<parser::OmpClause::Aligned, parser::OmpClause::Allocate, + parser::OmpClause::From, parser::OmpClause::Lastprivate, + parser::OmpClause::Map, parser::OmpClause::Reduction, + parser::OmpClause::To, parser::OmpClause::Enter>; // TODO:: Generate the tuples using TableGen. // Handle other constructs with OmpObjectList such as OpenMPThreadprivate. diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index f4a291d..6b33ca6 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -90,9 +90,9 @@ public: void Leave(const parser::OpenMPDeclarativeAssumes &); void Enter(const parser::OpenMPBlockConstruct &); void Leave(const parser::OpenMPBlockConstruct &); - void Leave(const parser::OmpBeginBlockDirective &); - void Enter(const parser::OmpEndBlockDirective &); - void Leave(const parser::OmpEndBlockDirective &); + void Leave(const parser::OmpBeginDirective &); + void Enter(const parser::OmpEndDirective &); + void Leave(const parser::OmpEndDirective &); void Enter(const parser::OpenMPSectionsConstruct &); void Leave(const parser::OpenMPSectionsConstruct &); diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp index 1447372..92dbe0e 100644 --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -165,10 +165,17 @@ public: bool CheckForNullPointer(const char *where = "as an operand here"); bool CheckForAssumedRank(const char *where = "as an operand here"); + bool AnyCUDADeviceData() const; + // Returns true if an interface has been defined for an intrinsic operator + // with one or more device operands. + bool HasDeviceDefinedIntrinsicOpOverride(const char *) const; + template <typename E> bool HasDeviceDefinedIntrinsicOpOverride(E opr) const { + return HasDeviceDefinedIntrinsicOpOverride( + context_.context().languageFeatures().GetNames(opr)); + } + // Find and return a user-defined operator or report an error. // The provided message is used if there is no such operator. - // If a definedOpSymbolPtr is provided, the caller must check - // for its accessibility. MaybeExpr TryDefinedOp( const char *, parser::MessageFixedText, bool isUserOp = false); template <typename E> @@ -183,6 +190,8 @@ public: void Dump(llvm::raw_ostream &); private: + bool HasDeviceDefinedIntrinsicOpOverride( + const std::vector<const char *> &) const; MaybeExpr TryDefinedOp( const std::vector<const char *> &, parser::MessageFixedText); MaybeExpr TryBoundOp(const Symbol &, int passIndex); @@ -202,7 +211,7 @@ private: void SayNoMatch( const std::string &, bool isAssignment = false, bool isAmbiguous = false); std::string TypeAsFortran(std::size_t); - bool AnyUntypedOrMissingOperand(); + bool AnyUntypedOrMissingOperand() const; ExpressionAnalyzer &context_; ActualArguments actuals_; @@ -4497,13 +4506,20 @@ void ArgumentAnalyzer::Analyze( bool ArgumentAnalyzer::IsIntrinsicRelational(RelationalOperator opr, const DynamicType &leftType, const DynamicType &rightType) const { CHECK(actuals_.size() == 2); - return semantics::IsIntrinsicRelational( - opr, leftType, GetRank(0), rightType, GetRank(1)); + return !(context_.context().languageFeatures().IsEnabled( + common::LanguageFeature::CUDA) && + HasDeviceDefinedIntrinsicOpOverride(opr)) && + semantics::IsIntrinsicRelational( + opr, leftType, GetRank(0), rightType, GetRank(1)); } bool ArgumentAnalyzer::IsIntrinsicNumeric(NumericOperator opr) const { std::optional<DynamicType> leftType{GetType(0)}; - if (actuals_.size() == 1) { + if (context_.context().languageFeatures().IsEnabled( + common::LanguageFeature::CUDA) && + HasDeviceDefinedIntrinsicOpOverride(AsFortran(opr))) { + return false; + } else if (actuals_.size() == 1) { if (IsBOZLiteral(0)) { return opr == NumericOperator::Add; // unary '+' } else { @@ -4617,6 +4633,53 @@ bool ArgumentAnalyzer::CheckForAssumedRank(const char *where) { return true; } +bool ArgumentAnalyzer::AnyCUDADeviceData() const { + for (const std::optional<ActualArgument> &arg : actuals_) { + if (arg) { + if (const Expr<SomeType> *expr{arg->UnwrapExpr()}) { + if (HasCUDADeviceAttrs(*expr)) { + return true; + } + } + } + } + return false; +} + +// Some operations can be defined with explicit non-type-bound interfaces +// that would erroneously conflict with intrinsic operations in their +// types and ranks but have one or more dummy arguments with the DEVICE +// attribute. +bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride( + const char *opr) const { + if (AnyCUDADeviceData() && !AnyUntypedOrMissingOperand()) { + std::string oprNameString{"operator("s + opr + ')'}; + parser::CharBlock oprName{oprNameString}; + parser::Messages buffer; + auto restorer{context_.GetContextualMessages().SetMessages(buffer)}; + const auto &scope{context_.context().FindScope(source_)}; + if (Symbol * generic{scope.FindSymbol(oprName)}) { + parser::Name name{generic->name(), generic}; + const Symbol *resultSymbol{nullptr}; + if (context_.AnalyzeDefinedOp( + name, ActualArguments{actuals_}, resultSymbol)) { + return true; + } + } + } + return false; +} + +bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride( + const std::vector<const char *> &oprNames) const { + for (const char *opr : oprNames) { + if (HasDeviceDefinedIntrinsicOpOverride(opr)) { + return true; + } + } + return false; +} + MaybeExpr ArgumentAnalyzer::TryDefinedOp( const char *opr, parser::MessageFixedText error, bool isUserOp) { if (AnyUntypedOrMissingOperand()) { @@ -5135,7 +5198,7 @@ std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) { } } -bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() { +bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() const { for (const auto &actual : actuals_) { if (!actual || (!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) { diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp index 336ce4b..af4000c 100644 --- a/flang/lib/Semantics/openmp-modifiers.cpp +++ b/flang/lib/Semantics/openmp-modifiers.cpp @@ -157,6 +157,22 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpAlwaysModifier>() { } template <> +const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpAutomapModifier>() { + static const OmpModifierDescriptor desc{ + /*name=*/"automap-modifier", + /*props=*/ + { + {60, {OmpProperty::Unique}}, + }, + /*clauses=*/ + { + {60, {Clause::OMPC_enter}}, + }, + }; + return desc; +} + +template <> const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpChunkModifier>() { static const OmpModifierDescriptor desc{ /*name=*/"chunk-modifier", diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp index da14507..7a492a4 100644 --- a/flang/lib/Semantics/openmp-utils.cpp +++ b/flang/lib/Semantics/openmp-utils.cpp @@ -270,28 +270,6 @@ struct DesignatorCollector : public evaluate::Traverse<DesignatorCollector, } }; -struct VariableFinder : public evaluate::AnyTraverse<VariableFinder> { - using Base = evaluate::AnyTraverse<VariableFinder>; - VariableFinder(const SomeExpr &v) : Base(*this), var(v) {} - - using Base::operator(); - - template <typename T> - bool operator()(const evaluate::Designator<T> &x) const { - auto copy{x}; - return evaluate::AsGenericExpr(std::move(copy)) == var; - } - - template <typename T> - bool operator()(const evaluate::FunctionRef<T> &x) const { - auto copy{x}; - return evaluate::AsGenericExpr(std::move(copy)) == var; - } - -private: - const SomeExpr &var; -}; - std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr) { return DesignatorCollector{}(expr); } @@ -380,10 +358,6 @@ const SomeExpr *HasStorageOverlap( return nullptr; } -bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super) { - return VariableFinder{sub}(super); -} - // Check if the ActionStmt is actually a [Pointer]AssignmentStmt. This is // to separate cases where the source has something that looks like an // assignment, but is semantically wrong (diagnosed by general semantic diff --git a/flang/lib/Semantics/openmp-utils.h b/flang/lib/Semantics/openmp-utils.h index 001fbeb..b8ad9ed 100644 --- a/flang/lib/Semantics/openmp-utils.h +++ b/flang/lib/Semantics/openmp-utils.h @@ -72,7 +72,6 @@ std::optional<bool> IsContiguous( std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr); const SomeExpr *HasStorageOverlap( const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs); -bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super); bool IsAssignment(const parser::ActionStmt *x); bool IsPointerAssignment(const evaluate::Assignment &x); const parser::Block &GetInnermostExecPart(const parser::Block &block); diff --git a/flang/lib/Semantics/pointer-assignment.cpp b/flang/lib/Semantics/pointer-assignment.cpp index 0908769..e767bf8 100644 --- a/flang/lib/Semantics/pointer-assignment.cpp +++ b/flang/lib/Semantics/pointer-assignment.cpp @@ -270,18 +270,18 @@ bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) { std::optional<MessageFixedText> msg; const auto &funcResult{proc->functionResult}; // C1025 if (!funcResult) { - msg = "%s is associated with the non-existent result of reference to" - " procedure"_err_en_US; + msg = + "%s is associated with the non-existent result of reference to procedure"_err_en_US; } else if (CharacterizeProcedure()) { // Shouldn't be here in this function unless lhs is an object pointer. - msg = "Procedure %s is associated with the result of a reference to" - " function '%s' that does not return a procedure pointer"_err_en_US; + msg = + "Procedure %s is associated with the result of a reference to function '%s' that does not return a procedure pointer"_err_en_US; } else if (funcResult->IsProcedurePointer()) { - msg = "Object %s is associated with the result of a reference to" - " function '%s' that is a procedure pointer"_err_en_US; + msg = + "Object %s is associated with the result of a reference to function '%s' that is a procedure pointer"_err_en_US; } else if (!funcResult->attrs.test(FunctionResult::Attr::Pointer)) { - msg = "%s is associated with the result of a reference to function '%s'" - " that is a not a pointer"_err_en_US; + msg = + "%s is associated with the result of a reference to function '%s' that is not a pointer"_err_en_US; } else if (isContiguous_ && !funcResult->attrs.test(FunctionResult::Attr::Contiguous)) { auto restorer{common::ScopedSet(lhs_, symbol)}; diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 4c3e509..bb28cfb 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -378,7 +378,7 @@ public: bool Pre(const parser::OpenMPBlockConstruct &); void Post(const parser::OpenMPBlockConstruct &); - void Post(const parser::OmpBeginBlockDirective &) { + void Post(const parser::OmpBeginDirective &x) { GetContext().withinConstruct = true; } @@ -519,6 +519,9 @@ public: bool Pre(const parser::OpenMPDeclarativeAllocate &); void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); } + bool Pre(const parser::OpenMPAtomicConstruct &); + void Post(const parser::OpenMPAtomicConstruct &) { PopContext(); } + bool Pre(const parser::OpenMPDispatchConstruct &); void Post(const parser::OpenMPDispatchConstruct &) { PopContext(); } @@ -1545,6 +1548,7 @@ void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { void AccAttributeVisitor::Post(const parser::Name &name) { auto *symbol{name.symbol}; if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { + symbol = &symbol->GetUltimate(); if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && !symbol->has<SubprogramDetails>() && !IsObjectWithDSA(*symbol)) { if (Symbol * found{currScope().FindSymbol(name.source)}) { @@ -1553,8 +1557,7 @@ void AccAttributeVisitor::Post(const parser::Name &name) { } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { // 2.5.14. context_.Say(name.source, - "The DEFAULT(NONE) clause requires that '%s' must be listed in " - "a data-mapping clause"_err_en_US, + "The DEFAULT(NONE) clause requires that '%s' must be listed in a data-mapping clause"_err_en_US, symbol->name()); } } @@ -1698,9 +1701,9 @@ static std::string ScopeSourcePos(const Fortran::semantics::Scope &scope); #endif bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { - const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; - const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - switch (beginDir.v) { + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; + llvm::omp::Directive dirId{dirSpec.DirId()}; + switch (dirId) { case llvm::omp::Directive::OMPD_masked: case llvm::omp::Directive::OMPD_parallel_masked: case llvm::omp::Directive::OMPD_master: @@ -1718,15 +1721,15 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { case llvm::omp::Directive::OMPD_parallel_workshare: case llvm::omp::Directive::OMPD_target_teams: case llvm::omp::Directive::OMPD_target_parallel: - PushContext(beginDir.source, beginDir.v); + PushContext(dirSpec.source, dirId); break; default: // TODO others break; } - if (beginDir.v == llvm::omp::Directive::OMPD_master || - beginDir.v == llvm::omp::Directive::OMPD_parallel_master) - IssueNonConformanceWarning(beginDir.v, beginDir.source, 52); + if (dirId == llvm::omp::Directive::OMPD_master || + dirId == llvm::omp::Directive::OMPD_parallel_master) + IssueNonConformanceWarning(dirId, dirSpec.source, 52); ClearDataSharingAttributeObjects(); ClearPrivateDataSharingAttributeObjects(); ClearAllocateNames(); @@ -1734,9 +1737,9 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { } void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) { - const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; - const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - switch (beginDir.v) { + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; + llvm::omp::Directive dirId{dirSpec.DirId()}; + switch (dirId) { case llvm::omp::Directive::OMPD_masked: case llvm::omp::Directive::OMPD_master: case llvm::omp::Directive::OMPD_parallel_masked: @@ -2152,7 +2155,8 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclareTargetConstruct &x) { ResolveOmpObjectList(linkClause->v, Symbol::Flag::OmpDeclareTarget); } else if (const auto *enterClause{ std::get_if<parser::OmpClause::Enter>(&clause.u)}) { - ResolveOmpObjectList(enterClause->v, Symbol::Flag::OmpDeclareTarget); + ResolveOmpObjectList(std::get<parser::OmpObjectList>(enterClause->v.t), + Symbol::Flag::OmpDeclareTarget); } } } @@ -2184,6 +2188,11 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) { return false; } +bool OmpAttributeVisitor::Pre(const parser::OpenMPAtomicConstruct &x) { + PushContext(x.source, llvm::omp::Directive::OMPD_atomic); + return true; +} + bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) { PushContext(x.source, llvm::omp::Directive::OMPD_dispatch); return true; @@ -2201,7 +2210,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) { } bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) { - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; PushContext(x.source, dirSpec.DirId()); for (const auto &clause : dirSpec.Clauses().v) { @@ -2287,7 +2296,7 @@ void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) { } void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) { - auto &dirSpec{std::get<parser::OmpDirectiveSpecification>(x.t)}; + const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()}; auto &block{std::get<parser::Block>(x.t)}; omp::SourcedActionStmt action{omp::GetActionStmt(block)}; diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index d08c669..25b1370 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1484,18 +1484,18 @@ public: } bool Pre(const parser::OpenMPBlockConstruct &); void Post(const parser::OpenMPBlockConstruct &); - bool Pre(const parser::OmpBeginBlockDirective &x) { + bool Pre(const parser::OmpBeginDirective &x) { AddOmpSourceRange(x.source); return true; } - void Post(const parser::OmpBeginBlockDirective &) { + void Post(const parser::OmpBeginDirective &) { messageHandler().set_currStmtSource(std::nullopt); } - bool Pre(const parser::OmpEndBlockDirective &x) { + bool Pre(const parser::OmpEndDirective &x) { AddOmpSourceRange(x.source); return true; } - void Post(const parser::OmpEndBlockDirective &) { + void Post(const parser::OmpEndDirective &) { messageHandler().set_currStmtSource(std::nullopt); } @@ -1646,7 +1646,8 @@ public: populateDeclareTargetNames(linkClause->v); } else if (const auto *enterClause{ std::get_if<parser::OmpClause::Enter>(&clause.u)}) { - populateDeclareTargetNames(enterClause->v); + populateDeclareTargetNames( + std::get<parser::OmpObjectList>(enterClause->v.t)); } } } @@ -1724,9 +1725,7 @@ private: }; bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) { - const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; - const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; - switch (beginDir.v) { + switch (x.BeginDir().DirId()) { case llvm::omp::Directive::OMPD_master: case llvm::omp::Directive::OMPD_ordered: return false; |