Skip to content

Commit df62afd

Browse files
committed
[flang] Unsplit COMPLEX operations
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
1 parent 9170308 commit df62afd

File tree

6 files changed

+52
-104
lines changed

6 files changed

+52
-104
lines changed

flang/include/flang/Evaluate/expression.h

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,11 @@ template <typename TO, TypeCategory FROMCAT = TO::category>
202202
struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
203203
// Fortran doesn't have conversions between kinds of CHARACTER apart from
204204
// assignments, and in those the data must be convertible to/from 7-bit ASCII.
205-
// Conversions between kinds of COMPLEX are represented piecewise.
206205
static_assert(((TO::category == TypeCategory::Integer ||
207206
TO::category == TypeCategory::Real) &&
208207
(FROMCAT == TypeCategory::Integer ||
209208
FROMCAT == TypeCategory::Real)) ||
210-
(TO::category == TypeCategory::Character &&
211-
FROMCAT == TypeCategory::Character) ||
212-
(TO::category == TypeCategory::Logical &&
213-
FROMCAT == TypeCategory::Logical));
209+
TO::category == FROMCAT);
214210
using Result = TO;
215211
using Operand = SomeKind<FROMCAT>;
216212
using Base = Operation<Convert, Result, Operand>;
@@ -542,8 +538,7 @@ class Expr<Type<TypeCategory::Real, KIND>>
542538

543539
private:
544540
// N.B. Real->Complex and Complex->Real conversions are done with CMPLX
545-
// and part access operations (resp.). Conversions between kinds of
546-
// Complex are done via decomposition to Real and reconstruction.
541+
// and part access operations (resp.).
547542
using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
548543
Convert<Result, TypeCategory::Real>>;
549544
using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
@@ -563,12 +558,10 @@ class Expr<Type<TypeCategory::Complex, KIND>>
563558
using Result = Type<TypeCategory::Complex, KIND>;
564559
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
565560
explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
566-
567-
// Note that many COMPLEX operations are represented as REAL operations
568-
// over their components (viz., conversions, negation, add, and subtract).
569-
using Operations =
570-
std::variant<Parentheses<Result>, Multiply<Result>, Divide<Result>,
571-
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
561+
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
562+
Convert<Result, TypeCategory::Complex>, Add<Result>, Subtract<Result>,
563+
Multiply<Result>, Divide<Result>, Power<Result>, RealToIntPower<Result>,
564+
ComplexConstructor<KIND>>;
572565
using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
573566
Designator<Result>, FunctionRef<Result>>;
574567

