Search code examples
cgccoptimizationvariable-assignment

Why did GCC optimizes important assignment, making it into a different program behavior?


So, I am making a C program for my console interpreter to detect which type is it, then return it to typeret and store the pointer into the void pointer retpoint. Now, what I am asking is not what is in the code, but rather why GCC optimizes this assignment in my function eval. I want to know why GCC optimizes something that is actually important.

int eval(wchar_t *expr) {
    // ....
    
    if (expr[0] == L'\'' && expr[csize - 1] == L'\'') { // <-- Ignore this.
        expr[csize - 1] = 0;
        typeret         = 0;
        
        retpoint    = (expr + 1);
    } else if (strin(L".", expr)) { // <-- Ignore this.
        typeret     = 2;
    } else {
        typeret     = 1;
        tvar        = _wtoi(expr); // <-- Here is the problem.
        retpoint    = &tvar;
    }
    
    return 0;
}

When I compile the code with the GCC command, gcc -Wsomewarning -o cim.exe cim.c -Os -s -ffunction-sections -fdata-sections -O3, the assignment is not detected (even without -ffunction-sections and -fdata-sections, the assignment is not detected too).

But, when I change the code that involves around tvar OR around _wtoi, the assignment is not ignored. Here is the example code whereby the assignment is not ignored.

int eval(wchar_t *expr) {
    int
        tvar; // If I set tvar to N, then the assignment is still ignored (making tvar = N).

    // ....

    if (expr[0] == L'\'' && expr[csize - 1] == L'\'') { // <-- Ignore this.
        expr[csize - 1] = 0;
        typeret         = 0;

        retpoint    = (expr + 1);
    } else if (strin(L".", expr)) { // <-- Ignore this.
        typeret     = 2;
    } else {
        typeret     = 1;
        tvar        = _wtoi(expr); // <-- Here is the problem.
        wprintf(L"%d", tvar); // or wprintf(L"%d", _wtoi(expr);
        retpoint    = &tvar;
    }

    return 0;
}

Now, of course, if I use the gcc -o cim.exe cim.c (without optimization), the first code works fine; however, the size is four times bigger than the optimized one.

Note

I am sure there is no undefined behavior in this program, and I am sure that I have given the data correctly.

The typeret and retpoint is a global data, whereas the tvar is a local variable.

Edit

I forgot to mention that I used GCC version (tdm64-1) 4.9.2 in Windows 10.

Thanks in advance.


Solution

  • In your first example, the variable tvar is not accessed, neither directly nor through the pointer retpoint, between the assignment tvar = _wtoi(expr); and when the function eval returns, and after that the lifetime of tvar ends because it is local. And you are only allowed to access tvar during its lifetime. So the compiler has every right to optimize out the assignment because it cannot have any effect on a correct program.

    If it does change the behavior of your program, then you probably have undefined behavior somewhere. Perhaps you are dereferencing the pointer retpoint after eval returns. That is definitely UB. In general, setting a global pointer to point to a local variable, and leaving it there after the function returns, is very suspicious.