diff --git a/flang/include/flang/Evaluate/constant.h b/flang/include/flang/Evaluate/constant.h --- a/flang/include/flang/Evaluate/constant.h +++ b/flang/include/flang/Evaluate/constant.h @@ -165,7 +165,8 @@ ~Constant(); bool operator==(const Constant &that) const { - return shape() == that.shape() && values_ == that.values_; + return LEN() == that.LEN() && shape() == that.shape() && + values_ == that.values_; } bool empty() const; std::size_t size() const; diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h --- a/flang/include/flang/Lower/AbstractConverter.h +++ b/flang/include/flang/Lower/AbstractConverter.h @@ -115,6 +115,17 @@ Fortran::semantics::Symbol::Flag flag, bool collectSymbols = true, bool collectHostAssociatedSymbols = false) = 0; + /// For the given literal constant \p expression, returns a unique name + /// that can be used to create a global object to represent this + /// literal constant. It will return the same name for equivalent + /// literal constant expressions. \p eleTy specifies the data type + /// of the constant elements. For array constants it specifies + /// the array's element type. + virtual llvm::StringRef + getUniqueLitName(mlir::Location loc, + std::unique_ptr expression, + mlir::Type eleTy) = 0; + //===--------------------------------------------------------------------===// // Expressions //===--------------------------------------------------------------------===// diff --git a/flang/include/flang/Lower/IterationSpace.h b/flang/include/flang/Lower/IterationSpace.h --- a/flang/include/flang/Lower/IterationSpace.h +++ b/flang/include/flang/Lower/IterationSpace.h @@ -37,30 +37,9 @@ class AbstractConverter; -unsigned getHashValue(FrontEndExpr x); -bool isEqual(FrontEndExpr x, FrontEndExpr y); } // namespace lower } // namespace Fortran -namespace llvm { -template <> -struct DenseMapInfo { - static inline Fortran::lower::FrontEndExpr getEmptyKey() { - return reinterpret_cast(~0); - } - static inline Fortran::lower::FrontEndExpr getTombstoneKey() { - return reinterpret_cast(~0 - 1); - } - static unsigned getHashValue(Fortran::lower::FrontEndExpr v) { - return Fortran::lower::getHashValue(v); - } - static bool isEqual(Fortran::lower::FrontEndExpr lhs, - Fortran::lower::FrontEndExpr rhs) { - return Fortran::lower::isEqual(lhs, rhs); - } -}; -} // namespace llvm - namespace Fortran::lower { /// Abstraction of the iteration space for building the elemental compute loop diff --git a/flang/include/flang/Lower/Mangler.h b/flang/include/flang/Lower/Mangler.h --- a/flang/include/flang/Lower/Mangler.h +++ b/flang/include/flang/Lower/Mangler.h @@ -54,7 +54,7 @@ std::string demangleName(llvm::StringRef name); std::string -mangleArrayLiteral(const uint8_t *addr, size_t size, +mangleArrayLiteral(size_t size, const Fortran::evaluate::ConstantSubscripts &shape, Fortran::common::TypeCategory cat, int kind = 0, Fortran::common::ConstantSubscript charLen = -1, @@ -64,9 +64,8 @@ std::string mangleArrayLiteral( mlir::Type, const Fortran::evaluate::Constant> &x) { - return mangleArrayLiteral( - reinterpret_cast(x.values().data()), - x.values().size() * sizeof(x.values()[0]), x.shape(), TC, KIND); + return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]), + x.shape(), TC, KIND); } template @@ -74,25 +73,18 @@ mangleArrayLiteral(mlir::Type, const Fortran::evaluate::Constant> &x) { - return mangleArrayLiteral( - reinterpret_cast(x.values().data()), - x.values().size() * sizeof(x.values()[0]), x.shape(), - Fortran::common::TypeCategory::Character, KIND, x.LEN()); + return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]), + x.shape(), Fortran::common::TypeCategory::Character, + KIND, x.LEN()); } -// FIXME: derived type mangling is safe but not reproducible between two -// compilation of a same file because `values().data()` is a nontrivial compile -// time data structure containing pointers and vectors. In particular, this -// means that similar structure constructors are not "combined" into the same -// global constant by lowering. inline std::string mangleArrayLiteral( mlir::Type eleTy, const Fortran::evaluate::Constant &x) { - return mangleArrayLiteral( - reinterpret_cast(x.values().data()), - x.values().size() * sizeof(x.values()[0]), x.shape(), - Fortran::common::TypeCategory::Derived, /*kind=*/0, /*charLen=*/-1, - eleTy.cast().getName()); + return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]), + x.shape(), Fortran::common::TypeCategory::Derived, + /*kind=*/0, /*charLen=*/-1, + eleTy.cast().getName()); } /// Return the compiler-generated name of a static namelist variable descriptor. diff --git a/flang/include/flang/Lower/Support/Utils.h b/flang/include/flang/Lower/Support/Utils.h --- a/flang/include/flang/Lower/Support/Utils.h +++ b/flang/include/flang/Lower/Support/Utils.h @@ -24,7 +24,7 @@ namespace Fortran::lower { using SomeExpr = Fortran::evaluate::Expr; -} +} // end namespace Fortran::lower //===----------------------------------------------------------------------===// // Small inline helper functions to deal with repetitive, clumsy conversions. @@ -85,4 +85,582 @@ return result; } +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::evalute::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(reinterpret_cast(&x)); + } + template + static unsigned getHashValue(const Fortran::common::Indirection &x) { + return getHashValue(x.value()); + } + template + static unsigned getHashValue(const std::optional &x) { + if (x.has_value()) + return getHashValue(x.value()); + return 0u; + } + static unsigned getHashValue(const Fortran::evaluate::Subscript &x) { + return std::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 subs = 1u; + for (const Fortran::evaluate::Subscript &v : x.subscript()) + subs -= getHashValue(v); + unsigned cosubs = 3u; + for (const Fortran::evaluate::Expr &v : + x.cosubscript()) + cosubs -= getHashValue(v); + unsigned syms = 7u; + for (const Fortran::evaluate::SymbolRef &v : x.base()) + syms += getHashValue(v); + return syms * 97u - subs - 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 std::visit([&](const auto &v) { return getHashValue(v); }, x.u); + } + static unsigned getHashValue(const Fortran::evaluate::ComplexPart &x) { + return getHashValue(x.complex()) - static_cast(x.part()); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Convert, TC2> + &x) { + return getHashValue(x.left()) - (static_cast(TC1) + 2u) - + (static_cast(KIND) + 5u); + } + template + static unsigned + getHashValue(const Fortran::evaluate::ComplexComponent &x) { + return getHashValue(x.left()) - + (static_cast(x.isImaginaryPart) + 1u) * 3u; + } + template + static unsigned getHashValue(const Fortran::evaluate::Parentheses &x) { + return getHashValue(x.left()) * 17u; + } + template + static unsigned getHashValue( + const Fortran::evaluate::Negate> &x) { + return getHashValue(x.left()) - (static_cast(TC) + 5u) - + (static_cast(KIND) + 7u); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Add> &x) { + return (getHashValue(x.left()) + getHashValue(x.right())) * 23u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Subtract> &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 19u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Multiply> &x) { + return (getHashValue(x.left()) + getHashValue(x.right())) * 29u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Divide> &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 31u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Power> &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 37u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned getHashValue( + const Fortran::evaluate::Extremum> &x) { + return (getHashValue(x.left()) + getHashValue(x.right())) * 41u + + static_cast(TC) + static_cast(KIND) + + static_cast(x.ordering) * 7u; + } + template + static unsigned getHashValue( + const Fortran::evaluate::RealToIntPower> + &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 43u + + static_cast(TC) + static_cast(KIND); + } + template + static unsigned + getHashValue(const Fortran::evaluate::ComplexConstructor &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 47u + + static_cast(KIND); + } + template + static unsigned getHashValue(const Fortran::evaluate::Concat &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 53u + + static_cast(KIND); + } + template + static unsigned getHashValue(const Fortran::evaluate::SetLength &x) { + return (getHashValue(x.left()) - getHashValue(x.right())) * 59u + + static_cast(KIND); + } + static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) { + return getHashValue(sym.get()); + } + static unsigned getHashValue(const Fortran::evaluate::Substring &x) { + return 61u * std::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 + static unsigned getHashValue(const Fortran::evaluate::Constant &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 std::visit([&](const auto &v) { return getHashValue(v); }, x.u); + } + static unsigned getHashValue(const Fortran::evaluate::ProcedureRef &x) { + unsigned args = 13u; + for (const std::optional &v : + x.arguments()) + args -= getHashValue(v); + return getHashValue(x.proc()) * 101u - args; + } + template + static unsigned + getHashValue(const Fortran::evaluate::ArrayConstructor &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(x.field()) * 13u + + static_cast(x.dimension()); + } + static unsigned + getHashValue(const Fortran::evaluate::StructureConstructor &x) { + // FIXME: hash the contents. + return 149u; + } + template + static unsigned getHashValue(const Fortran::evaluate::Not &x) { + return getHashValue(x.left()) * 61u + static_cast(KIND); + } + template + static unsigned + getHashValue(const Fortran::evaluate::LogicalOperation &x) { + unsigned result = getHashValue(x.left()) + getHashValue(x.right()); + return result * 67u + static_cast(x.logicalOperator) * 5u; + } + template + static unsigned getHashValue( + const Fortran::evaluate::Relational> + &x) { + return (getHashValue(x.left()) + getHashValue(x.right())) * 71u + + static_cast(TC) + static_cast(KIND) + + static_cast(x.opr) * 11u; + } + template + static unsigned getHashValue(const Fortran::evaluate::Expr &x) { + return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); + } + static unsigned getHashValue( + const Fortran::evaluate::Relational &x) { + return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); + } + template + static unsigned getHashValue(const Fortran::evaluate::Designator &x) { + return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); + } + template + static unsigned + getHashValue(const Fortran::evaluate::value::Integer &x) { + return static_cast(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 + static bool isEqual(const Fortran::common::Indirection &x, + const Fortran::common::Indirection &y) { + return isEqual(x.value(), y.value()); + } + template + static bool isEqual(const std::optional &x, const std::optional &y) { + if (x.has_value() && y.has_value()) + return isEqual(x.value(), y.value()); + return !x.has_value() && !y.has_value(); + } + template + static bool isEqual(const std::vector &x, const std::vector &y) { + if (x.size() != y.size()) + return false; + const std::size_t size = x.size(); + for (std::remove_const_t 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 std::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.subscript(), y.subscript()) && + 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 std::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 + static bool isEqual(const Fortran::evaluate::Convert &x, + const Fortran::evaluate::Convert &y) { + return isEqual(x.left(), y.left()); + } + template + static bool isEqual(const Fortran::evaluate::ComplexComponent &x, + const Fortran::evaluate::ComplexComponent &y) { + return isEqual(x.left(), y.left()) && + x.isImaginaryPart == y.isImaginaryPart; + } + template + static bool isEqual(const Fortran::evaluate::Parentheses &x, + const Fortran::evaluate::Parentheses &y) { + return isEqual(x.left(), y.left()); + } + template + static bool isEqual(const Fortran::evaluate::Negate &x, + const Fortran::evaluate::Negate &y) { + return isEqual(x.left(), y.left()); + } + template + static bool isBinaryEqual(const A &x, const A &y) { + return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); + } + template + static bool isEqual(const Fortran::evaluate::Add &x, + const Fortran::evaluate::Add &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Subtract &x, + const Fortran::evaluate::Subtract &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Multiply &x, + const Fortran::evaluate::Multiply &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Divide &x, + const Fortran::evaluate::Divide &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Power &x, + const Fortran::evaluate::Power &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Extremum &x, + const Fortran::evaluate::Extremum &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::RealToIntPower &x, + const Fortran::evaluate::RealToIntPower &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::ComplexConstructor &x, + const Fortran::evaluate::ComplexConstructor &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::Concat &x, + const Fortran::evaluate::Concat &y) { + return isBinaryEqual(x, y); + } + template + static bool isEqual(const Fortran::evaluate::SetLength &x, + const Fortran::evaluate::SetLength &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 std::visit( + [&](const auto &p, const auto &q) { return isEqual(p, q); }, + x.parent(), y.parent()) && + isEqual(x.lower(), y.lower()) && isEqual(x.lower(), y.lower()); + } + 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 + static bool isEqual(const Fortran::evaluate::Constant &x, + const Fortran::evaluate::Constant &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 std::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 + static bool isEqual(const Fortran::evaluate::ArrayConstructor &x, + const Fortran::evaluate::ArrayConstructor &y) { + llvm::report_fatal_error("not implemented"); + } + 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 + static bool isEqual(const Fortran::evaluate::Not &x, + const Fortran::evaluate::Not &y) { + return isEqual(x.left(), y.left()); + } + template + static bool isEqual(const Fortran::evaluate::LogicalOperation &x, + const Fortran::evaluate::LogicalOperation &y) { + return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); + } + template + static bool isEqual(const Fortran::evaluate::Relational &x, + const Fortran::evaluate::Relational &y) { + return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); + } + template + static bool isEqual(const Fortran::evaluate::Expr &x, + const Fortran::evaluate::Expr &y) { + return std::visit( + [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); + } + static bool + isEqual(const Fortran::evaluate::Relational &x, + const Fortran::evaluate::Relational &y) { + return std::visit( + [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); + } + template + static bool isEqual(const Fortran::evaluate::Designator &x, + const Fortran::evaluate::Designator &y) { + return std::visit( + [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); + } + template + static bool isEqual(const Fortran::evaluate::value::Integer &x, + const Fortran::evaluate::value::Integer &y) { + return x == y; + } + static bool isEqual(const Fortran::evaluate::NullPointer &x, + const Fortran::evaluate::NullPointer &y) { + return true; + } + template , bool> = true> + static bool isEqual(const A &, const B &) { + return false; + } +}; + +static inline unsigned getHashValue(const Fortran::lower::SomeExpr *x) { + return HashEvaluateExpr::getHashValue(*x); +} + +static bool isEqual(const Fortran::lower::SomeExpr *x, + const Fortran::lower::SomeExpr *y); +} // end namespace Fortran::lower + +// DenseMapInfo for pointers to Fortran::lower::SomeExpr. +namespace llvm { +template <> +struct DenseMapInfo { + static inline const Fortran::lower::SomeExpr *getEmptyKey() { + return reinterpret_cast(~0); + } + static inline const Fortran::lower::SomeExpr *getTombstoneKey() { + return reinterpret_cast(~0 - 1); + } + static unsigned getHashValue(const Fortran::lower::SomeExpr *v) { + return Fortran::lower::getHashValue(v); + } + static bool isEqual(const Fortran::lower::SomeExpr *lhs, + const Fortran::lower::SomeExpr *rhs) { + return Fortran::lower::isEqual(lhs, rhs); + } +}; +} // namespace llvm + +namespace Fortran::lower { +static inline bool isEqual(const Fortran::lower::SomeExpr *x, + const Fortran::lower::SomeExpr *y) { + const auto *empty = + llvm::DenseMapInfo::getEmptyKey(); + const auto *tombstone = + llvm::DenseMapInfo::getTombstoneKey(); + if (x == empty || y == empty || x == tombstone || y == tombstone) + return x == y; + return x == y || IsEqualEvaluateExpr::isEqual(*x, *y); +} +} // end namespace Fortran::lower + #endif // FORTRAN_LOWER_SUPPORT_UTILS_H diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -791,6 +791,40 @@ dispatchTableConverter.registerTypeSpec(*this, loc, typeSpec); } + llvm::StringRef + getUniqueLitName(mlir::Location loc, + std::unique_ptr expr, + mlir::Type eleTy) override final { + std::string namePrefix = + getConstantExprManglePrefix(loc, *expr.get(), eleTy); + auto [it, inserted] = literalNamesMap.try_emplace( + expr.get(), namePrefix + std::to_string(uniqueLitId)); + const auto &name = it->second; + if (inserted) { + // Keep ownership of the expr key. + literalExprsStorage.push_back(std::move(expr)); + + // If we've just added a new name, we have to make sure + // there is no global object with the same name in the module. + fir::GlobalOp global = builder->getNamedGlobal(name); + if (global) + fir::emitFatalError(loc, llvm::Twine("global object with name '") + + llvm::Twine(name) + + llvm::Twine("' already exists")); + ++uniqueLitId; + return name; + } + + // The name already exists. Verify that the prefix is the same. + if (!llvm::StringRef(name).starts_with(namePrefix)) + fir::emitFatalError(loc, llvm::Twine("conflicting prefixes: '") + + llvm::Twine(name) + + llvm::Twine("' does not start with '") + + llvm::Twine(namePrefix) + llvm::Twine("'")); + + return name; + } + private: FirConverter() = delete; FirConverter(const FirConverter &) = delete; @@ -4397,6 +4431,49 @@ return bridge.getLoweringOptions().getLowerToHighLevelFIR(); } + // Returns the mangling prefix for the given constant expression. + std::string getConstantExprManglePrefix(mlir::Location loc, + const Fortran::lower::SomeExpr &expr, + mlir::Type eleTy) { + return std::visit( + [&](const auto &x) -> std::string { + using T = std::decay_t; + if constexpr (Fortran::common::HasMember< + T, Fortran::lower::CategoryExpression>) { + if constexpr (T::Result::category == + Fortran::common::TypeCategory::Derived) { + if (const auto *constant = + std::get_if>(&x.u)) + return Fortran::lower::mangle::mangleArrayLiteral(eleTy, + *constant); + fir::emitFatalError(loc, + "non a constant derived type expression"); + } else { + return std::visit( + [&](const auto &someKind) -> std::string { + using T = std::decay_t; + using TK = Fortran::evaluate::Type; + if (const auto *constant = + std::get_if>( + &someKind.u)) { + return Fortran::lower::mangle::mangleArrayLiteral( + nullptr, *constant); + } + fir::emitFatalError( + loc, "not a Fortran::evaluate::Constant expression"); + return {}; + }, + x.u); + } + } else { + fir::emitFatalError(loc, "unexpected expression"); + } + }, + expr.u); + } + //===--------------------------------------------------------------------===// Fortran::lower::LoweringBridge &bridge; @@ -4423,6 +4500,23 @@ /// Tuple of host associated variables mlir::Value hostAssocTuple; + + /// A map of unique names for constant expressions. + /// The names are used for representing the constant expressions + /// with global constant initialized objects. + /// The names are usually prefixed by a mangling string based + /// on the element type of the constant expression, but the element + /// type is not used as a key into the map (so the assumption is that + /// the equivalent constant expressions are prefixed using the same + /// element type). + llvm::DenseMap literalNamesMap; + + /// Storage for Constant expressions used as keys for literalNamesMap. + llvm::SmallVector> + literalExprsStorage; + + /// A counter for uniquing names in `literalNamesMap`. + std::uint64_t uniqueLitId = 0; }; } // namespace diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp --- a/flang/lib/Lower/ConvertConstant.cpp +++ b/flang/lib/Lower/ConvertConstant.cpp @@ -415,9 +415,10 @@ if (!outlineBigConstantsInReadOnlyMemory) return genInlinedStructureCtorLitImpl(converter, loc, value, eleTy); fir::FirOpBuilder &builder = converter.getFirOpBuilder(); - std::string globalName = Fortran::lower::mangle::mangleArrayLiteral( - eleTy, - Fortran::evaluate::Constant(value)); + auto expr = std::make_unique(toEvExpr( + Fortran::evaluate::Constant(value))); + llvm::StringRef globalName = + converter.getUniqueLitName(loc, std::move(expr), eleTy); fir::GlobalOp global = builder.getNamedGlobal(globalName); if (!global) { global = builder.createGlobalConstant( @@ -525,8 +526,9 @@ const Fortran::evaluate::Constant &constant) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); mlir::Type eleTy = arrayTy.cast().getEleTy(); - std::string globalName = - Fortran::lower::mangle::mangleArrayLiteral(eleTy, constant); + llvm::StringRef globalName = converter.getUniqueLitName( + loc, std::make_unique(toEvExpr(constant)), + eleTy); fir::GlobalOp global = builder.getNamedGlobal(globalName); if (!global) { // Using a dense attribute for the initial value instead of creating an diff --git a/flang/lib/Lower/IterationSpace.cpp b/flang/lib/Lower/IterationSpace.cpp --- a/flang/lib/Lower/IterationSpace.cpp +++ b/flang/lib/Lower/IterationSpace.cpp @@ -19,541 +19,12 @@ #define DEBUG_TYPE "flang-lower-iteration-space" -namespace { -// 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::evalute::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(reinterpret_cast(&x)); - } - template - static unsigned getHashValue(const Fortran::common::Indirection &x) { - return getHashValue(x.value()); - } - template - static unsigned getHashValue(const std::optional &x) { - if (x.has_value()) - return getHashValue(x.value()); - return 0u; - } - static unsigned getHashValue(const Fortran::evaluate::Subscript &x) { - return std::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 subs = 1u; - for (const Fortran::evaluate::Subscript &v : x.subscript()) - subs -= getHashValue(v); - unsigned cosubs = 3u; - for (const Fortran::evaluate::Expr &v : - x.cosubscript()) - cosubs -= getHashValue(v); - unsigned syms = 7u; - for (const Fortran::evaluate::SymbolRef &v : x.base()) - syms += getHashValue(v); - return syms * 97u - subs - 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 std::visit([&](const auto &v) { return getHashValue(v); }, x.u); - } - static unsigned getHashValue(const Fortran::evaluate::ComplexPart &x) { - return getHashValue(x.complex()) - static_cast(x.part()); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Convert, TC2> - &x) { - return getHashValue(x.left()) - (static_cast(TC1) + 2u) - - (static_cast(KIND) + 5u); - } - template - static unsigned - getHashValue(const Fortran::evaluate::ComplexComponent &x) { - return getHashValue(x.left()) - - (static_cast(x.isImaginaryPart) + 1u) * 3u; - } - template - static unsigned getHashValue(const Fortran::evaluate::Parentheses &x) { - return getHashValue(x.left()) * 17u; - } - template - static unsigned getHashValue( - const Fortran::evaluate::Negate> &x) { - return getHashValue(x.left()) - (static_cast(TC) + 5u) - - (static_cast(KIND) + 7u); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Add> &x) { - return (getHashValue(x.left()) + getHashValue(x.right())) * 23u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Subtract> &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 19u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Multiply> &x) { - return (getHashValue(x.left()) + getHashValue(x.right())) * 29u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Divide> &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 31u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Power> &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 37u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned getHashValue( - const Fortran::evaluate::Extremum> &x) { - return (getHashValue(x.left()) + getHashValue(x.right())) * 41u + - static_cast(TC) + static_cast(KIND) + - static_cast(x.ordering) * 7u; - } - template - static unsigned getHashValue( - const Fortran::evaluate::RealToIntPower> - &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 43u + - static_cast(TC) + static_cast(KIND); - } - template - static unsigned - getHashValue(const Fortran::evaluate::ComplexConstructor &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 47u + - static_cast(KIND); - } - template - static unsigned getHashValue(const Fortran::evaluate::Concat &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 53u + - static_cast(KIND); - } - template - static unsigned getHashValue(const Fortran::evaluate::SetLength &x) { - return (getHashValue(x.left()) - getHashValue(x.right())) * 59u + - static_cast(KIND); - } - static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) { - return getHashValue(sym.get()); - } - static unsigned getHashValue(const Fortran::evaluate::Substring &x) { - return 61u * std::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 - static unsigned getHashValue(const Fortran::evaluate::Constant &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 std::visit([&](const auto &v) { return getHashValue(v); }, x.u); - } - static unsigned getHashValue(const Fortran::evaluate::ProcedureRef &x) { - unsigned args = 13u; - for (const std::optional &v : - x.arguments()) - args -= getHashValue(v); - return getHashValue(x.proc()) * 101u - args; - } - template - static unsigned - getHashValue(const Fortran::evaluate::ArrayConstructor &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(x.field()) * 13u + - static_cast(x.dimension()); - } - static unsigned - getHashValue(const Fortran::evaluate::StructureConstructor &x) { - // FIXME: hash the contents. - return 149u; - } - template - static unsigned getHashValue(const Fortran::evaluate::Not &x) { - return getHashValue(x.left()) * 61u + static_cast(KIND); - } - template - static unsigned - getHashValue(const Fortran::evaluate::LogicalOperation &x) { - unsigned result = getHashValue(x.left()) + getHashValue(x.right()); - return result * 67u + static_cast(x.logicalOperator) * 5u; - } - template - static unsigned getHashValue( - const Fortran::evaluate::Relational> - &x) { - return (getHashValue(x.left()) + getHashValue(x.right())) * 71u + - static_cast(TC) + static_cast(KIND) + - static_cast(x.opr) * 11u; - } - template - static unsigned getHashValue(const Fortran::evaluate::Expr &x) { - return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); - } - static unsigned getHashValue( - const Fortran::evaluate::Relational &x) { - return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); - } - template - static unsigned getHashValue(const Fortran::evaluate::Designator &x) { - return std::visit([&](const auto &v) { return getHashValue(v); }, x.u); - } - template - static unsigned - getHashValue(const Fortran::evaluate::value::Integer &x) { - return static_cast(x.ToSInt()); - } - static unsigned getHashValue(const Fortran::evaluate::NullPointer &x) { - return ~179u; - } -}; -} // namespace - unsigned Fortran::lower::getHashValue( const Fortran::lower::ExplicitIterSpace::ArrayBases &x) { return std::visit( [&](const auto *p) { return HashEvaluateExpr::getHashValue(*p); }, x); } -unsigned Fortran::lower::getHashValue(Fortran::lower::FrontEndExpr x) { - return HashEvaluateExpr::getHashValue(*x); -} - -namespace { -// 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 - static bool isEqual(const Fortran::common::Indirection &x, - const Fortran::common::Indirection &y) { - return isEqual(x.value(), y.value()); - } - template - static bool isEqual(const std::optional &x, const std::optional &y) { - if (x.has_value() && y.has_value()) - return isEqual(x.value(), y.value()); - return !x.has_value() && !y.has_value(); - } - template - static bool isEqual(const std::vector &x, const std::vector &y) { - if (x.size() != y.size()) - return false; - const std::size_t size = x.size(); - for (std::remove_const_t 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 std::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.subscript(), y.subscript()) && - 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 std::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 - static bool isEqual(const Fortran::evaluate::Convert &x, - const Fortran::evaluate::Convert &y) { - return isEqual(x.left(), y.left()); - } - template - static bool isEqual(const Fortran::evaluate::ComplexComponent &x, - const Fortran::evaluate::ComplexComponent &y) { - return isEqual(x.left(), y.left()) && - x.isImaginaryPart == y.isImaginaryPart; - } - template - static bool isEqual(const Fortran::evaluate::Parentheses &x, - const Fortran::evaluate::Parentheses &y) { - return isEqual(x.left(), y.left()); - } - template - static bool isEqual(const Fortran::evaluate::Negate &x, - const Fortran::evaluate::Negate &y) { - return isEqual(x.left(), y.left()); - } - template - static bool isBinaryEqual(const A &x, const A &y) { - return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); - } - template - static bool isEqual(const Fortran::evaluate::Add &x, - const Fortran::evaluate::Add &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Subtract &x, - const Fortran::evaluate::Subtract &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Multiply &x, - const Fortran::evaluate::Multiply &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Divide &x, - const Fortran::evaluate::Divide &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Power &x, - const Fortran::evaluate::Power &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Extremum &x, - const Fortran::evaluate::Extremum &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::RealToIntPower &x, - const Fortran::evaluate::RealToIntPower &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::ComplexConstructor &x, - const Fortran::evaluate::ComplexConstructor &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::Concat &x, - const Fortran::evaluate::Concat &y) { - return isBinaryEqual(x, y); - } - template - static bool isEqual(const Fortran::evaluate::SetLength &x, - const Fortran::evaluate::SetLength &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 std::visit( - [&](const auto &p, const auto &q) { return isEqual(p, q); }, - x.parent(), y.parent()) && - isEqual(x.lower(), y.lower()) && isEqual(x.lower(), y.lower()); - } - 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 - static bool isEqual(const Fortran::evaluate::Constant &x, - const Fortran::evaluate::Constant &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 std::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 - static bool isEqual(const Fortran::evaluate::ArrayConstructor &x, - const Fortran::evaluate::ArrayConstructor &y) { - llvm::report_fatal_error("not implemented"); - } - 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) { - llvm::report_fatal_error("not implemented"); - } - template - static bool isEqual(const Fortran::evaluate::Not &x, - const Fortran::evaluate::Not &y) { - return isEqual(x.left(), y.left()); - } - template - static bool isEqual(const Fortran::evaluate::LogicalOperation &x, - const Fortran::evaluate::LogicalOperation &y) { - return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); - } - template - static bool isEqual(const Fortran::evaluate::Relational &x, - const Fortran::evaluate::Relational &y) { - return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right()); - } - template - static bool isEqual(const Fortran::evaluate::Expr &x, - const Fortran::evaluate::Expr &y) { - return std::visit( - [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); - } - static bool - isEqual(const Fortran::evaluate::Relational &x, - const Fortran::evaluate::Relational &y) { - return std::visit( - [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); - } - template - static bool isEqual(const Fortran::evaluate::Designator &x, - const Fortran::evaluate::Designator &y) { - return std::visit( - [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u); - } - template - static bool isEqual(const Fortran::evaluate::value::Integer &x, - const Fortran::evaluate::value::Integer &y) { - return x == y; - } - static bool isEqual(const Fortran::evaluate::NullPointer &x, - const Fortran::evaluate::NullPointer &y) { - return true; - } - template , bool> = true> - static bool isEqual(const A &, const B &) { - return false; - } -}; -} // namespace - bool Fortran::lower::isEqual( const Fortran::lower::ExplicitIterSpace::ArrayBases &x, const Fortran::lower::ExplicitIterSpace::ArrayBases &y) { @@ -578,16 +49,6 @@ x, y); } -bool Fortran::lower::isEqual(Fortran::lower::FrontEndExpr x, - Fortran::lower::FrontEndExpr y) { - auto empty = llvm::DenseMapInfo::getEmptyKey(); - auto tombstone = - llvm::DenseMapInfo::getTombstoneKey(); - if (x == empty || y == empty || x == tombstone || y == tombstone) - return x == y; - return x == y || IsEqualEvaluateExpr::isEqual(*x, *y); -} - namespace { /// This class can recover the base array in an expression that contains diff --git a/flang/lib/Lower/Mangler.cpp b/flang/lib/Lower/Mangler.cpp --- a/flang/lib/Lower/Mangler.cpp +++ b/flang/lib/Lower/Mangler.cpp @@ -236,8 +236,7 @@ } std::string Fortran::lower::mangle::mangleArrayLiteral( - const uint8_t *addr, size_t size, - const Fortran::evaluate::ConstantSubscripts &shape, + size_t size, const Fortran::evaluate::ConstantSubscripts &shape, Fortran::common::TypeCategory cat, int kind, Fortran::common::ConstantSubscript charLen, llvm::StringRef derivedName) { std::string typeId; @@ -249,14 +248,8 @@ std::string name = fir::NameUniquer::doGenerated("ro."s.append(typeId).append(".")); if (!size) - return name += "null"; - llvm::MD5 hashValue{}; - hashValue.update(llvm::ArrayRef{addr, size}); - llvm::MD5::MD5Result hashResult; - hashValue.final(hashResult); - llvm::SmallString<32> hashString; - llvm::MD5::stringifyResult(hashResult, hashString); - return name += hashString.c_str(); + name += "null."; + return name; } std::string Fortran::lower::mangle::globalNamelistDescriptorName( diff --git a/flang/test/Lower/array-character.f90 b/flang/test/Lower/array-character.f90 --- a/flang/test/Lower/array-character.f90 +++ b/flang/test/Lower/array-character.f90 @@ -100,7 +100,7 @@ ! CHECK: %[[VAL_8:.*]] = fir.address_of(@_QQcl.{{.*}}) : !fir.ref>) -> !fir.ref ! CHECK: %[[VAL_10:.*]] = fir.call @_FortranAioBeginExternalListOutput(%[[VAL_0]], %[[VAL_9]], %{{.*}}) {{.*}}: (i32, !fir.ref, i32) -> !fir.ref - ! CHECK: %[[VAL_11:.*]] = fir.address_of(@_QQro.4x3xc1.1636b396a657de68ffb870a885ac44b4) : !fir.ref>> + ! CHECK: %[[VAL_11:.*]] = fir.address_of(@_QQro.4x3xc1.0) : !fir.ref>> ! CHECK: %[[VAL_12:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1> ! CHECK: %[[VAL_13:.*]] = fir.allocmem !fir.array<4x!fir.char<1,3>> ! CHECK: cf.br ^bb1(%[[VAL_6]], %[[VAL_5]] : index, index) @@ -174,7 +174,7 @@ ! CHECK: } end -! CHECK: fir.global internal @_QQro.4x3xc1.1636b396a657de68ffb870a885ac44b4 constant : !fir.array<4x!fir.char<1,3>> +! CHECK: fir.global internal @_QQro.4x3xc1.0 constant : !fir.array<4x!fir.char<1,3>> ! CHECK: AA ! CHECK: MM ! CHECK: ZZ diff --git a/flang/test/Lower/array-constructor-1.f90 b/flang/test/Lower/array-constructor-1.f90 --- a/flang/test/Lower/array-constructor-1.f90 +++ b/flang/test/Lower/array-constructor-1.f90 @@ -29,7 +29,7 @@ subroutine zero complex, parameter :: a(0) = [(((k,k=1,10),j=-2,2,-1),i=2,-2,-2)] complex, parameter :: b(0) = [(7,i=3,-3)] - ! CHECK: fir.address_of(@_QQro.0xz4.null) : !fir.ref>> + ! CHECK: fir.address_of(@_QQro.0xz4.null.0) : !fir.ref>> ! CHECK-NOT: _QQro print*, '>', a, '<' print*, '>', b, '<' diff --git a/flang/test/Lower/array-constructor-2.f90 b/flang/test/Lower/array-constructor-2.f90 --- a/flang/test/Lower/array-constructor-2.f90 +++ b/flang/test/Lower/array-constructor-2.f90 @@ -10,11 +10,11 @@ ! Array ctors for constant arrays should be outlined as constant globals. ! Look at inline constructor case - ! CHECK: %{{.*}} = fir.address_of(@_QQro.3xr4.6e55f044605a4991f15fd4505d83faf4) : !fir.ref> + ! CHECK: %{{.*}} = fir.address_of(@_QQro.3xr4.0) : !fir.ref> a = (/ 1.0, 2.0, 3.0 /) ! Look at PARAMETER case - ! CHECK: %{{.*}} = fir.address_of(@_QQro.4xi4.6a6af0eea868c84da59807d34f7e1a86) : !fir.ref> + ! CHECK: %{{.*}} = fir.address_of(@_QQro.4xi4.1) : !fir.ref> b = constant_array end subroutine test1 @@ -128,7 +128,7 @@ ! Array ctor with runtime element values and constant extents. ! Concatenation of array values of constant extent. ! CHECK: %[[res:.*]] = fir.allocmem !fir.array<4xf32> - ! CHECK: fir.address_of(@_QQro.2xr4.057a7f5ab69cb695657046b18832c330) : !fir.ref> + ! CHECK: fir.address_of(@_QQro.2xr4.2) : !fir.ref> ! CHECK: %[[tmp1:.*]] = fir.allocmem !fir.array<2xf32> ! CHECK: fir.call @llvm.memcpy.p0.p0.i64(%{{.*}}, %{{.*}}, %{{.*}}, %false{{.*}}) {{.*}}: (!fir.ref, !fir.ref, i64, i1) -> () ! CHECK: %[[tmp2:.*]] = fir.allocmem !fir.array<2xf32> @@ -172,6 +172,6 @@ a = (/ (CHAR(i), i=1,n) /) end subroutine test7 -! CHECK: fir.global internal @_QQro.3xr4.{{.*}}(dense<[1.000000e+00, 2.000000e+00, 3.000000e+00]> : tensor<3xf32>) constant : !fir.array<3xf32> +! CHECK: fir.global internal @_QQro.3xr4.0(dense<[1.000000e+00, 2.000000e+00, 3.000000e+00]> : tensor<3xf32>) constant : !fir.array<3xf32> -! CHECK: fir.global internal @_QQro.4xi4.{{.*}}(dense<[6, 7, 42, 9]> : tensor<4xi32>) constant : !fir.array<4xi32> +! CHECK: fir.global internal @_QQro.4xi4.1(dense<[6, 7, 42, 9]> : tensor<4xi32>) constant : !fir.array<4xi32> diff --git a/flang/test/Lower/array-expression-slice-1.f90 b/flang/test/Lower/array-expression-slice-1.f90 --- a/flang/test/Lower/array-expression-slice-1.f90 +++ b/flang/test/Lower/array-expression-slice-1.f90 @@ -227,7 +227,7 @@ ! CHECK: %[[VAL_185:.*]] = fir.call @_FortranAioEndIoStatement(%[[VAL_176]]) {{.*}}: (!fir.ref) -> i32 ! CHECK: br ^bb24 ! CHECK: ^bb24: -! CHECK: %[[VAL_186:.*]] = fir.address_of(@_QQro.3xi4.b7f1b733471804c07debf489e49d9c2f) : !fir.ref> +! CHECK: %[[VAL_186:.*]] = fir.address_of(@_QQro.3xi4.0) : !fir.ref> ! CHECK: br ^bb25(%[[VAL_6]], %[[VAL_11]] : index, index) ! CHECK: ^bb25(%[[VAL_187:.*]]: index, %[[VAL_188:.*]]: index): ! CHECK: %[[VAL_189:.*]] = arith.cmpi sgt, %[[VAL_188]], %[[VAL_6]] : index diff --git a/flang/test/Lower/constant-literal-mangling.f90 b/flang/test/Lower/constant-literal-mangling.f90 --- a/flang/test/Lower/constant-literal-mangling.f90 +++ b/flang/test/Lower/constant-literal-mangling.f90 @@ -5,36 +5,89 @@ integer :: i end type +type otherType + integer :: i +end type + print *, [42, 42] -! CHECK: fir.address_of(@_QQro.2xi4.53fa91e04725d4ee6f22cf1e2d38428a) +! CHECK: fir.address_of(@_QQro.2xi4.0) print *, reshape([42, 42, 42, 42, 42, 42], [2,3]) -! CHECK: fir.address_of(@_QQro.2x3xi4.9af8c8182bab45c4e7888ec3623db3b6) +! CHECK: fir.address_of(@_QQro.2x3xi4.1) print *, [42_8, 42_8] -! CHECK: fir.address_of(@_QQro.2xi8.3b1356831516d19b976038974b2673ac) +! CHECK: fir.address_of(@_QQro.2xi8.2) print *, [0.42, 0.42] -! CHECK: fir.address_of(@_QQro.2xr4.3c5becae2e4426ad1615e253139ceff8) +! CHECK: fir.address_of(@_QQro.2xr4.3) print *, [0.42_8, 0.42_8] -! CHECK: fir.address_of(@_QQro.2xr8.ebefec8f7537fbf54acc4530e75084e6) +! CHECK: fir.address_of(@_QQro.2xr8.4) print *, [.true.] -! CHECK: fir.address_of(@_QQro.1xl4.4352d88a78aa39750bf70cd6f27bcaa5) +! CHECK: fir.address_of(@_QQro.1xl4.5) print *, [.true._8] -! CHECK: fir.address_of(@_QQro.1xl8.33cdeccccebe80329f1fdbee7f5874cb) +! CHECK: fir.address_of(@_QQro.1xl8.6) print *, [(1., -1.), (-1., 1)] -! CHECK: fir.address_of(@_QQro.2xz4.ac09ecb1abceb4f9cad4b1a50000074e) +! CHECK: fir.address_of(@_QQro.2xz4.7) print *, [(1._8, -1._8), (-1._8, 1._8)] -! CHECK: fir.address_of(@_QQro.2xz8.a3652db37055e37d2cae8198ae4cd959) +! CHECK: fir.address_of(@_QQro.2xz8.8) print *, [someType(42), someType(43)] -! CHECK: fir.address_of(@_QQro.2x_QFTsometype. -! Note: the hash for derived types cannot clash with other constant in the same -! compilation unit, but is unstable because it hashes some noise contained in -! unused std::vector storage. +! CHECK: fir.address_of(@_QQro.2x_QFTsometype.9 + + ! Verify that literals of the same type/shape + ! are mapped to different global objects: + print *, [someType(11)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.10) + print *, [someType(42)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.11) + print *, [someType(11)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.10) + print *, [someType(42)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.11) + print *, [someType(11)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.10) + print *, [someType(42)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.11) + print *, [someType(11)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.10) + print *, [someType(42)] +! CHECK: fir.address_of(@_QQro.1x_QFTsometype.11) + + print *, [Character(4)::] +! CHECK: fir.address_of(@_QQro.0x4xc1.null.12) + print *, [Character(2)::] +! CHECK: fir.address_of(@_QQro.0x2xc1.null.13) + print *, [Character(2)::] +! CHECK: fir.address_of(@_QQro.0x2xc1.null.13) + + print *, [otherType(42)] +! CHECK: fir.address_of(@_QQro.1x_QFTothertype.14) + end + +! CHECK: fir.global internal @_QQro.1x_QFTsometype.10 constant : !fir.array<1x!fir.type<_QFTsometype{i:i32}>> { +! CHECK: %{{.*}} = arith.constant 11 : i32 +! CHECK: } + +! CHECK: fir.global internal @_QQro.1x_QFTsometype.11 constant : !fir.array<1x!fir.type<_QFTsometype{i:i32}>> { +! CHECK: %{{.*}} = arith.constant 42 : i32 +! CHECK: } + +! CHECK: fir.global internal @_QQro.0x4xc1.null.12 constant : !fir.array<0x!fir.char<1,4>> { +! CHECK: %[[T1:.*]] = fir.undefined !fir.array<0x!fir.char<1,4>> +! CHECK: fir.has_value %[[T1]] : !fir.array<0x!fir.char<1,4>> +! CHECK: } + +! CHECK: fir.global internal @_QQro.0x2xc1.null.13 constant : !fir.array<0x!fir.char<1,2>> { +! CHECK: %[[T2:.*]] = fir.undefined !fir.array<0x!fir.char<1,2>> +! CHECK: fir.has_value %[[T2]] : !fir.array<0x!fir.char<1,2>> +! CHECK: } + +! CHECK: fir.global internal @_QQro.1x_QFTothertype.14 constant : !fir.array<1x!fir.type<_QFTothertype{i:i32}>> { +! CHECK: %{{.*}} = arith.constant 42 : i32 +! CHECK: }