llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-flang-driver Author: Tom Eccles (tblah) <details> <summary>Changes</summary> Third part of generalisations requested in #<!-- -->207377. Extend the experimental split-sum transformation to complex addition and subtraction chains. Share the signed-term builder across explicitly supported real and complex categories while keeping integer expressions excluded. There are no known effect on benchmarks as a result of this patch. Assisted-by: Codex --- Full diff: https://github.com/llvm/llvm-project/pull/214775.diff 5 Files Affected: - (modified) clang/include/clang/Options/FlangOptions.td (+7-7) - (modified) flang/include/flang/Evaluate/tools.h (+3-3) - (modified) flang/lib/Evaluate/tools.cpp (+52-46) - (modified) flang/test/Driver/driver-help.f90 (+2-2) - (modified) flang/test/Lower/split-sum-expression-tree-lowering.f90 (+88) ``````````diff diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td index bafc063663fe2..3950847251828 100644 --- a/clang/include/clang/Options/FlangOptions.td +++ b/clang/include/clang/Options/FlangOptions.td @@ -315,16 +315,16 @@ defm real_sum_reassociation "f", "real-sum-reassociation", PosFlag<SetTrue, [], [], "Enable Fortran-standard compliant reassociation within " - "individual REAL sum expressions. This may change exact " - "floating-point results">, + "individual REAL and COMPLEX sum expressions. This may " + "change exact floating-point results">, NegFlag<SetFalse, [], [], - "Disable reassociation within individual REAL sum " - "expressions">>, + "Disable reassociation within individual REAL and COMPLEX " + "sum expressions">>, DocBrief<[{ Enable Fortran-standard compliant reassociation within individual - ``REAL`` sum expressions. This can improve optimization opportunities - and may change exact floating-point results while preserving - standard-conforming Fortran semantics. + ``REAL`` and ``COMPLEX`` sum expressions. This can improve optimization + opportunities and may change exact floating-point results while + preserving standard-conforming Fortran semantics. }]>; defm init_global_zero : BoolOptionWithoutMarshalling<"f", "init-global-zero", diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h index cce2325b33da7..c877ec5f5705b 100644 --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -1126,12 +1126,12 @@ bool HasProcedureRef(const Expr<SomeType> &expr); // Predicate: does an expression contain a VOLATILE or ASYNCHRONOUS symbol? bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr); -// Can a scalar real RHS expression in an assignment be rewritten as a split -// sum expression tree? +// Can a scalar real or complex RHS expression in an assignment be rewritten +// as a split sum expression tree? bool CanBuildSplitSumExpressionTree( const Expr<SomeType> &lhs, const Expr<SomeType> &rhs); -// Try to rewrite a scalar real sum as a split sum expression tree. +// Try to rewrite a scalar real or complex sum as a split sum expression tree. std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTree( const Expr<SomeType> &expr); diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp index 34deaaea289c1..589aab5132a65 100644 --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -1382,76 +1382,79 @@ bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr) { namespace { -template <int KIND> using Real = Type<common::TypeCategory::Real, KIND>; +template <common::TypeCategory CAT, int KIND> using Numeric = Type<CAT, KIND>; -template <int KIND> using RealExpr = Expr<Real<KIND>>; +template <common::TypeCategory CAT, int KIND> +using NumericExpr = Expr<Numeric<CAT, KIND>>; -template <int KIND> struct SignedRealTerm { - RealExpr<KIND> expr; +template <common::TypeCategory CAT, int KIND> struct SignedNumericTerm { + NumericExpr<CAT, KIND> expr; bool isPositive; }; -template <int KIND> struct SignedRealExpr { - RealExpr<KIND> expr; +template <common::TypeCategory CAT, int KIND> struct SignedNumericExpr { + NumericExpr<CAT, KIND> expr; bool isPositive; }; -template <int KIND> -static void flattenTopLevelAddSubtract(const RealExpr<KIND> &expr, - llvm::SmallVectorImpl<SignedRealTerm<KIND>> &terms, +template <common::TypeCategory CAT, int KIND> +static void flattenTopLevelAddSubtract(const NumericExpr<CAT, KIND> &expr, + llvm::SmallVectorImpl<SignedNumericTerm<CAT, KIND>> &terms, bool isPositive = true) { - // Only flatten real Add and Subtract nodes. Every other node, including + // Only flatten 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)) { + if (const auto *add = std::get_if<Add<Numeric<CAT, KIND>>>(&expr.u)) { flattenTopLevelAddSubtract(add->left(), terms, isPositive); flattenTopLevelAddSubtract(add->right(), terms, isPositive); return; } - if (const auto *subtract = std::get_if<Subtract<Real<KIND>>>(&expr.u)) { + if (const auto *subtract = + std::get_if<Subtract<Numeric<CAT, KIND>>>(&expr.u)) { flattenTopLevelAddSubtract(subtract->left(), terms, isPositive); flattenTopLevelAddSubtract(subtract->right(), terms, !isPositive); return; } - terms.push_back(SignedRealTerm<KIND>{expr, isPositive}); + terms.push_back(SignedNumericTerm<CAT, KIND>{expr, isPositive}); } -template <int KIND> -static SignedRealExpr<KIND> buildRightAssociatedSignedFold( - llvm::MutableArrayRef<SignedRealTerm<KIND>> terms) { +template <common::TypeCategory CAT, int KIND> +static SignedNumericExpr<CAT, KIND> buildRightAssociatedSignedFold( + llvm::MutableArrayRef<SignedNumericTerm<CAT, KIND>> terms) { assert(!terms.empty() && "cannot build empty signed fold"); const bool isPositive{terms.front().isPositive}; - RealExpr<KIND> result{std::move(terms.back().expr)}; + NumericExpr<CAT, KIND> result{std::move(terms.back().expr)}; for (std::size_t i{terms.size() - 1}; i > 0; --i) { - SignedRealTerm<KIND> &term{terms[i - 1]}; + SignedNumericTerm<CAT, 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)}}; + result = NumericExpr<CAT, KIND>{ + Add<Numeric<CAT, KIND>>{std::move(term.expr), std::move(result)}}; else - result = RealExpr<KIND>{ - Subtract<Real<KIND>>{std::move(term.expr), std::move(result)}}; + result = NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, KIND>>{ + std::move(term.expr), std::move(result)}}; } - return SignedRealExpr<KIND>{std::move(result), isPositive}; + return SignedNumericExpr<CAT, KIND>{std::move(result), isPositive}; } -template <int KIND> -static SignedRealExpr<KIND> buildSignedAdd( - SignedRealExpr<KIND> left, SignedRealExpr<KIND> right) { +template <common::TypeCategory CAT, int KIND> +static SignedNumericExpr<CAT, KIND> buildSignedAdd( + SignedNumericExpr<CAT, KIND> left, SignedNumericExpr<CAT, KIND> right) { if (left.isPositive == right.isPositive) { - return SignedRealExpr<KIND>{ - RealExpr<KIND>{ - Add<Real<KIND>>{std::move(left.expr), std::move(right.expr)}}, + return SignedNumericExpr<CAT, KIND>{ + NumericExpr<CAT, KIND>{Add<Numeric<CAT, 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)}}, + return SignedNumericExpr<CAT, KIND>{ + NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, 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)}}, + return SignedNumericExpr<CAT, KIND>{ + NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, KIND>>{ + std::move(right.expr), std::move(left.expr)}}, true}; } @@ -1460,25 +1463,25 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(const T &) { return std::nullopt; } -template <int KIND> +template <common::TypeCategory CAT, int KIND> static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree( - const RealExpr<KIND> &expr) { - if (!std::get_if<Add<Real<KIND>>>(&expr.u) && - !std::get_if<Subtract<Real<KIND>>>(&expr.u)) + const NumericExpr<CAT, KIND> &expr) { + if (!std::get_if<Add<Numeric<CAT, KIND>>>(&expr.u) && + !std::get_if<Subtract<Numeric<CAT, KIND>>>(&expr.u)) return std::nullopt; - llvm::SmallVector<SignedRealTerm<KIND>, 8> terms; + llvm::SmallVector<SignedNumericTerm<CAT, KIND>, 8> terms; flattenTopLevelAddSubtract(expr, terms); if (terms.size() <= 2) return std::nullopt; - llvm::MutableArrayRef<SignedRealTerm<KIND>> head{terms.data(), 2}; - llvm::MutableArrayRef<SignedRealTerm<KIND>> tail{ + llvm::MutableArrayRef<SignedNumericTerm<CAT, KIND>> head{terms.data(), 2}; + llvm::MutableArrayRef<SignedNumericTerm<CAT, 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)); + SignedNumericExpr<CAT, KIND> headExpr = buildRightAssociatedSignedFold(head); + SignedNumericExpr<CAT, KIND> tailExpr = buildRightAssociatedSignedFold(tail); + SignedNumericExpr<CAT, KIND> result = + buildSignedAdd(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)}; @@ -1487,7 +1490,10 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree( template <common::TypeCategory CAT> static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree( const Expr<SomeKind<CAT>> &expr) { - if constexpr (CAT == common::TypeCategory::Real) { + // Keep the supported categories explicit: integer reassociation requires a + // separate intermediate-range policy. + if constexpr (CAT == common::TypeCategory::Real || + CAT == common::TypeCategory::Complex) { return common::visit( [&](const auto &typedExpr) -> std::optional<Expr<SomeType>> { return tryBuildSplitSumExpressionTree(typedExpr); diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 index e77fc460850b2..c7c9ef887c594 100644 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -8,14 +8,14 @@ ! HELP-EMPTY: ! HELP-NEXT:OPTIONS: ! HELP: -freal-sum-reassociation -! HELP: Enable Fortran-standard compliant reassociation within individual REAL sum expressions +! HELP: Enable Fortran-standard compliant reassociation within individual REAL and COMPLEX sum expressions ! HELP: may change exact floating-point results ! HELP-FC1:USAGE: flang ! HELP-FC1-EMPTY: ! HELP-FC1-NEXT:OPTIONS: ! HELP-FC1: -freal-sum-reassociation -! HELP-FC1: Enable Fortran-standard compliant reassociation within individual REAL sum expressions +! HELP-FC1: Enable Fortran-standard compliant reassociation within individual REAL and COMPLEX sum expressions ! HELP-FC1: may change exact floating-point results ! ERROR: error: unknown argument '-helps'; did you mean '-help' diff --git a/flang/test/Lower/split-sum-expression-tree-lowering.f90 b/flang/test/Lower/split-sum-expression-tree-lowering.f90 index 1b1400f756177..93bb4396348c7 100644 --- a/flang/test/Lower/split-sum-expression-tree-lowering.f90 +++ b/flang/test/Lower/split-sum-expression-tree-lowering.f90 @@ -587,6 +587,94 @@ subroutine eligible_nested_unparenthesized_subtraction(x,a,b,c,d) ! DEFAULT: %[[RES:.*]] = arith.addf %[[XABC]], %[[DV]] ! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0 +! Complex addition and subtraction use the same signed-term split. The +! parenthesized c-d remains one opaque no_reassoc value. +! Default: (((x - a) + b) - (c-d)) +! Rewritten: (b - (c-d)) + (x - a) +subroutine eligible_complex_signed_parenthesized(x,a,b,c,d) + complex(4) :: x,a,b,c,d + x = x - a + b - (c-d) +end + +! SPLIT-LABEL: func.func @_QPeligible_complex_signed_parenthesized +! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEa"} +! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEb"} +! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEc"} +! SPLIT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEd"} +! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEx"} +! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0 +! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0 +! SPLIT: %[[DV:.*]] = fir.load %[[D]]#0 +! SPLIT: %[[CD_SUB:.*]] = fir.subc %[[CV]], %[[DV]] {{.*}} : complex<f32> +! SPLIT: %[[CD:.*]] = hlfir.no_reassoc %[[CD_SUB]] : complex<f32> +! SPLIT: %[[TAIL:.*]] = fir.subc %[[BV]], %[[CD]] {{.*}} : complex<f32> +! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0 +! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0 +! SPLIT: %[[HEAD:.*]] = fir.subc %[[XV]], %[[AV]] {{.*}} : complex<f32> +! SPLIT: %[[RES:.*]] = fir.addc %[[TAIL]], %[[HEAD]] {{.*}} : complex<f32> +! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0 + +! DEFAULT-LABEL: func.func @_QPeligible_complex_signed_parenthesized +! DEFAULT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEa"} +! DEFAULT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEb"} +! DEFAULT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEc"} +! DEFAULT-DAG: %[[D:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEd"} +! DEFAULT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_signed_parenthesizedEx"} +! DEFAULT: %[[XV:.*]] = fir.load %[[X]]#0 +! DEFAULT: %[[AV:.*]] = fir.load %[[A]]#0 +! DEFAULT: %[[XA:.*]] = fir.subc %[[XV]], %[[AV]] {{.*}} : complex<f32> +! DEFAULT: %[[BV:.*]] = fir.load %[[B]]#0 +! DEFAULT: %[[XAB:.*]] = fir.addc %[[XA]], %[[BV]] {{.*}} : complex<f32> +! DEFAULT: %[[CV:.*]] = fir.load %[[C]]#0 +! DEFAULT: %[[DV:.*]] = fir.load %[[D]]#0 +! DEFAULT: %[[CD_SUB:.*]] = fir.subc %[[CV]], %[[DV]] {{.*}} : complex<f32> +! DEFAULT: %[[CD:.*]] = hlfir.no_reassoc %[[CD_SUB]] : complex<f32> +! DEFAULT: %[[RES:.*]] = fir.subc %[[XAB]], %[[CD]] {{.*}} : complex<f32> +! DEFAULT: hlfir.assign %[[RES]] to %[[X]]#0 + +! A second complex kind exercises category dispatch independently of kind. +! Default: (((x + a) + b) + c) +! Rewritten: (b + c) + (x + a) +subroutine eligible_complex_kind8(x,a,b,c) + complex(8) :: x,a,b,c + x = x + a + b + c +end + +! SPLIT-LABEL: func.func @_QPeligible_complex_kind8 +! SPLIT-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_kind8Ea"} +! SPLIT-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_kind8Eb"} +! SPLIT-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_kind8Ec"} +! SPLIT-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFeligible_complex_kind8Ex"} +! SPLIT: %[[BV:.*]] = fir.load %[[B]]#0 +! SPLIT: %[[CV:.*]] = fir.load %[[C]]#0 +! SPLIT: %[[TAIL:.*]] = fir.addc %[[BV]], %[[CV]] {{.*}} : complex<f64> +! SPLIT: %[[XV:.*]] = fir.load %[[X]]#0 +! SPLIT: %[[AV:.*]] = fir.load %[[A]]#0 +! SPLIT: %[[HEAD:.*]] = fir.addc %[[XV]], %[[AV]] {{.*}} : complex<f64> +! SPLIT: %[[RES:.*]] = fir.addc %[[TAIL]], %[[HEAD]] {{.*}} : complex<f64> +! SPLIT: hlfir.assign %[[RES]] to %[[X]]#0 + +! It isn't as useful to re-write integer expressions because the middle-end can +! already re-associate them somewhat (within the bounds of avoiding overflow). +subroutine guard_integer(x,a,b,c) + integer :: x,a,b,c + x = x + a - b + c +end + +! NO-REWRITE-LABEL: func.func @_QPguard_integer +! NO-REWRITE-DAG: %[[A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFguard_integerEa"} +! NO-REWRITE-DAG: %[[B:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFguard_integerEb"} +! NO-REWRITE-DAG: %[[C:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFguard_integerEc"} +! NO-REWRITE-DAG: %[[X:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFguard_integerEx"} +! NO-REWRITE: %[[XV:.*]] = fir.load %[[X]]#0 +! NO-REWRITE: %[[AV:.*]] = fir.load %[[A]]#0 +! NO-REWRITE: %[[XA:.*]] = arith.addi %[[XV]], %[[AV]] +! NO-REWRITE: %[[BV:.*]] = fir.load %[[B]]#0 +! NO-REWRITE: %[[XAB:.*]] = arith.subi %[[XA]], %[[BV]] +! NO-REWRITE: %[[CV:.*]] = fir.load %[[C]]#0 +! NO-REWRITE: %[[RES:.*]] = arith.addi %[[XAB]], %[[CV]] +! NO-REWRITE: 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) `````````` </details> https://github.com/llvm/llvm-project/pull/214775 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
