Skip to content

Commit

Permalink
[flang] Unsplit COMPLEX operations
Browse files Browse the repository at this point in the history
COMPLEX negation, addition, subtraction, conversions of kind, and
equality/inequality were represented as component-wise REAL
operations.  It turns out to be easier for lowering if we
do not split and recombine these COMPLEX operations, and it
avoids a potential problem with COMPLEX valued function calls
in these contexts.  So add this suite of operations to the
typed expression representation in place of the component-wise
transformations, and support them in folding.

Differential revision: https://reviews.llvm.org/D91443
  • Loading branch information
klausler committed Nov 16, 2020
1 parent 9170308 commit df62afd
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 104 deletions.
25 changes: 9 additions & 16 deletions flang/include/flang/Evaluate/expression.h
Expand Up @@ -202,15 +202,11 @@ template <typename TO, TypeCategory FROMCAT = TO::category>
struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
// Fortran doesn't have conversions between kinds of CHARACTER apart from
// assignments, and in those the data must be convertible to/from 7-bit ASCII.
// Conversions between kinds of COMPLEX are represented piecewise.
static_assert(((TO::category == TypeCategory::Integer ||
TO::category == TypeCategory::Real) &&
(FROMCAT == TypeCategory::Integer ||
FROMCAT == TypeCategory::Real)) ||
(TO::category == TypeCategory::Character &&
FROMCAT == TypeCategory::Character) ||
(TO::category == TypeCategory::Logical &&
FROMCAT == TypeCategory::Logical));
TO::category == FROMCAT);
using Result = TO;
using Operand = SomeKind<FROMCAT>;
using Base = Operation<Convert, Result, Operand>;
Expand Down Expand Up @@ -542,8 +538,7 @@ class Expr<Type<TypeCategory::Real, KIND>>

private:
// N.B. Real->Complex and Complex->Real conversions are done with CMPLX
// and part access operations (resp.). Conversions between kinds of
// Complex are done via decomposition to Real and reconstruction.
// and part access operations (resp.).
using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
Convert<Result, TypeCategory::Real>>;
using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
Expand All @@ -563,12 +558,10 @@ class Expr<Type<TypeCategory::Complex, KIND>>
using Result = Type<TypeCategory::Complex, KIND>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}

// Note that many COMPLEX operations are represented as REAL operations
// over their components (viz., conversions, negation, add, and subtract).
using Operations =
std::variant<Parentheses<Result>, Multiply<Result>, Divide<Result>,
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
Convert<Result, TypeCategory::Complex>, Add<Result>, Subtract<Result>,
Multiply<Result>, Divide<Result>, Power<Result>, RealToIntPower<Result>,
ComplexConstructor<KIND>>;
using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
Designator<Result>, FunctionRef<Result>>;

