//===-- lib/Evaluate/fold-implementation.h --------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #ifndef FORTRAN_EVALUATE_FOLD_IMPLEMENTATION_H_ #define FORTRAN_EVALUATE_FOLD_IMPLEMENTATION_H_ #include "character.h" #include "host.h" #include "int-power.h" #include "flang/Common/indirection.h" #include "flang/Common/template.h" #include "flang/Common/unwrap.h" #include "flang/Evaluate/characteristics.h" #include "flang/Evaluate/common.h" #include "flang/Evaluate/constant.h" #include "flang/Evaluate/expression.h" #include "flang/Evaluate/fold.h" #include "flang/Evaluate/formatting.h" #include "flang/Evaluate/intrinsics-library.h" #include "flang/Evaluate/intrinsics.h" #include "flang/Evaluate/shape.h" #include "flang/Evaluate/tools.h" #include "flang/Evaluate/traverse.h" #include "flang/Evaluate/type.h" #include "flang/Parser/message.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" #include #include #include #include #include #include #include // Some environments, viz. clang on Darwin, allow the macro HUGE // to leak out of even when it is never directly included. #undef HUGE namespace Fortran::evaluate { // Utilities template class Folder { public: explicit Folder(FoldingContext &c) : context_{c} {} std::optional> GetNamedConstant(const Symbol &); std::optional> ApplySubscripts(const Constant &array, const std::vector> &subscripts); std::optional> ApplyComponent(Constant &&, const Symbol &component, const std::vector> * = nullptr); std::optional> GetConstantComponent( Component &, const std::vector> * = nullptr); std::optional> Folding(ArrayRef &); Expr Folding(Designator &&); Constant *Folding(std::optional &); Expr Reshape(FunctionRef &&); private: FoldingContext &context_; }; std::optional> GetConstantSubscript( FoldingContext &, Subscript &, const NamedEntity &, int dim); // Helper to use host runtime on scalars for folding. template std::optional(FoldingContext &, Scalar...)>> GetHostRuntimeWrapper(const std::string &name) { std::vector argTypes{TA{}.GetType()...}; if (auto hostWrapper{GetHostRuntimeWrapper(name, TR{}.GetType(), argTypes)}) { return [hostWrapper]( FoldingContext &context, Scalar... args) -> Scalar { std::vector> genericArgs{ AsGenericExpr(Constant{args})...}; return GetScalarConstantValue( (*hostWrapper)(context, std::move(genericArgs))) .value(); }; } return std::nullopt; } // FoldOperation() rewrites expression tree nodes. // If there is any possibility that the rewritten node will // not have the same representation type, the result of // FoldOperation() will be packaged in an Expr<> of the same // specific type. // no-op base case template common::IfNoLvalue>, A> FoldOperation( FoldingContext &, A &&x) { static_assert(!std::is_same_v>>, "call Fold() instead for Expr<>"); return Expr>{std::move(x)}; } Component FoldOperation(FoldingContext &, Component &&); NamedEntity FoldOperation(FoldingContext &, NamedEntity &&); Triplet FoldOperation(FoldingContext &, Triplet &&); Subscript FoldOperation(FoldingContext &, Subscript &&); ArrayRef FoldOperation(FoldingContext &, ArrayRef &&); CoarrayRef FoldOperation(FoldingContext &, CoarrayRef &&); DataRef FoldOperation(FoldingContext &, DataRef &&); Substring FoldOperation(FoldingContext &, Substring &&); ComplexPart FoldOperation(FoldingContext &, ComplexPart &&); template Expr FoldOperation(FoldingContext &context, FunctionRef &&); template Expr> FoldIntrinsicFunction( FoldingContext &context, FunctionRef> &&); template Expr> FoldIntrinsicFunction( FoldingContext &context, FunctionRef> &&); template Expr> FoldIntrinsicFunction( FoldingContext &context, FunctionRef> &&); template Expr> FoldIntrinsicFunction( FoldingContext &context, FunctionRef> &&); template Expr FoldOperation(FoldingContext &context, Designator &&designator) { return Folder{context}.Folding(std::move(designator)); } Expr FoldOperation( FoldingContext &, TypeParamInquiry &&); Expr FoldOperation( FoldingContext &context, ImpliedDoIndex &&); template Expr FoldOperation(FoldingContext &, ArrayConstructor &&); Expr FoldOperation(FoldingContext &, StructureConstructor &&); template std::optional> Folder::GetNamedConstant(const Symbol &symbol0) { const Symbol &symbol{ResolveAssociations(symbol0)}; if (IsNamedConstant(symbol)) { if (const auto *object{ symbol.detailsIf()}) { if (const auto *constant{UnwrapConstantValue(object->init())}) { return *constant; } } } return std::nullopt; } template std::optional> Folder::Folding(ArrayRef &aRef) { std::vector> subscripts; int dim{0}; for (Subscript &ss : aRef.subscript()) { if (auto constant{GetConstantSubscript(context_, ss, aRef.base(), dim++)}) { subscripts.emplace_back(std::move(*constant)); } else { return std::nullopt; } } if (Component * component{aRef.base().UnwrapComponent()}) { return GetConstantComponent(*component, &subscripts); } else if (std::optional> array{ GetNamedConstant(aRef.base().GetLastSymbol())}) { return ApplySubscripts(*array, subscripts); } else { return std::nullopt; } } template std::optional> Folder::ApplySubscripts(const Constant &array, const std::vector> &subscripts) { const auto &shape{array.shape()}; const auto &lbounds{array.lbounds()}; int rank{GetRank(shape)}; CHECK(rank == static_cast(subscripts.size())); std::size_t elements{1}; ConstantSubscripts resultShape; ConstantSubscripts ssLB; for (const auto &ss : subscripts) { CHECK(ss.Rank() <= 1); if (ss.Rank() == 1) { resultShape.push_back(static_cast(ss.size())); elements *= ss.size(); ssLB.push_back(ss.lbounds().front()); } } ConstantSubscripts ssAt(rank, 0), at(rank, 0), tmp(1, 0); std::vector> values; while (elements-- > 0) { bool increment{true}; int k{0}; for (int j{0}; j < rank; ++j) { if (subscripts[j].Rank() == 0) { at[j] = subscripts[j].GetScalarValue().value().ToInt64(); } else { CHECK(k < GetRank(resultShape)); tmp[0] = ssLB.at(k) + ssAt.at(k); at[j] = subscripts[j].At(tmp).ToInt64(); if (increment) { if (++ssAt[k] == resultShape[k]) { ssAt[k] = 0; } else { increment = false; } } ++k; } if (at[j] < lbounds[j] || at[j] >= lbounds[j] + shape[j]) { context_.messages().Say( "Subscript value (%jd) is out of range on dimension %d in reference to a constant array value"_err_en_US, at[j], j + 1); return std::nullopt; } } values.emplace_back(array.At(at)); CHECK(!increment || elements == 0); CHECK(k == GetRank(resultShape)); } if constexpr (T::category == TypeCategory::Character) { return Constant{array.LEN(), std::move(values), std::move(resultShape)}; } else if constexpr (std::is_same_v) { return Constant{array.result().derivedTypeSpec(), std::move(values), std::move(resultShape)}; } else { return Constant{std::move(values), std::move(resultShape)}; } } template std::optional> Folder::ApplyComponent( Constant &&structures, const Symbol &component, const std::vector> *subscripts) { if (auto scalar{structures.GetScalarValue()}) { if (std::optional> expr{scalar->Find(component)}) { if (const Constant *value{UnwrapConstantValue(expr.value())}) { if (!subscripts) { return std::move(*value); } else { return ApplySubscripts(*value, *subscripts); } } } } else { // A(:)%scalar_component & A(:)%array_component(subscripts) std::unique_ptr> array; if (structures.empty()) { return std::nullopt; } ConstantSubscripts at{structures.lbounds()}; do { StructureConstructor scalar{structures.At(at)}; if (std::optional> expr{scalar.Find(component)}) { if (const Constant *value{UnwrapConstantValue(expr.value())}) { if (!array.get()) { // This technique ensures that character length or derived type // information is propagated to the array constructor. auto *typedExpr{UnwrapExpr>(expr.value())}; CHECK(typedExpr); array = std::make_unique>(*typedExpr); } if (subscripts) { if (auto element{ApplySubscripts(*value, *subscripts)}) { CHECK(element->Rank() == 0); array->Push(Expr{std::move(*element)}); } else { return std::nullopt; } } else { CHECK(value->Rank() == 0); array->Push(Expr{*value}); } } else { return std::nullopt; } } } while (structures.IncrementSubscripts(at)); // Fold the ArrayConstructor<> into a Constant<>. CHECK(array); Expr result{Fold(context_, Expr{std::move(*array)})}; if (auto *constant{UnwrapConstantValue(result)}) { return constant->Reshape(common::Clone(structures.shape())); } } return std::nullopt; } template std::optional> Folder::GetConstantComponent(Component &component, const std::vector> *subscripts) { if (std::optional> structures{std::visit( common::visitors{ [&](const Symbol &symbol) { return Folder{context_}.GetNamedConstant(symbol); }, [&](ArrayRef &aRef) { return Folder{context_}.Folding(aRef); }, [&](Component &base) { return Folder{context_}.GetConstantComponent(base); }, [&](CoarrayRef &) { return std::optional>{}; }, }, component.base().u)}) { return ApplyComponent( std::move(*structures), component.GetLastSymbol(), subscripts); } else { return std::nullopt; } } template Expr Folder::Folding(Designator &&designator) { if constexpr (T::category == TypeCategory::Character) { if (auto *substring{common::Unwrap(designator.u)}) { if (std::optional> folded{ substring->Fold(context_)}) { if (auto value{GetScalarConstantValue(*folded)}) { return Expr{*value}; } } if (auto length{ToInt64(Fold(context_, substring->LEN()))}) { if (*length == 0) { return Expr{Constant{Scalar{}}}; } } } } return std::visit( common::visitors{ [&](SymbolRef &&symbol) { if (auto constant{GetNamedConstant(*symbol)}) { return Expr{std::move(*constant)}; } return Expr{std::move(designator)}; }, [&](ArrayRef &&aRef) { aRef = FoldOperation(context_, std::move(aRef)); if (auto c{Folding(aRef)}) { return Expr{std::move(*c)}; } else { return Expr{Designator{std::move(aRef)}}; } }, [&](Component &&component) { component = FoldOperation(context_, std::move(component)); if (auto c{GetConstantComponent(component)}) { return Expr{std::move(*c)}; } else { return Expr{Designator{std::move(component)}}; } }, [&](auto &&x) { return Expr{ Designator{FoldOperation(context_, std::move(x))}}; }, }, std::move(designator.u)); } // Apply type conversion and re-folding if necessary. // This is where BOZ arguments are converted. template Constant *Folder::Folding(std::optional &arg) { if (auto *expr{UnwrapExpr>(arg)}) { if (!UnwrapExpr>(*expr)) { if (auto converted{ConvertToType(T::GetType(), std::move(*expr))}) { *expr = Fold(context_, std::move(*converted)); } } return UnwrapConstantValue(*expr); } return nullptr; } template std::optional *...>> GetConstantArgumentsHelper( FoldingContext &context, ActualArguments &arguments, std::index_sequence) { static_assert( (... && IsSpecificIntrinsicType)); // TODO derived types for MERGE? static_assert(sizeof...(A) > 0); std::tuple *...> args{ Folder{context}.Folding(arguments.at(I))...}; if ((... && (std::get(args)))) { return args; } else { return std::nullopt; } } template std::optional *...>> GetConstantArguments( FoldingContext &context, ActualArguments &args) { return GetConstantArgumentsHelper( context, args, std::index_sequence_for{}); } template std::optional...>> GetScalarConstantArgumentsHelper( FoldingContext &context, ActualArguments &args, std::index_sequence) { if (auto constArgs{GetConstantArguments(context, args)}) { return std::tuple...>{ std::get(*constArgs)->GetScalarValue().value()...}; } else { return std::nullopt; } } template std::optional...>> GetScalarConstantArguments( FoldingContext &context, ActualArguments &args) { return GetScalarConstantArgumentsHelper( context, args, std::index_sequence_for{}); } // helpers to fold intrinsic function references // Define callable types used in a common utility that // takes care of array and cast/conversion aspects for elemental intrinsics template using ScalarFunc = std::function(const Scalar &...)>; template using ScalarFuncWithContext = std::function(FoldingContext &, const Scalar &...)>; template