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

            Bug ID: 121738
           Summary: Optimization: ((x & y) == y) == ((x | y) == x) == ((~x
                    & y) == 0) should be target independent
           Product: gcc
           Version: 15.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: other
          Assignee: unassigned at gcc dot gnu.org
          Reporter: Explorer09 at gmail dot com
  Target Milestone: ---

These three bitwise mask-and-compare expressions `((x & y) == y)`, `((x | y) ==
x)` and `((~x & y) == 0)` should be equivalent. While GCC does optimize these
into the same for some target architectures (such as x86), it can miss in other
architectures (such as RISC-V). This suggests me that GCC didn't treat these
three expressions as equivalent formally, and only optimize these in
target-specific optimization phases.

Because I don't know the details about "tree-optimization" or
"rtl-optimization", this is the best I can describe the problem. The
equivalence should be target-independent.

Test code:

```c
#include <stdbool.h>

bool test1a(unsigned int x, unsigned int y) {
    return ((x & y) == y);
}
bool test1b(unsigned int x, unsigned int y) {
    return ((x | y) == x);
}
bool test1c(unsigned int x, unsigned int y) {
    return ((~x & y) == 0);
}
unsigned int test2a(unsigned int x, unsigned int y) {
    if ((x & y) == y)
        return x - y;
    return 0x12345678;
}
unsigned int test2b(unsigned int x, unsigned int y) {
    if ((x | y) == x)
        return x - y;
    return 0x12345678;
}
unsigned int test2c(unsigned int x, unsigned int y) {
    if ((~x & y) == 0)
        return x - y;
    return 0x12345678;
}
```

GCC for RISC-V target generates different code (`-Os` option is used):

(It looks like the test1a version has the smallest code)

```assembly
test1a:
        and     a0,a0,a1
        sub     a0,a0,a1
        seqz    a0,a0
        ret
test1b:
        or      a1,a0,a1
        sub     a0,a1,a0
        seqz    a0,a0
        ret
test1c:
        not     a0,a0
        and     a0,a0,a1
        seqz    a0,a0
        ret
```

GCC for x86 and ARM targets makes test1 the same code. However, when the
conditionals become slightly more complex, GCC starts to miss the optimization
even for x86 and ARM targets.

GCC for x86-64 (`-Os` option) produces:

```assembly
test2a:
        movl    %edi, %eax
        andl    %esi, %eax
        subl    %eax, %edi
        cmpl    %esi, %eax
        movl    $305419896, %eax
        cmove   %edi, %eax
        ret
test2b:
        movl    %edi, %edx
        orl     %esi, %edx
        movl    %edx, %eax
        subl    %esi, %eax
        cmpl    %edi, %edx
        movl    $305419896, %edx
        cmovne  %edx, %eax
        ret
test2c:
        movl    %edi, %eax
        notl    %edi
        movl    $305419896, %edx
        subl    %esi, %eax
        testl   %esi, %edi
        cmovne  %edx, %eax
        ret
```

GCC for ARM64 (`-Os` option) produces:

```assembly
test2a:
        bic     w2, w0, w1
        bics    wzr, w1, w0
        mov     w0, 22136
        movk    w0, 0x1234, lsl 16
        csel    w0, w2, w0, eq
        ret
test2b:
        orr     w2, w0, w1
        cmp     w2, w0
        sub     w1, w2, w1
        mov     w0, 22136
        movk    w0, 0x1234, lsl 16
        csel    w0, w1, w0, eq
        ret
test2c:
        sub     w2, w0, w1
        bics    wzr, w1, w0
        mov     w0, 22136
        movk    w0, 0x1234, lsl 16
        csel    w0, w2, w0, eq
        ret
```

Note the code became different.

Bug 121682 is related to this one. When the equivalence of ((x & y) == y) ==
((x | y) == x) == ((~x & y) == 0) has been implemented, bug 121682 would become
easier to solve.

Reply via email to