500 lines · cpp
1//===-- lib/Semantics/openmp-utils.cpp ------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Common utilities used in OpenMP semantic checks.10//11//===----------------------------------------------------------------------===//12 13#include "flang/Semantics/openmp-utils.h"14 15#include "flang/Common/Fortran-consts.h"16#include "flang/Common/idioms.h"17#include "flang/Common/indirection.h"18#include "flang/Common/reference.h"19#include "flang/Common/visit.h"20#include "flang/Evaluate/check-expression.h"21#include "flang/Evaluate/expression.h"22#include "flang/Evaluate/tools.h"23#include "flang/Evaluate/traverse.h"24#include "flang/Evaluate/type.h"25#include "flang/Evaluate/variable.h"26#include "flang/Parser/openmp-utils.h"27#include "flang/Parser/parse-tree.h"28#include "flang/Semantics/expression.h"29#include "flang/Semantics/scope.h"30#include "flang/Semantics/semantics.h"31#include "flang/Semantics/symbol.h"32 33#include "llvm/ADT/ArrayRef.h"34#include "llvm/ADT/STLExtras.h"35 36#include <optional>37#include <string>38#include <tuple>39#include <type_traits>40#include <utility>41#include <variant>42#include <vector>43 44namespace Fortran::semantics::omp {45using namespace Fortran::parser::omp;46 47const Scope &GetScopingUnit(const Scope &scope) {48 const Scope *iter{&scope};49 for (; !iter->IsTopLevel(); iter = &iter->parent()) {50 switch (iter->kind()) {51 case Scope::Kind::BlockConstruct:52 case Scope::Kind::BlockData:53 case Scope::Kind::DerivedType:54 case Scope::Kind::MainProgram:55 case Scope::Kind::Module:56 case Scope::Kind::Subprogram:57 return *iter;58 default:59 break;60 }61 }62 return *iter;63}64 65const Scope &GetProgramUnit(const Scope &scope) {66 const Scope *unit{nullptr};67 for (const Scope *iter{&scope}; !iter->IsTopLevel(); iter = &iter->parent()) {68 switch (iter->kind()) {69 case Scope::Kind::BlockData:70 case Scope::Kind::MainProgram:71 case Scope::Kind::Module:72 return *iter;73 case Scope::Kind::Subprogram:74 // Ignore subprograms that are nested.75 unit = iter;76 break;77 default:78 break;79 }80 }81 assert(unit && "Scope not in a program unit");82 return *unit;83}84 85SourcedActionStmt GetActionStmt(const parser::ExecutionPartConstruct *x) {86 if (x == nullptr) {87 return SourcedActionStmt{};88 }89 if (auto *exec{std::get_if<parser::ExecutableConstruct>(&x->u)}) {90 using ActionStmt = parser::Statement<parser::ActionStmt>;91 if (auto *stmt{std::get_if<ActionStmt>(&exec->u)}) {92 return SourcedActionStmt{&stmt->statement, stmt->source};93 }94 }95 return SourcedActionStmt{};96}97 98SourcedActionStmt GetActionStmt(const parser::Block &block) {99 if (block.size() == 1) {100 return GetActionStmt(&block.front());101 }102 return SourcedActionStmt{};103}104 105std::string ThisVersion(unsigned version) {106 std::string tv{107 std::to_string(version / 10) + "." + std::to_string(version % 10)};108 return "OpenMP v" + tv;109}110 111std::string TryVersion(unsigned version) {112 return "try -fopenmp-version=" + std::to_string(version);113}114 115const parser::Designator *GetDesignatorFromObj(116 const parser::OmpObject &object) {117 return std::get_if<parser::Designator>(&object.u);118}119 120const parser::DataRef *GetDataRefFromObj(const parser::OmpObject &object) {121 if (auto *desg{GetDesignatorFromObj(object)}) {122 return std::get_if<parser::DataRef>(&desg->u);123 }124 return nullptr;125}126 127const parser::ArrayElement *GetArrayElementFromObj(128 const parser::OmpObject &object) {129 if (auto *dataRef{GetDataRefFromObj(object)}) {130 using ElementIndirection = common::Indirection<parser::ArrayElement>;131 if (auto *ind{std::get_if<ElementIndirection>(&dataRef->u)}) {132 return &ind->value();133 }134 }135 return nullptr;136}137 138const Symbol *GetObjectSymbol(const parser::OmpObject &object) {139 // Some symbols may be missing if the resolution failed, e.g. when an140 // undeclared name is used with implicit none.141 if (auto *name{std::get_if<parser::Name>(&object.u)}) {142 return name->symbol ? &name->symbol->GetUltimate() : nullptr;143 } else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) {144 auto &last{GetLastName(*desg)};145 return last.symbol ? &GetLastName(*desg).symbol->GetUltimate() : nullptr;146 }147 return nullptr;148}149 150std::optional<parser::CharBlock> GetObjectSource(151 const parser::OmpObject &object) {152 if (auto *name{std::get_if<parser::Name>(&object.u)}) {153 return name->source;154 } else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) {155 return GetLastName(*desg).source;156 }157 return std::nullopt;158}159 160const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument) {161 if (auto *locator{std::get_if<parser::OmpLocator>(&argument.u)}) {162 if (auto *object{std::get_if<parser::OmpObject>(&locator->u)}) {163 return GetObjectSymbol(*object);164 }165 }166 return nullptr;167}168 169const parser::OmpObject *GetArgumentObject(170 const parser::OmpArgument &argument) {171 if (auto *locator{std::get_if<parser::OmpLocator>(&argument.u)}) {172 return std::get_if<parser::OmpObject>(&locator->u);173 }174 return nullptr;175}176 177bool IsCommonBlock(const Symbol &sym) {178 return sym.detailsIf<CommonBlockDetails>() != nullptr;179}180 181bool IsVariableListItem(const Symbol &sym) {182 return evaluate::IsVariable(sym) || sym.attrs().test(Attr::POINTER);183}184 185bool IsExtendedListItem(const Symbol &sym) {186 return IsVariableListItem(sym) || sym.IsSubprogram();187}188 189bool IsTypeParamInquiry(const Symbol &sym) {190 return common::visit( //191 common::visitors{192 [&](const MiscDetails &d) {193 return d.kind() == MiscDetails::Kind::KindParamInquiry ||194 d.kind() == MiscDetails::Kind::LenParamInquiry;195 },196 [&](const TypeParamDetails &s) { return true; },197 [&](auto &&) { return false; },198 },199 sym.details());200}201 202bool IsStructureComponent(const Symbol &sym) {203 return sym.owner().kind() == Scope::Kind::DerivedType;204}205 206bool IsVarOrFunctionRef(const MaybeExpr &expr) {207 if (expr) {208 return evaluate::UnwrapProcedureRef(*expr) != nullptr ||209 evaluate::IsVariable(*expr);210 } else {211 return false;212 }213}214 215bool IsMapEnteringType(parser::OmpMapType::Value type) {216 switch (type) {217 case parser::OmpMapType::Value::Alloc:218 case parser::OmpMapType::Value::Storage:219 case parser::OmpMapType::Value::To:220 case parser::OmpMapType::Value::Tofrom:221 return true;222 default:223 return false;224 }225}226 227bool IsMapExitingType(parser::OmpMapType::Value type) {228 switch (type) {229 case parser::OmpMapType::Value::Delete:230 case parser::OmpMapType::Value::From:231 case parser::OmpMapType::Value::Release:232 case parser::OmpMapType::Value::Storage:233 case parser::OmpMapType::Value::Tofrom:234 return true;235 default:236 return false;237 }238}239 240MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {241 const parser::TypedExpr &typedExpr{parserExpr.typedExpr};242 // ForwardOwningPointer typedExpr243 // `- GenericExprWrapper ^.get()244 // `- std::optional<Expr> ^->v245 return DEREF(typedExpr.get()).v;246}247 248std::optional<evaluate::DynamicType> GetDynamicType(249 const parser::Expr &parserExpr) {250 if (auto maybeExpr{GetEvaluateExpr(parserExpr)}) {251 return maybeExpr->GetType();252 } else {253 return std::nullopt;254 }255}256 257namespace {258struct LogicalConstantVistor : public evaluate::Traverse<LogicalConstantVistor,259 std::optional<bool>, false> {260 using Result = std::optional<bool>;261 using Base = evaluate::Traverse<LogicalConstantVistor, Result, false>;262 LogicalConstantVistor() : Base(*this) {}263 264 Result Default() const { return std::nullopt; }265 266 using Base::operator();267 268 template <typename T> //269 Result operator()(const evaluate::Constant<T> &x) const {270 if constexpr (T::category == common::TypeCategory::Logical) {271 return llvm::transformOptional(272 x.GetScalarValue(), [](auto &&v) { return v.IsTrue(); });273 } else {274 return std::nullopt;275 }276 }277 278 template <typename... Rs> //279 Result Combine(Result &&result, Rs &&...results) const {280 if constexpr (sizeof...(results) == 0) {281 return result;282 } else {283 if (result.has_value()) {284 return result;285 } else {286 return Combine(std::move(results)...);287 }288 }289 }290};291} // namespace292 293std::optional<bool> GetLogicalValue(const SomeExpr &expr) {294 return LogicalConstantVistor{}(expr);295}296 297namespace {298struct ContiguousHelper {299 ContiguousHelper(SemanticsContext &context)300 : fctx_(context.foldingContext()) {}301 302 template <typename Contained>303 std::optional<bool> Visit(const common::Indirection<Contained> &x) {304 return Visit(x.value());305 }306 template <typename Contained>307 std::optional<bool> Visit(const common::Reference<Contained> &x) {308 return Visit(x.get());309 }310 template <typename T> std::optional<bool> Visit(const evaluate::Expr<T> &x) {311 return common::visit([&](auto &&s) { return Visit(s); }, x.u);312 }313 template <typename T>314 std::optional<bool> Visit(const evaluate::Designator<T> &x) {315 return common::visit(316 [this](auto &&s) { return evaluate::IsContiguous(s, fctx_); }, x.u);317 }318 template <typename T> std::optional<bool> Visit(const T &) {319 // Everything else.320 return std::nullopt;321 }322 323private:324 evaluate::FoldingContext &fctx_;325};326} // namespace327 328// Return values:329// - std::optional<bool>{true} if the object is known to be contiguous330// - std::optional<bool>{false} if the object is known not to be contiguous331// - std::nullopt if the object contiguity cannot be determined332std::optional<bool> IsContiguous(333 SemanticsContext &semaCtx, const parser::OmpObject &object) {334 return common::visit( //335 common::visitors{//336 [&](const parser::Name &x) {337 // Any member of a common block must be contiguous.338 return std::optional<bool>{true};339 },340 [&](const parser::Designator &x) {341 evaluate::ExpressionAnalyzer ea{semaCtx};342 if (MaybeExpr maybeExpr{ea.Analyze(x)}) {343 return ContiguousHelper{semaCtx}.Visit(*maybeExpr);344 }345 return std::optional<bool>{};346 },347 [&](const parser::OmpObject::Invalid &) {348 return std::optional<bool>{};349 }},350 object.u);351}352 353struct DesignatorCollector : public evaluate::Traverse<DesignatorCollector,354 std::vector<SomeExpr>, false> {355 using Result = std::vector<SomeExpr>;356 using Base = evaluate::Traverse<DesignatorCollector, Result, false>;357 DesignatorCollector() : Base(*this) {}358 359 Result Default() const { return {}; }360 361 using Base::operator();362 363 template <typename T> //364 Result operator()(const evaluate::Designator<T> &x) const {365 // Once in a designator, don't traverse it any further (i.e. only366 // collect top-level designators).367 auto copy{x};368 return Result{AsGenericExpr(std::move(copy))};369 }370 371 template <typename... Rs> //372 Result Combine(Result &&result, Rs &&...results) const {373 Result v(std::move(result));374 auto moveAppend{[](auto &accum, auto &&other) {375 for (auto &&s : other) {376 accum.push_back(std::move(s));377 }378 }};379 (moveAppend(v, std::move(results)), ...);380 return v;381 }382};383 384std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr) {385 return DesignatorCollector{}(expr);386}387 388static bool HasCommonDesignatorSymbols(389 const evaluate::SymbolVector &baseSyms, const SomeExpr &other) {390 // Compare the designators used in "other" with the designators whose391 // symbols are given in baseSyms.392 // This is a part of the check if these two expressions can access the same393 // storage: if the designators used in them are different enough, then they394 // will be assumed not to access the same memory.395 //396 // Consider an (array element) expression x%y(w%z), the corresponding symbol397 // vector will be {x, y, w, z} (i.e. the symbols for these names).398 // Check whether this exact sequence appears anywhere in any the symbol399 // vector for "other". This will be true for x(y) and x(y+1), so this is400 // not a sufficient condition, but can be used to eliminate candidates401 // before doing more exhaustive checks.402 //403 // If any of the symbols in this sequence are function names, assume that404 // there is no storage overlap, mostly because it would be impossible in405 // general to determine what storage the function will access.406 // Note: if f is pure, then two calls to f will access the same storage407 // when called with the same arguments. This check is not done yet.408 409 if (llvm::any_of(410 baseSyms, [](const SymbolRef &s) { return s->IsSubprogram(); })) {411 // If there is a function symbol in the chain then we can't infer much412 // about the accessed storage.413 return false;414 }415 416 auto isSubsequence{// Is u a subsequence of v.417 [](const evaluate::SymbolVector &u, const evaluate::SymbolVector &v) {418 size_t us{u.size()}, vs{v.size()};419 if (us > vs) {420 return false;421 }422 for (size_t off{0}; off != vs - us + 1; ++off) {423 bool same{true};424 for (size_t i{0}; i != us; ++i) {425 if (u[i] != v[off + i]) {426 same = false;427 break;428 }429 }430 if (same) {431 return true;432 }433 }434 return false;435 }};436 437 evaluate::SymbolVector otherSyms{evaluate::GetSymbolVector(other)};438 return isSubsequence(baseSyms, otherSyms);439}440 441static bool HasCommonTopLevelDesignators(442 const std::vector<SomeExpr> &baseDsgs, const SomeExpr &other) {443 // Compare designators directly as expressions. This will ensure444 // that x(y) and x(y+1) are not flagged as overlapping, whereas445 // the symbol vectors for both of these would be identical.446 std::vector<SomeExpr> otherDsgs{GetAllDesignators(other)};447 448 for (auto &s : baseDsgs) {449 if (llvm::any_of(otherDsgs, [&](auto &&t) { return s == t; })) {450 return true;451 }452 }453 return false;454}455 456const SomeExpr *HasStorageOverlap(457 const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs) {458 evaluate::SymbolVector baseSyms{evaluate::GetSymbolVector(base)};459 std::vector<SomeExpr> baseDsgs{GetAllDesignators(base)};460 461 for (const SomeExpr &expr : exprs) {462 if (!HasCommonDesignatorSymbols(baseSyms, expr)) {463 continue;464 }465 if (HasCommonTopLevelDesignators(baseDsgs, expr)) {466 return &expr;467 }468 }469 return nullptr;470}471 472// Check if the ActionStmt is actually a [Pointer]AssignmentStmt. This is473// to separate cases where the source has something that looks like an474// assignment, but is semantically wrong (diagnosed by general semantic475// checks), and where the source has some other statement (which we want476// to report as "should be an assignment").477bool IsAssignment(const parser::ActionStmt *x) {478 if (x == nullptr) {479 return false;480 }481 482 using AssignmentStmt = common::Indirection<parser::AssignmentStmt>;483 using PointerAssignmentStmt =484 common::Indirection<parser::PointerAssignmentStmt>;485 486 return common::visit(487 [](auto &&s) -> bool {488 using BareS = llvm::remove_cvref_t<decltype(s)>;489 return std::is_same_v<BareS, AssignmentStmt> ||490 std::is_same_v<BareS, PointerAssignmentStmt>;491 },492 x->u);493}494 495bool IsPointerAssignment(const evaluate::Assignment &x) {496 return std::holds_alternative<evaluate::Assignment::BoundsSpec>(x.u) ||497 std::holds_alternative<evaluate::Assignment::BoundsRemapping>(x.u);498}499} // namespace Fortran::semantics::omp500