Troubleshooting gcc 4.4.X version optimization Switch-enum bug

Source: Internet
Author: User

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&GT: 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&gt: 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
    1. 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
    2. Guard code after switch on enum is never reached-https://stackoverflow.com/questions/8679534/ guard-code-after-switch-on-enum-is-never-reached/8679627
    3. Bug 41425-switch with enums doesn ' t work-https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41425
    4. Options controlling C + + Dialect-https://gcc.gnu.org/onlinedocs/gcc/c_002b_002b-dialect-options.html#index-fstrict-enums
    5. 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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.