gcc miscompilation in Stockfish 1.6

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: gcc miscompilation in Stockfish 1.6

Post by bob »

mcostalba wrote:
bob wrote: GCC is a very robust compiler, breaking it is almost always a user issue rather than a compiler issue. As it is here.
If with -O1 and -O3 the functionality is different it is a compiler bug by definition. BTW rewriting Score as a struct instead of an enum (as posted above) does not change anything.

We already know it is not faster, but it is cleaner in this way because you avoid a lot of double assignments / updates scattered through the code (-70 lines of code if I remember correctly).
that's not necessarily a compiler bug. For example, if you have an undefined variable (used before it is defined) it will almost certainly behave differently at different levels of optimization, and it is not a compiler bug at all. There are lots of ways to produce just this kind of behaviour, and all of them are programming bugs rather than compiler bugs.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: gcc miscompilation in Stockfish 1.6

Post by bob »

mcostalba wrote:
Bo Persson wrote: In my experience you find a genuine code generation bug about once every 10 years. This might very well be your turn, but I wouldn't bet on it.
May be it is my turn ;-)

I think I have found the miscompiled line, with following version it works for me:

Code: Select all

/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value.

enum Score &#123; ENSURE_32_BIT_SIZE = 1 << 31 &#125;;

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;(&#40;s & 0xffff&#41; << 16&#41; >> 16&#41;; &#125;
The first thing is to ensure gcc uses an integer for the enum and not a smaller type (and this is allowed by standard so is not a gcc bug), but the crucial point is the rewrite of the eg_value() function that now gives the correct result also with gcc -O3 (and I have verified both Intel and MSVC compile also this version correctly).

So the problem was this one:

Code: Select all

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;int16_t&#40;s & 0xffff&#41;); &#125;
Function eg_value() extracts the signed lower 16 bits from the 32 bit enum 's'. This function got miscompiled under gcc -O3 with some recent gcc version.


If someone could test with a 64 bit compile (I don't have a 64 bit machine), I would be very glad so that I can at last release 1.6.2 with all the stuff fixed, included a rewrite of pop_1st_bit() to avoid the reported aliasing issue.
There are several ways to produce this kind of behaviour. I assume you already use -Wall so that it will produce complaints about uninitialized variables and such? next issue is pointers. Higher optimization levels assume that two different pointers do not point to the same memory address. Eugene's egtb.cpp is an example of code that will completely break because of this if you don't tell the compiler that this is an issue.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: gcc miscompilation in Stockfish 1.6

Post by bob »

mcostalba wrote:
Gian-Carlo Pascutto wrote:
mcostalba wrote: Sorry, I don't understand this. Why should be an error a right shift of a negative number ?
It's not an error, it's implementation defined. Meaning, if you switch compilers or compiler versions, or even optimization settings, the result is allowed to change.

If you care for the exact outcome (which I would presume), then the code is wrong.
I would think the right shift of an int is sign extended.
There is nothing in the C standard that guarantees that. See section 6.5.7, point 5:

http://www.open-std.org/JTC1/SC22/WG14/ ... /n1256.pdf
Thanks,

so I presume also this one should be changed

Code: Select all

inline Value mg_value&#40;Score s&#41; &#123; return Value&#40;&#40;int&#40;s&#41; + 32768&#41; >> 16&#41;; &#125; 

Have you a suggestion on how to extract the _signed_ 16 bits of the lower and upper part of a 32 bit field (integer) ?
Yes. Don't store negative numbers like that. Do like the IEEE floating point exponents and use "excess N (N=127 in 32 bit IEEE)". That is, add a number that is guaranteed to produce a positive result. Then subtract it after extracting the positive number. Then you get away from this kind of issue.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: gcc miscompilation in Stockfish 1.6

Post by mcostalba »

Gerd Isenberg wrote: You may use div instead of shift right:

Code: Select all

inline Value mg_value&#40;Score s&#41; &#123;return Value&#40;int&#40;s & ~0xffff&#41; / 0x10000&#41;;&#125; 
inline Value eg_value&#40;Score s&#41; &#123;return Value&#40;int&#40;short&#40;s &  0xffff&#41;));&#125; 
Thanks regarding the divide it works with a little adjustment to take in account the lower 16 bits carry:

Code: Select all

inline Value mg_value&#40;Score s&#41; &#123; return Value&#40;(&#40;int&#40;s&#41; + 32768&#41; & ~0xffff&#41; / 0x10000&#41;; &#125;
Instead cast to short doesn't because according to the standard, "If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined."

See http://www.velocityreviews.com/forums/t ... d-int.html

So probably I will use the follow one that works and it _seems_ standard compliant

Code: Select all

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;&#40;int&#41;&#40;unsigned&#40;s&#41; & 0x7fffu&#41; - &#40;int&#41;&#40;unsigned&#40;s&#41; & 0x8000u&#41;); &#125;
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: gcc miscompilation in Stockfish 1.6

Post by mcostalba »

Zach Wegner wrote:
zamar wrote:Note that here compiler is free to choose between signed and unsigned type:

Code: Select all

enum Score &#123; ENSURE_32_BIT_SIZE = 1 << 31 &#125;;
while here compiler is forced to use signed int:

Code: Select all

enum Score &#123; ENSURE_32_BIT_SIZE_MAX = 1 << 31,  ENSURE_32_BIT_SIZE_MIN = -&#40;1<<31&#41;&#125;;
No, for the last one it would have to use something more than 32 bit. In 32 bits, (1<<31) and -(1<<31) are the same number. And in the first example, it would have to use unsigned ints, bit 31 is the sign bit so (1<<31) is negative in signed arithmetic. Your examples would work if you change the 31s to 30s, though.
Ok, what about something like this ?

Code: Select all

enum Score &#123; ENSURE_32_BITS_SIZE_P = &#40;1 << 30&#41;,  ENSURE_32_BITS_SIZE_N = -&#40;1<<30&#41;&#125;;
User avatar
Bo Persson
Posts: 243
Joined: Sat Mar 11, 2006 8:31 am
Location: Malmö, Sweden
Full name: Bo Persson

Re: gcc miscompilation in Stockfish 1.6

Post by Bo Persson »

mcostalba wrote:
Gian-Carlo Pascutto wrote: There is nothing in the C standard that guarantees that. See section 6.5.7, point 5:

http://www.open-std.org/JTC1/SC22/WG14/ ... /n1256.pdf
Ok, so what about this one ?

Code: Select all

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;&#40;int&#41;&#40;unsigned&#40;s&#41; & 0x7fffu&#41; - &#40;int&#41;&#40;unsigned&#40;s&#41; & 0x8000u&#41;); &#125;
I have verified it works with all the compilers and does not rely on shifts or cast to shorts. I have found it googling around and it seems safe to me.
And how much do we now save, compared to actually storing two separate values of type short?
:-)
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: gcc miscompilation in Stockfish 1.6

Post by Gerd Isenberg »

mcostalba wrote:
Gerd Isenberg wrote: You may use div instead of shift right:

Code: Select all

inline Value mg_value&#40;Score s&#41; &#123;return Value&#40;int&#40;s & ~0xffff&#41; / 0x10000&#41;;&#125; 
inline Value eg_value&#40;Score s&#41; &#123;return Value&#40;int&#40;short&#40;s &  0xffff&#41;));&#125; 
Thanks regarding the divide it works with a little adjustment to take in account the lower 16 bits carry:

Code: Select all

inline Value mg_value&#40;Score s&#41; &#123; return Value&#40;(&#40;int&#40;s&#41; + 32768&#41; & ~0xffff&#41; / 0x10000&#41;; &#125;
Instead cast to short doesn't because according to the standard, "If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined."

See http://www.velocityreviews.com/forums/t ... d-int.html

So probably I will use the follow one that works and it _seems_ standard compliant

Code: Select all

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;&#40;int&#41;&#40;unsigned&#40;s&#41; & 0x7fffu&#41; - &#40;int&#41;&#40;unsigned&#40;s&#41; & 0x8000u&#41;); &#125;
I forgot you didn't have SWAR of signed shorts inside an signed int, but upper dependent on lower - therefor Bob's advice for a bias to positive integers seems very reasonable. The standard compliant for the lower part is very elegant and simple and was new to me.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: gcc miscompilation in Stockfish 1.6

Post by mcostalba »

Bo Persson wrote: And how much do we now save, compared to actually storing two separate values of type short?
:-)
It is too late to roll back, there are hundreds of lines of code to change scattered in many files if we want to come back to the "separated variables" approach.

