| //===-- Lower/Support/Utils.cpp -- utilities --------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Lower/Support/Utils.h" |
| |
| #include "flang/Common/indirection.h" |
| #include "flang/Lower/AbstractConverter.h" |
| #include "flang/Lower/ConvertVariable.h" |
| #include "flang/Lower/IterationSpace.h" |
| #include "flang/Lower/Support/PrivateReductionUtils.h" |
| #include "flang/Optimizer/Builder/HLFIRTools.h" |
| #include "flang/Optimizer/Builder/Todo.h" |
| #include "flang/Optimizer/HLFIR/HLFIRDialect.h" |
| #include "flang/Semantics/tools.h" |
| #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
| #include <cstdint> |
| #include <optional> |
| #include <type_traits> |
| |
| namespace Fortran::lower { |
| // Fortran::evaluate::Expr are functional values organized like an AST. A |
| // Fortran::evaluate::Expr is meant to be moved and cloned. Using the front end |
| // tools can often cause copies and extra wrapper classes to be added to any |
| // Fortran::evaluate::Expr. These values should not be assumed or relied upon to |
| // have an *object* identity. They are deeply recursive, irregular structures |
| // built from a large number of classes which do not use inheritance and |
| // necessitate a large volume of boilerplate code as a result. |
| // |
| // Contrastingly, LLVM data structures make ubiquitous assumptions about an |
| // object's identity via pointers to the object. An object's location in memory |
| // is thus very often an identifying relation. |
| |
| // This class defines a hash computation of a Fortran::evaluate::Expr tree value |
| // so it can be used with llvm::DenseMap. The Fortran::evaluate::Expr need not |
| // have the same address. |
| class HashEvaluateExpr { |
| public: |
| // A Se::Symbol is the only part of an Fortran::evaluate::Expr with an |
| // identity property. |
| static unsigned getHashValue(const Fortran::semantics::Symbol &x) { |
| return static_cast<unsigned>(reinterpret_cast<std::intptr_t>(&x)); |
| } |
| template <typename A, bool COPY> |
| static unsigned getHashValue(const Fortran::common::Indirection<A, COPY> &x) { |
| return getHashValue(x.value()); |
| } |
| template <typename A> |
| static unsigned getHashValue(const std::optional<A> &x) { |
| if (x.has_value()) |
| return getHashValue(x.value()); |
| return 0u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::Subscript &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::Triplet &x) { |
| return getHashValue(x.lower()) - getHashValue(x.upper()) * 5u - |
| getHashValue(x.stride()) * 11u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::Component &x) { |
| return getHashValue(x.base()) * 83u - getHashValue(x.GetLastSymbol()); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::ArrayRef &x) { |
| unsigned subs = 1u; |
| for (const Fortran::evaluate::Subscript &v : x.subscript()) |
| subs -= getHashValue(v); |
| return getHashValue(x.base()) * 89u - subs; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::CoarrayRef &x) { |
| unsigned cosubs = 3u; |
| for (const Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger> &v : |
| x.cosubscript()) |
| cosubs -= getHashValue(v); |
| return getHashValue(x.base()) * 97u - cosubs + getHashValue(x.stat()) + |
| 257u + getHashValue(x.team()); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::NamedEntity &x) { |
| if (x.IsSymbol()) |
| return getHashValue(x.GetFirstSymbol()) * 11u; |
| return getHashValue(x.GetComponent()) * 13u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::DataRef &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::ComplexPart &x) { |
| return getHashValue(x.complex()) - static_cast<unsigned>(x.part()); |
| } |
| template <Fortran::common::TypeCategory TC1, int KIND, |
| Fortran::common::TypeCategory TC2> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, TC2> |
| &x) { |
| return getHashValue(x.left()) - (static_cast<unsigned>(TC1) + 2u) - |
| (static_cast<unsigned>(KIND) + 5u); |
| } |
| template <int KIND> |
| static unsigned |
| getHashValue(const Fortran::evaluate::ComplexComponent<KIND> &x) { |
| return getHashValue(x.left()) - |
| (static_cast<unsigned>(x.isImaginaryPart) + 1u) * 3u; |
| } |
| template <typename T> |
| static unsigned getHashValue(const Fortran::evaluate::Parentheses<T> &x) { |
| return getHashValue(x.left()) * 17u; |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Negate<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return getHashValue(x.left()) - (static_cast<unsigned>(TC) + 5u) - |
| (static_cast<unsigned>(KIND) + 7u); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Add<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) + getHashValue(x.right())) * 23u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Subtract<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 19u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Multiply<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) + getHashValue(x.right())) * 29u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Divide<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 31u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 37u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) { |
| return (getHashValue(x.left()) + getHashValue(x.right())) * 41u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) + |
| static_cast<unsigned>(x.ordering) * 7u; |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>> |
| &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 43u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND); |
| } |
| template <int KIND> |
| static unsigned |
| getHashValue(const Fortran::evaluate::ComplexConstructor<KIND> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 47u + |
| static_cast<unsigned>(KIND); |
| } |
| template <int KIND> |
| static unsigned getHashValue(const Fortran::evaluate::Concat<KIND> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 53u + |
| static_cast<unsigned>(KIND); |
| } |
| template <int KIND> |
| static unsigned getHashValue(const Fortran::evaluate::SetLength<KIND> &x) { |
| return (getHashValue(x.left()) - getHashValue(x.right())) * 59u + |
| static_cast<unsigned>(KIND); |
| } |
| static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) { |
| return getHashValue(sym.get()); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::Substring &x) { |
| return 61u * |
| Fortran::common::visit( |
| [&](const auto &p) { return getHashValue(p); }, x.parent()) - |
| getHashValue(x.lower()) - (getHashValue(x.lower()) + 1u); |
| } |
| static unsigned |
| getHashValue(const Fortran::evaluate::StaticDataObject::Pointer &x) { |
| return llvm::hash_value(x->name()); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::SpecificIntrinsic &x) { |
| return llvm::hash_value(x.name); |
| } |
| template <typename A> |
| static unsigned getHashValue(const Fortran::evaluate::Constant<A> &x) { |
| // FIXME: Should hash the content. |
| return 103u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::ActualArgument &x) { |
| if (const Fortran::evaluate::Symbol *sym = x.GetAssumedTypeDummy()) |
| return getHashValue(*sym); |
| return getHashValue(*x.UnwrapExpr()); |
| } |
| static unsigned |
| getHashValue(const Fortran::evaluate::ProcedureDesignator &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::ProcedureRef &x) { |
| unsigned args = 13u; |
| for (const std::optional<Fortran::evaluate::ActualArgument> &v : |
| x.arguments()) |
| args -= getHashValue(v); |
| return getHashValue(x.proc()) * 101u - args; |
| } |
| template <typename A> |
| static unsigned |
| getHashValue(const Fortran::evaluate::ArrayConstructor<A> &x) { |
| // FIXME: hash the contents. |
| return 127u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::ImpliedDoIndex &x) { |
| return llvm::hash_value(toStringRef(x.name).str()) * 131u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::TypeParamInquiry &x) { |
| return getHashValue(x.base()) * 137u - getHashValue(x.parameter()) * 3u; |
| } |
| static unsigned getHashValue(const Fortran::evaluate::DescriptorInquiry &x) { |
| return getHashValue(x.base()) * 139u - |
| static_cast<unsigned>(x.field()) * 13u + |
| static_cast<unsigned>(x.dimension()); |
| } |
| static unsigned |
| getHashValue(const Fortran::evaluate::StructureConstructor &x) { |
| // FIXME: hash the contents. |
| return 149u; |
| } |
| template <int KIND> |
| static unsigned getHashValue(const Fortran::evaluate::Not<KIND> &x) { |
| return getHashValue(x.left()) * 61u + static_cast<unsigned>(KIND); |
| } |
| template <int KIND> |
| static unsigned |
| getHashValue(const Fortran::evaluate::LogicalOperation<KIND> &x) { |
| unsigned result = getHashValue(x.left()) + getHashValue(x.right()); |
| return result * 67u + static_cast<unsigned>(x.logicalOperator) * 5u; |
| } |
| template <Fortran::common::TypeCategory TC, int KIND> |
| static unsigned getHashValue( |
| const Fortran::evaluate::Relational<Fortran::evaluate::Type<TC, KIND>> |
| &x) { |
| return (getHashValue(x.left()) + getHashValue(x.right())) * 71u + |
| static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) + |
| static_cast<unsigned>(x.opr) * 11u; |
| } |
| template <typename A> |
| static unsigned getHashValue(const Fortran::evaluate::Expr<A> &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| static unsigned getHashValue( |
| const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| template <typename A> |
| static unsigned getHashValue(const Fortran::evaluate::Designator<A> &x) { |
| return Fortran::common::visit( |
| [&](const auto &v) { return getHashValue(v); }, x.u); |
| } |
| template <int BITS> |
| static unsigned |
| getHashValue(const Fortran::evaluate::value::Integer<BITS> &x) { |
| return static_cast<unsigned>(x.ToSInt()); |
| } |
| static unsigned getHashValue(const Fortran::evaluate::NullPointer &x) { |
| return ~179u; |
| } |
| }; |
| |
| // Define the is equals test for using Fortran::evaluate::Expr values with |
| // llvm::DenseMap. |
| class IsEqualEvaluateExpr { |
| public: |
| // A Se::Symbol is the only part of an Fortran::evaluate::Expr with an |
| // identity property. |
| static bool isEqual(const Fortran::semantics::Symbol &x, |
| const Fortran::semantics::Symbol &y) { |
| return isEqual(&x, &y); |
| } |
| static bool isEqual(const Fortran::semantics::Symbol *x, |
| const Fortran::semantics::Symbol *y) { |
| return x == y; |
| } |
| template <typename A, bool COPY> |
| static bool isEqual(const Fortran::common::Indirection<A, COPY> &x, |
| const Fortran::common::Indirection<A, COPY> &y) { |
| return isEqual(x.value(), y.value()); |
| } |
| template <typename A> |
| static bool isEqual(const std::optional<A> &x, const std::optional<A> &y) { |
| if (x.has_value() && y.has_value()) |
| return isEqual(x.value(), y.value()); |
| return !x.has_value() && !y.has_value(); |
| } |
| template <typename A> |
| static bool isEqual(const std::vector<A> &x, const std::vector<A> &y) { |
| if (x.size() != y.size()) |
| return false; |
| const std::size_t size = x.size(); |
| for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i) |
| if (!isEqual(x[i], y[i])) |
| return false; |
| return true; |
| } |
| static bool isEqual(const Fortran::evaluate::Subscript &x, |
| const Fortran::evaluate::Subscript &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| static bool isEqual(const Fortran::evaluate::Triplet &x, |
| const Fortran::evaluate::Triplet &y) { |
| return isEqual(x.lower(), y.lower()) && isEqual(x.upper(), y.upper()) && |
| isEqual(x.stride(), y.stride()); |
| } |
| static bool isEqual(const Fortran::evaluate::Component &x, |
| const Fortran::evaluate::Component &y) { |
| return isEqual(x.base(), y.base()) && |
| isEqual(x.GetLastSymbol(), y.GetLastSymbol()); |
| } |
| static bool isEqual(const Fortran::evaluate::ArrayRef &x, |
| const Fortran::evaluate::ArrayRef &y) { |
| return isEqual(x.base(), y.base()) && isEqual(x.subscript(), y.subscript()); |
| } |
| static bool isEqual(const Fortran::evaluate::CoarrayRef &x, |
| const Fortran::evaluate::CoarrayRef &y) { |
| return isEqual(x.base(), y.base()) && |
| isEqual(x.cosubscript(), y.cosubscript()) && |
| isEqual(x.stat(), y.stat()) && isEqual(x.team(), y.team()); |
| } |
| static bool isEqual(const Fortran::evaluate::NamedEntity &x, |
| const Fortran::evaluate::NamedEntity &y) { |
| if (x.IsSymbol() && y.IsSymbol()) |
| return isEqual(x.GetFirstSymbol(), y.GetFirstSymbol()); |
| return !x.IsSymbol() && !y.IsSymbol() && |
| isEqual(x.GetComponent(), y.GetComponent()); |
| } |
| static bool isEqual(const Fortran::evaluate::DataRef &x, |
| const Fortran::evaluate::DataRef &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| static bool isEqual(const Fortran::evaluate::ComplexPart &x, |
| const Fortran::evaluate::ComplexPart &y) { |
| return isEqual(x.complex(), y.complex()) && x.part() == y.part(); |
| } |
| template <typename A, Fortran::common::TypeCategory TC2> |
| static bool isEqual(const Fortran::evaluate::Convert<A, TC2> &x, |
| const Fortran::evaluate::Convert<A, TC2> &y) { |
| return isEqual(x.left(), y.left()); |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::ComplexComponent<KIND> &x, |
| const Fortran::evaluate::ComplexComponent<KIND> &y) { |
| return isEqual(x.left(), y.left()) && |
| x.isImaginaryPart == y.isImaginaryPart; |
| } |
| template <typename T> |
| static bool isEqual(const Fortran::evaluate::Parentheses<T> &x, |
| const Fortran::evaluate::Parentheses<T> &y) { |
| return isEqual(x.left(), y.left()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Negate<A> &x, |
| const Fortran::evaluate::Negate<A> &y) { |
| return isEqual(x.left(), y.left()); |
| } |
| template <typename A> |
| static bool isBinaryEqual(const A &x, const A &y) { |
| return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Add<A> &x, |
| const Fortran::evaluate::Add<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Subtract<A> &x, |
| const Fortran::evaluate::Subtract<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Multiply<A> &x, |
| const Fortran::evaluate::Multiply<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Divide<A> &x, |
| const Fortran::evaluate::Divide<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Power<A> &x, |
| const Fortran::evaluate::Power<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Extremum<A> &x, |
| const Fortran::evaluate::Extremum<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::RealToIntPower<A> &x, |
| const Fortran::evaluate::RealToIntPower<A> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::ComplexConstructor<KIND> &x, |
| const Fortran::evaluate::ComplexConstructor<KIND> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::Concat<KIND> &x, |
| const Fortran::evaluate::Concat<KIND> &y) { |
| return isBinaryEqual(x, y); |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::SetLength<KIND> &x, |
| const Fortran::evaluate::SetLength<KIND> &y) { |
| return isBinaryEqual(x, y); |
| } |
| static bool isEqual(const Fortran::semantics::SymbolRef &x, |
| const Fortran::semantics::SymbolRef &y) { |
| return isEqual(x.get(), y.get()); |
| } |
| static bool isEqual(const Fortran::evaluate::Substring &x, |
| const Fortran::evaluate::Substring &y) { |
| return Fortran::common::visit( |
| [&](const auto &p, const auto &q) { return isEqual(p, q); }, |
| x.parent(), y.parent()) && |
| isEqual(x.lower(), y.lower()) && isEqual(x.upper(), y.upper()); |
| } |
| static bool isEqual(const Fortran::evaluate::StaticDataObject::Pointer &x, |
| const Fortran::evaluate::StaticDataObject::Pointer &y) { |
| return x->name() == y->name(); |
| } |
| static bool isEqual(const Fortran::evaluate::SpecificIntrinsic &x, |
| const Fortran::evaluate::SpecificIntrinsic &y) { |
| return x.name == y.name; |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Constant<A> &x, |
| const Fortran::evaluate::Constant<A> &y) { |
| return x == y; |
| } |
| static bool isEqual(const Fortran::evaluate::ActualArgument &x, |
| const Fortran::evaluate::ActualArgument &y) { |
| if (const Fortran::evaluate::Symbol *xs = x.GetAssumedTypeDummy()) { |
| if (const Fortran::evaluate::Symbol *ys = y.GetAssumedTypeDummy()) |
| return isEqual(*xs, *ys); |
| return false; |
| } |
| return !y.GetAssumedTypeDummy() && |
| isEqual(*x.UnwrapExpr(), *y.UnwrapExpr()); |
| } |
| static bool isEqual(const Fortran::evaluate::ProcedureDesignator &x, |
| const Fortran::evaluate::ProcedureDesignator &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| static bool isEqual(const Fortran::evaluate::ProcedureRef &x, |
| const Fortran::evaluate::ProcedureRef &y) { |
| return isEqual(x.proc(), y.proc()) && isEqual(x.arguments(), y.arguments()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::ImpliedDo<A> &x, |
| const Fortran::evaluate::ImpliedDo<A> &y) { |
| return isEqual(x.values(), y.values()) && isEqual(x.lower(), y.lower()) && |
| isEqual(x.upper(), y.upper()) && isEqual(x.stride(), y.stride()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::ArrayConstructorValues<A> &x, |
| const Fortran::evaluate::ArrayConstructorValues<A> &y) { |
| using Expr = Fortran::evaluate::Expr<A>; |
| using ImpliedDo = Fortran::evaluate::ImpliedDo<A>; |
| for (const auto &[xValue, yValue] : llvm::zip(x, y)) { |
| bool checkElement = Fortran::common::visit( |
| common::visitors{ |
| [&](const Expr &v, const Expr &w) { return isEqual(v, w); }, |
| [&](const ImpliedDo &v, const ImpliedDo &w) { |
| return isEqual(v, w); |
| }, |
| [&](const Expr &, const ImpliedDo &) { return false; }, |
| [&](const ImpliedDo &, const Expr &) { return false; }, |
| }, |
| xValue.u, yValue.u); |
| if (!checkElement) { |
| return false; |
| } |
| } |
| return true; |
| } |
| static bool isEqual(const Fortran::evaluate::SubscriptInteger &x, |
| const Fortran::evaluate::SubscriptInteger &y) { |
| return x == y; |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::ArrayConstructor<A> &x, |
| const Fortran::evaluate::ArrayConstructor<A> &y) { |
| bool checkCharacterType = true; |
| if constexpr (A::category == Fortran::common::TypeCategory::Character) { |
| checkCharacterType = isEqual(*x.LEN(), *y.LEN()); |
| } |
| using Base = Fortran::evaluate::ArrayConstructorValues<A>; |
| return isEqual((Base)x, (Base)y) && |
| (x.GetType() == y.GetType() && checkCharacterType); |
| } |
| static bool isEqual(const Fortran::evaluate::ImpliedDoIndex &x, |
| const Fortran::evaluate::ImpliedDoIndex &y) { |
| return toStringRef(x.name) == toStringRef(y.name); |
| } |
| static bool isEqual(const Fortran::evaluate::TypeParamInquiry &x, |
| const Fortran::evaluate::TypeParamInquiry &y) { |
| return isEqual(x.base(), y.base()) && isEqual(x.parameter(), y.parameter()); |
| } |
| static bool isEqual(const Fortran::evaluate::DescriptorInquiry &x, |
| const Fortran::evaluate::DescriptorInquiry &y) { |
| return isEqual(x.base(), y.base()) && x.field() == y.field() && |
| x.dimension() == y.dimension(); |
| } |
| static bool isEqual(const Fortran::evaluate::StructureConstructor &x, |
| const Fortran::evaluate::StructureConstructor &y) { |
| const auto &xValues = x.values(); |
| const auto &yValues = y.values(); |
| if (xValues.size() != yValues.size()) |
| return false; |
| if (x.derivedTypeSpec() != y.derivedTypeSpec()) |
| return false; |
| for (const auto &[xSymbol, xValue] : xValues) { |
| auto yIt = yValues.find(xSymbol); |
| // This should probably never happen, since the derived type |
| // should be the same. |
| if (yIt == yValues.end()) |
| return false; |
| if (!isEqual(xValue, yIt->second)) |
| return false; |
| } |
| return true; |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::Not<KIND> &x, |
| const Fortran::evaluate::Not<KIND> &y) { |
| return isEqual(x.left(), y.left()); |
| } |
| template <int KIND> |
| static bool isEqual(const Fortran::evaluate::LogicalOperation<KIND> &x, |
| const Fortran::evaluate::LogicalOperation<KIND> &y) { |
| return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Relational<A> &x, |
| const Fortran::evaluate::Relational<A> &y) { |
| return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Expr<A> &x, |
| const Fortran::evaluate::Expr<A> &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| static bool |
| isEqual(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x, |
| const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| template <typename A> |
| static bool isEqual(const Fortran::evaluate::Designator<A> &x, |
| const Fortran::evaluate::Designator<A> &y) { |
| return Fortran::common::visit( |
| [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); |
| } |
| template <int BITS> |
| static bool isEqual(const Fortran::evaluate::value::Integer<BITS> &x, |
| const Fortran::evaluate::value::Integer<BITS> &y) { |
| return x == y; |
| } |
| static bool isEqual(const Fortran::evaluate::NullPointer &x, |
| const Fortran::evaluate::NullPointer &y) { |
| return true; |
| } |
| template <typename A, typename B, |
| std::enable_if_t<!std::is_same_v<A, B>, bool> = true> |
| static bool isEqual(const A &, const B &) { |
| return false; |
| } |
| }; |
| |
| unsigned getHashValue(const Fortran::lower::SomeExpr *x) { |
| return HashEvaluateExpr::getHashValue(*x); |
| } |
| |
| unsigned getHashValue(const Fortran::lower::ExplicitIterSpace::ArrayBases &x) { |
| return Fortran::common::visit( |
| [&](const auto *p) { return HashEvaluateExpr::getHashValue(*p); }, x); |
| } |
| |
| bool isEqual(const Fortran::lower::SomeExpr *x, |
| const Fortran::lower::SomeExpr *y) { |
| const auto *empty = |
| llvm::DenseMapInfo<const Fortran::lower::SomeExpr *>::getEmptyKey(); |
| const auto *tombstone = |
| llvm::DenseMapInfo<const Fortran::lower::SomeExpr *>::getTombstoneKey(); |
| if (x == empty || y == empty || x == tombstone || y == tombstone) |
| return x == y; |
| return x == y || IsEqualEvaluateExpr::isEqual(*x, *y); |
| } |
| |
| bool isEqual(const Fortran::lower::ExplicitIterSpace::ArrayBases &x, |
| const Fortran::lower::ExplicitIterSpace::ArrayBases &y) { |
| return Fortran::common::visit( |
| Fortran::common::visitors{ |
| // Fortran::semantics::Symbol * are the exception here. These pointers |
| // have identity; if two Symbol * values are the same (different) then |
| // they are the same (different) logical symbol. |
| [&](Fortran::lower::FrontEndSymbol p, |
| Fortran::lower::FrontEndSymbol q) { return p == q; }, |
| [&](const auto *p, const auto *q) { |
| if constexpr (std::is_same_v<decltype(p), decltype(q)>) { |
| return IsEqualEvaluateExpr::isEqual(*p, *q); |
| } else { |
| // Different subtree types are never equal. |
| return false; |
| } |
| }}, |
| x, y); |
| } |
| |
| void copyFirstPrivateSymbol(lower::AbstractConverter &converter, |
| const semantics::Symbol *sym, |
| mlir::OpBuilder::InsertPoint *copyAssignIP) { |
| if (sym->test(semantics::Symbol::Flag::OmpFirstPrivate) || |
| sym->test(semantics::Symbol::Flag::LocalityLocalInit)) |
| converter.copyHostAssociateVar(*sym, copyAssignIP); |
| } |
| |
| template <typename OpType, typename OperandsStructType> |
| void privatizeSymbol( |
| lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, |
| lower::SymMap &symTable, |
| llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, |
| llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, |
| const semantics::Symbol *symToPrivatize, OperandsStructType *clauseOps) { |
| constexpr bool isDoConcurrent = |
| std::is_same_v<OpType, fir::LocalitySpecifierOp>; |
| mlir::OpBuilder::InsertPoint dcIP; |
| |
| if (isDoConcurrent) { |
| dcIP = firOpBuilder.saveInsertionPoint(); |
| firOpBuilder.setInsertionPoint( |
| firOpBuilder.getRegion().getParentOfType<fir::DoConcurrentOp>()); |
| } |
| |
| const semantics::Symbol *sym = |
| isDoConcurrent ? &symToPrivatize->GetUltimate() : symToPrivatize; |
| const lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym); |
| assert(hsb && "Host symbol box not found"); |
| |
| mlir::Location symLoc = hsb.getAddr().getLoc(); |
| std::string privatizerName = sym->name().ToString() + ".privatizer"; |
| bool emitCopyRegion = |
| symToPrivatize->test(semantics::Symbol::Flag::OmpFirstPrivate) || |
| symToPrivatize->test(semantics::Symbol::Flag::LocalityLocalInit); |
| |
| mlir::Value privVal = hsb.getAddr(); |
| mlir::Type allocType = privVal.getType(); |
| if (!mlir::isa<fir::PointerType>(privVal.getType())) |
| allocType = fir::unwrapRefType(privVal.getType()); |
| |
| if (auto poly = mlir::dyn_cast<fir::ClassType>(allocType)) { |
| if (!mlir::isa<fir::PointerType>(poly.getEleTy()) && emitCopyRegion) |
| TODO(symLoc, "create polymorphic host associated copy"); |
| } |
| |
| // fir.array<> cannot be converted to any single llvm type and fir helpers |
| // are not available in openmp to llvmir translation so we cannot generate |
| // an alloca for a fir.array type there. Get around this by boxing all |
| // arrays. |
| if (mlir::isa<fir::SequenceType>(allocType)) { |
| hlfir::Entity entity{hsb.getAddr()}; |
| entity = genVariableBox(symLoc, firOpBuilder, entity); |
| privVal = entity.getBase(); |
| allocType = privVal.getType(); |
| } |
| |
| if (mlir::isa<fir::BaseBoxType>(privVal.getType())) { |
| // Boxes should be passed by reference into nested regions: |
| auto oldIP = firOpBuilder.saveInsertionPoint(); |
| firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); |
| auto alloca = |
| fir::AllocaOp::create(firOpBuilder, symLoc, privVal.getType()); |
| firOpBuilder.restoreInsertionPoint(oldIP); |
| fir::StoreOp::create(firOpBuilder, symLoc, privVal, alloca); |
| privVal = alloca; |
| } |
| |
| mlir::Type argType = privVal.getType(); |
| |
| OpType privatizerOp = [&]() { |
| auto moduleOp = firOpBuilder.getModule(); |
| auto uniquePrivatizerName = fir::getTypeAsString( |
| allocType, converter.getKindMap(), |
| converter.mangleName(*sym) + |
| (emitCopyRegion ? "_firstprivate" : "_private")); |
| |
| if (auto existingPrivatizer = |
| moduleOp.lookupSymbol<OpType>(uniquePrivatizerName)) |
| return existingPrivatizer; |
| |
| mlir::OpBuilder::InsertionGuard guard(firOpBuilder); |
| firOpBuilder.setInsertionPointToStart(moduleOp.getBody()); |
| OpType result; |
| |
| if constexpr (std::is_same_v<OpType, mlir::omp::PrivateClauseOp>) { |
| result = OpType::create( |
| firOpBuilder, symLoc, uniquePrivatizerName, allocType, |
| emitCopyRegion ? mlir::omp::DataSharingClauseType::FirstPrivate |
| : mlir::omp::DataSharingClauseType::Private); |
| } else { |
| result = |
| OpType::create(firOpBuilder, symLoc, uniquePrivatizerName, allocType, |
| emitCopyRegion ? fir::LocalitySpecifierType::LocalInit |
| : fir::LocalitySpecifierType::Local); |
| } |
| |
| fir::ExtendedValue symExV = converter.getSymbolExtendedValue(*sym); |
| lower::SymMapScope outerScope(symTable); |
| |
| // Populate the `init` region. |
| // We need to initialize in the following cases: |
| // 1. The allocation was for a derived type which requires initialization |
| // (this can be skipped if it will be initialized anyway by the copy |
| // region, unless the derived type has allocatable components) |
| // 2. The allocation was for any kind of box |
| // 3. The allocation was for a boxed character |
| const bool needsInitialization = |
| (Fortran::lower::hasDefaultInitialization(sym->GetUltimate()) && |
| (!emitCopyRegion || hlfir::mayHaveAllocatableComponent(allocType))) || |
| mlir::isa<fir::BaseBoxType>(allocType) || |
| mlir::isa<fir::BoxCharType>(allocType); |
| if (needsInitialization) { |
| lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol( |
| isDoConcurrent ? symToPrivatize->GetUltimate() : *symToPrivatize); |
| |
| assert(hsb && "Host symbol box not found"); |
| hlfir::Entity entity{hsb.getAddr()}; |
| bool cannotHaveNonDefaultLowerBounds = |
| !entity.mayHaveNonDefaultLowerBounds(); |
| |
| mlir::Region &initRegion = result.getInitRegion(); |
| mlir::Location symLoc = hsb.getAddr().getLoc(); |
| mlir::Block *initBlock = firOpBuilder.createBlock( |
| &initRegion, /*insertPt=*/{}, {argType, argType}, {symLoc, symLoc}); |
| |
| bool emitCopyRegion = |
| symToPrivatize->test(semantics::Symbol::Flag::OmpFirstPrivate) || |
| symToPrivatize->test( |
| Fortran::semantics::Symbol::Flag::LocalityLocalInit); |
| |
| populateByRefInitAndCleanupRegions( |
| converter, symLoc, argType, /*scalarInitValue=*/nullptr, initBlock, |
| result.getInitPrivateArg(), result.getInitMoldArg(), |
| result.getDeallocRegion(), |
| emitCopyRegion ? DeclOperationKind::FirstPrivateOrLocalInit |
| : DeclOperationKind::PrivateOrLocal, |
| symToPrivatize, cannotHaveNonDefaultLowerBounds, isDoConcurrent); |
| // TODO: currently there are false positives from dead uses of the mold |
| // arg |
| if (result.initReadsFromMold()) |
| mightHaveReadHostSym.insert(symToPrivatize); |
| } |
| |
| // Populate the `copy` region if this is a `firstprivate`. |
| if (emitCopyRegion) { |
| mlir::Region ©Region = result.getCopyRegion(); |
| // First block argument corresponding to the original/host value while |
| // second block argument corresponding to the privatized value. |
| mlir::Block *copyEntryBlock = firOpBuilder.createBlock( |
| ©Region, /*insertPt=*/{}, {argType, argType}, {symLoc, symLoc}); |
| firOpBuilder.setInsertionPointToEnd(copyEntryBlock); |
| |
| auto addSymbol = [&](unsigned argIdx, const semantics::Symbol *symToMap, |
| bool force = false) { |
| symExV.match( |
| [&](const fir::MutableBoxValue &box) { |
| symTable.addSymbol( |
| *symToMap, |
| fir::substBase(box, copyRegion.getArgument(argIdx)), force); |
| }, |
| [&](const auto &box) { |
| symTable.addSymbol(*symToMap, copyRegion.getArgument(argIdx), |
| force); |
| }); |
| }; |
| |
| addSymbol(0, sym, true); |
| lower::SymMapScope innerScope(symTable); |
| addSymbol(1, symToPrivatize); |
| |
| auto ip = firOpBuilder.saveInsertionPoint(); |
| copyFirstPrivateSymbol(converter, symToPrivatize, &ip); |
| |
| if constexpr (std::is_same_v<OpType, mlir::omp::PrivateClauseOp>) { |
| mlir::omp::YieldOp::create( |
| firOpBuilder, hsb.getAddr().getLoc(), |
| symTable.shallowLookupSymbol(*symToPrivatize).getAddr()); |
| } else { |
| fir::YieldOp::create( |
| firOpBuilder, hsb.getAddr().getLoc(), |
| symTable.shallowLookupSymbol(*symToPrivatize).getAddr()); |
| } |
| } |
| |
| return result; |
| }(); |
| |
| if (clauseOps) { |
| clauseOps->privateSyms.push_back(mlir::SymbolRefAttr::get(privatizerOp)); |
| clauseOps->privateVars.push_back(privVal); |
| } |
| |
| if (isDoConcurrent) |
| allPrivatizedSymbols.insert(symToPrivatize); |
| |
| if (isDoConcurrent) |
| firOpBuilder.restoreInsertionPoint(dcIP); |
| } |
| |
| template void |
| privatizeSymbol<mlir::omp::PrivateClauseOp, mlir::omp::PrivateClauseOps>( |
| lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, |
| lower::SymMap &symTable, |
| llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, |
| llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, |
| const semantics::Symbol *symToPrivatize, |
| mlir::omp::PrivateClauseOps *clauseOps); |
| |
| template void |
| privatizeSymbol<fir::LocalitySpecifierOp, fir::LocalitySpecifierOperands>( |
| lower::AbstractConverter &converter, fir::FirOpBuilder &firOpBuilder, |
| lower::SymMap &symTable, |
| llvm::SetVector<const semantics::Symbol *> &allPrivatizedSymbols, |
| llvm::SmallPtrSet<const semantics::Symbol *, 16> &mightHaveReadHostSym, |
| const semantics::Symbol *symToPrivatize, |
| fir::LocalitySpecifierOperands *clauseOps); |
| |
| } // end namespace Fortran::lower |