On Fri, Sep 25, 2026 at 10:31 AM Stefan Schulze Frielinghaus
<[email protected]> wrote:
>
> From: Stefan Schulze Frielinghaus <[email protected]>
>
> As a first step I went for implementing this during simplify RTX.
> However, in the end this works only partly. For example, a candidate
> insn created during a 3-insn combine resulting from the simplification
> has a vector constant as an operand, now, which may have been previously
> a register operand. This may lead to fail predicate general_operand in
> case the constant is not a legitimate one which leads to rejecting the
> insn instead of fixing it up during LRA. This was the motivation for
> also doing the same on tree level. Then we end up prior expand with a
> PLUS and MULT which in turn means after expand we do so, too, and
> combine can merge both (independent of the vector constant which may
> have been pushed into the constant pool at this particular time).
>
> Note, the RTX simplification still works for vector constants which are
> accepted by TARGET_LEGITIMATE_CONSTANT_P. For s390, the different code
> paths are demonstrated in the subsequent patch where
> test_<mode>_minus_vrepi profits already from the rtx canonicalization,
> and test_<mode>_minus_constpool profits from the tree canonicalization.
>
> Of course, this is not uncontroversial. In case the multiplication is
> used in more than one place, the combination might be undesired. The
> same holds true in case the constant vector is used in multiple places
> but the negated version is not. The former is partly addressed by
> match.pd requiring a single use for mult. Of course, there still might
> be cases where this may prohibit CSE optimizations later on.
>
> If this is about to being accepted, then one could also introduce a
> mach.pd rule for A-((-B)*C) -> (B*C)+A which is already taken care of in
> simplify RTX due to the recursive call.
>
> This is motivated by the fact that on s390 we only have vector
> multiply-and-add but no subtract variant over the integers.
>
> Note, w.r.t. the SPEC CPU 2017 suite, the results are underwhelming in
> the sense that only two times the canonicalization helps for emitting a
> VMAL instead of VML+VS. On the other hand the results are also a
> relieve in the sense that they show that no extra multiplications were
> introduced due to pulling in the minus.
>
> Side remark: We generously accept all scalar constants during
> TARGET_LEGITIMATE_CONSTANT_P, since those which do not end up as
> immediates, RA does a very nice job by reloading and sharing those
> reloads among insns. In contrast, vector constants can never end up as
> immediates which is why putting those early into the constant pool leads
> generally to better code. Only in those few cases where a constant can
> be constructed via a single instruction, we do not utilize the constant
> pool.
> ---
> Bootstrapped and regtested on
> - aarch64-unknown-linux-gnu
> - powerpc64le-unknown-linux-gnu
> - s390x-ibm-linux-gnu
> - x86_64-pc-linux-gnu
> If there is consensus to commit this, I would add A-((-B)*C) -> (B*C)+A
> and give this another round of testing.
>
> gcc/match.pd | 12 ++++++++++++
> gcc/simplify-rtx.cc | 17 ++++++++++++++++-
> 2 files changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 268bf3f11e5..a5a476e17a3 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2532,6 +2532,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (if (!FIXED_POINT_TYPE_P (type))
> (plus @0 (negate @1))))
>
> +/* A - (B * C) -> (B * (-C)) + A if C is a constant vector. */
This should be catched by A - B -> A + (-B) when B can be easily
negated via negate_expr_p. match.pd does not implement
the fold-const MULT_EXPR case and I remember Kyrylo messing
with negating of vectors, so CCed.
The negate_expr_p match will have the issue of single-uses as you
say. So I'm not entirely convinced this is a good idea as this
"canonicalization" doesn't really help much, unlike for example
knowing that A - INTEGER_CST will never appear (besides for INT_MIN),
but it will be A + INTEGER_CST always.
> +/* Perform this only in case the multiplication has a single use. */
> +(simplify
> + (minus @0 (mult:cs @1 VECTOR_CST@2))
> + (if (VECTOR_INTEGER_TYPE_P (type))
> + (with { tree neg_const_vec = const_unop (NEGATE_EXPR, type, @2); }
> + /* Be defensive here and check that the negation of a constant
> + vector can be represented which may not be the case for
> + scalable vectors. */
> + (if (neg_const_vec)
> + (plus (mult @1 { neg_const_vec; }) @0)))))
> +
> /* A - MIN (B, C) -> A + MAX (-B, -C)
> A - MAX (B, C) -> A + MIN (-B, -C) if B and C are easily negatable. */
> (for minmax (min max)
> diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
> index 38c8abfe3e7..ce3e5469812 100644
> --- a/gcc/simplify-rtx.cc
> +++ b/gcc/simplify-rtx.cc
> @@ -3690,12 +3690,27 @@ simplify_context::simplify_binary_operation_1
> (rtx_code code,
> && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
> return tem;
>
> - /* Handle vector series. */
> + /* Handle integer vector operations. */
> if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
> {
> tem = simplify_binary_operation_series (code, mode, op0, op1);
> if (tem)
> return tem;
> +
> + /* Canonicalize A-(B*C) into (B*(-C))+A if C is a constant vector.
> */
> + rtx neg_const_vec;
> + if (GET_CODE (op1) == MULT
> + && CONST_VECTOR_P (XEXP (op1, 1))
> + /* Be defensive here and check that the negation of a constant
> + vector can be represented which may not be the case for
> + scalable vectors. */
> + && (neg_const_vec
> + = simplify_unary_operation (NEG, mode, XEXP (op1, 1),
> mode)))
> + {
> + rtx new_mult = simplify_gen_binary (MULT, mode, XEXP (op1, 0),
> + neg_const_vec);
> + return simplify_gen_binary (PLUS, mode, new_mult, op0);
> + }
> }
> break;
>
> --
> 2.55.0
>