[llvm-branch-commits] [flang] [flang][Lower] Reassociate nested assignment sums (PR #217287)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 19 03:51:58 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
@llvm/pr-subscribers-flang-fir-hlfir
Author: Tom Eccles (tblah)
<details>
<summary>Changes</summary>
Search eligible assignment RHS expressions top-down for scalar REAL
and COMPLEX additive spines. Rewrite a qualifying node once; otherwise
recurse through ordinary operations, conversions, conditional wrappers,
and separate arguments of pure calls. Preserve effect-sensitive
boundaries.
Rename the public rewrite entry point to reflect recursive behavior. Add
coverage for nested conversions, conditions, direct and conditional call
arguments, parentheses, impure calls, short sums, single rewriting, and
contexts outside assignments.
Assisted-by: Codex
---
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
---
Full diff: https://github.com/llvm/llvm-project/pull/217287.diff
5 Files Affected:
- (modified) flang/include/flang/Evaluate/tools.h (+3-2)
- (modified) flang/lib/Evaluate/tools.cpp (+148-6)
- (modified) flang/lib/Lower/Bridge.cpp (+1-1)
- (modified) flang/test/Lower/split-sum-expression-tree-lowering.f90 (+166-2)
- (modified) flang/unittests/Evaluate/expression.cpp (+53)
``````````diff
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index c877ec5f5705b..9d29528cbbceb 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1131,8 +1131,9 @@ bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr);
bool CanBuildSplitSumExpressionTree(
const Expr<SomeType> &lhs, const Expr<SomeType> &rhs);
-// Try to rewrite a scalar real or complex sum as a split sum expression tree.
-std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTree(
+// Try to rewrite eligible scalar real or complex sums within an expression as
+// split sum expression trees.
+std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTrees(
const Expr<SomeType> &expr);
// Utilities for attaching the location of the declaration of a symbol
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index e10a8b37babf6..66d4b6810e10a 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -10,6 +10,7 @@
#include "flang/Common/idioms.h"
#include "flang/Common/type-kinds.h"
#include "flang/Evaluate/characteristics.h"
+#include "flang/Evaluate/rewrite.h"
#include "flang/Evaluate/traverse.h"
#include "flang/Parser/message.h"
#include "flang/Semantics/tools.h"
@@ -1546,6 +1547,151 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(
return std::nullopt;
}
+template <typename> struct IsExpr : std::false_type {};
+template <typename T> struct IsExpr<Expr<T>> : std::true_type {};
+
+template <typename> struct IsFunctionRef : std::false_type {};
+template <typename T> struct IsFunctionRef<FunctionRef<T>> : std::true_type {};
+
+template <typename> struct IsConditionalExpr : std::false_type {};
+template <typename T>
+struct IsConditionalExpr<ConditionalExpr<T>> : std::true_type {};
+
+class SplitSumExpressionTreeRewriter {
+public:
+ std::optional<Expr<SomeType>> Rewrite(const Expr<SomeType> &expr) {
+ Expr<SomeType> rewritten{rewriteExpr(Expr<SomeType>{expr})};
+ if (changed_)
+ return rewritten;
+ return std::nullopt;
+ }
+
+private:
+ template <typename T>
+ static std::optional<Expr<T>> tryRewriteCurrent(const Expr<T> &) {
+ return std::nullopt;
+ }
+
+ template <common::TypeCategory CAT, int KIND>
+ static std::optional<NumericExpr<CAT, KIND>> tryRewriteCurrent(
+ const NumericExpr<CAT, KIND> &expr) {
+ if constexpr (CAT == common::TypeCategory::Real ||
+ CAT == common::TypeCategory::Complex)
+ return tryBuildSplitSumExpressionTree(expr);
+ return std::nullopt;
+ }
+
+ template <typename D, std::size_t... Is>
+ D rewriteOperation(D &&op, std::index_sequence<Is...>) {
+ return D{rewriteExpr(std::move(op.template operand<Is>()))...};
+ }
+
+ template <typename T, std::size_t... Is>
+ Extremum<T> rewriteOperation(Extremum<T> &&op, std::index_sequence<Is...>) {
+ return Extremum<T>{
+ op.ordering, rewriteExpr(std::move(op.template operand<Is>()))...};
+ }
+
+ template <int KIND, std::size_t... Is>
+ ComplexComponent<KIND> rewriteOperation(
+ ComplexComponent<KIND> &&op, std::index_sequence<Is...>) {
+ return ComplexComponent<KIND>{op.isImaginaryPart,
+ rewriteExpr(std::move(op.template operand<Is>()))...};
+ }
+
+ template <int KIND, std::size_t... Is>
+ LogicalOperation<KIND> rewriteOperation(
+ LogicalOperation<KIND> &&op, std::index_sequence<Is...>) {
+ return LogicalOperation<KIND>{op.logicalOperator,
+ rewriteExpr(std::move(op.template operand<Is>()))...};
+ }
+
+ template <typename T, std::size_t... Is>
+ Relational<T> rewriteOperation(
+ Relational<T> &&op, std::index_sequence<Is...>) {
+ return Relational<T>{
+ op.opr, rewriteExpr(std::move(op.template operand<Is>()))...};
+ }
+
+ Relational<SomeType> rewriteRelational(Relational<SomeType> &&relational) {
+ return common::visit(
+ [&](auto &&typed) -> Relational<SomeType> {
+ using RelationalType = std::decay_t<decltype(typed)>;
+ return Relational<SomeType>{rewriteOperation(std::move(typed),
+ std::make_index_sequence<RelationalType::operands>{})};
+ },
+ std::move(relational.u));
+ }
+
+ template <typename T>
+ ConditionalExpr<T> rewriteConditional(ConditionalExpr<T> &&conditional) {
+ return ConditionalExpr<T>{rewriteExpr(std::move(conditional.condition())),
+ rewriteExpr(std::move(conditional.thenValue())),
+ rewriteExpr(std::move(conditional.elseValue()))};
+ }
+
+ void rewriteConditionalArgument(ActualArgument::ConditionalArg &arg) {
+ arg.condition() = rewriteExpr(std::move(arg.condition()));
+ const auto rewriteConsequent =
+ [&](ActualArgument::ConditionalArg::Consequent &consequent) {
+ if (consequent)
+ consequent->value() = rewriteExpr(std::move(consequent->value()));
+ };
+ rewriteConsequent(arg.consequent());
+ arg.VisitTail(
+ [&](ActualArgument::ConditionalArg &nested) {
+ rewriteConditionalArgument(nested);
+ },
+ rewriteConsequent);
+ }
+
+ template <typename T> FunctionRef<T> rewriteFunction(FunctionRef<T> &&ref) {
+ if (!ref.proc().IsPure())
+ return std::move(ref);
+ for (std::optional<ActualArgument> &maybeArg : ref.arguments()) {
+ if (maybeArg) {
+ if (Expr<SomeType> * argExpr{maybeArg->UnwrapExpr()})
+ *argExpr = rewriteExpr(std::move(*argExpr));
+ else if (ActualArgument::ConditionalArg *
+ conditional{maybeArg->GetConditionalArg()})
+ rewriteConditionalArgument(*conditional);
+ }
+ }
+ return std::move(ref);
+ }
+
+ template <typename T> Expr<T> rewriteExpr(Expr<T> &&expr) {
+ if (std::optional<Expr<T>> rewritten{tryRewriteCurrent(expr)}) {
+ changed_ = true;
+ return std::move(*rewritten);
+ }
+ return common::visit(
+ [&](auto &&node) -> Expr<T> {
+ using Node = std::decay_t<decltype(node)>;
+ if constexpr (IsExpr<Node>::value) {
+ return Expr<T>{rewriteExpr(std::move(node))};
+ } else if constexpr (IsFunctionRef<Node>::value) {
+ return Expr<T>{rewriteFunction(std::move(node))};
+ } else if constexpr (IsConditionalExpr<Node>::value) {
+ return Expr<T>{rewriteConditional(std::move(node))};
+ } else if constexpr (std::is_same_v<Node, Relational<SomeType>>) {
+ return Expr<T>{rewriteRelational(std::move(node))};
+ } else if constexpr (rewrite::is_operation_v<Node>) {
+ if constexpr (std::is_same_v<Node, Parentheses<T>>)
+ return Expr<T>{std::move(node)};
+ else
+ return Expr<T>{rewriteOperation(
+ std::move(node), std::make_index_sequence<Node::operands>{})};
+ } else {
+ return Expr<T>{std::move(node)};
+ }
+ },
+ std::move(expr.u));
+ }
+
+ bool changed_{false};
+};
+
} // namespace
bool CanBuildSplitSumExpressionTree(
@@ -1556,13 +1702,9 @@ bool CanBuildSplitSumExpressionTree(
!HasVolatileOrAsynchronousSymbol(lhs);
}
-std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTree(
+std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTrees(
const Expr<SomeType> &expr) {
- return common::visit(
- [&](const auto &typedExpr) -> std::optional<Expr<SomeType>> {
- return tryBuildSplitSumExpressionTree(typedExpr);
- },
- expr.u);
+ return SplitSumExpressionTreeRewriter{}.Rewrite(expr);
}
bool IsArraySection(const Expr<SomeType> &expr) {
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index bff6b51e50e18..c9c47118b787f 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5548,7 +5548,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::evaluate::CanBuildSplitSumExpressionTree(assign.lhs,
assign.rhs)) {
rewritten =
- Fortran::evaluate::TryBuildSplitSumExpressionTree(assign.rhs);
+ Fortran::evaluate::TryBuildSplitSumExpressionTrees(assign.rhs);
if (rewritten)
rhsExpr = &*rewritten;
}
diff --git a/flang/test/Lower/split-sum-expression-tree-lowering.f90 b/flang/test/Lower/split-sum-expression-tree-lowering.f90
index f681f4ebe45fe..4b9b49219d8b8 100644
--- a/flang/test/Lower/split-sum-expression-tree-lowering.f90
+++ b/flang/test/Lower/split-sum-expression-tree-lowering.f90
@@ -752,8 +752,8 @@ subroutine guard_call(x,a,b,c,d,e)
! Default: (((x + sqrt((a+b)+c)) + d*e) + f*g)
! Rewritten: ((d*e + f*g) + (x + sqrt((a+b)+c)))
-! The pure call is an opaque outer term. Its additive argument retains source
-! order in this stage.
+! A qualifying outer sum is rewritten once. The pure call remains an opaque
+! term, so its additive argument retains source order.
subroutine eligible_pure_call(x,a,b,c,d,e,f,g)
real(8) :: x,a,b,c,d,e,f,g
x = x + sqrt(a+b+c) + d*e + f*g
@@ -837,6 +837,170 @@ subroutine guard_nested_impure_call(x,a,b,c,d,e)
! NO-REWRITE: %[[RES:.*]] = arith.addf %[[HEAD_BC]], %[[DE]]
! NO-REWRITE: hlfir.assign %[[RES]]
+! Default: d * real((a+b)+c,8)
+! Rewritten: d * real(c+(a+b),8)
+subroutine nested_conversion_operand(x,a,b,c,d)
+ real(8) :: x,d
+ real(4) :: a,b,c
+ x = d * real(a+b+c,8)
+end
+
+! SPLIT-LABEL: func.func @_QPnested_conversion_operand
+! SPLIT: %[[DV:.*]] = fir.load
+! SPLIT: %[[CV:.*]] = fir.load
+! SPLIT: %[[AV:.*]] = fir.load
+! SPLIT: %[[BV:.*]] = fir.load
+! SPLIT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! SPLIT: %[[SUM:.*]] = arith.addf %[[CV]], %[[AB]]
+! SPLIT: %[[CONVERT:.*]] = fir.convert %[[SUM]]
+! SPLIT: %[[RES:.*]] = arith.mulf %[[DV]], %[[CONVERT]]
+! SPLIT: hlfir.assign %[[RES]]
+
+! DEFAULT-LABEL: func.func @_QPnested_conversion_operand
+! DEFAULT: %[[DV:.*]] = fir.load
+! DEFAULT: %[[AV:.*]] = fir.load
+! DEFAULT: %[[BV:.*]] = fir.load
+! DEFAULT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load
+! DEFAULT: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! DEFAULT: %[[CONVERT:.*]] = fir.convert %[[SUM]]
+! DEFAULT: %[[RES:.*]] = arith.mulf %[[DV]], %[[CONVERT]]
+! DEFAULT: hlfir.assign %[[RES]]
+
+! Default: sqrt((a+b)+c)
+! Rewritten: sqrt(c+(a+b))
+subroutine nested_pure_call_argument(x,a,b,c)
+ real(8) :: x,a,b,c
+ x = sqrt(a+b+c)
+end
+
+! SPLIT-LABEL: func.func @_QPnested_pure_call_argument
+! SPLIT: %[[CV:.*]] = fir.load
+! SPLIT: %[[AV:.*]] = fir.load
+! SPLIT: %[[BV:.*]] = fir.load
+! SPLIT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! SPLIT: %[[SUM:.*]] = arith.addf %[[CV]], %[[AB]]
+! SPLIT: %[[SQRT:.*]] = math.sqrt %[[SUM]]
+! SPLIT: hlfir.assign %[[SQRT]]
+
+! DEFAULT-LABEL: func.func @_QPnested_pure_call_argument
+! DEFAULT: %[[AV:.*]] = fir.load
+! DEFAULT: %[[BV:.*]] = fir.load
+! DEFAULT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load
+! DEFAULT: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! DEFAULT: %[[SQRT:.*]] = math.sqrt %[[SUM]]
+! DEFAULT: hlfir.assign %[[SQRT]]
+
+! Default: atan2((a+b)+c,(d+e)+f)
+! Rewritten: atan2(c+(a+b),f+(d+e))
+subroutine nested_separate_call_arguments(x,a,b,c,d,e,f)
+ real(8) :: x,a,b,c,d,e,f
+ x = atan2(a+b+c,d+e+f)
+end
+
+! SPLIT-LABEL: func.func @_QPnested_separate_call_arguments
+! SPLIT: %[[CV:.*]] = fir.load
+! SPLIT: %[[AV:.*]] = fir.load
+! SPLIT: %[[BV:.*]] = fir.load
+! SPLIT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! SPLIT: %[[FIRST:.*]] = arith.addf %[[CV]], %[[AB]]
+! SPLIT: %[[FV:.*]] = fir.load
+! SPLIT: %[[DV:.*]] = fir.load
+! SPLIT: %[[EV:.*]] = fir.load
+! SPLIT: %[[DE:.*]] = arith.addf %[[DV]], %[[EV]]
+! SPLIT: %[[SECOND:.*]] = arith.addf %[[FV]], %[[DE]]
+! SPLIT: math.atan2 %[[FIRST]], %[[SECOND]]
+
+! Default: (flag ? (a+b)+c : d)
+! Rewritten: (flag ? c+(a+b) : d)
+subroutine nested_conditional_branch(x,flag,a,b,c,d)
+ real(8) :: x,a,b,c,d
+ logical :: flag
+ x = (flag ? a+b+c : d)
+end
+
+! SPLIT-LABEL: func.func @_QPnested_conditional_branch
+! SPLIT: fir.if
+! SPLIT: %[[CV:.*]] = fir.load
+! SPLIT: %[[AV:.*]] = fir.load
+! SPLIT: %[[BV:.*]] = fir.load
+! SPLIT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! SPLIT: %[[SUM:.*]] = arith.addf %[[CV]], %[[AB]]
+! SPLIT: fir.result %[[SUM]]
+
+! DEFAULT-LABEL: func.func @_QPnested_conditional_branch
+! DEFAULT: fir.if
+! DEFAULT: %[[AV:.*]] = fir.load
+! DEFAULT: %[[BV:.*]] = fir.load
+! DEFAULT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load
+! DEFAULT: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! DEFAULT: fir.result %[[SUM]]
+
+! Default: ((a+b)+c > d ? e : f)
+! Rewritten: (c+(a+b) > d ? e : f)
+subroutine nested_relational_operand(x,a,b,c,d,e,f)
+ real(8) :: x,a,b,c,d,e,f
+ x = (a+b+c > d ? e : f)
+end
+
+! SPLIT-LABEL: func.func @_QPnested_relational_operand
+! SPLIT: %[[CV:.*]] = fir.load
+! SPLIT: %[[AV:.*]] = fir.load
+! SPLIT: %[[BV:.*]] = fir.load
+! SPLIT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! SPLIT: %[[SUM:.*]] = arith.addf %[[CV]], %[[AB]]
+! SPLIT: arith.cmpf ogt, %[[SUM]]
+
+! DEFAULT-LABEL: func.func @_QPnested_relational_operand
+! DEFAULT: %[[AV:.*]] = fir.load
+! DEFAULT: %[[BV:.*]] = fir.load
+! DEFAULT: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! DEFAULT: %[[CV:.*]] = fir.load
+! DEFAULT: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! DEFAULT: arith.cmpf ogt, %[[SUM]]
+
+subroutine guard_parenthesized_call_argument(x,a,b,c)
+ real(8) :: x,a,b,c
+ x = sqrt((a+b+c))
+end
+
+! NO-REWRITE-LABEL: func.func @_QPguard_parenthesized_call_argument
+! NO-REWRITE: %[[AV:.*]] = fir.load
+! NO-REWRITE: %[[BV:.*]] = fir.load
+! NO-REWRITE: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! NO-REWRITE: %[[CV:.*]] = fir.load
+! NO-REWRITE: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! NO-REWRITE: %[[PAREN:.*]] = hlfir.no_reassoc %[[SUM]]
+! NO-REWRITE: %[[SQRT:.*]] = math.sqrt %[[PAREN]]
+! NO-REWRITE: hlfir.assign %[[SQRT]]
+
+subroutine guard_short_call_argument(x,a,b)
+ real(8) :: x,a,b
+ x = sqrt(a+b)
+end
+
+! NO-REWRITE-LABEL: func.func @_QPguard_short_call_argument
+! NO-REWRITE: %[[AV:.*]] = fir.load
+! NO-REWRITE: %[[BV:.*]] = fir.load
+! NO-REWRITE: %[[SUM:.*]] = arith.addf %[[AV]], %[[BV]]
+! NO-REWRITE: %[[SQRT:.*]] = math.sqrt %[[SUM]]
+! NO-REWRITE: hlfir.assign %[[SQRT]]
+
+subroutine guard_non_assignment_context(a,b,c)
+ real(8) :: a,b,c
+ call consume(a+b+c)
+end
+
+! NO-REWRITE-LABEL: func.func @_QPguard_non_assignment_context
+! NO-REWRITE: %[[AV:.*]] = fir.load
+! NO-REWRITE: %[[BV:.*]] = fir.load
+! NO-REWRITE: %[[AB:.*]] = arith.addf %[[AV]], %[[BV]]
+! NO-REWRITE: %[[CV:.*]] = fir.load
+! NO-REWRITE: %[[SUM:.*]] = arith.addf %[[AB]], %[[CV]]
+! NO-REWRITE: fir.call @_QPconsume
+
subroutine guard_array(n,x,a,b,c,d,e,f)
integer :: n
real(8) :: x(n),a(n),b(n),c(n),d(n),e(n),f(n)
diff --git a/flang/unittests/Evaluate/expression.cpp b/flang/unittests/Evaluate/expression.cpp
index d575f36def287..0c1b1cf03aeef 100644
--- a/flang/unittests/Evaluate/expression.cpp
+++ b/flang/unittests/Evaluate/expression.cpp
@@ -1,4 +1,5 @@
#include "flang/Evaluate/expression.h"
+#include "flang/Evaluate/characteristics.h"
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/intrinsics.h"
#include "flang/Evaluate/target.h"
@@ -35,5 +36,57 @@ int main() {
a = b;
MATCH("2_4", a.AsFortran());
MATCH("2_4", b.AsFortran());
+
+ using RealType = Type<TypeCategory::Real, 8>;
+ using RealExpr = Expr<RealType>;
+ using RealScalar = Scalar<RealType>;
+ const auto makeSum = [] {
+ return RealExpr{RealScalar{}} + RealExpr{RealScalar{}} +
+ RealExpr{RealScalar{}};
+ };
+ using ConditionalArg = ActualArgument::ConditionalArg;
+ using Consequent = ConditionalArg::Consequent;
+ const auto makeConsequent = [&]() -> Consequent {
+ return Fortran::common::CopyableIndirection<Expr<SomeType>>{
+ AsGenericExpr(makeSum())};
+ };
+ ConditionalArg nestedConditional{
+ Expr<SomeLogical>{Expr<LogicalResult>{false}}, makeConsequent(),
+ Consequent{std::nullopt}};
+ ConditionalArg conditional{Expr<SomeLogical>{Expr<LogicalResult>{false}},
+ makeConsequent(),
+ Fortran::common::CopyableIndirection<ConditionalArg>{
+ std::move(nestedConditional)}};
+ characteristics::Procedure::Attrs pureAttrs;
+ pureAttrs.set(characteristics::Procedure::Attr::Pure);
+ characteristics::Procedure pureProcedure{
+ characteristics::FunctionResult{
+ DynamicType{TypeCategory::Real, RealType::kind}},
+ characteristics::DummyArguments{}, pureAttrs};
+ ActualArguments arguments;
+ arguments.emplace_back(ActualArgument{std::move(conditional)});
+ FunctionRef<RealType> pureCall{ProcedureDesignator{SpecificIntrinsic{
+ "test_pure", std::move(pureProcedure)}},
+ std::move(arguments)};
+ auto rewrittenCall = TryBuildSplitSumExpressionTrees(
+ AsGenericExpr(RealExpr{std::move(pureCall)}));
+ TEST(rewrittenCall.has_value());
+ const FunctionRef<RealType> *rewrittenFunction =
+ UnwrapExpr<FunctionRef<RealType>>(*rewrittenCall);
+ TEST(rewrittenFunction != nullptr);
+ const ConditionalArg *rewrittenConditional =
+ rewrittenFunction->arguments().front()->GetConditionalArg();
+ TEST(rewrittenConditional != nullptr);
+ const auto isSplitSum = [](const Consequent &consequent) {
+ const auto *outer = UnwrapExpr<Add<RealType>>(consequent->value());
+ return outer && !UnwrapExpr<Add<RealType>>(outer->left()) &&
+ UnwrapExpr<Add<RealType>>(outer->right());
+ };
+ TEST(isSplitSum(rewrittenConditional->consequent()));
+ rewrittenConditional->VisitTail(
+ [&](const ConditionalArg &nested) {
+ TEST(isSplitSum(nested.consequent()));
+ },
+ [&](const Consequent &) { TEST(false); });
return testing::Complete();
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/217287
More information about the llvm-branch-commits
mailing list