As an afterthought I agree that this trick of using a single integer for two scores absolutely does not deserve the effort to make it work !

Anyhow it is already in, so let's keep it. I hope that other authors that will want to use the same approach have the road already well marked, because I think we have done the grunt work to fix all the subtle issues of this trick.

Lesson learned: avoid tricks as much as possible ;-)


On the good side I have to say that using only a single Score allows to simplify the code in many places in evaluation, and this is good because evaluation is already difficult as is and a bit of simplification there is a good thing IMHO.
User avatar
Bo Persson
Posts: 243
Joined: Sat Mar 11, 2006 8:31 am
Location: Malmö, Sweden
Full name: Bo Persson

Re: gcc miscompilation in Stockfish 1.6

Post by Bo Persson »

Gian-Carlo Pascutto wrote:
mcostalba wrote: Sorry, I don't understand this. Why should be an error a right shift of a negative number ?
It's not an error, it's implementation defined. Meaning, if you switch compilers or compiler versions, or even optimization settings, the result is allowed to change.

If you care for the exact outcome (which I would presume), then the code is wrong.
I would think the right shift of an int is sign extended.
There is nothing in the C standard that guarantees that. See section 6.5.7, point 5:

http://www.open-std.org/JTC1/SC22/WG14/ ... /n1256.pdf
On the other hand, gcc does define the shift as expected.