@@ -614,6 +607,7 @@ struct Relational : public Operation<Relational<T>, LogicalResult, T, T> {
614607
using Operand = typename Base::template Operand<0>;
615608
static_assert(Operand::category == TypeCategory::Integer ||
616609
Operand::category == TypeCategory::Real ||
610+
Operand::category == TypeCategory::Complex ||
617611
Operand::category == TypeCategory::Character);
618612
CLASS_BOILERPLATE(Relational)
619613
Relational(
@@ -625,9 +619,8 @@ struct Relational : public Operation<Relational<T>, LogicalResult, T, T> {
625619
};
626620

627621
template <> class Relational<SomeType> {
628-
// COMPLEX data are compared piecewise.
629-
using DirectlyComparableTypes =
630-
common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
622+
using DirectlyComparableTypes = common::CombineTuples<IntegerTypes, RealTypes,
623+
ComplexTypes, CharacterTypes>;
631624

632625
public:
633626
using Result = LogicalResult;

flang/include/flang/Evaluate/tools.h

Lines changed: 21 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -331,49 +331,29 @@ template <typename A> const Symbol *GetFirstSymbol(const A &x) {
331331
template <typename TO, TypeCategory FROMCAT>
332332
Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
333333
static_assert(IsSpecificIntrinsicType<TO>);
334-
if constexpr (FROMCAT != TO::category) {
335-
if constexpr (TO::category == TypeCategory::Complex) {
336-
using Part = typename TO::Part;
337-
Scalar<Part> zero;
338-
return Expr<TO>{ComplexConstructor<TO::kind>{
339-
ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
340-
} else if constexpr (FROMCAT == TypeCategory::Complex) {
341-
// Extract and convert the real component of a complex value
342-
return std::visit(
343-
[&](auto &&z) {
344-
using ZType = ResultType<decltype(z)>;
345-
using Part = typename ZType::Part;
346-
return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
347-
Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
348-
},
349-
std::move(x.u));
350-
} else {
351-
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
352-
}
353-
} else {
354-
// Same type category
334+
if constexpr (FROMCAT == TO::category) {
355335
if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
356336
return std::move(*already);
357-
}
358-
if constexpr (TO::category == TypeCategory::Complex) {
359-
// Extract, convert, and recombine the components.
360-
return Expr<TO>{std::visit(
361-
[](auto &z) {
362-
using FromType = ResultType<decltype(z)>;
363-
using FromPart = typename FromType::Part;
364-
using FromGeneric = SomeKind<TypeCategory::Real>;
365-
using ToPart = typename TO::Part;
366-
Convert<ToPart, TypeCategory::Real> re{Expr<FromGeneric>{
367-
Expr<FromPart>{ComplexComponent<FromType::kind>{false, z}}}};
368-
Convert<ToPart, TypeCategory::Real> im{Expr<FromGeneric>{
369-
Expr<FromPart>{ComplexComponent<FromType::kind>{true, z}}}};
370-
return ComplexConstructor<TO::kind>{
371-
AsExpr(std::move(re)), AsExpr(std::move(im))};
372-
},
373-
x.u)};
374337
} else {
375-
return Expr<TO>{Convert<TO, TO::category>{std::move(x)}};
338+
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
376339
}
340+
} else if constexpr (TO::category == TypeCategory::Complex) {
341+
using Part = typename TO::Part;
342+
Scalar<Part> zero;
343+
return Expr<TO>{ComplexConstructor<TO::kind>{
344+
ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
345+
} else if constexpr (FROMCAT == TypeCategory::Complex) {
346+
// Extract and convert the real component of a complex value
347+
return std::visit(
348+
[&](auto &&z) {
349+
using ZType = ResultType<decltype(z)>;
350+
using Part = typename ZType::Part;
351+
return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
352+
Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
353+
},
354+
std::move(x.u));
355+
} else {
356+
return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
377357
}
378358
}
379359

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

525505
// Combine two expressions of the same specific numeric type with an operation
526-
// to produce a new expression. Implements piecewise addition and subtraction
527-
// for COMPLEX.
506+
// to produce a new expression.
528507
template <template <typename> class OPR, typename SPECIFIC>
529508
Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
530509
static_assert(IsSpecificIntrinsicType<SPECIFIC>);
531-
if constexpr (SPECIFIC::category == TypeCategory::Complex &&
532-
(std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
533-
std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>)) {
534-
static constexpr int kind{SPECIFIC::kind};
535-
using Part = Type<TypeCategory::Real, kind>;
536-
return AsExpr(ComplexConstructor<kind>{
537-
AsExpr(OPR<Part>{AsExpr(ComplexComponent<kind>{false, x}),
538-
AsExpr(ComplexComponent<kind>{false, y})}),
539-
AsExpr(OPR<Part>{AsExpr(ComplexComponent<kind>{true, x}),
540-
AsExpr(ComplexComponent<kind>{true, y})})});
541-
} else {
542-
return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
543-
}
510+
return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
544511
}
545512

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

623-
template <int K>
624-
Expr<Type<TypeCategory::Complex, K>> operator-(
625-
Expr<Type<TypeCategory::Complex, K>> &&x) {
626-
using Part = Type<TypeCategory::Real, K>;
627-
return AsExpr(ComplexConstructor<K>{
628-
AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{false, x})}),
629-
AsExpr(Negate<Part>{AsExpr(ComplexComponent<K>{true, x})})});
630-
}
631-
632590
template <TypeCategory C, int K>
633591
Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
634592
return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));

flang/lib/Evaluate/fold-implementation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,15 @@ Expr<TO> FoldOperation(
12361236
}
12371237
return ScalarConstantToExpr(std::move(converted.value));
12381238
}
1239+
} else if constexpr (TO::category == TypeCategory::Complex) {
1240+
if constexpr (Operand::category == TypeCategory::Complex) {
1241+
return FoldOperation(ctx,
1242+
ComplexConstructor<TO::kind>{
1243+
AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
1244+
Constant<typename Operand::Part>{value->REAL()})}),
1245+
AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
1246+
Constant<typename Operand::Part>{value->AIMAG()})})});
1247+
}
12391248
} else if constexpr (TO::category == TypeCategory::Character &&
12401249
Operand::category == TypeCategory::Character) {
12411250
if (auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {

flang/lib/Evaluate/fold-logical.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,13 @@ Expr<LogicalResult> FoldOperation(
134134
Satisfies(relation.opr, folded->first.CompareSigned(folded->second));
135135
} else if constexpr (T::category == TypeCategory::Real) {
136136
result = Satisfies(relation.opr, folded->first.Compare(folded->second));
137+
} else if constexpr (T::category == TypeCategory::Complex) {
138+
result = (relation.opr == RelationalOperator::EQ) ==
139+
folded->first.Equals(folded->second);
137140
} else if constexpr (T::category == TypeCategory::Character) {
138141
result = Satisfies(relation.opr, Compare(folded->first, folded->second));
139142
} else {
140-
static_assert(T::category != TypeCategory::Complex &&
141-
T::category != TypeCategory::Logical);
143+
static_assert(T::category != TypeCategory::Logical);
142144
}
143145
return Expr<LogicalResult>{Constant<LogicalResult>{result}};
144146
}

flang/lib/Evaluate/formatting.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ template <typename TO, TypeCategory FROMCAT>
349349
llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
350350
static_assert(TO::category == TypeCategory::Integer ||
351351
TO::category == TypeCategory::Real ||
352+
TO::category == TypeCategory::Complex ||
352353
TO::category == TypeCategory::Character ||
353354
TO::category == TypeCategory::Logical,
354355
"Convert<> to bad category!");
@@ -358,6 +359,8 @@ llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
358359
this->left().AsFortran(o << "int(");
359360
} else if constexpr (TO::category == TypeCategory::Real) {
360361
this->left().AsFortran(o << "real(");
362+
} else if constexpr (TO::category == TypeCategory::Complex) {
363+
this->left().AsFortran(o << "cmplx(");
361364
} else {
362365
this->left().AsFortran(o << "logical(");
363366
}

flang/lib/Evaluate/tools.cpp

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
6969
return symbol && symbol->attrs().test(semantics::Attr::POINTER);
7070
}
7171

