forked from OSchip/llvm-project
[flang] Implement shape analysis of TRANSFER intrinsic function result
The shape (esp. the size) of the result of a call to TRANSFER is implemented according to the definition in the standard. Differential Revision: https://reviews.llvm.org/D85866
This commit is contained in:
parent
180d6ed667
commit
fad31d6032
|
@ -78,6 +78,7 @@ public:
|
|||
|
||||
bool operator==(const TypeAndShape &) const;
|
||||
bool operator!=(const TypeAndShape &that) const { return !(*this == that); }
|
||||
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
const semantics::Symbol &, FoldingContext &);
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
|
@ -90,6 +91,8 @@ public:
|
|||
const semantics::ProcInterface &);
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
const semantics::DeclTypeSpec &);
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
const ActualArgument &, FoldingContext &);
|
||||
|
||||
template <typename A>
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
|
@ -114,6 +117,24 @@ public:
|
|||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
template <typename A>
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
const std::optional<A> &x, FoldingContext &context) {
|
||||
if (x) {
|
||||
return Characterize(*x, context);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
template <typename A>
|
||||
static std::optional<TypeAndShape> Characterize(
|
||||
const A *x, FoldingContext &context) {
|
||||
if (x) {
|
||||
return Characterize(*x, context);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
DynamicType type() const { return type_; }
|
||||
TypeAndShape &set_type(DynamicType t) {
|
||||
|
|
|
@ -42,8 +42,10 @@ public:
|
|||
if (offset < 0 || offset + bytes > data_.size()) {
|
||||
return OutOfRange;
|
||||
} else {
|
||||
auto elementBytes{x.GetType().MeasureSizeInBytes()};
|
||||
if (!elementBytes || bytes != x.values().size() * *elementBytes) {
|
||||
auto elementBytes{ToInt64(x.GetType().MeasureSizeInBytes())};
|
||||
if (!elementBytes ||
|
||||
bytes !=
|
||||
x.values().size() * static_cast<std::size_t>(*elementBytes)) {
|
||||
return SizeMismatch;
|
||||
} else {
|
||||
std::memcpy(&data_.at(offset), &x.values().at(0), bytes);
|
||||
|
|
|
@ -138,8 +138,9 @@ public:
|
|||
constexpr const semantics::ParamValue *charLength() const {
|
||||
return charLength_;
|
||||
}
|
||||
std::optional<common::ConstantSubscript> GetCharLength() const;
|
||||
std::optional<std::size_t> MeasureSizeInBytes() const;
|
||||
std::optional<Expr<SubscriptInteger>> GetCharLength() const;
|
||||
std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
|
||||
FoldingContext * = nullptr) const;
|
||||
|
||||
std::string AsFortran() const;
|
||||
std::string AsFortran(std::string &&charLenExpr) const;
|
||||
|
|
|
@ -121,6 +121,11 @@ std::optional<TypeAndShape> TypeAndShape::Characterize(
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<TypeAndShape> TypeAndShape::Characterize(
|
||||
const ActualArgument &arg, FoldingContext &context) {
|
||||
return Characterize(arg.UnwrapExpr(), context);
|
||||
}
|
||||
|
||||
bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
|
||||
const TypeAndShape &that, const char *thisIs, const char *thatIs,
|
||||
bool isElemental) const {
|
||||
|
@ -183,7 +188,7 @@ llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const {
|
|||
o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
|
||||
attrs_.Dump(o, EnumToString);
|
||||
if (!shape_.empty()) {
|
||||
o << " dimension(";
|
||||
o << " dimension";
|
||||
char sep{'('};
|
||||
for (const auto &expr : shape_) {
|
||||
o << sep;
|
||||
|
|
|
@ -27,10 +27,10 @@ std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
|
|||
} else if (symbol.has<semantics::ObjectEntityDetails>() &&
|
||||
!IsNamedConstant(symbol)) {
|
||||
if (auto type{DynamicType::From(symbol)}) {
|
||||
if (auto bytes{type->MeasureSizeInBytes()}) {
|
||||
if (auto bytes{ToInt64(type->MeasureSizeInBytes(&context_))}) {
|
||||
if (auto extents{GetConstantExtents(context_, symbol)}) {
|
||||
OffsetSymbol result{symbol, *bytes};
|
||||
auto stride{static_cast<ConstantSubscript>(*bytes)};
|
||||
OffsetSymbol result{symbol, static_cast<std::size_t>(*bytes)};
|
||||
auto stride{*bytes};
|
||||
for (auto extent : *extents) {
|
||||
if (extent == 0) {
|
||||
return std::nullopt;
|
||||
|
@ -57,7 +57,7 @@ std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
|
|||
const ArrayRef &x, ConstantSubscript which) {
|
||||
const Symbol &array{x.base().GetLastSymbol()};
|
||||
if (auto type{DynamicType::From(array)}) {
|
||||
if (auto bytes{type->MeasureSizeInBytes()}) {
|
||||
if (auto bytes{ToInt64(type->MeasureSizeInBytes(&context_))}) {
|
||||
if (auto extents{GetConstantExtents(context_, array)}) {
|
||||
Shape lbs{GetLowerBounds(context_, x.base())};
|
||||
if (auto lowerBounds{AsConstantExtents(context_, lbs)}) {
|
||||
|
@ -73,7 +73,7 @@ std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
|
|||
if (!result) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto stride{static_cast<ConstantSubscript>(*bytes)};
|
||||
auto stride{*bytes};
|
||||
int dim{0};
|
||||
for (const Subscript &subscript : x.subscript()) {
|
||||
ConstantSubscript lower{lowerBounds->at(dim)};
|
||||
|
@ -217,14 +217,14 @@ static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context,
|
|||
auto extents{AsConstantExtents(context, shape)};
|
||||
Shape lbs{GetLowerBounds(context, entity)};
|
||||
auto lower{AsConstantExtents(context, lbs)};
|
||||
auto elementBytes{elementType.MeasureSizeInBytes()};
|
||||
auto elementBytes{ToInt64(elementType.MeasureSizeInBytes(&context))};
|
||||
if (!extents || !lower || !elementBytes || *elementBytes <= 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
int rank{GetRank(shape)};
|
||||
CHECK(extents->size() == static_cast<std::size_t>(rank) &&
|
||||
lower->size() == extents->size());
|
||||
auto element{offset / *elementBytes};
|
||||
auto element{offset / static_cast<std::size_t>(*elementBytes)};
|
||||
std::vector<Subscript> subscripts;
|
||||
auto at{element};
|
||||
for (int dim{0}; dim + 1 < rank; ++dim) {
|
||||
|
@ -239,7 +239,7 @@ static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context,
|
|||
}
|
||||
// This final subscript might be out of range for use in error reporting.
|
||||
subscripts.emplace_back(ExtentExpr{(*lower)[rank - 1] + at});
|
||||
offset -= element * *elementBytes;
|
||||
offset -= element * static_cast<std::size_t>(*elementBytes);
|
||||
return ArrayRef{std::move(entity), std::move(subscripts)};
|
||||
}
|
||||
|
||||
|
@ -315,12 +315,12 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
|
|||
if (std::optional<Expr<SomeType>> result{
|
||||
TypedWrapper<Designator>(*type, std::move(*dataRef))}) {
|
||||
if (IsAllocatableOrPointer(symbol)) {
|
||||
} else if (auto elementBytes{type->MeasureSizeInBytes()}) {
|
||||
} else if (auto elementBytes{
|
||||
ToInt64(type->MeasureSizeInBytes(&context))}) {
|
||||
if (auto *zExpr{std::get_if<Expr<SomeComplex>>(&result->u)}) {
|
||||
if (size * 2 > *elementBytes) {
|
||||
if (size * 2 > static_cast<std::size_t>(*elementBytes)) {
|
||||
return result;
|
||||
} else if (offset == 0 ||
|
||||
offset * 2 == static_cast<ConstantSubscript>(*elementBytes)) {
|
||||
} else if (offset == 0 || offset * 2 == *elementBytes) {
|
||||
// Pick a COMPLEX component
|
||||
auto part{
|
||||
offset == 0 ? ComplexPart::Part::RE : ComplexPart::Part::IM};
|
||||
|
@ -334,7 +334,7 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
|
|||
}
|
||||
} else if (auto *cExpr{
|
||||
std::get_if<Expr<SomeCharacter>>(&result->u)}) {
|
||||
if (offset > 0 || size != *elementBytes) {
|
||||
if (offset > 0 || size != static_cast<std::size_t>(*elementBytes)) {
|
||||
// Select a substring
|
||||
return std::visit(
|
||||
[&](const auto &x) -> std::optional<Expr<SomeType>> {
|
||||
|
|
|
@ -88,9 +88,10 @@ public:
|
|||
using Scalar = typename Const::Element;
|
||||
std::size_t elements{TotalElementCount(extents_)};
|
||||
std::vector<Scalar> typedValue(elements);
|
||||
auto stride{type_.MeasureSizeInBytes()};
|
||||
CHECK(stride > 0);
|
||||
CHECK(offset_ + elements * *stride <= image_.data_.size());
|
||||
auto elemBytes{ToInt64(type_.MeasureSizeInBytes(&context_))};
|
||||
CHECK(elemBytes && *elemBytes >= 0);
|
||||
std::size_t stride{static_cast<std::size_t>(*elemBytes)};
|
||||
CHECK(offset_ + elements * stride <= image_.data_.size());
|
||||
if constexpr (T::category == TypeCategory::Derived) {
|
||||
const semantics::DerivedTypeSpec &derived{type_.GetDerivedTypeSpec()};
|
||||
for (auto iter : DEREF(derived.scope())) {
|
||||
|
@ -102,7 +103,7 @@ public:
|
|||
CHECK(componentType);
|
||||
auto at{offset_ + component.offset()};
|
||||
if (isPointer) {
|
||||
for (std::size_t j{0}; j < elements; ++j, at += *stride) {
|
||||
for (std::size_t j{0}; j < elements; ++j, at += stride) {
|
||||
Result value{image_.AsConstantDataPointer(*componentType, at)};
|
||||
CHECK(value);
|
||||
typedValue[j].emplace(component, std::move(*value));
|
||||
|
@ -110,7 +111,7 @@ public:
|
|||
} else {
|
||||
auto componentExtents{GetConstantExtents(context_, component)};
|
||||
CHECK(componentExtents);
|
||||
for (std::size_t j{0}; j < elements; ++j, at += *stride) {
|
||||
for (std::size_t j{0}; j < elements; ++j, at += stride) {
|
||||
Result value{image_.AsConstant(
|
||||
context_, *componentType, *componentExtents, at)};
|
||||
CHECK(value);
|
||||
|
@ -122,20 +123,20 @@ public:
|
|||
return AsGenericExpr(
|
||||
Const{derived, std::move(typedValue), std::move(extents_)});
|
||||
} else if constexpr (T::category == TypeCategory::Character) {
|
||||
auto length{static_cast<ConstantSubscript>(*stride) / T::kind};
|
||||
auto length{static_cast<ConstantSubscript>(stride) / T::kind};
|
||||
for (std::size_t j{0}; j < elements; ++j) {
|
||||
using Char = typename Scalar::value_type;
|
||||
const Char *data{reinterpret_cast<const Char *>(
|
||||
&image_.data_[offset_ + j * *stride])};
|
||||
&image_.data_[offset_ + j * stride])};
|
||||
typedValue[j].assign(data, length);
|
||||
}
|
||||
return AsGenericExpr(
|
||||
Const{length, std::move(typedValue), std::move(extents_)});
|
||||
} else {
|
||||
// Lengthless intrinsic type
|
||||
CHECK(sizeof(Scalar) <= *stride);
|
||||
CHECK(sizeof(Scalar) <= stride);
|
||||
for (std::size_t j{0}; j < elements; ++j) {
|
||||
std::memcpy(&typedValue[j], &image_.data_[offset_ + j * *stride],
|
||||
std::memcpy(&typedValue[j], &image_.data_[offset_ + j * stride],
|
||||
sizeof(Scalar));
|
||||
}
|
||||
return AsGenericExpr(Const{std::move(typedValue), std::move(extents_)});
|
||||
|
|
|
@ -626,6 +626,43 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (intrinsic->name == "transfer") {
|
||||
if (call.arguments().size() == 3 && call.arguments().at(2)) {
|
||||
// SIZE= is present; shape is vector [SIZE=]
|
||||
if (const auto *size{
|
||||
UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}) {
|
||||
return Shape{
|
||||
MaybeExtentExpr{ConvertToType<ExtentType>(common::Clone(*size))}};
|
||||
}
|
||||
} else if (auto moldTypeAndShape{
|
||||
characteristics::TypeAndShape::Characterize(
|
||||
call.arguments().at(1), context_)}) {
|
||||
if (GetRank(moldTypeAndShape->shape()) == 0) {
|
||||
// SIZE= is absent and MOLD= is scalar: result is scalar
|
||||
return Scalar();
|
||||
} else {
|
||||
// SIZE= is absent and MOLD= is array: result is vector whose
|
||||
// length is determined by sizes of types. See 16.9.193p4 case(ii).
|
||||
if (auto sourceTypeAndShape{
|
||||
characteristics::TypeAndShape::Characterize(
|
||||
call.arguments().at(0), context_)}) {
|
||||
auto sourceElements{
|
||||
GetSize(common::Clone(sourceTypeAndShape->shape()))};
|
||||
auto sourceElementBytes{
|
||||
sourceTypeAndShape->type().MeasureSizeInBytes(&context_)};
|
||||
auto moldElementBytes{
|
||||
moldTypeAndShape->type().MeasureSizeInBytes(&context_)};
|
||||
if (sourceElements && sourceElementBytes && moldElementBytes) {
|
||||
ExtentExpr extent{Fold(context_,
|
||||
((std::move(*sourceElements) *
|
||||
std::move(*sourceElementBytes)) +
|
||||
common::Clone(*moldElementBytes) - ExtentExpr{1}) /
|
||||
common::Clone(*moldElementBytes))};
|
||||
return Shape{MaybeExtentExpr{std::move(extent)}};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (intrinsic->name == "transpose") {
|
||||
if (call.arguments().size() >= 1) {
|
||||
if (auto shape{(*this)(call.arguments().at(0))}) {
|
||||
|
|
|
@ -103,11 +103,10 @@ bool DynamicType::operator==(const DynamicType &that) const {
|
|||
PointeeComparison(derived_, that.derived_);
|
||||
}
|
||||
|
||||
std::optional<common::ConstantSubscript> DynamicType::GetCharLength() const {
|
||||
if (category_ == TypeCategory::Character && charLength_ &&
|
||||
charLength_->isExplicit()) {
|
||||
if (const auto &len{charLength_->GetExplicit()}) {
|
||||
return ToInt64(len);
|
||||
std::optional<Expr<SubscriptInteger>> DynamicType::GetCharLength() const {
|
||||
if (category_ == TypeCategory::Character && charLength_) {
|
||||
if (auto length{charLength_->GetExplicit()}) {
|
||||
return ConvertToType<SubscriptInteger>(std::move(*length));
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
|
@ -125,24 +124,31 @@ static constexpr int RealKindBytes(int kind) {
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<std::size_t> DynamicType::MeasureSizeInBytes() const {
|
||||
std::optional<Expr<SubscriptInteger>> DynamicType::MeasureSizeInBytes(
|
||||
FoldingContext *context) const {
|
||||
switch (category_) {
|
||||
case TypeCategory::Integer:
|
||||
return kind_;
|
||||
return Expr<SubscriptInteger>{kind_};
|
||||
case TypeCategory::Real:
|
||||
return RealKindBytes(kind_);
|
||||
return Expr<SubscriptInteger>{RealKindBytes(kind_)};
|
||||
case TypeCategory::Complex:
|
||||
return 2 * RealKindBytes(kind_);
|
||||
return Expr<SubscriptInteger>{2 * RealKindBytes(kind_)};
|
||||
case TypeCategory::Character:
|
||||
if (auto len{GetCharLength()}) {
|
||||
return kind_ * *len;
|
||||
auto result{Expr<SubscriptInteger>{kind_} * std::move(*len)};
|
||||
if (context) {
|
||||
return Fold(*context, std::move(result));
|
||||
} else {
|
||||
return std::move(result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TypeCategory::Logical:
|
||||
return kind_;
|
||||
return Expr<SubscriptInteger>{kind_};
|
||||
case TypeCategory::Derived:
|
||||
if (derived_ && derived_->scope()) {
|
||||
return derived_->scope()->size();
|
||||
return Expr<SubscriptInteger>{
|
||||
static_cast<common::ConstantSubscript>(derived_->scope()->size())};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -396,13 +396,14 @@ auto ComputeOffsetsHelper::GetIntrinsicSizeAndAlignment(
|
|||
if (category == TypeCategory::Character) {
|
||||
return {static_cast<std::size_t>(kind)};
|
||||
}
|
||||
std::optional<std::size_t> size{
|
||||
evaluate::DynamicType{category, kind}.MeasureSizeInBytes()};
|
||||
CHECK(size.has_value());
|
||||
auto bytes{evaluate::ToInt64(
|
||||
evaluate::DynamicType{category, kind}.MeasureSizeInBytes())};
|
||||
CHECK(bytes && *bytes > 0);
|
||||
std::size_t size{static_cast<std::size_t>(*bytes)};
|
||||
if (category == TypeCategory::Complex) {
|
||||
return {*size, *size >> 1};
|
||||
return {size, size >> 1};
|
||||
} else {
|
||||
return {*size};
|
||||
return {size};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -229,7 +229,9 @@ DataInitializationCompiler::ConvertElement(
|
|||
// (most) other Fortran compilers do. Pad on the right with spaces
|
||||
// when short, truncate the right if long.
|
||||
// TODO: big-endian targets
|
||||
std::size_t bytes{type.MeasureSizeInBytes().value()};
|
||||
std::size_t bytes{static_cast<std::size_t>(evaluate::ToInt64(
|
||||
type.MeasureSizeInBytes(&exprAnalyzer_.GetFoldingContext()))
|
||||
.value())};
|
||||
evaluate::BOZLiteralConstant bits{0};
|
||||
for (std::size_t j{0}; j < bytes; ++j) {
|
||||
char ch{j >= chValue->size() ? ' ' : chValue->at(j)};
|
||||
|
@ -425,12 +427,16 @@ static bool CombineSomeEquivalencedInits(
|
|||
}
|
||||
// Compute the minimum common granularity
|
||||
if (auto dyType{evaluate::DynamicType::From(symbol)}) {
|
||||
minElementBytes = dyType->MeasureSizeInBytes().value_or(1);
|
||||
minElementBytes = evaluate::ToInt64(
|
||||
dyType->MeasureSizeInBytes(&exprAnalyzer.GetFoldingContext()))
|
||||
.value_or(1);
|
||||
}
|
||||
for (const Symbol *s : conflicts) {
|
||||
if (auto dyType{evaluate::DynamicType::From(*s)}) {
|
||||
minElementBytes =
|
||||
std::min(minElementBytes, dyType->MeasureSizeInBytes().value_or(1));
|
||||
minElementBytes = std::min(minElementBytes,
|
||||
static_cast<std::size_t>(evaluate::ToInt64(
|
||||
dyType->MeasureSizeInBytes(&exprAnalyzer.GetFoldingContext()))
|
||||
.value_or(1)));
|
||||
} else {
|
||||
minElementBytes = 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
! RUN: %S/test_folding.sh %s %t %f18
|
||||
! Tests folding of SHAPE(TRANSFER(...))
|
||||
|
||||
module m
|
||||
logical, parameter :: test_size_1 = size(shape(transfer(123456789,0_1,size=4))) == 1
|
||||
logical, parameter :: test_size_2 = all(shape(transfer(123456789,0_1,size=4)) == [4])
|
||||
logical, parameter :: test_scalar_1 = size(shape(transfer(123456789, 0_1))) == 0
|
||||
logical, parameter :: test_vector_1 = size(shape(transfer(123456789, [0_1]))) == 1
|
||||
logical, parameter :: test_vector_2 = all(shape(transfer(123456789, [0_1])) == [4])
|
||||
logical, parameter :: test_array_1 = size(shape(transfer(123456789, reshape([0_1],[1,1])))) == 1
|
||||
logical, parameter :: test_array_2 = all(shape(transfer(123456789, reshape([0_1],[1,1]))) == [4])
|
||||
logical, parameter :: test_array_3 = all(shape(transfer([1.,2.,3.], [(0.,0.)])) == [2])
|
||||
end module
|
Loading…
Reference in New Issue