brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.4 KiB · 18a37d6 Raw
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