863 lines
36 KiB
C++
863 lines
36 KiB
C++
//===-- 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::SmallSet<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::SmallSet<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::SmallSet<const semantics::Symbol *, 16> &mightHaveReadHostSym,
|
|
const semantics::Symbol *symToPrivatize,
|
|
fir::LocalitySpecifierOperands *clauseOps);
|
|
|
|
} // end namespace Fortran::lower
|