Skip to content

[flang] Don't blow up when combining mixed COMPLEX operations #66235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2023

Conversation

klausler
Copy link
Contributor

Expression processing applies some straightforward rewriting of mixed complex/real and complex/integer operations to avoid having to promote the real/integer operand to complex and then perform a complex operation; for example, (a,b)+x becomes (a+x,b) rather than (a,b)+(x,0). But this can blow up the expression representation when the complex operand cannot be duplicated cheaply. So apply this technique only to complex operands that are appropriate to duplicate.

Fixes #65142.

@klausler klausler requested a review from vdonaldson September 13, 2023 16:42
@klausler klausler requested a review from a team as a code owner September 13, 2023 16:42
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:semantics labels Sep 13, 2023
@llvmbot
Copy link
Member

llvmbot commented Sep 13, 2023

@llvm/pr-subscribers-flang-semantics

Changes Expression processing applies some straightforward rewriting of mixed complex/real and complex/integer operations to avoid having to promote the real/integer operand to complex and then perform a complex operation; for example, (a,b)+x becomes (a+x,b) rather than (a,b)+(x,0). But this can blow up the expression representation when the complex operand cannot be duplicated cheaply. So apply this technique only to complex operands that are appropriate to duplicate.

Fixes #65142.

Patch is 20.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66235.diff

3 Files Affected:

  • (modified) flang/include/flang/Evaluate/tools.h (-10)
  • (modified) flang/lib/Evaluate/tools.cpp (+175-88)
  • (added) flang/test/Evaluate/bug65142.f90 (+14)

