diff mbox

match.pd: reassociate multiplications with constants

Message ID alpine.LNX.2.20.13.1707131911060.20069@monopod.intra.ispras.ru
State New
Headers show

Commit Message

Alexander Monakov July 13, 2017, 4:37 p.m. UTC
Hi,

This is a followup to https://gcc.gnu.org/ml/gcc-patches/2017-05/msg01545.html

Recently due to a fix for PR 80800 GCC has lost the ability to reassociate
signed multiplications chains to go from 'X * CST1 * Y * CST2'
to 'X * Y * (CST1 * CST2)'.  The fix to that PR prevents extract_muldiv from
introducing '(X * (CST1 * CST2)) * Y', which was wrong because it may cause
intermediate signed overflow (unexpected if Y == 0).

As mentioned in that thread, we can reassociate constants to outermost operands
instead: this is safe because CST1 cannot be 0 or -1, since those are handled
by other match.pd rules.

(in fact it's possible to reassociate negates too, and go from '(-X) * Y * CST'
to '(X * Y) * (-CST)' if (-CST) doesn't overflow (again, we know that CST != 1),
but I'm not sure how valuable that is in practice, so opted not to do that yet)

The following patch reinstates folding by adding a new match.pd rule that moves
constants to outermost operands, where they can be merged by fold_binary
machinery if their product doesn't overflow.  Bootstrapped and regtested on
amd64, OK for trunk?

	* match.pd ((X * CST) * Y): Reassociate to (X * Y) * CST.

Comments

Marc Glisse July 13, 2017, 6:30 p.m. UTC | #1
On Thu, 13 Jul 2017, Alexander Monakov wrote:

> This is a followup to https://gcc.gnu.org/ml/gcc-patches/2017-05/msg01545.html
>
> Recently due to a fix for PR 80800 GCC has lost the ability to reassociate
> signed multiplications chains to go from 'X * CST1 * Y * CST2'
> to 'X * Y * (CST1 * CST2)'.  The fix to that PR prevents extract_muldiv from
> introducing '(X * (CST1 * CST2)) * Y', which was wrong because it may cause
> intermediate signed overflow (unexpected if Y == 0).
>
> As mentioned in that thread, we can reassociate constants to outermost operands
> instead: this is safe because CST1 cannot be 0 or -1, since those are handled
> by other match.pd rules.
>
> (in fact it's possible to reassociate negates too, and go from '(-X) * Y * CST'
> to '(X * Y) * (-CST)' if (-CST) doesn't overflow (again, we know that CST != 1),
> but I'm not sure how valuable that is in practice, so opted not to do that yet)
>
> The following patch reinstates folding by adding a new match.pd rule that moves
> constants to outermost operands, where they can be merged by fold_binary
> machinery if their product doesn't overflow.  Bootstrapped and regtested on
> amd64, OK for trunk?
>
> 	* match.pd ((X * CST) * Y): Reassociate to (X * Y) * CST.
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 4c64b21..e49f879 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2139,6 +2139,15 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>  (mult @0 integer_minus_onep)
>  (negate @0))
>
> +/* Reassociate (X * CST) * Y to (X * Y) * CST.  This does not introduce
> +   signed overflow: previous rules handle CST being -1 or 0, and for
> +   the rest we know that if X * Y overflows, so does (X * CST) * Y.  */
> +(if (!TYPE_OVERFLOW_SANITIZED (type) && !TYPE_SATURATING (type))
> + (simplify
> +  (mult:c (mult @0 INTEGER_CST@1) @2)
> +  (if (TREE_CODE (@2) != INTEGER_CST)
> +   (mult (mult @0 @2) @1))))
> +
> /* True if we can easily extract the real and imaginary parts of a complex
>    number.  */
> (match compositional_complex

Thanks. I guess it makes sense as a canonicalization (there are always 
cases where those make it harder to optimize, but hopefully fewer than 
those where they help).

I notice that we do not turn (X*10)*10 into X*100 in GIMPLE. X*big*big 
where abs(big*big)>abs(INT_MIN) can be optimized to 0, the only hard case 
is when the product of the constants is -INT_MIN, which we could turn into 
X<<31 for instance (sadly loses range info), or (-X)*INT_MIN or whatever. 
That would make a nice follow-up, if you are interested.

Relying on inner expressions being folded can be slightly dangerous, 
especially for generic IIRC. It seems easy enough to check that @1 is 
neither 0 nor -1 for safety.

Probably needs :s on the inner multiplication.

Unless the test on TYPE_OVERFLOW_SANITIZED etc is shared with adjacent 
transformations, I'd rather put it inside, with the other if, but that's a 
matter of taste.

One small testcase please? Or is there already one that is currently 
failing?

(I cannot ok patches, only comment)
Alexander Monakov July 13, 2017, 7:41 p.m. UTC | #2
On Thu, 13 Jul 2017, Marc Glisse wrote:
> I notice that we do not turn (X*10)*10 into X*100 in GIMPLE.

Sorry, could you clarify what you mean here?  I think we certainly do that,
just not via match.pd, but in 'associate:' case of fold_binary_loc.

> Relying on inner expressions being folded can be slightly dangerous,
> especially for generic IIRC. It seems easy enough to check that @1 is neither
> 0 nor -1 for safety.

I wanted to add a gcc_checking_assert to that effect, but it's not used in
match.pd anywhere.  Is there a nice way to do that?

Thanks!
Alexander
Marc Glisse July 14, 2017, 5:59 a.m. UTC | #3
On Thu, 13 Jul 2017, Alexander Monakov wrote:

> On Thu, 13 Jul 2017, Marc Glisse wrote:
>> I notice that we do not turn (X*10)*10 into X*100 in GIMPLE.
>
> Sorry, could you clarify what you mean here?  I think we certainly do that,
> just not via match.pd, but in 'associate:' case of fold_binary_loc.

fold_binary_loc is for GENERIC, so mostly for front-end time optimization 
of expressions.

int f(int a){
   int b=a*10;
   return b*10;
}

$ gcc-snapshot -O3 -S -fdump-tree-optimized a.c
$ cat a.c.228t.optimized
[...]
   b_2 = a_1(D) * 10;
   _3 = b_2 * 10;
   return _3;

>> Relying on inner expressions being folded can be slightly dangerous,
>> especially for generic IIRC. It seems easy enough to check that @1 is neither
>> 0 nor -1 for safety.
>
> I wanted to add a gcc_checking_assert to that effect, but it's not used in
> match.pd anywhere.  Is there a nice way to do that?

You can use (with { arbitrary C++ code } ... ) to add an assertion (you 
can use @0 in the block of C++ code).

I was more thinking of an "if" than an assertion though.
Richard Biener July 18, 2017, 8:23 a.m. UTC | #4
On Fri, Jul 14, 2017 at 7:59 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Thu, 13 Jul 2017, Alexander Monakov wrote:
>
>> On Thu, 13 Jul 2017, Marc Glisse wrote:
>>>
>>> I notice that we do not turn (X*10)*10 into X*100 in GIMPLE.
>>
>>
>> Sorry, could you clarify what you mean here?  I think we certainly do
>> that,
>> just not via match.pd, but in 'associate:' case of fold_binary_loc.
>
>
> fold_binary_loc is for GENERIC, so mostly for front-end time optimization of
> expressions.
>
> int f(int a){
>   int b=a*10;
>   return b*10;
> }
>
> $ gcc-snapshot -O3 -S -fdump-tree-optimized a.c
> $ cat a.c.228t.optimized
> [...]
>   b_2 = a_1(D) * 10;
>   _3 = b_2 * 10;
>   return _3;

Yeah, but I think we best address this by adding support to re-associate
expressions with !TYPE_OVERFLOW_WRAPS to the reassoc pass.
It's not going to be an easy task if you want to avoid re-writing everything
to unsigned arithmetic.

Simple cases might be worth doing with patterns (like the case above).

Richard.

>>> Relying on inner expressions being folded can be slightly dangerous,
>>> especially for generic IIRC. It seems easy enough to check that @1 is
>>> neither
>>> 0 nor -1 for safety.
>>
>>
>> I wanted to add a gcc_checking_assert to that effect, but it's not used in
>> match.pd anywhere.  Is there a nice way to do that?
>
>
> You can use (with { arbitrary C++ code } ... ) to add an assertion (you can
> use @0 in the block of C++ code).
>
> I was more thinking of an "if" than an assertion though.
>
> --
> Marc Glisse
diff mbox

Patch

diff --git a/gcc/match.pd b/gcc/match.pd
index 4c64b21..e49f879 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -2139,6 +2139,15 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (mult @0 integer_minus_onep)
  (negate @0))

+/* Reassociate (X * CST) * Y to (X * Y) * CST.  This does not introduce
+   signed overflow: previous rules handle CST being -1 or 0, and for
+   the rest we know that if X * Y overflows, so does (X * CST) * Y.  */
+(if (!TYPE_OVERFLOW_SANITIZED (type) && !TYPE_SATURATING (type))
+ (simplify
+  (mult:c (mult @0 INTEGER_CST@1) @2)
+  (if (TREE_CODE (@2) != INTEGER_CST)
+   (mult (mult @0 @2) @1))))
+
 /* True if we can easily extract the real and imaginary parts of a complex
    number.  */
 (match compositional_complex