2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Evaluate/fold-integer.cpp -------------------------------------===//
|
2020-01-01 04:20:42 +08:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-01-01 04:20:42 +08:00
|
|
|
|
|
|
|
#include "fold-implementation.h"
|
2021-06-16 06:18:41 +08:00
|
|
|
#include "fold-reduction.h"
|
2021-02-03 06:15:50 +08:00
|
|
|
#include "flang/Evaluate/check-expression.h"
|
2020-01-01 04:20:42 +08:00
|
|
|
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2022-04-21 00:47:21 +08:00
|
|
|
// Given a collection of ConstantSubscripts values, package them as a Constant.
|
|
|
|
// Return scalar value if asScalar == true and shape-dim array otherwise.
|
|
|
|
template <typename T>
|
|
|
|
Expr<T> PackageConstantBounds(
|
|
|
|
const ConstantSubscripts &&bounds, bool asScalar = false) {
|
|
|
|
if (asScalar) {
|
|
|
|
return Expr<T>{Constant<T>{bounds.at(0)}};
|
|
|
|
} else {
|
|
|
|
// As rank-dim array
|
|
|
|
const int rank{GetRank(bounds)};
|
|
|
|
std::vector<Scalar<T>> packed(rank);
|
|
|
|
std::transform(bounds.begin(), bounds.end(), packed.begin(),
|
|
|
|
[](ConstantSubscript x) { return Scalar<T>(x); });
|
|
|
|
return Expr<T>{Constant<T>{std::move(packed), ConstantSubscripts{rank}}};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
// Class to retrieve the constant bound of an expression which is an
|
2021-02-03 06:15:50 +08:00
|
|
|
// array that devolves to a type of Constant<T>
|
2022-04-27 21:14:10 +08:00
|
|
|
class GetConstantArrayBoundHelper {
|
2021-02-03 06:15:50 +08:00
|
|
|
public:
|
2022-04-27 21:14:10 +08:00
|
|
|
template <typename T>
|
|
|
|
static Expr<T> GetLbound(
|
|
|
|
const Expr<SomeType> &array, std::optional<int> dim) {
|
|
|
|
return PackageConstantBounds<T>(
|
|
|
|
GetConstantArrayBoundHelper(dim, /*getLbound=*/true).Get(array),
|
|
|
|
dim.has_value());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static Expr<T> GetUbound(
|
|
|
|
const Expr<SomeType> &array, std::optional<int> dim) {
|
|
|
|
return PackageConstantBounds<T>(
|
|
|
|
GetConstantArrayBoundHelper(dim, /*getLbound=*/false).Get(array),
|
|
|
|
dim.has_value());
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
GetConstantArrayBoundHelper(
|
|
|
|
std::optional<ConstantSubscript> dim, bool getLbound)
|
|
|
|
: dim_{dim}, getLbound_{getLbound} {}
|
2021-02-03 06:15:50 +08:00
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
template <typename T> ConstantSubscripts Get(const T &) {
|
2021-02-03 06:15:50 +08:00
|
|
|
// The method is needed for template expansion, but we should never get
|
|
|
|
// here in practice.
|
|
|
|
CHECK(false);
|
2022-04-21 00:47:21 +08:00
|
|
|
return {0};
|
2021-02-03 06:15:50 +08:00
|
|
|
}
|
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
template <typename T> ConstantSubscripts Get(const Constant<T> &x) {
|
|
|
|
if (getLbound_) {
|
|
|
|
// Return the lower bound
|
|
|
|
if (dim_) {
|
|
|
|
return {x.lbounds().at(*dim_)};
|
|
|
|
} else {
|
|
|
|
return x.lbounds();
|
|
|
|
}
|
2022-04-21 00:47:21 +08:00
|
|
|
} else {
|
2022-04-28 17:56:16 +08:00
|
|
|
// Return the upper bound
|
|
|
|
if (arrayFromParenthesesExpr) {
|
|
|
|
// Underlying array comes from (x) expression - return shapes
|
|
|
|
if (dim_) {
|
|
|
|
return {x.shape().at(*dim_)};
|
|
|
|
} else {
|
|
|
|
return x.shape();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return x.ComputeUbounds(dim_);
|
|
|
|
}
|
2022-04-21 00:47:21 +08:00
|
|
|
}
|
2021-02-03 06:15:50 +08:00
|
|
|
}
|
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
template <typename T> ConstantSubscripts Get(const Parentheses<T> &x) {
|
2022-04-28 17:56:16 +08:00
|
|
|
// Cause of temp variable inside parentheses - return [1, ... 1] for lower
|
|
|
|
// bounds and shape for upper bounds
|
|
|
|
if (getLbound_) {
|
|
|
|
return ConstantSubscripts(x.Rank(), ConstantSubscript{1});
|
|
|
|
} else {
|
|
|
|
// Indicate that underlying array comes from parentheses expression.
|
|
|
|
// Continue to unwrap expression until we hit a constant
|
|
|
|
arrayFromParenthesesExpr = true;
|
|
|
|
return Get(x.left());
|
|
|
|
}
|
2021-02-03 06:15:50 +08:00
|
|
|
}
|
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
template <typename T> ConstantSubscripts Get(const Expr<T> &x) {
|
2021-02-03 06:15:50 +08:00
|
|
|
// recurse through Expr<T>'a until we hit a constant
|
2022-04-27 21:14:10 +08:00
|
|
|
return common::visit([&](const auto &inner) { return Get(inner); },
|
2021-02-03 06:15:50 +08:00
|
|
|
// [&](const auto &) { return 0; },
|
|
|
|
x.u);
|
|
|
|
}
|
|
|
|
|
2022-04-27 21:14:10 +08:00
|
|
|
const std::optional<ConstantSubscript> dim_;
|
|
|
|
const bool getLbound_;
|
2022-04-28 17:56:16 +08:00
|
|
|
bool arrayFromParenthesesExpr{false};
|
2021-02-03 06:15:50 +08:00
|
|
|
};
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <int KIND>
|
2020-01-01 04:20:42 +08:00
|
|
|
Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context,
|
|
|
|
FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
|
|
|
|
using T = Type<TypeCategory::Integer, KIND>;
|
|
|
|
ActualArguments &args{funcRef.arguments()};
|
|
|
|
if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
|
|
|
|
if (int rank{array->Rank()}; rank > 0) {
|
|
|
|
std::optional<int> dim;
|
|
|
|
if (funcRef.Rank() == 0) {
|
|
|
|
// Optional DIM= argument is present: result is scalar.
|
|
|
|
if (auto dim64{GetInt64Arg(args[1])}) {
|
|
|
|
if (*dim64 < 1 || *dim64 > rank) {
|
|
|
|
context.messages().Say("DIM=%jd dimension is out of range for "
|
2021-02-02 07:26:48 +08:00
|
|
|
"rank-%d array"_err_en_US,
|
2020-04-04 08:28:23 +08:00
|
|
|
*dim64, rank);
|
2020-01-01 04:20:42 +08:00
|
|
|
return MakeInvalidIntrinsic<T>(std::move(funcRef));
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
dim = *dim64 - 1; // 1-based to 0-based
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// DIM= is present but not constant
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool lowerBoundsAreOne{true};
|
|
|
|
if (auto named{ExtractNamedEntity(*array)}) {
|
|
|
|
const Symbol &symbol{named->GetLastSymbol()};
|
|
|
|
if (symbol.Rank() == rank) {
|
|
|
|
lowerBoundsAreOne = false;
|
|
|
|
if (dim) {
|
2022-03-10 05:43:54 +08:00
|
|
|
if (auto lb{GetLBOUND(context, *named, *dim)}) {
|
|
|
|
return Fold(context, ConvertToType<T>(std::move(*lb)));
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (auto extents{
|
2022-03-10 05:43:54 +08:00
|
|
|
AsExtentArrayExpr(GetLBOUNDs(context, *named))}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
return Fold(context,
|
|
|
|
ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
|
|
|
|
}
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
lowerBoundsAreOne = symbol.Rank() == 0; // LBOUND(array%component)
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-03 06:15:50 +08:00
|
|
|
if (IsActuallyConstant(*array)) {
|
2022-04-27 21:14:10 +08:00
|
|
|
return GetConstantArrayBoundHelper::GetLbound<T>(*array, dim);
|
2021-02-03 06:15:50 +08:00
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
if (lowerBoundsAreOne) {
|
2022-04-21 00:47:21 +08:00
|
|
|
ConstantSubscripts ones(rank, ConstantSubscript{1});
|
|
|
|
return PackageConstantBounds<T>(std::move(ones), dim.has_value());
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <int KIND>
|
2020-01-01 04:20:42 +08:00
|
|
|
Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
|
|
|
|
FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
|
|
|
|
using T = Type<TypeCategory::Integer, KIND>;
|
|
|
|
ActualArguments &args{funcRef.arguments()};
|
|
|
|
if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
|
|
|
|
if (int rank{array->Rank()}; rank > 0) {
|
|
|
|
std::optional<int> dim;
|
|
|
|
if (funcRef.Rank() == 0) {
|
|
|
|
// Optional DIM= argument is present: result is scalar.
|
|
|
|
if (auto dim64{GetInt64Arg(args[1])}) {
|
|
|
|
if (*dim64 < 1 || *dim64 > rank) {
|
|
|
|
context.messages().Say("DIM=%jd dimension is out of range for "
|
2021-02-02 07:26:48 +08:00
|
|
|
"rank-%d array"_err_en_US,
|
2020-04-04 08:28:23 +08:00
|
|
|
*dim64, rank);
|
2020-01-01 04:20:42 +08:00
|
|
|
return MakeInvalidIntrinsic<T>(std::move(funcRef));
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
dim = *dim64 - 1; // 1-based to 0-based
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// DIM= is present but not constant
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool takeBoundsFromShape{true};
|
|
|
|
if (auto named{ExtractNamedEntity(*array)}) {
|
|
|
|
const Symbol &symbol{named->GetLastSymbol()};
|
|
|
|
if (symbol.Rank() == rank) {
|
|
|
|
takeBoundsFromShape = false;
|
|
|
|
if (dim) {
|
2021-02-02 07:26:48 +08:00
|
|
|
if (semantics::IsAssumedSizeArray(symbol) && *dim == rank - 1) {
|
|
|
|
context.messages().Say("DIM=%jd dimension is out of range for "
|
|
|
|
"rank-%d assumed-size array"_err_en_US,
|
|
|
|
rank, rank);
|
|
|
|
return MakeInvalidIntrinsic<T>(std::move(funcRef));
|
2022-03-24 16:06:59 +08:00
|
|
|
} else if (auto ub{GetUBOUND(context, *named, *dim)}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
return Fold(context, ConvertToType<T>(std::move(*ub)));
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-24 16:06:59 +08:00
|
|
|
Shape ubounds{GetUBOUNDs(context, *named)};
|
2020-01-01 04:20:42 +08:00
|
|
|
if (semantics::IsAssumedSizeArray(symbol)) {
|
|
|
|
CHECK(!ubounds.back());
|
|
|
|
ubounds.back() = ExtentExpr{-1};
|
|
|
|
}
|
|
|
|
if (auto extents{AsExtentArrayExpr(ubounds)}) {
|
|
|
|
return Fold(context,
|
|
|
|
ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
takeBoundsFromShape = symbol.Rank() == 0; // UBOUND(array%component)
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
2022-04-27 21:14:10 +08:00
|
|
|
if (IsActuallyConstant(*array)) {
|
|
|
|
return GetConstantArrayBoundHelper::GetUbound<T>(*array, dim);
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
if (takeBoundsFromShape) {
|
2022-01-11 02:09:45 +08:00
|
|
|
if (auto shape{GetContextFreeShape(context, *array)}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
if (dim) {
|
|
|
|
if (auto &dimSize{shape->at(*dim)}) {
|
|
|
|
return Fold(context,
|
|
|
|
ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)}));
|
|
|
|
}
|
|
|
|
} else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
|
|
|
|
return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
|
2021-09-11 06:55:55 +08:00
|
|
|
// COUNT()
|
|
|
|
template <typename T>
|
|
|
|
static Expr<T> FoldCount(FoldingContext &context, FunctionRef<T> &&ref) {
|
|
|
|
static_assert(T::category == TypeCategory::Integer);
|
|
|
|
ActualArguments &arg{ref.arguments()};
|
|
|
|
if (const Constant<LogicalResult> *mask{arg.empty()
|
|
|
|
? nullptr
|
|
|
|
: Folder<LogicalResult>{context}.Folding(arg[0])}) {
|
2021-09-29 00:56:38 +08:00
|
|
|
std::optional<int> dim;
|
|
|
|
if (CheckReductionDIM(dim, context, arg, 1, mask->Rank())) {
|
2021-09-11 06:55:55 +08:00
|
|
|
auto accumulator{[&](Scalar<T> &element, const ConstantSubscripts &at) {
|
|
|
|
if (mask->At(at).IsTrue()) {
|
|
|
|
element = element.AddSigned(Scalar<T>{1}).value;
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
return Expr<T>{DoReduction<T>(*mask, dim, Scalar<T>{}, accumulator)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Expr<T>{std::move(ref)};
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:42:22 +08:00
|
|
|
// FINDLOC(), MAXLOC(), & MINLOC()
|
|
|
|
enum class WhichLocation { Findloc, Maxloc, Minloc };
|
|
|
|
template <WhichLocation WHICH> class LocationHelper {
|
2021-09-29 00:56:38 +08:00
|
|
|
public:
|
2021-09-30 07:42:22 +08:00
|
|
|
LocationHelper(
|
2021-09-29 00:56:38 +08:00
|
|
|
DynamicType &&type, ActualArguments &arg, FoldingContext &context)
|
|
|
|
: type_{type}, arg_{arg}, context_{context} {}
|
|
|
|
using Result = std::optional<Constant<SubscriptInteger>>;
|
2021-09-30 07:42:22 +08:00
|
|
|
using Types = std::conditional_t<WHICH == WhichLocation::Findloc,
|
|
|
|
AllIntrinsicTypes, RelationalTypes>;
|
2021-09-29 00:56:38 +08:00
|
|
|
|
|
|
|
template <typename T> Result Test() const {
|
|
|
|
if (T::category != type_.category() || T::kind != type_.kind()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
CHECK(arg_.size() == (WHICH == WhichLocation::Findloc ? 6 : 5));
|
2021-09-29 00:56:38 +08:00
|
|
|
Folder<T> folder{context_};
|
|
|
|
Constant<T> *array{folder.Folding(arg_[0])};
|
2021-09-30 07:42:22 +08:00
|
|
|
if (!array) {
|
2021-09-29 00:56:38 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
std::optional<Constant<T>> value;
|
|
|
|
if constexpr (WHICH == WhichLocation::Findloc) {
|
|
|
|
if (const Constant<T> *p{folder.Folding(arg_[1])}) {
|
|
|
|
value.emplace(*p);
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2021-09-29 00:56:38 +08:00
|
|
|
std::optional<int> dim;
|
|
|
|
Constant<LogicalResult> *mask{
|
2021-09-30 07:42:22 +08:00
|
|
|
GetReductionMASK(arg_[maskArg], array->shape(), context_)};
|
|
|
|
if ((!mask && arg_[maskArg]) ||
|
|
|
|
!CheckReductionDIM(dim, context_, arg_, dimArg, array->Rank())) {
|
2021-09-29 00:56:38 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
bool back{false};
|
2021-09-30 07:42:22 +08:00
|
|
|
if (arg_[backArg]) {
|
|
|
|
const auto *backConst{
|
|
|
|
Folder<LogicalResult>{context_}.Folding(arg_[backArg])};
|
2021-09-29 00:56:38 +08:00
|
|
|
if (backConst) {
|
|
|
|
back = backConst->GetScalarValue().value().IsTrue();
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
const RelationalOperator relation{WHICH == WhichLocation::Findloc
|
|
|
|
? RelationalOperator::EQ
|
|
|
|
: WHICH == WhichLocation::Maxloc
|
|
|
|
? (back ? RelationalOperator::GE : RelationalOperator::GT)
|
|
|
|
: back ? RelationalOperator::LE
|
|
|
|
: RelationalOperator::LT};
|
2021-09-29 00:56:38 +08:00
|
|
|
// Use lower bounds of 1 exclusively.
|
|
|
|
array->SetLowerBoundsToOne();
|
|
|
|
ConstantSubscripts at{array->lbounds()}, maskAt, resultIndices, resultShape;
|
|
|
|
if (mask) {
|
|
|
|
mask->SetLowerBoundsToOne();
|
|
|
|
maskAt = mask->lbounds();
|
|
|
|
}
|
|
|
|
if (dim) { // DIM=
|
|
|
|
if (*dim < 1 || *dim > array->Rank()) {
|
2021-10-28 02:45:11 +08:00
|
|
|
context_.messages().Say("DIM=%d is out of range"_err_en_US, *dim);
|
2021-09-29 00:56:38 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
int zbDim{*dim - 1};
|
|
|
|
resultShape = array->shape();
|
|
|
|
resultShape.erase(
|
|
|
|
resultShape.begin() + zbDim); // scalar if array is vector
|
|
|
|
ConstantSubscript dimLength{array->shape()[zbDim]};
|
|
|
|
ConstantSubscript n{GetSize(resultShape)};
|
|
|
|
for (ConstantSubscript j{0}; j < n; ++j) {
|
2021-10-28 02:45:11 +08:00
|
|
|
ConstantSubscript hit{0};
|
|
|
|
if constexpr (WHICH == WhichLocation::Maxloc ||
|
|
|
|
WHICH == WhichLocation::Minloc) {
|
|
|
|
value.reset();
|
|
|
|
}
|
2021-09-29 00:56:38 +08:00
|
|
|
for (ConstantSubscript k{0}; k < dimLength;
|
|
|
|
++k, ++at[zbDim], mask && ++maskAt[zbDim]) {
|
|
|
|
if ((!mask || mask->At(maskAt).IsTrue()) &&
|
2021-09-30 07:42:22 +08:00
|
|
|
IsHit(array->At(at), value, relation)) {
|
2021-09-29 00:56:38 +08:00
|
|
|
hit = at[zbDim];
|
2021-10-28 02:45:11 +08:00
|
|
|
if constexpr (WHICH == WhichLocation::Findloc) {
|
|
|
|
if (!back) {
|
|
|
|
break;
|
|
|
|
}
|
2021-09-29 00:56:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resultIndices.emplace_back(hit);
|
2022-02-19 06:08:57 +08:00
|
|
|
at[zbDim] = std::max<ConstantSubscript>(dimLength, 1);
|
2021-09-29 00:56:38 +08:00
|
|
|
array->IncrementSubscripts(at);
|
2021-10-28 02:45:11 +08:00
|
|
|
at[zbDim] = 1;
|
2021-09-29 00:56:38 +08:00
|
|
|
if (mask) {
|
2022-02-19 06:08:57 +08:00
|
|
|
maskAt[zbDim] = mask->lbounds()[zbDim] +
|
|
|
|
std::max<ConstantSubscript>(dimLength, 1) - 1;
|
2021-09-29 00:56:38 +08:00
|
|
|
mask->IncrementSubscripts(maskAt);
|
|
|
|
maskAt[zbDim] = mask->lbounds()[zbDim];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // no DIM=
|
|
|
|
resultShape = ConstantSubscripts{array->Rank()}; // always a vector
|
|
|
|
ConstantSubscript n{GetSize(array->shape())};
|
|
|
|
resultIndices = ConstantSubscripts(array->Rank(), 0);
|
|
|
|
for (ConstantSubscript j{0}; j < n; ++j, array->IncrementSubscripts(at),
|
|
|
|
mask && mask->IncrementSubscripts(maskAt)) {
|
|
|
|
if ((!mask || mask->At(maskAt).IsTrue()) &&
|
2021-09-30 07:42:22 +08:00
|
|
|
IsHit(array->At(at), value, relation)) {
|
2021-09-29 00:56:38 +08:00
|
|
|
resultIndices = at;
|
2021-10-28 02:45:11 +08:00
|
|
|
if constexpr (WHICH == WhichLocation::Findloc) {
|
|
|
|
if (!back) {
|
|
|
|
break;
|
|
|
|
}
|
2021-09-29 00:56:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<Scalar<SubscriptInteger>> resultElements;
|
|
|
|
for (ConstantSubscript j : resultIndices) {
|
|
|
|
resultElements.emplace_back(j);
|
|
|
|
}
|
|
|
|
return Constant<SubscriptInteger>{
|
|
|
|
std::move(resultElements), std::move(resultShape)};
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename T>
|
2021-09-30 07:42:22 +08:00
|
|
|
bool IsHit(typename Constant<T>::Element element,
|
|
|
|
std::optional<Constant<T>> &value,
|
|
|
|
[[maybe_unused]] RelationalOperator relation) const {
|
2021-09-29 00:56:38 +08:00
|
|
|
std::optional<Expr<LogicalResult>> cmp;
|
2021-10-28 02:45:11 +08:00
|
|
|
bool result{true};
|
2021-09-30 07:42:22 +08:00
|
|
|
if (value) {
|
|
|
|
if constexpr (T::category == TypeCategory::Logical) {
|
|
|
|
// array(at) .EQV. value?
|
|
|
|
static_assert(WHICH == WhichLocation::Findloc);
|
2022-03-29 01:05:36 +08:00
|
|
|
cmp.emplace(ConvertToType<LogicalResult>(
|
|
|
|
Expr<T>{LogicalOperation<T::kind>{LogicalOperator::Eqv,
|
|
|
|
Expr<T>{Constant<T>{element}}, Expr<T>{Constant<T>{*value}}}}));
|
2021-09-30 07:42:22 +08:00
|
|
|
} else { // compare array(at) to value
|
2022-03-29 01:05:36 +08:00
|
|
|
cmp.emplace(PackageRelation(relation, Expr<T>{Constant<T>{element}},
|
|
|
|
Expr<T>{Constant<T>{*value}}));
|
2021-09-30 07:42:22 +08:00
|
|
|
}
|
|
|
|
Expr<LogicalResult> folded{Fold(context_, std::move(*cmp))};
|
2021-10-28 02:45:11 +08:00
|
|
|
result = GetScalarConstantValue<LogicalResult>(folded).value().IsTrue();
|
|
|
|
} else {
|
|
|
|
// first unmasked element for MAXLOC/MINLOC - always take it
|
|
|
|
}
|
|
|
|
if constexpr (WHICH == WhichLocation::Maxloc ||
|
|
|
|
WHICH == WhichLocation::Minloc) {
|
|
|
|
if (result) {
|
|
|
|
value.emplace(std::move(element));
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
}
|
2021-10-28 02:45:11 +08:00
|
|
|
return result;
|
2021-09-29 00:56:38 +08:00
|
|
|
}
|
|
|
|
|
2021-09-30 07:42:22 +08:00
|
|
|
static constexpr int dimArg{WHICH == WhichLocation::Findloc ? 2 : 1};
|
|
|
|
static constexpr int maskArg{dimArg + 1};
|
|
|
|
static constexpr int backArg{maskArg + 2};
|
|
|
|
|
2021-09-29 00:56:38 +08:00
|
|
|
DynamicType type_;
|
|
|
|
ActualArguments &arg_;
|
|
|
|
FoldingContext &context_;
|
|
|
|
};
|
|
|
|
|
2021-09-30 07:42:22 +08:00
|
|
|
template <WhichLocation which>
|
|
|
|
static std::optional<Constant<SubscriptInteger>> FoldLocationCall(
|
2021-09-29 00:56:38 +08:00
|
|
|
ActualArguments &arg, FoldingContext &context) {
|
|
|
|
if (arg[0]) {
|
|
|
|
if (auto type{arg[0]->GetType()}) {
|
2021-09-30 07:42:22 +08:00
|
|
|
return common::SearchTypes(
|
|
|
|
LocationHelper<which>{std::move(*type), arg, context});
|
2021-09-29 00:56:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:42:22 +08:00
|
|
|
template <WhichLocation which, typename T>
|
|
|
|
static Expr<T> FoldLocation(FoldingContext &context, FunctionRef<T> &&ref) {
|
2021-09-29 00:56:38 +08:00
|
|
|
static_assert(T::category == TypeCategory::Integer);
|
|
|
|
if (std::optional<Constant<SubscriptInteger>> found{
|
2021-09-30 07:42:22 +08:00
|
|
|
FoldLocationCall<which>(ref.arguments(), context)}) {
|
2021-09-29 00:56:38 +08:00
|
|
|
return Expr<T>{Fold(
|
|
|
|
context, ConvertToType<T>(Expr<SubscriptInteger>{std::move(*found)}))};
|
|
|
|
} else {
|
|
|
|
return Expr<T>{std::move(ref)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-19 02:24:32 +08:00
|
|
|
// for IALL, IANY, & IPARITY
|
|
|
|
template <typename T>
|
|
|
|
static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref,
|
|
|
|
Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
|
|
|
|
Scalar<T> identity) {
|
|
|
|
static_assert(T::category == TypeCategory::Integer);
|
2021-09-29 00:56:38 +08:00
|
|
|
std::optional<int> dim;
|
2021-06-19 02:24:32 +08:00
|
|
|
if (std::optional<Constant<T>> array{
|
|
|
|
ProcessReductionArgs<T>(context, ref.arguments(), dim, identity,
|
|
|
|
/*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) {
|
2021-09-11 06:55:55 +08:00
|
|
|
auto accumulator{[&](Scalar<T> &element, const ConstantSubscripts &at) {
|
2021-06-19 02:24:32 +08:00
|
|
|
element = (element.*operation)(array->At(at));
|
|
|
|
}};
|
2021-09-11 06:55:55 +08:00
|
|
|
return Expr<T>{DoReduction<T>(*array, dim, identity, accumulator)};
|
2021-06-19 02:24:32 +08:00
|
|
|
}
|
|
|
|
return Expr<T>{std::move(ref)};
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <int KIND>
|
2020-01-01 04:20:42 +08:00
|
|
|
Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
|
|
|
|
FoldingContext &context,
|
|
|
|
FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
|
|
|
|
using T = Type<TypeCategory::Integer, KIND>;
|
|
|
|
using Int4 = Type<TypeCategory::Integer, 4>;
|
|
|
|
ActualArguments &args{funcRef.arguments()};
|
|
|
|
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
|
|
|
|
CHECK(intrinsic);
|
|
|
|
std::string name{intrinsic->name};
|
2022-01-06 01:54:16 +08:00
|
|
|
if (name == "abs") { // incl. babs, iiabs, jiaabs, & kiabs
|
2020-01-01 04:20:42 +08:00
|
|
|
return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> {
|
|
|
|
typename Scalar<T>::ValueWithOverflow j{i.ABS()};
|
|
|
|
if (j.overflow) {
|
|
|
|
context.messages().Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"abs(integer(kind=%d)) folding overflowed"_warn_en_US, KIND);
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
return j.value;
|
|
|
|
}));
|
|
|
|
} else if (name == "bit_size") {
|
|
|
|
return Expr<T>{Scalar<T>::bits};
|
2020-01-04 03:34:16 +08:00
|
|
|
} else if (name == "ceiling" || name == "floor" || name == "nint") {
|
|
|
|
if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
|
|
|
// NINT rounds ties away from zero, not to even
|
2020-07-02 07:51:44 +08:00
|
|
|
common::RoundingMode mode{name == "ceiling" ? common::RoundingMode::Up
|
|
|
|
: name == "floor" ? common::RoundingMode::Down
|
2020-01-10 00:10:57 +08:00
|
|
|
: common::RoundingMode::TiesAwayFromZero};
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-04 03:34:16 +08:00
|
|
|
[&](const auto &kx) {
|
|
|
|
using TR = ResultType<decltype(kx)>;
|
|
|
|
return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, TR>([&](const Scalar<TR> &x) {
|
|
|
|
auto y{x.template ToInteger<Scalar<T>>(mode)};
|
|
|
|
if (y.flags.test(RealFlag::Overflow)) {
|
|
|
|
context.messages().Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"%s intrinsic folding overflow"_warn_en_US, name);
|
2020-01-04 03:34:16 +08:00
|
|
|
}
|
|
|
|
return y.value;
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
cx->u);
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "count") {
|
2021-09-11 06:55:55 +08:00
|
|
|
return FoldCount<T>(context, std::move(funcRef));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "digits") {
|
|
|
|
if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<ResultType<decltype(kx)>>::DIGITS;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
} else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<ResultType<decltype(kx)>>::DIGITS;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
}
|
|
|
|
} else if (name == "dim") {
|
|
|
|
return FoldElementalIntrinsic<T, T, T>(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::DIM);
|
|
|
|
} else if (name == "dshiftl" || name == "dshiftr") {
|
|
|
|
const auto fptr{
|
|
|
|
name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR};
|
|
|
|
// Third argument can be of any kind. However, it must be smaller or equal
|
|
|
|
// than BIT_SIZE. It can be converted to Int4 to simplify.
|
|
|
|
return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T, T, Int4>(
|
|
|
|
[&fptr](const Scalar<T> &i, const Scalar<T> &j,
|
|
|
|
const Scalar<Int4> &shift) -> Scalar<T> {
|
|
|
|
return std::invoke(fptr, i, j, static_cast<int>(shift.ToInt64()));
|
|
|
|
}));
|
|
|
|
} else if (name == "exponent") {
|
|
|
|
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[&funcRef, &context](const auto &x) -> Expr<T> {
|
|
|
|
using TR = typename std::decay_t<decltype(x)>::Result;
|
|
|
|
return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
|
|
|
|
&Scalar<TR>::template EXPONENT<Scalar<T>>);
|
|
|
|
},
|
|
|
|
sx->u);
|
|
|
|
} else {
|
2020-01-07 07:29:53 +08:00
|
|
|
DIE("exponent argument must be real");
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
2021-09-29 00:56:38 +08:00
|
|
|
} else if (name == "findloc") {
|
2021-09-30 07:42:22 +08:00
|
|
|
return FoldLocation<WhichLocation::Findloc, T>(context, std::move(funcRef));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "huge") {
|
|
|
|
return Expr<T>{Scalar<T>::HUGE()};
|
|
|
|
} else if (name == "iachar" || name == "ichar") {
|
|
|
|
auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])};
|
|
|
|
CHECK(someChar);
|
|
|
|
if (auto len{ToInt64(someChar->LEN())}) {
|
|
|
|
if (len.value() != 1) {
|
|
|
|
// Do not die, this was not checked before
|
|
|
|
context.messages().Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"Character in intrinsic function %s must have length one"_warn_en_US,
|
2020-01-01 04:20:42 +08:00
|
|
|
name);
|
|
|
|
} else {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[&funcRef, &context](const auto &str) -> Expr<T> {
|
|
|
|
using Char = typename std::decay_t<decltype(str)>::Result;
|
|
|
|
return FoldElementalIntrinsic<T, Char>(context,
|
|
|
|
std::move(funcRef),
|
|
|
|
ScalarFunc<T, Char>([](const Scalar<Char> &c) {
|
|
|
|
return Scalar<T>{CharacterUtils<Char::kind>::ICHAR(c)};
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
someChar->u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == "iand" || name == "ior" || name == "ieor") {
|
|
|
|
auto fptr{&Scalar<T>::IAND};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (name == "iand") { // done in fptr declaration
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "ior") {
|
|
|
|
fptr = &Scalar<T>::IOR;
|
|
|
|
} else if (name == "ieor") {
|
|
|
|
fptr = &Scalar<T>::IEOR;
|
|
|
|
} else {
|
|
|
|
common::die("missing case to fold intrinsic function %s", name.c_str());
|
|
|
|
}
|
|
|
|
return FoldElementalIntrinsic<T, T, T>(
|
|
|
|
context, std::move(funcRef), ScalarFunc<T, T, T>(fptr));
|
2021-06-19 02:24:32 +08:00
|
|
|
} else if (name == "iall") {
|
|
|
|
return FoldBitReduction(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::IAND, Scalar<T>{}.NOT());
|
|
|
|
} else if (name == "iany") {
|
|
|
|
return FoldBitReduction(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::IOR, Scalar<T>{});
|
2021-10-07 07:29:00 +08:00
|
|
|
} else if (name == "ibclr" || name == "ibset") {
|
|
|
|
// Second argument can be of any kind. However, it must be smaller
|
|
|
|
// than BIT_SIZE. It can be converted to Int4 to simplify.
|
2020-01-01 04:20:42 +08:00
|
|
|
auto fptr{&Scalar<T>::IBCLR};
|
2021-10-07 07:29:00 +08:00
|
|
|
if (name == "ibclr") { // done in fptr definition
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "ibset") {
|
|
|
|
fptr = &Scalar<T>::IBSET;
|
|
|
|
} else {
|
|
|
|
common::die("missing case to fold intrinsic function %s", name.c_str());
|
|
|
|
}
|
|
|
|
return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
|
2021-10-07 07:29:00 +08:00
|
|
|
ScalarFunc<T, T, Int4>([&](const Scalar<T> &i,
|
|
|
|
const Scalar<Int4> &pos) -> Scalar<T> {
|
|
|
|
auto posVal{static_cast<int>(pos.ToInt64())};
|
|
|
|
if (posVal < 0) {
|
|
|
|
context.messages().Say(
|
|
|
|
"bit position for %s (%d) is negative"_err_en_US, name, posVal);
|
|
|
|
} else if (posVal >= i.bits) {
|
|
|
|
context.messages().Say(
|
|
|
|
"bit position for %s (%d) is not less than %d"_err_en_US, name,
|
|
|
|
posVal, i.bits);
|
|
|
|
}
|
|
|
|
return std::invoke(fptr, i, posVal);
|
|
|
|
}));
|
2022-04-01 04:36:09 +08:00
|
|
|
} else if (name == "ibits") {
|
|
|
|
return FoldElementalIntrinsic<T, T, Int4, Int4>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T, Int4, Int4>([&](const Scalar<T> &i,
|
|
|
|
const Scalar<Int4> &pos,
|
|
|
|
const Scalar<Int4> &len) -> Scalar<T> {
|
|
|
|
auto posVal{static_cast<int>(pos.ToInt64())};
|
|
|
|
auto lenVal{static_cast<int>(len.ToInt64())};
|
|
|
|
if (posVal < 0) {
|
|
|
|
context.messages().Say(
|
|
|
|
"bit position for IBITS(POS=%d,LEN=%d) is negative"_err_en_US,
|
|
|
|
posVal, lenVal);
|
|
|
|
} else if (lenVal < 0) {
|
|
|
|
context.messages().Say(
|
|
|
|
"bit length for IBITS(POS=%d,LEN=%d) is negative"_err_en_US,
|
|
|
|
posVal, lenVal);
|
|
|
|
} else if (posVal + lenVal > i.bits) {
|
|
|
|
context.messages().Say(
|
|
|
|
"IBITS(POS=%d,LEN=%d) must have POS+LEN no greater than %d"_err_en_US,
|
|
|
|
posVal + lenVal, i.bits);
|
|
|
|
}
|
|
|
|
return i.IBITS(posVal, lenVal);
|
|
|
|
}));
|
2020-01-07 07:29:53 +08:00
|
|
|
} else if (name == "index" || name == "scan" || name == "verify") {
|
|
|
|
if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-07 07:29:53 +08:00
|
|
|
[&](const auto &kch) -> Expr<T> {
|
|
|
|
using TC = typename std::decay_t<decltype(kch)>::Result;
|
2020-03-29 12:00:16 +08:00
|
|
|
if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK=
|
2020-01-07 07:29:53 +08:00
|
|
|
return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context,
|
|
|
|
std::move(funcRef),
|
|
|
|
ScalarFunc<T, TC, TC, LogicalResult>{
|
|
|
|
[&name](const Scalar<TC> &str, const Scalar<TC> &other,
|
|
|
|
const Scalar<LogicalResult> &back) -> Scalar<T> {
|
|
|
|
return name == "index"
|
|
|
|
? CharacterUtils<TC::kind>::INDEX(
|
|
|
|
str, other, back.IsTrue())
|
|
|
|
: name == "scan" ? CharacterUtils<TC::kind>::SCAN(
|
|
|
|
str, other, back.IsTrue())
|
|
|
|
: CharacterUtils<TC::kind>::VERIFY(
|
|
|
|
str, other, back.IsTrue());
|
|
|
|
}});
|
|
|
|
} else {
|
|
|
|
return FoldElementalIntrinsic<T, TC, TC>(context,
|
|
|
|
std::move(funcRef),
|
|
|
|
ScalarFunc<T, TC, TC>{
|
|
|
|
[&name](const Scalar<TC> &str,
|
|
|
|
const Scalar<TC> &other) -> Scalar<T> {
|
|
|
|
return name == "index"
|
|
|
|
? CharacterUtils<TC::kind>::INDEX(str, other)
|
|
|
|
: name == "scan"
|
2020-07-02 07:51:44 +08:00
|
|
|
? CharacterUtils<TC::kind>::SCAN(str, other)
|
|
|
|
: CharacterUtils<TC::kind>::VERIFY(str, other);
|
2020-01-07 07:29:53 +08:00
|
|
|
}});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
charExpr->u);
|
|
|
|
} else {
|
|
|
|
DIE("first argument must be CHARACTER");
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "int") {
|
|
|
|
if (auto *expr{UnwrapExpr<Expr<SomeType>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[&](auto &&x) -> Expr<T> {
|
|
|
|
using From = std::decay_t<decltype(x)>;
|
|
|
|
if constexpr (std::is_same_v<From, BOZLiteralConstant> ||
|
|
|
|
IsNumericCategoryExpr<From>()) {
|
|
|
|
return Fold(context, ConvertToType<T>(std::move(x)));
|
|
|
|
}
|
2020-01-07 07:29:53 +08:00
|
|
|
DIE("int() argument type not valid");
|
2020-01-01 04:20:42 +08:00
|
|
|
},
|
|
|
|
std::move(expr->u));
|
|
|
|
}
|
|
|
|
} else if (name == "int_ptr_kind") {
|
|
|
|
return Expr<T>{8};
|
|
|
|
} else if (name == "kind") {
|
|
|
|
if constexpr (common::HasMember<T, IntegerTypes>) {
|
|
|
|
return Expr<T>{args[0].value().GetType()->kind()};
|
|
|
|
} else {
|
2020-01-07 07:29:53 +08:00
|
|
|
DIE("kind() result not integral");
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
2021-06-19 02:24:32 +08:00
|
|
|
} else if (name == "iparity") {
|
|
|
|
return FoldBitReduction(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{});
|
2021-10-21 01:37:09 +08:00
|
|
|
} else if (name == "ishft") {
|
2021-10-07 07:29:00 +08:00
|
|
|
return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T, Int4>([&](const Scalar<T> &i,
|
|
|
|
const Scalar<Int4> &pos) -> Scalar<T> {
|
|
|
|
auto posVal{static_cast<int>(pos.ToInt64())};
|
2021-10-21 01:37:09 +08:00
|
|
|
if (posVal < -i.bits) {
|
2021-10-07 07:29:00 +08:00
|
|
|
context.messages().Say(
|
2021-10-21 01:37:09 +08:00
|
|
|
"SHIFT=%d count for ishft is less than %d"_err_en_US, posVal,
|
|
|
|
-i.bits);
|
2021-10-07 07:29:00 +08:00
|
|
|
} else if (posVal > i.bits) {
|
|
|
|
context.messages().Say(
|
2021-10-21 01:37:09 +08:00
|
|
|
"SHIFT=%d count for ishft is greater than %d"_err_en_US, posVal,
|
|
|
|
i.bits);
|
2021-10-07 07:29:00 +08:00
|
|
|
}
|
2021-10-21 01:37:09 +08:00
|
|
|
return i.ISHFT(posVal);
|
2021-10-07 07:29:00 +08:00
|
|
|
}));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "lbound") {
|
|
|
|
return LBOUND(context, std::move(funcRef));
|
|
|
|
} else if (name == "leadz" || name == "trailz" || name == "poppar" ||
|
|
|
|
name == "popcnt") {
|
|
|
|
if (auto *sn{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[&funcRef, &context, &name](const auto &n) -> Expr<T> {
|
|
|
|
using TI = typename std::decay_t<decltype(n)>::Result;
|
|
|
|
if (name == "poppar") {
|
|
|
|
return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> {
|
|
|
|
return Scalar<T>{i.POPPAR() ? 1 : 0};
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
auto fptr{&Scalar<TI>::LEADZ};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (name == "leadz") { // done in fptr definition
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "trailz") {
|
|
|
|
fptr = &Scalar<TI>::TRAILZ;
|
|
|
|
} else if (name == "popcnt") {
|
|
|
|
fptr = &Scalar<TI>::POPCNT;
|
|
|
|
} else {
|
|
|
|
common::die(
|
|
|
|
"missing case to fold intrinsic function %s", name.c_str());
|
|
|
|
}
|
|
|
|
return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, TI>([&fptr](const Scalar<TI> &i) -> Scalar<T> {
|
|
|
|
return Scalar<T>{std::invoke(fptr, i)};
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
sn->u);
|
|
|
|
} else {
|
2020-01-07 07:29:53 +08:00
|
|
|
DIE("leadz argument must be integer");
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
} else if (name == "len") {
|
|
|
|
if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[&](auto &kx) {
|
|
|
|
if (auto len{kx.LEN()}) {
|
2022-02-10 05:11:05 +08:00
|
|
|
if (IsScopeInvariantExpr(*len)) {
|
|
|
|
return Fold(context, ConvertToType<T>(*std::move(len)));
|
|
|
|
} else {
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
} else {
|
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
charExpr->u);
|
|
|
|
} else {
|
2020-01-07 07:29:53 +08:00
|
|
|
DIE("len() argument must be of character type");
|
|
|
|
}
|
|
|
|
} else if (name == "len_trim") {
|
|
|
|
if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-07 07:29:53 +08:00
|
|
|
[&](const auto &kch) -> Expr<T> {
|
|
|
|
using TC = typename std::decay_t<decltype(kch)>::Result;
|
|
|
|
return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, TC>{[](const Scalar<TC> &str) -> Scalar<T> {
|
|
|
|
return CharacterUtils<TC::kind>::LEN_TRIM(str);
|
|
|
|
}});
|
|
|
|
},
|
|
|
|
charExpr->u);
|
|
|
|
} else {
|
|
|
|
DIE("len_trim() argument must be of character type");
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
} else if (name == "maskl" || name == "maskr") {
|
|
|
|
// Argument can be of any kind but value has to be smaller than BIT_SIZE.
|
|
|
|
// It can be safely converted to Int4 to simplify.
|
|
|
|
const auto fptr{name == "maskl" ? &Scalar<T>::MASKL : &Scalar<T>::MASKR};
|
|
|
|
return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> {
|
|
|
|
return fptr(static_cast<int>(places.ToInt64()));
|
|
|
|
}));
|
|
|
|
} else if (name == "max") {
|
|
|
|
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
|
[flang] AMAX0, MIN1... rewrite to MAX/MIN: make result conversion explicit
Summary:
This patch changes speficic extremum functions rewrite to generic MIN/MAX.
It applies to AMAX0, AMIN0, AMAX1, AMIN1, MAX0, MIN0, MAX1, MIN1, DMAX1,
and DMIN1.
- Do not re-write specific extremums to MAX/MIN in intrinsic Probe and let
folding rewrite it and introduc the conversion on the MIN/MAX result.
- Also make operand promotion explicit in MIN/MAX folding.
For instance, after this patch:
AMAX0(int8, int4) is rewritten to REAL(MAX(int8, INT(int4, 8)))
All this care is to avoid rewritting it to MAX(REAL(int8), REAL(int4))
that may not always be numerically equivalent to the first rewrite.
Reviewers: klausler, schweitz, sscalpone, jdoerfert, DavidTruby
Reviewed By: klausler, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D81940
2020-06-18 13:46:19 +08:00
|
|
|
} else if (name == "max0" || name == "max1") {
|
|
|
|
return RewriteSpecificMINorMAX(context, std::move(funcRef));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "maxexponent") {
|
|
|
|
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &x) {
|
|
|
|
using TR = typename std::decay_t<decltype(x)>::Result;
|
|
|
|
return Expr<T>{Scalar<TR>::MAXEXPONENT};
|
|
|
|
},
|
|
|
|
sx->u);
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
} else if (name == "maxloc") {
|
|
|
|
return FoldLocation<WhichLocation::Maxloc, T>(context, std::move(funcRef));
|
2021-06-16 06:18:41 +08:00
|
|
|
} else if (name == "maxval") {
|
|
|
|
return FoldMaxvalMinval<T>(context, std::move(funcRef),
|
|
|
|
RelationalOperator::GT, T::Scalar::Least());
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "merge") {
|
|
|
|
return FoldMerge<T>(context, std::move(funcRef));
|
|
|
|
} else if (name == "merge_bits") {
|
|
|
|
return FoldElementalIntrinsic<T, T, T, T>(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::MERGE_BITS);
|
2021-09-30 07:42:22 +08:00
|
|
|
} else if (name == "min") {
|
|
|
|
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
|
|
|
|
} else if (name == "min0" || name == "min1") {
|
|
|
|
return RewriteSpecificMINorMAX(context, std::move(funcRef));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "minexponent") {
|
|
|
|
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &x) {
|
|
|
|
using TR = typename std::decay_t<decltype(x)>::Result;
|
|
|
|
return Expr<T>{Scalar<TR>::MINEXPONENT};
|
|
|
|
},
|
|
|
|
sx->u);
|
|
|
|
}
|
2021-09-30 07:42:22 +08:00
|
|
|
} else if (name == "minloc") {
|
|
|
|
return FoldLocation<WhichLocation::Minloc, T>(context, std::move(funcRef));
|
2021-06-16 06:18:41 +08:00
|
|
|
} else if (name == "minval") {
|
|
|
|
return FoldMaxvalMinval<T>(
|
|
|
|
context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE());
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "mod") {
|
|
|
|
return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
|
|
|
|
ScalarFuncWithContext<T, T, T>(
|
|
|
|
[](FoldingContext &context, const Scalar<T> &x,
|
|
|
|
const Scalar<T> &y) -> Scalar<T> {
|
|
|
|
auto quotRem{x.DivideSigned(y)};
|
|
|
|
if (quotRem.divisionByZero) {
|
2022-03-08 05:57:37 +08:00
|
|
|
context.messages().Say("mod() by zero"_warn_en_US);
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (quotRem.overflow) {
|
2022-03-08 05:57:37 +08:00
|
|
|
context.messages().Say("mod() folding overflowed"_warn_en_US);
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
return quotRem.remainder;
|
|
|
|
}));
|
|
|
|
} else if (name == "modulo") {
|
|
|
|
return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
|
|
|
|
ScalarFuncWithContext<T, T, T>(
|
|
|
|
[](FoldingContext &context, const Scalar<T> &x,
|
|
|
|
const Scalar<T> &y) -> Scalar<T> {
|
|
|
|
auto result{x.MODULO(y)};
|
|
|
|
if (result.overflow) {
|
2022-03-08 05:57:37 +08:00
|
|
|
context.messages().Say(
|
|
|
|
"modulo() folding overflowed"_warn_en_US);
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
return result.value;
|
|
|
|
}));
|
2021-06-19 10:18:27 +08:00
|
|
|
} else if (name == "not") {
|
|
|
|
return FoldElementalIntrinsic<T, T>(
|
|
|
|
context, std::move(funcRef), &Scalar<T>::NOT);
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "precision") {
|
|
|
|
if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<ResultType<decltype(kx)>>::PRECISION;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
}
|
2021-06-19 02:24:32 +08:00
|
|
|
} else if (name == "product") {
|
|
|
|
return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{1});
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "radix") {
|
|
|
|
return Expr<T>{2};
|
|
|
|
} else if (name == "range") {
|
|
|
|
if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<ResultType<decltype(kx)>>::RANGE;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
} else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<ResultType<decltype(kx)>>::RANGE;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return Expr<T>{common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kx) {
|
|
|
|
return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE;
|
|
|
|
},
|
|
|
|
cx->u)};
|
|
|
|
}
|
|
|
|
} else if (name == "rank") {
|
2020-03-06 00:06:58 +08:00
|
|
|
if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
|
|
|
|
if (auto named{ExtractNamedEntity(*array)}) {
|
|
|
|
const Symbol &symbol{named->GetLastSymbol()};
|
2021-09-14 04:45:30 +08:00
|
|
|
if (IsAssumedRank(symbol)) {
|
2020-03-06 00:06:58 +08:00
|
|
|
// DescriptorInquiry can only be placed in expression of kind
|
|
|
|
// DescriptorInquiry::Result::kind.
|
|
|
|
return ConvertToType<T>(Expr<
|
|
|
|
Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{
|
|
|
|
DescriptorInquiry{*named, DescriptorInquiry::Field::Rank}});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Expr<T>{args[0].value().Rank()};
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
return Expr<T>{args[0].value().Rank()};
|
|
|
|
} else if (name == "selected_char_kind") {
|
|
|
|
if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) {
|
|
|
|
if (std::optional<std::string> value{chCon->GetScalarValue()}) {
|
|
|
|
int defaultKind{
|
|
|
|
context.defaults().GetDefaultKind(TypeCategory::Character)};
|
|
|
|
return Expr<T>{SelectedCharKind(*value, defaultKind)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == "selected_int_kind") {
|
|
|
|
if (auto p{GetInt64Arg(args[0])}) {
|
|
|
|
return Expr<T>{SelectedIntKind(*p)};
|
|
|
|
}
|
2021-04-08 04:21:10 +08:00
|
|
|
} else if (name == "selected_real_kind" ||
|
|
|
|
name == "__builtin_ieee_selected_real_kind") {
|
2020-01-01 04:20:42 +08:00
|
|
|
if (auto p{GetInt64ArgOr(args[0], 0)}) {
|
|
|
|
if (auto r{GetInt64ArgOr(args[1], 0)}) {
|
|
|
|
if (auto radix{GetInt64ArgOr(args[2], 2)}) {
|
|
|
|
return Expr<T>{SelectedRealKind(*p, *r, *radix)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == "shape") {
|
2022-01-11 02:09:45 +08:00
|
|
|
if (auto shape{GetContextFreeShape(context, args[0])}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
|
|
|
|
return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
|
|
|
|
}
|
|
|
|
}
|
2021-10-21 01:37:09 +08:00
|
|
|
} else if (name == "shifta" || name == "shiftr" || name == "shiftl") {
|
|
|
|
// Second argument can be of any kind. However, it must be smaller or
|
|
|
|
// equal than BIT_SIZE. It can be converted to Int4 to simplify.
|
|
|
|
auto fptr{&Scalar<T>::SHIFTA};
|
|
|
|
if (name == "shifta") { // done in fptr definition
|
|
|
|
} else if (name == "shiftr") {
|
|
|
|
fptr = &Scalar<T>::SHIFTR;
|
|
|
|
} else if (name == "shiftl") {
|
|
|
|
fptr = &Scalar<T>::SHIFTL;
|
|
|
|
} else {
|
|
|
|
common::die("missing case to fold intrinsic function %s", name.c_str());
|
|
|
|
}
|
|
|
|
return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T, Int4>([&](const Scalar<T> &i,
|
|
|
|
const Scalar<Int4> &pos) -> Scalar<T> {
|
|
|
|
auto posVal{static_cast<int>(pos.ToInt64())};
|
|
|
|
if (posVal < 0) {
|
|
|
|
context.messages().Say(
|
|
|
|
"SHIFT=%d count for %s is negative"_err_en_US, posVal, name);
|
|
|
|
} else if (posVal > i.bits) {
|
|
|
|
context.messages().Say(
|
|
|
|
"SHIFT=%d count for %s is greater than %d"_err_en_US, posVal,
|
|
|
|
name, i.bits);
|
|
|
|
}
|
|
|
|
return std::invoke(fptr, i, posVal);
|
|
|
|
}));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "sign") {
|
|
|
|
return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
|
|
|
|
ScalarFunc<T, T, T>(
|
|
|
|
[&context](const Scalar<T> &j, const Scalar<T> &k) -> Scalar<T> {
|
|
|
|
typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)};
|
|
|
|
if (result.overflow) {
|
|
|
|
context.messages().Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"sign(integer(kind=%d)) folding overflowed"_warn_en_US,
|
|
|
|
KIND);
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
return result.value;
|
|
|
|
}));
|
|
|
|
} else if (name == "size") {
|
2022-01-11 02:09:45 +08:00
|
|
|
if (auto shape{GetContextFreeShape(context, args[0])}) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (auto &dimArg{args[1]}) { // DIM= is present, get one extent
|
2020-01-01 04:20:42 +08:00
|
|
|
if (auto dim{GetInt64Arg(args[1])}) {
|
|
|
|
int rank{GetRank(*shape)};
|
|
|
|
if (*dim >= 1 && *dim <= rank) {
|
2021-09-14 04:45:30 +08:00
|
|
|
const Symbol *symbol{UnwrapWholeSymbolDataRef(args[0])};
|
|
|
|
if (symbol && IsAssumedSizeArray(*symbol) && *dim == rank) {
|
|
|
|
context.messages().Say(
|
|
|
|
"size(array,dim=%jd) of last dimension is not available for rank-%d assumed-size array dummy argument"_err_en_US,
|
|
|
|
*dim, rank);
|
|
|
|
return MakeInvalidIntrinsic<T>(std::move(funcRef));
|
|
|
|
} else if (auto &extent{shape->at(*dim - 1)}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
return Fold(context, ConvertToType<T>(std::move(*extent)));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
context.messages().Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"size(array,dim=%jd) dimension is out of range for rank-%d array"_warn_en_US,
|
2020-04-04 08:28:23 +08:00
|
|
|
*dim, rank);
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (auto extents{common::AllElementsPresent(std::move(*shape))}) {
|
|
|
|
// DIM= is absent; compute PRODUCT(SHAPE())
|
|
|
|
ExtentExpr product{1};
|
|
|
|
for (auto &&extent : std::move(*extents)) {
|
|
|
|
product = std::move(product) * std::move(extent);
|
|
|
|
}
|
|
|
|
return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))};
|
|
|
|
}
|
|
|
|
}
|
2020-12-16 03:06:44 +08:00
|
|
|
} else if (name == "sizeof") { // in bytes; extension
|
|
|
|
if (auto info{
|
|
|
|
characteristics::TypeAndShape::Characterize(args[0], context)}) {
|
|
|
|
if (auto bytes{info->MeasureSizeInBytes(context)}) {
|
|
|
|
return Expr<T>{Fold(context, ConvertToType<T>(std::move(*bytes)))};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == "storage_size") { // in bits
|
2021-02-03 06:39:05 +08:00
|
|
|
if (auto info{
|
|
|
|
characteristics::TypeAndShape::Characterize(args[0], context)}) {
|
|
|
|
if (auto bytes{info->MeasureElementSizeInBytes(context, true)}) {
|
|
|
|
return Expr<T>{
|
|
|
|
Fold(context, Expr<T>{8} * ConvertToType<T>(std::move(*bytes)))};
|
2020-12-16 03:06:44 +08:00
|
|
|
}
|
|
|
|
}
|
2021-06-19 02:24:32 +08:00
|
|
|
} else if (name == "sum") {
|
|
|
|
return FoldSum<T>(context, std::move(funcRef));
|
2020-01-01 04:20:42 +08:00
|
|
|
} else if (name == "ubound") {
|
|
|
|
return UBOUND(context, std::move(funcRef));
|
|
|
|
}
|
2022-04-01 04:36:09 +08:00
|
|
|
// TODO: dot_product, ishftc, matmul, sign, transfer
|
2020-01-01 04:20:42 +08:00
|
|
|
return Expr<T>{std::move(funcRef)};
|
|
|
|
}
|
|
|
|
|
2021-04-22 06:12:07 +08:00
|
|
|
// Substitutes a bare type parameter reference with its value if it has one now
|
|
|
|
// in an instantiation. Bare LEN type parameters are substituted only when
|
|
|
|
// the known value is constant.
|
2020-08-26 00:40:20 +08:00
|
|
|
Expr<TypeParamInquiry::Result> FoldOperation(
|
|
|
|
FoldingContext &context, TypeParamInquiry &&inquiry) {
|
2021-04-07 00:25:35 +08:00
|
|
|
std::optional<NamedEntity> base{inquiry.base()};
|
|
|
|
parser::CharBlock parameterName{inquiry.parameter().name()};
|
|
|
|
if (base) {
|
|
|
|
// Handling "designator%typeParam". Get the value of the type parameter
|
|
|
|
// from the instantiation of the base
|
|
|
|
if (const semantics::DeclTypeSpec *
|
|
|
|
declType{base->GetLastSymbol().GetType()}) {
|
|
|
|
if (const semantics::ParamValue *
|
|
|
|
paramValue{
|
|
|
|
declType->derivedTypeSpec().FindParameter(parameterName)}) {
|
|
|
|
const semantics::MaybeIntExpr ¶mExpr{paramValue->GetExplicit()};
|
|
|
|
if (paramExpr && IsConstantExpr(*paramExpr)) {
|
|
|
|
Expr<SomeInteger> intExpr{*paramExpr};
|
|
|
|
return Fold(context,
|
|
|
|
ConvertToType<TypeParamInquiry::Result>(std::move(intExpr)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-04-22 06:12:07 +08:00
|
|
|
// A "bare" type parameter: replace with its value, if that's now known
|
|
|
|
// in a current derived type instantiation, for KIND type parameters.
|
2020-01-01 04:20:42 +08:00
|
|
|
if (const auto *pdt{context.pdtInstance()}) {
|
2021-04-22 06:12:07 +08:00
|
|
|
bool isLen{false};
|
2020-01-01 04:20:42 +08:00
|
|
|
if (const semantics::Scope * scope{context.pdtInstance()->scope()}) {
|
2021-04-07 00:25:35 +08:00
|
|
|
auto iter{scope->find(parameterName)};
|
2020-01-01 04:20:42 +08:00
|
|
|
if (iter != scope->end()) {
|
|
|
|
const Symbol &symbol{*iter->second};
|
|
|
|
const auto *details{symbol.detailsIf<semantics::TypeParamDetails>()};
|
2021-04-07 00:25:35 +08:00
|
|
|
if (details) {
|
2021-04-22 06:12:07 +08:00
|
|
|
isLen = details->attr() == common::TypeParamAttr::Len;
|
2021-04-07 00:25:35 +08:00
|
|
|
const semantics::MaybeIntExpr &initExpr{details->init()};
|
2021-04-22 06:12:07 +08:00
|
|
|
if (initExpr && IsConstantExpr(*initExpr) &&
|
|
|
|
(!isLen || ToInt64(*initExpr))) {
|
2021-04-07 00:25:35 +08:00
|
|
|
Expr<SomeInteger> expr{*initExpr};
|
|
|
|
return Fold(context,
|
|
|
|
ConvertToType<TypeParamInquiry::Result>(std::move(expr)));
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-07 00:25:35 +08:00
|
|
|
if (const auto *value{pdt->FindParameter(parameterName)}) {
|
2020-01-01 04:20:42 +08:00
|
|
|
if (value->isExplicit()) {
|
2021-04-22 06:12:07 +08:00
|
|
|
auto folded{Fold(context,
|
2020-08-26 00:40:20 +08:00
|
|
|
AsExpr(ConvertToType<TypeParamInquiry::Result>(
|
2021-04-22 06:12:07 +08:00
|
|
|
Expr<SomeInteger>{value->GetExplicit().value()})))};
|
|
|
|
if (!isLen || ToInt64(folded)) {
|
|
|
|
return folded;
|
|
|
|
}
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-26 00:40:20 +08:00
|
|
|
return AsExpr(std::move(inquiry));
|
2020-01-01 04:20:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-01-01 04:20:42 +08:00
|
|
|
[](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::int64_t> ToInt64(const Expr<SomeType> &expr) {
|
|
|
|
if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(expr)}) {
|
|
|
|
return ToInt64(*intExpr);
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-30 08:22:37 +08:00
|
|
|
#ifdef _MSC_VER // disable bogus warning about missing definitions
|
|
|
|
#pragma warning(disable : 4661)
|
|
|
|
#endif
|
2020-01-01 04:20:42 +08:00
|
|
|
FOR_EACH_INTEGER_KIND(template class ExpressionBase, )
|
|
|
|
template class ExpressionBase<SomeInteger>;
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::evaluate
|