Expand Down Expand Up @@ -614,6 +607,7 @@ struct Relational : public Operation<Relational<T>, LogicalResult, T, T> {
using Operand = typename Base::template Operand<0>;
static_assert(Operand::category == TypeCategory::Integer ||
Operand::category == TypeCategory::Real ||
Operand::category == TypeCategory::Complex ||
Operand::category == TypeCategory::Character);
CLASS_BOILERPLATE(Relational)
Relational(
Expand All @@ -625,9 +619,8 @@ struct Relational : public Operation<Relational<T>, LogicalResult, T, T> {
};

template <> class Relational<SomeType> {
// COMPLEX data are compared piecewise.
using DirectlyComparableTypes =
common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
using DirectlyComparableTypes = common::CombineTuples<IntegerTypes, RealTypes,
ComplexTypes, CharacterTypes>;

public:
using Result = LogicalResult;
Expand Down
84 changes: 21 additions & 63 deletions flang/include/flang/Evaluate/tools.h
Expand Up @@ -331,49 +331,29 @@ template <typename A> const Symbol *GetFirstSymbol(const A &x) {
template <typename TO, TypeCategory FROMCAT>
Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
static_assert(IsSpecificIntrinsicType<TO>);
if constexpr (FROMCAT != TO::category) {
if constexpr (TO::category == TypeCategory::Complex) {
using Part = typename TO::Part;
Scalar<Part> zero;
return Expr<TO>{ComplexConstructor<TO::kind>{
ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
} else if constexpr (FROMCAT == TypeCategory::Complex) {
// Extract and convert the real component of a complex value
return std::visit(
[&](auto &&z) {
using ZType = ResultType<decltype(z)>;
using Part = typename ZType::Part;
return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
},
std::move(x.u));
} else {
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
}
} else {
// Same type category
if constexpr (FROMCAT == TO::category) {
if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
return std::move(*already);
}
if constexpr (TO::category == TypeCategory::Complex) {
// Extract, convert, and recombine the components.
return Expr<TO>{std::visit(
[](auto &z) {
using FromType = ResultType<decltype(z)>;
using FromPart = typename FromType::Part;
using FromGeneric = SomeKind<TypeCategory::Real>;
using ToPart = typename TO::Part;
Convert<ToPart, TypeCategory::Real> re{Expr<FromGeneric>{
Expr<FromPart>{ComplexComponent<FromType::kind>{false, z}}}};
Convert<ToPart, TypeCategory::Real> im{Expr<FromGeneric>{
Expr<FromPart>{ComplexComponent<FromType::kind>{true, z}}}};
return ComplexConstructor<TO::kind>{
AsExpr(std::move(re)), AsExpr(std::move(im))};
},
x.u)};
} else {
return Expr<TO>{Convert<TO, TO::category>{std::move(x)}};
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
}
} else if constexpr (TO::category == TypeCategory::Complex) {
using Part = typename TO::Part;
Scalar<Part> zero;
return Expr<TO>{ComplexConstructor<TO::kind>{
ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
} else if constexpr (FROMCAT == TypeCategory::Complex) {
// Extract and convert the real component of a complex value
return std::visit(
[&](auto &&z) {
using ZType = ResultType<decltype(z)>;
using Part = typename ZType::Part;
return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
},
std::move(x.u));
} else {
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
}
}

Expand Down Expand Up @@ -523,24 +503,11 @@ template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
}

// Combine two expressions of the same specific numeric type with an operation
// to produce a new expression. Implements piecewise addition and subtraction
// for COMPLEX.
// to produce a new expression.
template <template <typename> class OPR, typename SPECIFIC>
Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
static_assert(IsSpecificIntrinsicType<SPECIFIC>);
if constexpr (SPECIFIC::category == TypeCategory::Complex &&
(std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>)) {
static constexpr int kind{SPECIFIC::kind};
using Part = Type<TypeCategory::Real, kind>;
return AsExpr(ComplexConstructor<kind>{
AsExpr(OPR<Part>{AsExpr(ComplexComponent<kind>{false, x}),
AsExpr(ComplexComponent<kind>{false, y})}),
AsExpr(OPR<Part>{AsExpr(ComplexComponent<kind>{true, x}),
AsExpr(ComplexComponent<kind>{true, y})})});
} else {
return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
}
return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
}

// Given two expressions of arbitrary kind in the same intrinsic type
Expand Down Expand Up @@ -620,15 +587,6 @@ Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
return AsExpr(Negate<Type<C, K>>{std::move(x)});
}

template <int K>
Expr<Type<TypeCategory::Complex, K>> operator-(
Expr<Type<TypeCategory::Complex, K>> &&x) {
using Part = Type<TypeCategory::Real, K>;
return AsExpr(ComplexConstructor<K>{
AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{false, x})}),
AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{true, x})})});
}

