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

            Bug ID: 88916
           Summary: [x86] suboptimal code generated for integer
                    comparisons joined with boolean operators
           Product: gcc
           Version: 8.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: wojciech_mula at poczta dot onet.pl
  Target Milestone: ---

Let's consider these two simple, yet pretty useful functions:

--test.c---
int both_nonnegative(long a, long b) {
    return (a >= 0) && (b >= 0);
}

int both_nonzero(unsigned long a, unsigned long b) {
    return (a > 0) && (b > 0);
}
---eof--

$ gcc --version
gcc (Debian 8.2.0-13) 8.2.0

$ gcc -O3 test.c -march=skylake -S
$ cat test.s
both_nonnegative:
    notq    %rdi
    movq    %rdi, %rax
    notq    %rsi
    shrq    $63, %rax
    shrq    $63, %rsi
    andl    %esi, %eax
    ret

both_nonzero:
    testq   %rdi, %rdi
    setne   %al
    xorl    %edx, %edx
    testq   %rsi, %rsi
    setne   %dl
    andl    %edx, %eax
    ret

I checked different target machines (haswell, broadwell and cannonlake),
however the result remained the same. Also GCC trunk on godbolt.org 
produces the same assembly code.

The first function, `both_nonnegative`, can be rewritten as:

    (((unsigned long)(a) | (unsigned long)(b)) >> 63) ^ 1

yielding something like this:

    both_nonnegative:
        orq     %rsi, %rdi
        movq    %rdi, %rax
        shrq    $63, %rax
        xorl    $1, %eax
        ret

It's also possible to use this expression:
(long)(unsigned long)a | (unsigned long)b) < 0,
but the assembly output is almost the same.

The condition from `both_nonzero` can be expressed as:

    ((unsigned long)a | (unsigned long)b) != 0

GCC compiles it to:

    both_nonzero:
        xorl    %eax, %eax
        orq     %rsi, %rdi
        setne   %al
        retq

Reply via email to