72-
// Conversions of complex component expressions to REAL.
72+
// Conversions of COMPLEX component expressions to REAL.
7373
ConvertRealOperandsResult ConvertRealOperands(
7474
parser::ContextualMessages &messages, Expr<SomeType> &&x,
7575
Expr<SomeType> &&y, int defaultRealKind) {
@@ -498,31 +498,14 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
498498
},
499499
[&](Expr<SomeComplex> &&zx,
500500
Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
501-
if (opr != RelationalOperator::EQ &&
502-
opr != RelationalOperator::NE) {
501+
if (opr == RelationalOperator::EQ ||
502+
opr == RelationalOperator::NE) {
503+
return PromoteAndRelate(opr, std::move(zx), std::move(zy));
504+
} else {
503505
messages.Say(
504506
"COMPLEX data may be compared only for equality"_err_en_US);
505-
} else {
506-
auto rr{Relate(messages, opr,
507-
AsGenericExpr(GetComplexPart(zx, false)),
508-
AsGenericExpr(GetComplexPart(zy, false)))};
509-
auto ri{
510-
Relate(messages, opr, AsGenericExpr(GetComplexPart(zx, true)),
511-
AsGenericExpr(GetComplexPart(zy, true)))};
512-
if (auto parts{
513-
common::AllPresent(std::move(rr), std::move(ri))}) {
514-
// (a,b)==(c,d) -> (a==c) .AND. (b==d)
515-
// (a,b)/=(c,d) -> (a/=c) .OR. (b/=d)
516-
LogicalOperator combine{opr == RelationalOperator::EQ
517-
? LogicalOperator::And
518-
: LogicalOperator::Or};
519-
return Expr<LogicalResult>{
520-
LogicalOperation<LogicalResult::kind>{combine,
521-
std::get<0>(std::move(*parts)),
522-
std::get<1>(std::move(*parts))}};
523-
}
507+
return std::nullopt;
524508
}
525-
return std::nullopt;
526509
},
527510
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
528511
return Relate(messages, opr, std::move(x),

0 commit comments

Comments
 (0)