https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78317

            Bug ID: 78317
           Summary: "if (x & constant) z |= constant" should not be
                    rendered with jumps and conditional moves
           Product: gcc
           Version: 6.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: dhowells at redhat dot com
  Target Milestone: ---

Created attachment 40025
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=40025&action=edit
Test code

The following code:

        unsigned test1(unsigned x)
        {
                unsigned z = 0;
                if (x & 0x10)
                        z |= 0x10;
                return z;
        }

on x86_64 compiled with -Os to:

   0:   89 f8                   mov    %edi,%eax
   2:   ba 10 00 00 00          mov    $0x10,%edx
   7:   83 e0 10                and    $0x10,%eax
   a:   0f 45 c2                cmovne %edx,%eax
   d:   c3                      retq   

when what it should probable do is what clang does:

   0:   83 e7 10                and    $0x10,%edi
   3:   89 f8                   mov    %edi,%eax
   5:   c3                      retq   

as the bit can be transferred by an AND and an OR.

Further, two or more such statements can be combined, for instance:

        unsigned test2(unsigned x)
        {
                unsigned z = 0;
                if (x & 0x10)
                        z |= 0x10;
                if (x & 0x40)
                        z |= 0x40;
                return z;
        }

but gcc gives the following:

   e:   89 f8                   mov    %edi,%eax
  10:   ba 10 00 00 00          mov    $0x10,%edx
  15:   83 e0 10                and    $0x10,%eax
  18:   0f 45 c2                cmovne %edx,%eax
  1b:   89 c2                   mov    %eax,%edx
  1d:   83 ca 40                or     $0x40,%edx
  20:   40 80 e7 40             and    $0x40,%dil
  24:   0f 45 c2                cmovne %edx,%eax
  27:   c3                      retq   

when clang gives:

   6:   83 e7 50                and    $0x50,%edi
   9:   89 f8                   mov    %edi,%eax
   b:   c3                      retq   


If z isn't passed in, but rather is initialised to another argument, say y:

        unsigned test3(unsigned x, unsigned y)
        {
                unsigned z = y;
                if (x & 0x10)
                        z |= 0x10;
                return z;
        }

        unsigned test4(unsigned x, unsigned y)
        {
                unsigned z = y;
                if (x & 0x10)
                        z |= 0x10;
                if (x & 0x40)
                        z |= 0x40;
                return z;
        }

then gcc gives:

0000000000000028 <test3>:
  28:   89 f2                   mov    %esi,%edx
  2a:   89 f0                   mov    %esi,%eax
  2c:   83 ca 10                or     $0x10,%edx
  2f:   40 80 e7 10             and    $0x10,%dil
  33:   0f 45 c2                cmovne %edx,%eax
  36:   c3                      retq   

0000000000000037 <test4>:
  37:   89 f2                   mov    %esi,%edx
  39:   89 f0                   mov    %esi,%eax
  3b:   83 ca 10                or     $0x10,%edx
  3e:   40 f6 c7 10             test   $0x10,%dil
  42:   0f 45 c2                cmovne %edx,%eax
  45:   89 c2                   mov    %eax,%edx
  47:   83 ca 40                or     $0x40,%edx
  4a:   40 80 e7 40             and    $0x40,%dil
  4e:   0f 45 c2                cmovne %edx,%eax
  51:   c3                      retq   

and clang gives:

000000000000000c <test3>:
   c:   83 e7 10                and    $0x10,%edi
   f:   09 f7                   or     %esi,%edi
  11:   89 f8                   mov    %edi,%eax
  13:   c3                      retq   

0000000000000014 <test4>:
  14:   89 f8                   mov    %edi,%eax
  16:   83 e0 10                and    $0x10,%eax
  19:   09 f0                   or     %esi,%eax
  1b:   83 e7 40                and    $0x40,%edi
  1e:   09 c7                   or     %eax,%edi
  20:   89 f8                   mov    %edi,%eax
  22:   c3                      retq   

Both gcc and clang give suboptimal code for test4().  What they should do is:

        and    $0x50,%edi
        or     %esi,%edi
        mov    %edi,%eax
        retq

Note that gcc also produces similarly suboptimal output for targets other than
x86_64.

Reply via email to