Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC: program doesn't work with compilation option -O3

I'm writing a C++ program that doesn't work (I get a segmentation fault) when I compile it with optimizations (options -O1, -O2, -O3, etc.), but it works just fine when I compile it without optimizations.

Is there any chance that the error is in my code? or should I assume that this is a bug in GCC?

My GCC version is 3.4.6.

Is there any known workaround for this kind of problem?

There is a big difference in speed between the optimized and unoptimized version of my program, so I really need to use optimizations.


This is my original functor. The one that works fine with no levels of optimizations and throws a segmentation fault with any level of optimization:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){
        return distance(point,p1) < distance(point,p2) ;
    }
} ;

And this one works flawlessly with any level of optimization:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){

        float d1=distance(point,p1) ;
        float d2=distance(point,p2) ;

        std::cout << "" ;  //without this line, I get a segmentation fault anyways

        return d1 < d2 ;
    }
} ;

Unfortunately, this problem is hard to reproduce because it happens with some specific values. I get the segmentation fault upon sorting just one out of more than a thousand vectors, so it really depends on the specific combination of values each vector has.

like image 755
GetFree Avatar asked Nov 11 '08 05:11

GetFree


3 Answers

Now that you posted the code fragment and a working workaround was found (@Windows programmer's answer), I can say that perhaps what you are looking for is -ffloat-store.

-ffloat-store

Do not store floating point variables in registers, and inhibit other options that might change whether a floating point value is taken from a register or memory.

This option prevents undesirable excess precision on machines such as the 68000 where the floating registers (of the 68881) keep more precision than a double is supposed to have. Similarly for the x86 architecture. For most programs, the excess precision does only good, but a few programs rely on the precise definition of IEEE floating point. Use -ffloat-store for such programs, after modifying them to store all pertinent intermediate computations into variables.

Source: http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Optimize-Options.html

like image 100
CesarB Avatar answered Oct 25 '22 12:10

CesarB


I would assume your code is wrong first.
Though it is hard to tell.

Does your code compile with 0 warnings?

 g++ -Wall -Wextra -pedantic -ansi
like image 41
Martin York Avatar answered Oct 25 '22 12:10

Martin York


Here's some code that seems to work, until you hit -O3...

#include <stdio.h>

int main()
{
    int i = 0, j = 1, k = 2;
    printf("%d %d %d\n", *(&j-1), *(&j), *(&j+1));
    return 0;
}

Without optimisations, I get "2 1 0"; with optimisations I get "40 1 2293680". Why? Because i and k got optimised out!

But I was taking the address of j and going out of the memory region allocated to j. That's not allowed by the standard. It's most likely that your problem is caused by a similar deviation from the standard.

I find valgrind is often helpful at times like these.

EDIT: Some commenters are under the impression that the standard allows arbitrary pointer arithmetic. It does not. Remember that some architectures have funny addressing schemes, alignment may be important, and you may get problems if you overflow certain registers!

The words of the [draft] standard, on adding/subtracting an integer to/from a pointer (emphasis added):

"If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined."

Seeing as &j doesn't even point to an array object, &j-1 and &j+1 can hardly point to part of the same array object. So simply evaluating &j+1 (let alone dereferencing it) is undefined behaviour.

On x86 we can be pretty confident that adding one to a pointer is fairly safe and just takes us to the next memory location. In the code above, the problem occurs when we make assumptions about what that memory contains, which of course the standard doesn't go near.

like image 20
Artelius Avatar answered Oct 25 '22 12:10

Artelius