Fold sign of LSHIFT_EXPR to eliminate no-op conversions.
authorRoger Sayle <roger@nextmovesoftware.com>
Mon, 23 Aug 2021 11:37:04 +0000 (12:37 +0100)
committerRoger Sayle <roger@nextmovesoftware.com>
Mon, 23 Aug 2021 11:37:04 +0000 (12:37 +0100)
commit1d244020246cb155e4de62ca3b302b920a1f513f
treef0f5c470146c9205c4cfd7809ccd86099e848f0d
parentb320edc0c29c838b0090c3c9be14187d132f73f2
Fold sign of LSHIFT_EXPR to eliminate no-op conversions.

This short patch teaches fold that it is "safe" to change the sign
of a left shift, to reduce the number of type conversions in gimple.
As an example:

unsigned int foo(unsigned int i) {
  return (int)i << 8;
}

is currently optimized to:

unsigned int foo (unsigned int i)
{
  int i.0_1;
  int _2;
  unsigned int _4;

  <bb 2> [local count: 1073741824]:
  i.0_1 = (int) i_3(D);
  _2 = i.0_1 << 8;
  _4 = (unsigned int) _2;
  return _4;
}

with this patch, this now becomes:

unsigned int foo (unsigned int i)
{
  unsigned int _2;

  <bb 2> [local count: 1073741824]:
  _2 = i_1(D) << 8;
  return _2;
}

which generates exactly the same assembly language.  Aside from the
reduced memory usage, the real benefit is that no-op conversions tend
to interfere with many folding optimizations.  For example,

unsigned int bar(unsigned char i) {
    return (i ^ (i<<16)) | (i<<8);
}

currently gets (tangled in conversions and) optimized to:

unsigned int bar (unsigned char i)
{
  unsigned int _1;
  unsigned int _2;
  int _3;
  int _4;
  unsigned int _6;
  unsigned int _8;

  <bb 2> [local count: 1073741824]:
  _1 = (unsigned int) i_5(D);
  _2 = _1 * 65537;
  _3 = (int) i_5(D);
  _4 = _3 << 8;
  _8 = (unsigned int) _4;
  _6 = _2 | _8;
  return _6;
}

but with this patch, bar now optimizes down to:

unsigned int bar(unsigned char i)
{
  unsigned int _1;
  unsigned int _4;

  <bb 2> [local count: 1073741824]:
  _1 = (unsigned int) i_3(D);
  _4 = _1 * 65793;
  return _4;

}

2021-08-23  Roger Sayle  <roger@nextmovesoftware.com>

gcc/ChangeLog
* match.pd (shift transformations): Change the sign of an
LSHIFT_EXPR if it reduces the number of explicit conversions.

gcc/testsuite/ChangeLog
* gcc.dg/fold-convlshift-1.c: New test case.
* gcc.dg/fold-convlshift-2.c: New test case.
gcc/match.pd
gcc/testsuite/gcc.dg/fold-convlshift-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-convlshift-2.c [new file with mode: 0644]