c++bitwise-operatorslogical-operators

forcing usage of bitwise and instead of boolean and


In C++, a bool is guaranteed to be 0 or 1

C++ (§4.5/4):
An rvalue of type bool can be converted to an rvalue of type int, with 
false becoming zero and true becoming one.

Consider the following function and what g++5.2 generates with -O3

int foo(bool a, bool b)
{
    if(a&b) return 3;
    else return 5;
}


0000000000000000 <_Z3foobb>:
   0:   40 84 ff                test   %dil,%dil
   3:   74 13                   je     18 <_Z3foobb+0x18>
   5:   40 84 f6                test   %sil,%sil
   8:   b8 03 00 00 00          mov    $0x3,%eax
   d:   74 09                   je     18 <_Z3foobb+0x18>
   f:   f3 c3                   repz retq 
   11:  0f 1f 80 00 00 00 00    nopl   0x0(%rax)
   18:  b8 05 00 00 00          mov    $0x5,%eax
   1d:  c3                      retq   

As seen, above, it is generating two test instructions which indicates that it is still treating the if as a if(a&&b) instead of a bitwise and.

Even if I first explicitly convert the two bools to two chars , it still generates the same output as above.

Since I know that the two operands a and b can only have 0/1 as values, is there some way to get gcc to generate just one test instruction. This is indeed what it does if the function takes two ints instead of two bools.


Solution

  • With &, some compiler already produces different asm without jump:

    clang 3.6 (rc2):

    foo(bool, bool):                               # @foo(bool, bool)
        testb   %sil, %dil
        sete    %al
        movzbl  %al, %eax
        leal    3(%rax,%rax), %eax
        retq
    

    A hack, in your case is to use * which has the same true-table for true/false

    int foo(bool a, bool b)
    {
        if (a * b) return 3;
        else return 5;
    }
    

    Which produces:

    foo(bool, bool):
        movzbl  %sil, %esi  # b, b
        movzbl  %dil, %edi  # a, tmp70
        imull   %esi, %edi  # b, tmp70
        cmpl    $1, %edi    #, tmp70
        sbbl    %eax, %eax  # D.1960
        andl    $2, %eax    #, D.1960
        addl    $3, %eax    #, D.1960
        ret
    

    Demo