Cause
An accidental encounter with a strange bug, the phenomenon is that the same C + + code using the gcc4.4.x version before the optimization and optimization of the result is not the same, optimized code logic is incorrect.
The sample code is as follows:
//main.cpp#include <stdio.h>enum Type { ERR_A = -1, ERR_B = 0, ERR_C = 1,};void func(Type tt) { switch(tt){ case ERR_A: printf("case ERR_A, tt = %d\n", tt); break; case ERR_B: printf("case ERR_B, tt = %d\n", tt); break; case ERR_C: printf("case ERR_C, tt = %d\n", tt); break; default: printf("case default, tt = %d\n", tt); break; }}int main(){ Type tt = (Type)4; func(tt); //预期输出case default tt = (Type)1; func(tt); //预期输出case ERR_C tt = (Type)-4; func(tt); //预期输出case default return 0;}
This code was used g++ -O0
and compiled separately g++ -O1
, the result is surprising, when tt=4, switch jumped to 1 of the branch.
$ g++ -O0 -g -o main main.cpp$ ./maincase default, tt = 4case ERR_C, tt = 1case default, tt = -4$ g++ -O1 -g -o main main.cpp$ ./maincase ERR_C, tt = 1case ERR_C, tt = 1case default, tt = -4
Troubleshooting Procedures
Considering the existence of an enum, it may be that the enumeration has been optimized by GCC beyond the defined range, and a post is found on the web to the effect that the enum is stored in the int type, while the 32bit has faster processing efficiency in the CPU. With the single-step debug and watch command, the value of the TT is always 4 and has not been changed, so you can exclude the case of enum undefined.
So only to see the assembly code, it turns out that this is the most effective way, than the blind guess to save time.
You can use commands to disas
view assembly code while debugging, or you can use objdump
assembly code that looks directly at the binary.
Compare the assembly code in both the debug (top) and release (bottom) cases.
# Not open optimization (GDB) b 26Breakpoint 1 at 0x400620:file main.cpp, line 26. (GDB) R ... (GDB) n27 func (TT);(gdb) Sfunc (tt=4) at main.cpp:1010 switch (TT) {(GDB) Disas/mdump of assembler code F or function func (type): 9 void func (Type TT) {0X00000000004005A4 <+0>: Push%RBP 0x00000000004005a5 & LT;+1>: mov%rsp,%rbp 0x00000000004005a8 <+4>: Sub $0x10,%rsp 0x00000000004005ac <+8>: mov%edi,-0x4 (%RBP) ten switch (TT) {=> 0X00000000004005AF <+11>: mov-0x4 (%RBP),%eax 0x0000000 0004005B2 <+14>: Test%eax,%eax 0x00000000004005b4 <+16>: je 0x4005d6 <func (Type) +50> 0x 00000000004005b6 <+18>: CMP $0x1,%eax 0x00000000004005b9 <+21>: je 0x4005ec <func (Type) +72&G T 0X00000000004005BB <+23>: CMP $0xffffffffffffffff,%eax 0x00000000004005be <+26>: jne 0x400602 &L T;func (Type) +94>11 case Err_a:12 printf ("Case err_a, tt =%d\n", TT); 0x00000000004005c0 <+28>: mov-0x4 (%RBP),%eax ... Err_b:15 printf ("case err_b, tt =%d\n", TT); 0x00000000004005d6 <+50>: mov-0x4 (%RBP),%eax ... Err_c:18 printf ("case err_c, tt =%d\n", TT); 0x00000000004005ec <+72>: mov-0x4 (%RBP),%eax ... default:21 printf ("case default, TT =%d\n", TT); 0x0000000000400602 <+94>: mov-0x4 (%RBP),%eax
# Open O1 optimization option (GDB) b 26Breakpoint 1 at 0x400611:file main.cpp, line 26. (GDB) R ... (GDB) ncase err_c, TT = 129 func (TT);(gdb) Sfunc (tt=err_c) at main.cpp:99 void func (Type tt) {(GDB) Disas/ Mdump of assembler code for function func (type): 9 void func (Type TT) {=> 0x00000000004005a4 <+0>: Sub $0X8,%RSP10 switch (TT) {0x00000000004005a8 <+4>: Test%edi,%edi 0X00000000004005AA <+6>: Je 0x4005cb <func (Type) +39> 0x00000000004005ac <+8>: Test%edi,%edi 0x00000000004005ae <+1 0>: JG 0x4005e1 <func (Type) +61> 0x00000000004005b0 <+12>: CMP $0xffffffffffffffff,%edi 0x0 0000000004005B3 <+15>: jne 0x4005f7 <func (Type) +83> one case err_a:12 printf ("Case err_a, tt =%d\n", TT); 0X00000000004005B5 <+17>: mov $0xffffffff,%esi ... Err_b:15 printf ("case err_b, tt =%d\n", TT); 0x00000000004005CB <+39>: mov $0x0,%esi ... Err_c:18 printf ("case err_c, tt =%d\n", TT); 0x00000000004005e1 <+61>: mov $0x1,%esi ... default:21 printf ("case default, TT =%d\n", TT); 0x00000000004005f7 <+83>: mov%edi,%esi ...
As you can see O0
, the assembly logic is: equals 0 o'clock jumps to case B, equals 1 jumps to case C, does not equal-1 jumps to default, equals-1 to case A.
At the O1
time, the assembly logic is: equals 0 o'clock jumps to case B, greater than 0 jumps directly to case C, does not equal-1 jumps to default, equals 1 to case A.
The reason for the error is that when the compilation optimization is turned on, GCC will default to a case C (1) greater than 0, which is presumably due to the use of bitwise operations, and the use of the test
Add and cmp
subtract operations, using test to improve the efficiency of the Operation . But this change in code logic makes the optimization of logic errors obviously unacceptable.
Official explanation
Such a strange problem, although found in the cause, but the heart still can not accept this is the GCC mistake.
After Google a few, found this post , sure enough, someone also stepped on the same pit.
This is a GCC4.4 version of the bug, although this optimization is unreasonable, but still be as a "feature" is retained ...
In high-version gcc, the -std=c++03 -fstrict-enum
option can be used to turn on this "feature", which assumes that the programmer will ensure that the enum's value is within its definition.
Finally, there are two ways to solve this problem, either by doing an enum scope check before switch, or by using a later version of GCC.
Other
Finally, with a query on the data you see the GCC to switch to do the optimization ...
Reference
- What is the size of a enum type data in C + +? -Https://stackoverflow.com/questions/8115550/what-is-the-size-of-an-enum-type-data-in-c
- Guard code after switch on enum is never reached-https://stackoverflow.com/questions/8679534/ guard-code-after-switch-on-enum-is-never-reached/8679627
- Bug 41425-switch with enums doesn ' t work-https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41425
- Options controlling C + + Dialect-https://gcc.gnu.org/onlinedocs/gcc/c_002b_002b-dialect-options.html#index-fstrict-enums
- From Switch Statement to code-http://lazarenko.me/switch/
Original address: https://lidawn.github.io/2018/09/02/gcc-bug/
Troubleshooting gcc 4.4.X version optimization Switch-enum bug