[flang-commits] [flang] [flang][Lower] Flatten signed real sum terms (PR #211829)

via flang-commits flang-commits at lists.llvm.org
Fri Jul 24 08:27:37 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Tom Eccles (tblah)

<details>
<summary>Changes</summary>

Second part of generalisations requested in #<!-- -->207377.

Extend real sum reassociation to flatten unparenthesized addition and subtraction into signed terms. Rebuild split groups with addition and subtraction while preserving parenthesized subtrees as opaque values.

I did not observe any benchmark result changes as a result of this patch.

Assisted-by: Codex

---

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


2 Files Affected:

- (modified) flang/lib/Evaluate/tools.cpp (+78-25) 
- (modified) flang/test/Lower/split-sum-expression-tree-lowering.f90 (+224-27) 


``````````diff
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 8742bb5000588..7b480255e2fec 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1360,33 +1360,79 @@ bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr) {
   return HasVolatileOrAsynchronousSymbolHelper{}(expr);
 }
 
+namespace {
+
 template <int KIND> using Real = Type<common::TypeCategory::Real, KIND>;
 
 template <int KIND> using RealExpr = Expr<Real<KIND>>;
 
+template <int KIND> struct SignedRealTerm {
+  RealExpr<KIND> expr;
+  bool isPositive;
+};
+
+template <int KIND> struct SignedRealExpr {
+  RealExpr<KIND> expr;
+  bool isPositive;
+};
+
 template <int KIND>
-static void flattenTopLevelAdds(
-    const RealExpr<KIND> &expr, llvm::SmallVectorImpl<RealExpr<KIND>> &terms) {
-  // Only flatten real Add nodes. Every other node, including Parentheses and
-  // Subtract, is one opaque term whose internal expression tree is preserved.
+static void flattenTopLevelAddSubtract(const RealExpr<KIND> &expr,
+    llvm::SmallVectorImpl<SignedRealTerm<KIND>> &terms,
+    bool isPositive = true) {
+  // Only flatten real Add and Subtract nodes. Every other node, including
+  // Parentheses, is one opaque signed term whose tree is preserved.
   if (const auto *add = std::get_if<Add<Real<KIND>>>(&expr.u)) {
-    flattenTopLevelAdds(add->left(), terms);
-    flattenTopLevelAdds(add->right(), terms);
+    flattenTopLevelAddSubtract(add->left(), terms, isPositive);
+    flattenTopLevelAddSubtract(add->right(), terms, isPositive);
+    return;
+  }
+  if (const auto *subtract = std::get_if<Subtract<Real<KIND>>>(&expr.u)) {
+    flattenTopLevelAddSubtract(subtract->left(), terms, isPositive);
+    flattenTopLevelAddSubtract(subtract->right(), terms, !isPositive);
     return;
   }
-  terms.push_back(expr);
+  terms.push_back(SignedRealTerm<KIND>{expr, isPositive});
 }
 
 template <int KIND>
-static RealExpr<KIND> buildRightAssociatedAddFold(
-    llvm::ArrayRef<RealExpr<KIND>> terms) {
-  assert(!terms.empty() && "cannot build empty add fold");
-  if (terms.size() == 1)
-    return terms.front();
-  RealExpr<KIND> result{terms.back()};
-  for (const RealExpr<KIND> &term : llvm::reverse(terms.drop_back()))
-    result = RealExpr<KIND>{Add<Real<KIND>>{term, result}};
-  return result;
+static SignedRealExpr<KIND> buildRightAssociatedSignedFold(
+    llvm::MutableArrayRef<SignedRealTerm<KIND>> terms) {
+  assert(!terms.empty() && "cannot build empty signed fold");
+  const bool isPositive{terms.front().isPositive};
+  RealExpr<KIND> result{std::move(terms.back().expr)};
+  for (std::size_t i{terms.size() - 1}; i > 0; --i) {
+    SignedRealTerm<KIND> &term{terms[i - 1]};
+    const bool useAdd{term.isPositive == terms[i].isPositive};
+    if (useAdd)
+      result = RealExpr<KIND>{
+          Add<Real<KIND>>{std::move(term.expr), std::move(result)}};
+    else
+      result = RealExpr<KIND>{
+          Subtract<Real<KIND>>{std::move(term.expr), std::move(result)}};
+  }
+  return SignedRealExpr<KIND>{std::move(result), isPositive};
+}
+
+template <int KIND>
+static SignedRealExpr<KIND> buildSignedAdd(
+    SignedRealExpr<KIND> left, SignedRealExpr<KIND> right) {
+  if (left.isPositive == right.isPositive) {
+    return SignedRealExpr<KIND>{
+        RealExpr<KIND>{
+            Add<Real<KIND>>{std::move(left.expr), std::move(right.expr)}},
+        left.isPositive};
+  }
+  if (left.isPositive) {
+    return SignedRealExpr<KIND>{
+        RealExpr<KIND>{
+            Subtract<Real<KIND>>{std::move(left.expr), std::move(right.expr)}},
+        true};
+  }
+  // Prefer Y-X to introducing a unary negation for -X+Y.
+  return SignedRealExpr<KIND>{RealExpr<KIND>{Subtract<Real<KIND>>{
+                                  std::move(right.expr), std::move(left.expr)}},
+      true};
 }
 
 template <typename T>
@@ -1397,20 +1443,25 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(const T &) {
 template <int KIND>
 static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(
     const RealExpr<KIND> &expr) {
-  if (!std::get_if<Add<Real<KIND>>>(&expr.u))
+  if (!std::get_if<Add<Real<KIND>>>(&expr.u) &&
+      !std::get_if<Subtract<Real<KIND>>>(&expr.u))
     return std::nullopt;
 
-  llvm::SmallVector<RealExpr<KIND>, 8> terms;
-  flattenTopLevelAdds(expr, terms);
+  llvm::SmallVector<SignedRealTerm<KIND>, 8> terms;
+  flattenTopLevelAddSubtract(expr, terms);
   if (terms.size() <= 2)
     return std::nullopt;
 
-  llvm::SmallVector<RealExpr<KIND>, 2> head{terms[0], terms[1]};
-  llvm::SmallVector<RealExpr<KIND>, 8> tail(terms.begin() + 2, terms.end());
-  RealExpr<KIND> headExpr = buildRightAssociatedAddFold<KIND>(head);
-  RealExpr<KIND> tailExpr = buildRightAssociatedAddFold<KIND>(tail);
-  return Expr<SomeType>{
-      RealExpr<KIND>{Add<Real<KIND>>{std::move(tailExpr), headExpr}}};
+  llvm::MutableArrayRef<SignedRealTerm<KIND>> head{terms.data(), 2};
+  llvm::MutableArrayRef<SignedRealTerm<KIND>> tail{
+      terms.data() + 2, terms.size() - 2};
+  SignedRealExpr<KIND> headExpr = buildRightAssociatedSignedFold<KIND>(head);
+  SignedRealExpr<KIND> tailExpr = buildRightAssociatedSignedFold<KIND>(tail);
+  SignedRealExpr<KIND> result =
+      buildSignedAdd<KIND>(std::move(tailExpr), std::move(headExpr));
+  assert(result.isPositive &&
+      "the first flattened term and therefore the split sum are positive");
+  return Expr<SomeType>{std::move(result.expr)};
 }
 
 template <common::TypeCategory CAT>
@@ -1426,6 +1477,8 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(
   return std::nullopt;
 }
 
+} // namespace
+
 bool CanBuildSplitSumExpressionTree(
     const Expr<SomeType> &lhs, const Expr<SomeType> &rhs) {
   return rhs.Rank() == 0 && lhs.Rank() == 0 && !HasVectorSubscript(rhs) &&
diff --git a/flang/test/Lower/split-sum-expression-tree-lowering.f90 b/flang/test/Lower/split-sum-expression-tree-lowering.f90
index 9ba921b9f5a07..1b1400f756177 100644
--- a/flang/test/Lower/split-sum-expression-tree-lowering.f90
+++ b/flang/test/Lower/split-sum-expression-tree-lowering.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -emit-hlfir -freal-sum-reassociation -o - %s | FileCheck %s --check-prefixes=SPLIT,NO-REWRITE
+! RUN: %flang_fc1 -emit-hlfir -freal-sum-reassociation -o - %s | FileCheck %s --check-prefixes=SPLIT,NO-REWRITE --implicit-check-not=arith.negf
 ! RUN: %flang_fc1 -emit-hlfir -fno-real-sum-reassociation -o - %s | FileCheck %s --check-prefixes=DEFAULT,NO-REWRITE
 ! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s --check-prefixes=DEFAULT,NO-REWRITE
 
@@ -380,46 +380,46 @@ subroutine guard_whole_rhs_parentheses(x,a,b,c,d,e,f)
 ! NO-REWRITE: %[[PAREN:.*]] = hlfir.no_reassoc %[[SUM]]
 ! NO-REWRITE: hlfir.assign %[[PAREN]] to %[[X]]#0
 
-! Subtract is one opaque term below the Add spine. This does not flatten the
-! expression into signed terms.
+! The unparenthesized Subtract is flattened into separate positive and negative
+! terms instead of remaining an opaque head term.
 ! Default:   (((x - a*b) + c*d) + e*f)
-! Rewritten: (e*f + ((x - a*b) + c*d))
-subroutine eligible_opaque_subtract(x,a,b,c,d,e,f)
+! Rewritten: (c*d + e*f) + (x - a*b)
+subroutine eligible_signed_subtract(x,a,b,c,d,e,f)
   real(8) :: x,a,b,c,d,e,f
   x = x - a*b + c*d + e*f
 end
 
-! SPLIT-LABEL: func.func @_QPeligible_opaque_subtract
-! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEa"}
-! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEb"}
-! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEc"}
-! SPLIT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEd"}
-! SPLIT-DAG: %[[E:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEe"}
-! SPLIT-DAG: %[[F:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEf"}
-! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEx"}
+! SPLIT-LABEL: func.func @_QPeligible_signed_subtract
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEa"}
+! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEb"}
+! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEc"}
+! SPLIT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEd"}
+! SPLIT-DAG: %[[E:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEe"}
+! SPLIT-DAG: %[[F:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEf"}
+! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEx"}
+! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
+! SPLIT: %[[DV:.*]] = fir.load %[[D]]#0
+! SPLIT: %[[CD:.*]] = arith.mulf %[[CV]], %[[DV]]
 ! SPLIT: %[[EV:.*]] = fir.load %[[E]]#0
 ! SPLIT: %[[FV:.*]] = fir.load %[[F]]#0
 ! SPLIT: %[[EF:.*]] = arith.mulf %[[EV]], %[[FV]]
+! SPLIT: %[[TAIL:.*]] = arith.addf %[[CD]], %[[EF]]
 ! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0
 ! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0
 ! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0
 ! SPLIT: %[[AB:.*]] = arith.mulf %[[AV]], %[[BV]]
-! SPLIT: %[[XAB:.*]] = arith.subf %[[XV]], %[[AB]]
-! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
-! SPLIT: %[[DV:.*]] = fir.load %[[D]]#0
-! SPLIT: %[[CD:.*]] = arith.mulf %[[CV]], %[[DV]]
-! SPLIT: %[[HEAD:.*]] = arith.addf %[[XAB]], %[[CD]]
-! SPLIT: %[[RES:.*]] = arith.addf %[[EF]], %[[HEAD]]
+! SPLIT: %[[HEAD:.*]] = arith.subf %[[XV]], %[[AB]]
+! SPLIT: %[[RES:.*]] = arith.addf %[[TAIL]], %[[HEAD]]
 ! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0
 
-! DEFAULT-LABEL: func.func @_QPeligible_opaque_subtract
-! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEa"}
-! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEb"}
-! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEc"}
-! DEFAULT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEd"}
-! DEFAULT-DAG: %[[E:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEe"}
-! DEFAULT-DAG: %[[F:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEf"}
-! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_opaque_subtractEx"}
+! DEFAULT-LABEL: func.func @_QPeligible_signed_subtract
+! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEa"}
+! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEb"}
+! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEc"}
+! DEFAULT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEd"}
+! DEFAULT-DAG: %[[E:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEe"}
+! DEFAULT-DAG: %[[F:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEf"}
+! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_signed_subtractEx"}
 ! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0
 ! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0
 ! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0
@@ -435,6 +435,203 @@ subroutine eligible_opaque_subtract(x,a,b,c,d,e,f)
 ! DEFAULT: %[[RES:.*]] = arith.addf %[[XABCD]], %[[EF]]
 ! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0
 
+! The tail starts negative. Rebuild -b+c as -(b-c), then use the explicitly
+! permitted -X+Y -> Y-X reassociation to avoid a unary negation:
+! Default:   (((x + a) - b) + c)
+! Rewritten: (x + a) - (b - c)
+subroutine eligible_leading_negative_tail(x,a,b,c)
+  real(8) :: x,a,b,c
+  x = x + a - b + c
+end
+
+! SPLIT-LABEL: func.func @_QPeligible_leading_negative_tail
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEa"}
+! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEb"}
+! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEc"}
+! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEx"}
+! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0
+! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0
+! SPLIT: %[[HEAD:.*]] = arith.addf %[[XV]], %[[AV]]
+! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0
+! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
+! SPLIT: %[[TAIL:.*]] = arith.subf %[[BV]], %[[CV]]
+! SPLIT: %[[RES:.*]] = arith.subf %[[HEAD]], %[[TAIL]]
+! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! DEFAULT-LABEL: func.func @_QPeligible_leading_negative_tail
+! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEa"}
+! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEb"}
+! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEc"}
+! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_leading_negative_tailEx"}
+! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0
+! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0
+! DEFAULT: %[[XA:.*]] = arith.addf %[[XV]], %[[AV]]
+! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0
+! DEFAULT: %[[XAB:.*]] = arith.subf %[[XA]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load %[[C]]#0
+! DEFAULT: %[[RES:.*]] = arith.addf %[[XAB]], %[[CV]]
+! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! The tail ends negative and can be rebuilt directly with Subtract.
+! Default:   (((x + a) + b) - c)
+! Rewritten: (b - c) + (x + a)
+subroutine eligible_trailing_negative_tail(x,a,b,c)
+  real(8) :: x,a,b,c
+  x = x + a + b - c
+end
+
+! SPLIT-LABEL: func.func @_QPeligible_trailing_negative_tail
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEa"}
+! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEb"}
+! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEc"}
+! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEx"}
+! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0
+! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
+! SPLIT: %[[TAIL:.*]] = arith.subf %[[BV]], %[[CV]]
+! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0
+! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0
+! SPLIT: %[[HEAD:.*]] = arith.addf %[[XV]], %[[AV]]
+! SPLIT: %[[RES:.*]] = arith.addf %[[TAIL]], %[[HEAD]]
+! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! DEFAULT-LABEL: func.func @_QPeligible_trailing_negative_tail
+! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEa"}
+! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEb"}
+! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEc"}
+! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_trailing_negative_tailEx"}
+! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0
+! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0
+! DEFAULT: %[[XA:.*]] = arith.addf %[[XV]], %[[AV]]
+! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0
+! DEFAULT: %[[XAB:.*]] = arith.addf %[[XA]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load %[[C]]#0
+! DEFAULT: %[[RES:.*]] = arith.subf %[[XAB]], %[[CV]]
+! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! A root Subtract and consecutive negative terms are eligible. The entirely
+! negative tail is represented by its positive magnitude, without unary minus.
+! Default:   (((x - a) - b) - c)
+! Rewritten: (x - a) - (b + c)
+subroutine eligible_consecutive_subtraction(x,a,b,c)
+  real(8) :: x,a,b,c
+  x = x - a - b - c
+end
+
+! SPLIT-LABEL: func.func @_QPeligible_consecutive_subtraction
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEa"}
+! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEb"}
+! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEc"}
+! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEx"}
+! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0
+! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0
+! SPLIT: %[[HEAD:.*]] = arith.subf %[[XV]], %[[AV]]
+! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0
+! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
+! SPLIT: %[[TAIL:.*]] = arith.addf %[[BV]], %[[CV]]
+! SPLIT: %[[RES:.*]] = arith.subf %[[HEAD]], %[[TAIL]]
+! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! DEFAULT-LABEL: func.func @_QPeligible_consecutive_subtraction
+! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEa"}
+! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEb"}
+! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEc"}
+! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_consecutive_subtractionEx"}
+! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0
+! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0
+! DEFAULT: %[[XA:.*]] = arith.subf %[[XV]], %[[AV]]
+! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0
+! DEFAULT: %[[XAB:.*]] = arith.subf %[[XA]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load %[[C]]#0
+! DEFAULT: %[[RES:.*]] = arith.subf %[[XAB]], %[[CV]]
+! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! Nested unparenthesized Add and Subtract nodes all contribute signed terms.
+! Default:   ((((x - a) + b) - c) + d)
+! Rewritten: (b - (c - d)) + (x - a)
+subroutine eligible_nested_unparenthesized_subtraction(x,a,b,c,d)
+  real(8) :: x,a,b,c,d
+  x = x - a + b - c + d
+end
+
+! SPLIT-LABEL: func.func @_QPeligible_nested_unparenthesized_subtraction
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEa"}
+! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEb"}
+! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEc"}
+! SPLIT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEd"}
+! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEx"}
+! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0
+! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0
+! SPLIT: %[[DV:.*]] = fir.load %[[D]]#0
+! SPLIT: %[[CD:.*]] = arith.subf %[[CV]], %[[DV]]
+! SPLIT: %[[TAIL:.*]] = arith.subf %[[BV]], %[[CD]]
+! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0
+! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0
+! SPLIT: %[[HEAD:.*]] = arith.subf %[[XV]], %[[AV]]
+! SPLIT: %[[RES:.*]] = arith.addf %[[TAIL]], %[[HEAD]]
+! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! DEFAULT-LABEL: func.func @_QPeligible_nested_unparenthesized_subtraction
+! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEa"}
+! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEb"}
+! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEc"}
+! DEFAULT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEd"}
+! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_nested_unparenthesized_subtractionEx"}
+! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0
+! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0
+! DEFAULT: %[[XA:.*]] = arith.subf %[[XV]], %[[AV]]
+! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0
+! DEFAULT: %[[XAB:.*]] = arith.addf %[[XA]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load %[[C]]#0
+! DEFAULT: %[[XABC:.*]] = arith.subf %[[XAB]], %[[CV]]
+! DEFAULT: %[[DV:.*]] = fir.load %[[D]]#0
+! DEFAULT: %[[RES:.*]] = arith.addf %[[XABC]], %[[DV]]
+! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0
+
+! Subtraction immediately outside a parenthesized term changes the term's
+! outer sign, but the parenthesized b-c remains one opaque no_reassoc value.
+! Default:   (((x + a) - (b-c)) + d)
+! Rewritten: (x + a) - ((b-c) - d)
+subroutine eligible_subtract_parenthesized_term(x,a,b,c,d)
+  real(8) :: x,a,b,c,d
+  x = x + a - (b-c) + d
+end
+
+! SPLIT-LABEL: func.func @_QPeligible_subtract_parenthesized_term
+! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFe...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/211829


More information about the flang-commits mailing list