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. */
+/* 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