http://gcc.gnu.org/onlinedocs/gcc-4.4.2 ... ementation

I would expect all compilers for x86 hardware to do exactly that. The exception is there for 1's complement or signed magnitude hardware, where shifting bits is really interesting.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: gcc miscompilation in Stockfish 1.6

Post by Sven »

Bo Persson wrote:
mcostalba wrote:
Gian-Carlo Pascutto wrote: There is nothing in the C standard that guarantees that. See section 6.5.7, point 5:

http://www.open-std.org/JTC1/SC22/WG14/ ... /n1256.pdf
Ok, so what about this one ?

Code: Select all

inline Value eg_value&#40;Score s&#41; &#123; return Value&#40;&#40;int&#41;&#40;unsigned&#40;s&#41; & 0x7fffu&#41; - &#40;int&#41;&#40;unsigned&#40;s&#41; & 0x8000u&#41;); &#125;
I have verified it works with all the compilers and does not rely on shifts or cast to shorts. I have found it googling around and it seems safe to me.
And how much do we now save, compared to actually storing two separate values of type short?
:-)
Exactly what I wanted to say, right in this minute. I think the whole issue has gone too far into bit twiddling "at its best" while not being necessary at all.

I just renew my "class Score" proposal. This one compiles under GCC and includes everything you would really need. There is no bit operation involved, just two 16-bit values are kept and operated upon simultaneously. The user code needs a little bit of editing but I think it should be worth the small effort:

Code: Select all

class Score &#123;
public&#58;
    // constructors
    Score&#40;) &#58; mg&#40;0&#41;, eg&#40;0&#41; &#123;&#125;
    Score&#40;int mg_val, int eg_val&#41; &#58; mg&#40;mg_val&#41;, eg&#40;eg_val&#41; &#123;&#125;
    Score&#40;int eg_val&#41; &#58; mg&#40;0&#41;, eg&#40;eg_val&#41; &#123;&#125; // maybe subject of discussion

    // accessors
    int mg_value&#40;) const &#123; return int&#40;mg&#41;; &#125;
    int eg_value&#40;) const &#123; return int&#40;eg&#41;; &#125;

    // unary minus operator
    Score operator-() &#123; return Score&#40;-int&#40;mg&#41;, -int&#40;eg&#41;); &#125;

    // binary member operators
    Score operator+&#40;Score s&#41; &#123; return Score&#40;mg + s.mg, eg + s.eg&#41;; &#125;
    Score operator-&#40;Score s&#41; &#123; return Score&#40;mg - s.mg, eg - s.eg&#41;; &#125;
    Score operator+=&#40;Score s&#41; &#123; mg += s.mg; eg += s.eg; return *this; &#125;
    Score operator-=&#40;Score s&#41; &#123; mg -= s.mg; eg -= s.eg; return *this; &#125;

    // binary friend operators
    friend bool operator==&#40;Score const & s1, Score const & s2&#41;
        &#123; return s1.mg == s2.mg && s1.eg == s2.eg; &#125;
    friend bool operator!=&#40;Score const & s1, Score const & s2&#41; &#123; return !&#40;s1 == s2&#41;; &#125;
    friend Score operator*&#40;int i, Score s&#41; &#123; return Score&#40;i * s.mg, i * s.eg&#41;; &#125;
    friend Score operator*&#40;Score s, int i&#41; &#123; return i * s; &#125;

    // Division must be handled separately for each term
    friend Score operator/&#40;Score s, int i&#41; &#123; return Score&#40;s.mg / i, s.eg / i&#41;; &#125;

    // Only declared but not defined. We don't want to multiply two scores due to
    // a very high risk of overflow. So user should explicitly convert to integer.
    friend Score operator*&#40;Score s1, Score s2&#41;;

private&#58;
    int16_t mg;
    int16_t eg;
&#125;;
Maybe someone comes up with something even better?

Sven