<pre>
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index b3f8f4a67a7b5dd..a3aac185f8542f9 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -149,16 +149,6 @@ common::IfNoLvalue&lt;Expr&lt;SomeKind&lt;ResultType&lt;A&gt;::category&gt;&gt;, A&gt; AsCategoryExpr(

Expr&lt;SomeType&gt; Parenthesize(Expr&lt;SomeType&gt; &amp;&amp;);

-Expr&lt;SomeReal&gt; GetComplexPart(

  • const Expr&lt;SomeComplex&gt; &amp;, bool isImaginary = false);
    -Expr&lt;SomeReal&gt; GetComplexPart(Expr&lt;SomeComplex&gt; &amp;&amp;, bool isImaginary = false);

-template &lt;int KIND&gt;
-Expr&lt;SomeComplex&gt; MakeComplex(Expr&lt;Type&lt;TypeCategory::Real, KIND&gt;&gt; &amp;&amp;re,

  • Expr&lt;Type&lt;TypeCategory::Real, KIND&gt;&gt; &amp;&amp;im) {
  • return AsCategoryExpr(ComplexConstructor&lt;KIND&gt;{std::move(re), std::move(im)});
    -}

template &lt;typename A&gt; constexpr bool IsNumericCategoryExpr() {
if constexpr (common::HasMember&lt;A, TypelessExpression&gt;) {
return false;
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index d2fa5c9b5f36be6..b1bb1c1c6be8b1d 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -180,8 +180,9 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; Package(
std::optional&lt;Expr&lt;SomeKind&lt;CAT&gt;&gt;&gt; &amp;&amp;catExpr) {
if (catExpr) {
return {AsGenericExpr(std::move(*catExpr))};

  • } else {
  • return std::nullopt;
    }
  • return NoExpr();
    }

// Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
@@ -204,6 +205,12 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; MixedRealLeft(
std::move(rx.u)));
}

+template &lt;int KIND&gt;
+Expr&lt;SomeComplex&gt; MakeComplex(Expr&lt;Type&lt;TypeCategory::Real, KIND&gt;&gt; &amp;&amp;re,

  • Expr&lt;Type&lt;TypeCategory::Real, KIND&gt;&gt; &amp;&amp;im) {
  • return AsCategoryExpr(ComplexConstructor&lt;KIND&gt;{std::move(re), std::move(im)});
    +}

std::optional&lt;Expr&lt;SomeComplex&gt;&gt; ConstructComplex(
parser::ContextualMessages &amp;messages, Expr&lt;SomeType&gt; &amp;&amp;real,
Expr&lt;SomeType&gt; &amp;&amp;imaginary, int defaultRealKind) {
@@ -228,24 +235,87 @@ std::optional&lt;Expr&lt;SomeComplex&gt;&gt; ConstructComplex(
return std::nullopt;
}

-Expr&lt;SomeReal&gt; GetComplexPart(const Expr&lt;SomeComplex&gt; &amp;z, bool isImaginary) {

  • return common::visit(
  •  [&amp;amp;](const auto &amp;amp;zk) {
    
  •    static constexpr int kind{ResultType&amp;lt;decltype(zk)&amp;gt;::kind};
    
  •    return AsCategoryExpr(ComplexComponent&amp;lt;kind&amp;gt;{isImaginary, zk});
    
  •  },
    
  •  z.u);
    

-}
+// Extracts the real or imaginary part of the result of a COMPLEX
+// expression, when that expression is simple enough to be duplicated.
+template &lt;bool GET_IMAGINARY&gt; struct ComplexPartExtractor {

  • template &lt;typename A&gt; static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(const A &amp;) {
  • return std::nullopt;
  • }

-Expr&lt;SomeReal&gt; GetComplexPart(Expr&lt;SomeComplex&gt; &amp;&amp;z, bool isImaginary) {

  • return common::visit(
  •  [&amp;amp;](auto &amp;amp;&amp;amp;zk) {
    
  •    static constexpr int kind{ResultType&amp;lt;decltype(zk)&amp;gt;::kind};
    
  •    return AsCategoryExpr(
    
  •        ComplexComponent&amp;lt;kind&amp;gt;{isImaginary, std::move(zk)});
    
  •  },
    
  •  z.u);
    

-}

  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Parentheses&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt; &amp;amp;kz) {
    
  • if (auto x{Get(kz.left())}) {
  •  return AsGenericExpr(AsSpecificExpr(
    
  •      Parentheses&amp;lt;Type&amp;lt;TypeCategory::Real, KIND&amp;gt;&amp;gt;{std::move(*x)}));
    
  • } else {
  •  return std::nullopt;
    
  • }
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Negate&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt; &amp;amp;kz) {
    
  • if (auto x{Get(kz.left())}) {
  •  return AsGenericExpr(AsSpecificExpr(
    
  •      Negate&amp;lt;Type&amp;lt;TypeCategory::Real, KIND&amp;gt;&amp;gt;{std::move(*x)}));
    
  • } else {
  •  return std::nullopt;
    
  • }
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Convert&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;, TypeCategory::Complex&amp;gt;
    
  •      &amp;amp;kz) {
    
  • if (auto x{Get(kz.left())}) {
  •  return AsGenericExpr(AsSpecificExpr(
    
  •      Convert&amp;lt;Type&amp;lt;TypeCategory::Real, KIND&amp;gt;, TypeCategory::Real&amp;gt;{
    
  •          AsGenericExpr(std::move(*x))}));
    
  • } else {
  •  return std::nullopt;
    
  • }
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(const ComplexConstructor&lt;KIND&gt; &amp;kz) {
  • return GET_IMAGINARY ? Get(kz.right()) : Get(kz.left());
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Constant&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt; &amp;amp;kz) {
    
  • if (auto cz{kz.GetScalarValue()}) {
  •  return AsGenericExpr(
    
  •      AsSpecificExpr(GET_IMAGINARY ? cz-&amp;gt;AIMAG() : cz-&amp;gt;REAL()));
    
  • } else {
  •  return std::nullopt;
    
  • }
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Designator&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt; &amp;amp;kz) {
    
  • if (const auto *symbolRef{std::get_if&lt;SymbolRef&gt;(&amp;kz.u)}) {
  •  return AsGenericExpr(AsSpecificExpr(
    
  •      Designator&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt;{ComplexPart{
    
  •          DataRef{*symbolRef},
    
  •          GET_IMAGINARY ? ComplexPart::Part::IM : ComplexPart::Part::RE}}));
    
  • } else {
  •  return std::nullopt;
    
  • }
  • }
  • template &lt;int KIND&gt;
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(
  •  const Expr&amp;lt;Type&amp;lt;TypeCategory::Complex, KIND&amp;gt;&amp;gt; &amp;amp;kz) {
    
  • return Get(kz.u);
  • }
  • static std::optional&lt;Expr&lt;SomeReal&gt;&gt; Get(const Expr&lt;SomeComplex&gt; &amp;z) {
  • return Get(z.u);
  • }
    +};

// Convert REAL to COMPLEX of the same kind. Preserving the real operand kind
// and then applying complex operand promotion rules allows the result to have
@@ -266,19 +336,31 @@ Expr&lt;SomeComplex&gt; PromoteRealToComplex(Expr&lt;SomeReal&gt; &amp;&amp;someX) {
// corresponding COMPLEX+COMPLEX operation.
template &lt;template &lt;typename&gt; class OPR, TypeCategory RCAT&gt;
std::optional&lt;Expr&lt;SomeType&gt;&gt; MixedComplexLeft(

  • parser::ContextualMessages &amp;messages, Expr&lt;SomeComplex&gt; &amp;&amp;zx,
  • Expr&lt;SomeKind&lt;RCAT&gt;&gt; &amp;&amp;iry, [[maybe_unused]] int defaultRealKind) {
  • Expr&lt;SomeReal&gt; zr{GetComplexPart(zx, false)};
  • Expr&lt;SomeReal&gt; zi{GetComplexPart(zx, true)};
  • if constexpr (std::is_same_v&lt;OPR&lt;LargestReal&gt;, Add&lt;LargestReal&gt;&gt; ||
  • parser::ContextualMessages &amp;messages, const Expr&lt;SomeComplex&gt; &amp;zx,
  • const Expr&lt;SomeKind&lt;RCAT&gt;&gt; &amp;iry, [[maybe_unused]] int defaultRealKind) {
  • if constexpr (RCAT == TypeCategory::Integer &amp;&amp;
  •  std::is_same_v&amp;lt;OPR&amp;lt;LargestReal&amp;gt;, Power&amp;lt;LargestReal&amp;gt;&amp;gt;) {
    
  • // COMPLEX**INTEGER is a special case that doesn&#x27;t convert the exponent.
  • return Package(common::visit(
  •    [&amp;amp;](const auto &amp;amp;zxk) {
    
  •      using Ty = ResultType&amp;lt;decltype(zxk)&amp;gt;;
    
  •      return AsCategoryExpr(AsExpr(
    
  •          RealToIntPower&amp;lt;Ty&amp;gt;{common::Clone(zxk), common::Clone(iry)}));
    
  •    },
    
  •    zx.u));
    
  • }
  • std::optional&lt;Expr&lt;SomeReal&gt;&gt; zr{ComplexPartExtractor&lt;false&gt;{}.Get(zx)};
  • std::optional&lt;Expr&lt;SomeReal&gt;&gt; zi{ComplexPartExtractor&lt;true&gt;{}.Get(zx)};
  • if (!zr || !zi) {
  • } else if constexpr (std::is_same_v&lt;OPR&lt;LargestReal&gt;, Add&lt;LargestReal&gt;&gt; ||
    std::is_same_v&lt;OPR&lt;LargestReal&gt;, Subtract&lt;LargestReal&gt;&gt;) {
    // (a,b) + x -&gt; (a+x, b)
    // (a,b) - x -&gt; (a-x, b)
    if (std::optional&lt;Expr&lt;SomeType&gt;&gt; rr{
  •        NumericOperation&amp;lt;OPR&amp;gt;(messages, AsGenericExpr(std::move(zr)),
    
  •            AsGenericExpr(std::move(iry)), defaultRealKind)}) {
    
  •        NumericOperation&amp;lt;OPR&amp;gt;(messages, AsGenericExpr(std::move(*zr)),
    
  •            AsGenericExpr(common::Clone(iry)), defaultRealKind)}) {
     return Package(ConstructComplex(messages, std::move(*rr),
    
  •      AsGenericExpr(std::move(zi)), defaultRealKind));
    
  •      AsGenericExpr(std::move(*zi)), defaultRealKind));
    
    }
    } else if constexpr (allowOperandDuplication &amp;&amp;
    (std::is_same_v&lt;OPR&lt;LargestReal&gt;, Multiply&lt;LargestReal&gt;&gt; ||
    @@ -286,36 +368,16 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; MixedComplexLeft(
    // (a,b) * x -&gt; (ax, bx)
    // (a,b) / x -&gt; (a/x, b/x)
    auto copy{iry};
  • auto rr{NumericOperation&lt;OPR&gt;(messages, AsGenericExpr(std::move(zr)),
  •    AsGenericExpr(std::move(iry)), defaultRealKind)};
    
  • auto ri{NumericOperation&lt;OPR&gt;(messages, AsGenericExpr(std::move(zi)),
  • auto rr{NumericOperation&lt;OPR&gt;(messages, AsGenericExpr(std::move(*zr)),
  •    AsGenericExpr(common::Clone(iry)), defaultRealKind)};
    
  • auto ri{NumericOperation&lt;OPR&gt;(messages, AsGenericExpr(std::move(*zi)),
    AsGenericExpr(std::move(copy)), defaultRealKind)};
    if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
    return Package(ConstructComplex(messages, std::get&lt;0&gt;(std::move(*parts)),
    std::get&lt;1&gt;(std::move(*parts)), defaultRealKind));
    }
  • } else if constexpr (RCAT == TypeCategory::Integer &amp;&amp;
  •  std::is_same_v&amp;lt;OPR&amp;lt;LargestReal&amp;gt;, Power&amp;lt;LargestReal&amp;gt;&amp;gt;) {
    
  • // COMPLEX**INTEGER is a special case that doesn&#x27;t convert the exponent.
  • static_assert(RCAT == TypeCategory::Integer);
  • return Package(common::visit(
  •    [&amp;amp;](auto &amp;amp;&amp;amp;zxk) {
    
  •      using Ty = ResultType&amp;lt;decltype(zxk)&amp;gt;;
    
  •      return AsCategoryExpr(
    
  •          AsExpr(RealToIntPower&amp;lt;Ty&amp;gt;{std::move(zxk), std::move(iry)}));
    
  •    },
    
  •    std::move(zx.u)));
    
  • } else {
  • // (a,b) ** x -&gt; (a,b) ** (x,0)
  • if constexpr (RCAT == TypeCategory::Integer) {
  •  Expr&amp;lt;SomeComplex&amp;gt; zy{ConvertTo(zx, std::move(iry))};
    
  •  return Package(PromoteAndCombine&amp;lt;OPR&amp;gt;(std::move(zx), std::move(zy)));
    
  • } else {
  •  Expr&amp;lt;SomeComplex&amp;gt; zy{PromoteRealToComplex(std::move(iry))};
    
  •  return Package(PromoteAndCombine&amp;lt;OPR&amp;gt;(std::move(zx), std::move(zy)));
    
  • }
    }
  • return NoExpr();
  • return std::nullopt;
    }

// Mixed COMPLEX operations with the COMPLEX operand on the right.
@@ -325,39 +387,49 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; MixedComplexLeft(
// x / (a,b) -&gt; (x,0) / (a,b) (and **)
template &lt;template &lt;typename&gt; class OPR, TypeCategory LCAT&gt;
std::optional&lt;Expr&lt;SomeType&gt;&gt; MixedComplexRight(

  • parser::ContextualMessages &amp;messages, Expr&lt;SomeKind&lt;LCAT&gt;&gt; &amp;&amp;irx,
  • Expr&lt;SomeComplex&gt; &amp;&amp;zy, [[maybe_unused]] int defaultRealKind) {
  • parser::ContextualMessages &amp;messages, const Expr&lt;SomeKind&lt;LCAT&gt;&gt; &amp;irx,
  • const Expr&lt;SomeComplex&gt; &amp;zy, [[maybe_unused]] int defaultRealKind) {
    if constexpr (std::is_same_v&lt;OPR&lt;LargestReal&gt;, Add&lt;LargestReal&gt;&gt;) {
    // x + (a,b) -&gt; (a,b) + x -&gt; (a+x, b)
  • return MixedComplexLeft&lt;OPR, LCAT&gt;(
  •    messages, std::move(zy), std::move(irx), defaultRealKind);
    
  • return MixedComplexLeft&lt;OPR, LCAT&gt;(messages, zy, irx, defaultRealKind);
    } else if constexpr (allowOperandDuplication &amp;&amp;
    std::is_same_v&lt;OPR&lt;LargestReal&gt;, Multiply&lt;LargestReal&gt;&gt;) {
    // x * (a,b) -&gt; (a,b) * x -&gt; (ax, bx)
  • return MixedComplexLeft&lt;OPR, LCAT&gt;(
  •    messages, std::move(zy), std::move(irx), defaultRealKind);
    
  • return MixedComplexLeft&lt;OPR, LCAT&gt;(messages, zy, irx, defaultRealKind);
    } else if constexpr (std::is_same_v&lt;OPR&lt;LargestReal&gt;,
    Subtract&lt;LargestReal&gt;&gt;) {
    // x - (a,b) -&gt; (x-a, -b)
  • Expr&lt;SomeReal&gt; zr{GetComplexPart(zy, false)};
  • Expr&lt;SomeReal&gt; zi{GetComplexPart(zy, true)};
  • if (std::optional&lt;Expr&lt;SomeType&gt;&gt; rr{
  •        NumericOperation&amp;lt;Subtract&amp;gt;(messages, AsGenericExpr(std::move(irx)),
    
  •            AsGenericExpr(std::move(zr)), defaultRealKind)}) {
    
  •  return Package(ConstructComplex(messages, std::move(*rr),
    
  •      AsGenericExpr(-std::move(zi)), defaultRealKind));
    
  • }
  • } else {
  • // x / (a,b) -&gt; (x,0) / (a,b)
  • if constexpr (LCAT == TypeCategory::Integer) {
  •  Expr&amp;lt;SomeComplex&amp;gt; zx{ConvertTo(zy, std::move(irx))};
    
  •  return Package(PromoteAndCombine&amp;lt;OPR&amp;gt;(std::move(zx), std::move(zy)));
    
  • } else {
  •  Expr&amp;lt;SomeComplex&amp;gt; zx{PromoteRealToComplex(std::move(irx))};
    
  •  return Package(PromoteAndCombine&amp;lt;OPR&amp;gt;(std::move(zx), std::move(zy)));
    
  • std::optional&lt;Expr&lt;SomeReal&gt;&gt; zr{ComplexPartExtractor&lt;false&gt;{}.Get(zy)};
  • std::optional&lt;Expr&lt;SomeReal&gt;&gt; zi{ComplexPartExtractor&lt;true&gt;{}.Get(zy)};
  • if (zr &amp;&amp; zi) {
  •  if (std::optional&amp;lt;Expr&amp;lt;SomeType&amp;gt;&amp;gt; rr{NumericOperation&amp;lt;Subtract&amp;gt;(messages,
    
  •          AsGenericExpr(common::Clone(irx)), AsGenericExpr(std::move(*zr)),
    
  •          defaultRealKind)}) {
    
  •    return Package(ConstructComplex(messages, std::move(*rr),
    
  •        AsGenericExpr(-std::move(*zi)), defaultRealKind));
    
  •  }
    
    }
    }
  • return NoExpr();
  • return std::nullopt;
    +}

+// Promotes REAL(rk) and COMPLEX(zk) operands COMPLEX(max(rk,zk))
+// then combine them with an operator.
+template &lt;template &lt;typename&gt; class OPR, TypeCategory XCAT, TypeCategory YCAT&gt;
+Expr&lt;SomeComplex&gt; PromoteMixedComplexReal(

  • Expr&lt;SomeKind&lt;XCAT&gt;&gt; &amp;&amp;x, Expr&lt;SomeKind&lt;YCAT&gt;&gt; &amp;&amp;y) {
  • static_assert(XCAT == TypeCategory::Complex || YCAT == TypeCategory::Complex);
  • static_assert(XCAT == TypeCategory::Real || YCAT == TypeCategory::Real);
  • return common::visit(
  •  [&amp;amp;](const auto &amp;amp;kx, const auto &amp;amp;ky) {
    
  •    constexpr int maxKind{std::max(
    
  •        ResultType&amp;lt;decltype(kx)&amp;gt;::kind, ResultType&amp;lt;decltype(ky)&amp;gt;::kind)};
    
  •    using ZTy = Type&amp;lt;TypeCategory::Complex, maxKind&amp;gt;;
    
  •    return Expr&amp;lt;SomeComplex&amp;gt;{
    
  •        Expr&amp;lt;ZTy&amp;gt;{OPR&amp;lt;ZTy&amp;gt;{ConvertToType&amp;lt;ZTy&amp;gt;(std::move(x)),
    
  •            ConvertToType&amp;lt;ZTy&amp;gt;(std::move(y))}}};
    
  •  },
    
  •  x.u, y.u);
    

}

// N.B. When a &quot;typeless&quot; BOZ literal constant appears as one (not both!) of
@@ -397,20 +469,40 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; NumericOperation(
std::move(zx), std::move(zy)));
},
[&amp;](Expr&lt;SomeComplex&gt; &amp;&amp;zx, Expr&lt;SomeInteger&gt; &amp;&amp;iy) {

  •        return MixedComplexLeft&amp;lt;OPR&amp;gt;(
    
  •            messages, std::move(zx), std::move(iy), defaultRealKind);
    
  •        if (auto result{
    
  •                MixedComplexLeft&amp;lt;OPR&amp;gt;(messages, zx, iy, defaultRealKind)}) {
    
  •          return result;
    
  •        } else {
    
  •          return Package(PromoteAndCombine&amp;lt;OPR, TypeCategory::Complex&amp;gt;(
    
  •              std::move(zx), ConvertTo(zx, std::move(iy))));
    
  •        }
         },
         [&amp;amp;](Expr&amp;lt;SomeComplex&amp;gt; &amp;amp;&amp;amp;zx, Expr&amp;lt;SomeReal&amp;gt; &amp;amp;&amp;amp;ry) {
    
  •        return MixedComplexLeft&amp;lt;OPR&amp;gt;(
    
  •            messages, std::move(zx), std::move(ry), defaultRealKind);
    
  •        if (auto result{
    
  •                MixedComplexLeft&amp;lt;OPR&amp;gt;(messages, zx, ry, defaultRealKind)}) {
    
  •          return result;
    
  •        } else {
    
  •          return Package(
    
  •              PromoteMixedComplexReal&amp;lt;OPR&amp;gt;(std::move(zx), std::move(ry)));
    
  •        }
         },
         [&amp;amp;](Expr&amp;lt;SomeInteger&amp;gt; &amp;amp;&amp;amp;ix, Expr&amp;lt;SomeComplex&amp;gt; &amp;amp;&amp;amp;zy) {
    
  •        return MixedComplexRight&amp;lt;OPR&amp;gt;(
    
  •            messages, std::move(ix), std::move(zy), defaultRealKind);
    
  •        if (auto result{MixedComplexRight&amp;lt;OPR&amp;gt;(
    
  •                messages, ix, zy, defaultRealKind)}) {
    
  •          return result;
    
  •        } else {
    
  •          return Package(PromoteAndCombine&amp;lt;OPR, TypeCategory::Complex&amp;gt;(
    
  •              ConvertTo(zy, std::move(ix)), std::move(zy)));
    
  •        }
         },
         [&amp;amp;](Expr&amp;lt;SomeReal&amp;gt; &amp;amp;&amp;amp;rx, Expr&amp;lt;SomeComplex&amp;gt; &amp;amp;&amp;amp;zy) {
    
  •        return MixedComplexRight&amp;lt;OPR&amp;gt;(
    
  •            messages, std::move(rx), std::move(zy), defaultRealKind);
    
  •        if (auto result{MixedComplexRight&amp;lt;OPR&amp;gt;(
    
  •                messages, rx, zy, defaultRealKind)}) {
    
  •          return result;
    
  •        } else {
    
  •          return Package(
    
  •              PromoteMixedComplexReal&amp;lt;OPR&amp;gt;(std::move(rx), std::move(zy)));
    
  •        }
         },
         // Operations with one typeless operand
         [&amp;amp;](BOZLiteralConstant &amp;amp;&amp;amp;bx, Expr&amp;lt;SomeInteger&amp;gt; &amp;amp;&amp;amp;iy) {
    

@@ -433,7 +525,6 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; NumericOperation(
},
// Default case
[&amp;](auto &amp;&amp;, auto &amp;&amp;) {

  •        // TODO: defined operator
           messages.Say(&amp;quot;non-numeric operands to numeric operation&amp;quot;_err_en_US);
           return NoExpr();
         },
    

@@ -481,17 +572,14 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; Negation(
[&amp;](Expr&lt;SomeReal&gt; &amp;&amp;x) { return Package(-std::move(x)); },
[&amp;](Expr&lt;SomeComplex&gt; &amp;&amp;x) { return Package(-std::move(x)); },
[&amp;](Expr&lt;SomeCharacter&gt; &amp;&amp;) {

  •        // TODO: defined operator
           messages.Say(&amp;quot;CHARACTER cannot be negated&amp;quot;_err_en_US);
           return NoExpr();
         },
         [&amp;amp;](Expr&amp;lt;SomeLogical&amp;gt; &amp;amp;&amp;amp;) {
    
  •        // TODO: defined operator
           messages.Say(&amp;quot;LOGICAL cannot be negated&amp;quot;_err_en_US);
           return NoExpr();
         },
         [&amp;amp;](Expr&amp;lt;SomeDerived&amp;gt; &amp;amp;&amp;amp;) {
    
  •        // TODO: defined operator
           messages.Say(&amp;quot;Operand cannot be negated&amp;quot;_err_en_US);
           return NoExpr();
         },
    

@@ -643,8 +731,7 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; ConvertToType(
if (auto length{type.GetCharLength()}) {
converted = common::visit(
[&amp;](auto &amp;&amp;x) {

  •          using Ty = std::decay_t&amp;lt;decltype(x)&amp;gt;;
    
  •          using CharacterType = typename Ty::Result;
    
  •          using CharacterType = ResultType&amp;lt;decltype(x)&amp;gt;;
             return Expr&amp;lt;SomeCharacter&amp;gt;{
                 Expr&amp;lt;CharacterType&amp;gt;{SetLength&amp;lt;CharacterType::kind&amp;gt;{
                     std::move(x), std::move(*length)}}};
    

@@ -1100,7 +1187,7 @@ static std::optional&lt;Expr&lt;SomeType&gt;&gt; DataConstantConversionHelper(
if (const auto *someExpr{UnwrapExpr&lt;Expr&lt;SomeKind&lt;FROM&gt;&gt;&gt;(*sized)}) {
return common::visit(
[](const auto &amp;w) -&gt; std::optional&lt;Expr&lt;SomeType&gt;&gt; {

  •        using FromType = typename std::decay_t&amp;lt;decltype(w)&amp;gt;::Result;
    
  •        using FromType = ResultType&amp;lt;decltype(w)&amp;gt;;
           static constexpr int kind{FromType::kind};
           if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
             if (const auto *fromConst{UnwrapExpr&amp;lt;Constant&amp;lt;FromType&amp;gt;&amp;gt;(w)}) {
    

diff --git a/flang/test/Evaluate/bug65142.f90 b/flang/test/Evaluate/bug65142.f90
new file mode 100644
index 000000000000000..e9bac4f5bbe0cb5
--- /dev/null
+++ b/flang/test/Evaluate/bug65142.f90
@@ -0,0 +1,14 @@
+! RUN: %flang_fc1 -fdebug-unparse %s 2&gt;&amp;1 | FileCheck %s
+! Ensure that expression rewriting doesn&#x27;t blow up when many
+! mixed complex operations are combined.
+! The result of folding (8,1.542956)**9 is c...

Expression processing applies some straightforward rewriting
of mixed complex/real and complex/integer operations to avoid
having to promote the real/integer operand to complex and then
perform a complex operation; for example, (a,b)+x becomes
(a+x,b) rather than (a,b)+(x,0).  But this can blow up the
expression representation when the complex operand cannot be
duplicated cheaply.  So apply this technique only to complex
operands that are appropriate to duplicate.

Fixes llvm#65142.

Pull request: llvm#66235
@klausler klausler merged commit daa5da0 into llvm:main Sep 13, 2023
@klausler klausler deleted the bug65142 branch September 13, 2023 23:34
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this pull request Sep 19, 2023
…6235)

Expression processing applies some straightforward rewriting of mixed
complex/real and complex/integer operations to avoid having to promote
the real/integer operand to complex and then perform a complex
operation; for example, (a,b)+x becomes (a+x,b) rather than (a,b)+(x,0).
But this can blow up the expression representation when the complex
operand cannot be duplicated cheaply. So apply this technique only to
complex operands that are appropriate to duplicate.

Fixes llvm#65142.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[flang] Long compile times and large memory usage for complex number expressions
3 participants