Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions flang/include/flang/Evaluate/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -1144,15 +1144,14 @@ std::optional<std::string> FindImpureCall(
std::optional<std::string> FindImpureCall(
FoldingContext &, const ProcedureRef &);

// Predicate: is a scalar expression suitable for naive scalar expansion
// in the flattening of an array expression?
// TODO: capture such scalar expansions in temporaries, flatten everything
class UnexpandabilityFindingVisitor
: public AnyTraverse<UnexpandabilityFindingVisitor> {
// Predicate: does an expression contain anything that would prevent it from
// being duplicated so that two instances of it then appear in the same
// expression?
class UnsafeToCopyVisitor : public AnyTraverse<UnsafeToCopyVisitor> {
public:
using Base = AnyTraverse<UnexpandabilityFindingVisitor>;
using Base = AnyTraverse<UnsafeToCopyVisitor>;
using Base::operator();
explicit UnexpandabilityFindingVisitor(bool admitPureCall)
explicit UnsafeToCopyVisitor(bool admitPureCall)
: Base{*this}, admitPureCall_{admitPureCall} {}
template <typename T> bool operator()(const FunctionRef<T> &procRef) {
return !admitPureCall_ || !procRef.proc().IsPure();
Expand All @@ -1163,14 +1162,22 @@ class UnexpandabilityFindingVisitor
bool admitPureCall_{false};
};

template <typename A>
bool IsSafelyCopyable(const A &x, bool admitPureCall = false) {
return !UnsafeToCopyVisitor{admitPureCall}(x);
}

// Predicate: is a scalar expression suitable for naive scalar expansion
// in the flattening of an array expression?
// TODO: capture such scalar expansions in temporaries, flatten everything
template <typename T>
bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context,
const Shape &shape, bool admitPureCall = false) {
if (UnexpandabilityFindingVisitor{admitPureCall}(expr)) {
if (IsSafelyCopyable(expr, admitPureCall)) {
return true;
} else {
auto extents{AsConstantExtents(context, shape)};
return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1;
} else {
return true;
}
}

Expand Down
10 changes: 6 additions & 4 deletions flang/lib/Evaluate/shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ MaybeExtentExpr GetRawUpperBound(
} else if (semantics::IsAssumedSizeArray(symbol) &&
dimension + 1 == symbol.Rank()) {
return std::nullopt;
} else {
} else if (IsSafelyCopyable(base, /*admitPureCall=*/true)) {
return ComputeUpperBound(
GetRawLowerBound(base, dimension), GetExtent(base, dimension));
}
Expand Down Expand Up @@ -678,9 +678,11 @@ static MaybeExtentExpr GetUBOUND(FoldingContext *context,
} else if (semantics::IsAssumedSizeArray(symbol) &&
dimension + 1 == symbol.Rank()) {
return std::nullopt; // UBOUND() folding replaces with -1
} else if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
return ComputeUpperBound(
std::move(*lb), GetExtent(base, dimension, invariantOnly));
} else if (IsSafelyCopyable(base, /*admitPureCall=*/true)) {
if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
return ComputeUpperBound(
std::move(*lb), GetExtent(base, dimension, invariantOnly));
}
}
}
} else if (const auto *assoc{
Expand Down
18 changes: 18 additions & 0 deletions flang/test/Evaluate/bug153031.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! Ensure that UBOUND() calculation from LBOUND()+SIZE() isn't applied to
! variables containing references to impure functions.
type t
real, allocatable :: a(:)
end type
interface
pure integer function pure(n)
integer, intent(in) :: n
end
end interface
type(t) :: x(10)
allocate(x(1)%a(2))
!CHECK: PRINT *, ubound(x(int(impure(1_4),kind=8))%a,dim=1_4)
print *, ubound(x(impure(1))%a, dim=1)
!CHECK: PRINT *, int(size(x(int(pure(1_4),kind=8))%a,dim=1,kind=8)+lbound(x(int(pure(1_4),kind=8))%a,dim=1,kind=8)-1_8,kind=4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so because it's PURE, it's ok to call it multiple times with the same value, because it's supposed to produce the same results on the same inputs. (And there's no way in Fortran to check how many times it's called without making it impure :-) )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should Flang also pull out the evaluation of the PURE function to get better runtime performance even though it has no side-effect being evaluated multiple times?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSE can do so in optimization if it wants.

print *, ubound(x(pure(1))%a, dim=1)
end
Loading