14
votes

I reproduce the problem by this simple demo:

// bool_test_func.cpp
#include <stdio.h>

void func(bool* b) {
  int a = (*b ? 0 : 1);
  printf("%d\n", a); // EXPECT ether 0 or 1 here
}

// bool_test.cpp
void func(bool* b);

int main() {
  int n = 128;
  func((bool*)&n);
  return 0;
}

-O0 compile and run:

g++ -g -O0 -Wall -o bool_test bool_test.cpp bool_test_func.cpp
mikewei@maclinux:~/testing/c++$ ./bool_test
0

-O1 compile and run (unexpected result):

g++ -g -O1 -Wall -o bool_test bool_test.cpp bool_test_func.cpp
mikewei@maclinux:~/testing/c++$ ./bool_test
129

When I check the -O2 ASM code, I think it is a g++ bug, g++'s optimzation code always think the bool value is ether 1 or 0:


    00000000004005e6 :
      4005e6:       48 83 ec 08             sub    $0x8,%rsp
      4005ea:       0f b6 37                movzbl (%rdi),%esi
      4005ed:       83 f6 01                xor    $0x1,%esi  #just XOR the bool val
      4005f0:       40 0f b6 f6             movzbl %sil,%esi
      4005f4:       bf 94 06 40 00          mov    $0x400694,%edi
      4005f9:       b8 00 00 00 00          mov    $0x0,%eax
      4005fe:       e8 9d fe ff ff          callq  4004a0 
      400603:       48 83 c4 08             add    $0x8,%rsp
      400607:       c3                      retq
      400608:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
      40060f:       00

 

gcc version 4.9.2 (Debian 4.9.2-10)

Is this g++ behavior by design? How can I disable this wrong optimaztion? Thanks~

1
Interesting #include.erip
The optimization is not wrong : a bool can only contain 0 or 1. What you're passing in is an int type-punned as a bool, undefined behaviour ensues.Quentin
Rather bold statement that this is a gcc bug...Petr
Not sure I agree with the dupe close reason, supposed original is actually due to uninitialised variable, nothing to do with ub caused by type punning.paxdiablo
Not sure whether I up-vote this for being a good example of UB or down-vote for UB and arrogance over this "wrong optimization", and the use of C headers and casts?djgandy

1 Answers

49
votes

I think it is a g++ bug.

Have to respectfully disagree there.

How can I disable this wrong optimisation?

Even if that is possible, it's not actually wrong :-)

You are passing a non-bool pointer as a bool pointer (the underlying item is not of the bool type) and I'm pretty certain that this (known as type punning) invokes undefined behaviour.

Hence the code is free to do whatever it wants.

You can think of the prototype void func(bool* b) as a contract you agree to to only ever pass pointers to bool values. If you violate that contract, all bets are off. In this particular case, your code:

int a = (*b ? 0 : 1);

is telling it to convert false to 1 and true to 0. If you were to supply valid input (false/0 or true/1), the xor input, 1 would be exactly the right thing to do.

However, because you're giving it 128, the xor results in 129.