template <TypeCategory C, int K>
Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
Expand Down
9 changes: 9 additions & 0 deletions flang/lib/Evaluate/fold-implementation.h
Expand Up @@ -1236,6 +1236,15 @@ Expr<TO> FoldOperation(
}
return ScalarConstantToExpr(std::move(converted.value));
}
} else if constexpr (TO::category == TypeCategory::Complex) {
if constexpr (Operand::category == TypeCategory::Complex) {
return FoldOperation(ctx,
ComplexConstructor<TO::kind>{
AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
Constant<typename Operand::Part>{value->REAL()})}),
AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
Constant<typename Operand::Part>{value->AIMAG()})})});
}
} else if constexpr (TO::category == TypeCategory::Character &&
Operand::category == TypeCategory::Character) {
if (auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {
Expand Down
6 changes: 4 additions & 2 deletions flang/lib/Evaluate/fold-logical.cpp
Expand Up @@ -134,11 +134,13 @@ Expr<LogicalResult> FoldOperation(
Satisfies(relation.opr, folded->first.CompareSigned(folded->second));
} else if constexpr (T::category == TypeCategory::Real) {
result = Satisfies(relation.opr, folded->first.Compare(folded->second));
} else if constexpr (T::category == TypeCategory::Complex) {
result = (relation.opr == RelationalOperator::EQ) ==
folded->first.Equals(folded->second);
} else if constexpr (T::category == TypeCategory::Character) {
result = Satisfies(relation.opr, Compare(folded->first, folded->second));
} else {
static_assert(T::category != TypeCategory::Complex &&
T::category != TypeCategory::Logical);
static_assert(T::category != TypeCategory::Logical);
}
return Expr<LogicalResult>{Constant<LogicalResult>{result}};
}
Expand Down
3 changes: 3 additions & 0 deletions flang/lib/Evaluate/formatting.cpp
Expand Up @@ -349,6 +349,7 @@ template <typename TO, TypeCategory FROMCAT>
llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
static_assert(TO::category == TypeCategory::Integer ||
TO::category == TypeCategory::Real ||
TO::category == TypeCategory::Complex ||
TO::category == TypeCategory::Character ||
TO::category == TypeCategory::Logical,
"Convert<> to bad category!");
Expand All @@ -358,6 +359,8 @@ llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
this->left().AsFortran(o << "int(");
} else if constexpr (TO::category == TypeCategory::Real) {
this->left().AsFortran(o << "real(");
} else if constexpr (TO::category == TypeCategory::Complex) {
this->left().AsFortran(o << "cmplx(");
} else {
this->left().AsFortran(o << "logical(");
}
Expand Down
29 changes: 6 additions & 23 deletions flang/lib/Evaluate/tools.cpp
Expand Up @@ -69,7 +69,7 @@ auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
return symbol && symbol->attrs().test(semantics::Attr::POINTER);
}

// Conversions of complex component expressions to REAL.
// Conversions of COMPLEX component expressions to REAL.
ConvertRealOperandsResult ConvertRealOperands(
parser::ContextualMessages &messages, Expr<SomeType> &&x,
Expr<SomeType> &&y, int defaultRealKind) {
Expand Down Expand Up @@ -498,31 +498,14 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
},
[&](Expr<SomeComplex> &&zx,
Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
if (opr != RelationalOperator::EQ &&
opr != RelationalOperator::NE) {
if (opr == RelationalOperator::EQ ||
opr == RelationalOperator::NE) {
return PromoteAndRelate(opr, std::move(zx), std::move(zy));
} else {
messages.Say(
"COMPLEX data may be compared only for equality"_err_en_US);
} else {
auto rr{Relate(messages, opr,
AsGenericExpr(GetComplexPart(zx, false)),
AsGenericExpr(GetComplexPart(zy, false)))};
auto ri{
Relate(messages, opr, AsGenericExpr(GetComplexPart(zx, true)),
AsGenericExpr(GetComplexPart(zy, true)))};
if (auto parts{
common::AllPresent(std::move(rr), std::move(ri))}) {
// (a,b)==(c,d) -> (a==c) .AND. (b==d)
// (a,b)/=(c,d) -> (a/=c) .OR. (b/=d)
LogicalOperator combine{opr == RelationalOperator::EQ
? LogicalOperator::And
: LogicalOperator::Or};
return Expr<LogicalResult>{
LogicalOperation<LogicalResult::kind>{combine,
std::get<0>(std::move(*parts)),
std::get<1>(std::move(*parts))}};
}
return std::nullopt;
}
return std::nullopt;
},
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
return Relate(messages, opr, std::move(x),
Expand Down

0 comments on commit df62afd

Please sign in to comment.