Compiler Problem

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

LoopList

Compiler Problem

Post by LoopList »

Hi

The following code seems to work correct. The code does not make sense, but has an interesting behaviour in Microsoft Visual Studio 2010 as x64 Release with the flag Ob1 or Ob2 (perhaps full optimization).

The variables current->value and temp should have the value 50100, but with x64 Release Ob2 in VC2010 the output is 50000.

I don't know the reason?

Perhaps you can copy the code and see yourself!

Console output:

Code: Select all

value...........50000
temp............50000
Regards
Fritz

Code: Select all

#include <stdio.h>

typedef unsigned __int64 bitboard_t;  // long long

const int IS_ENPASSANT = 1 << 15;

struct move_info_c &#123;
  int move;
  int value;
&#125;;

struct board_c &#123;
  int color_on&#91;64&#93;;
  int piece_on&#91;64&#93;;
  bitboard_t piece_bb&#91;6&#93;;
  bitboard_t color_bb&#91;2&#93;;
&#125;;

class sort_c &#123;
public&#58;
  sort_c&#40;);
  move_info_c * last_move;
  move_info_c move_list&#91;256&#93;;
&#125;;

static bitboard_t pawn_attack_bb&#91;2&#93;&#91;64&#93;;
static board_c B;

static void initialize_fen_position&#40;const char * position, const char * to_move, const char * castle, const char * enpassant&#41;;
static move_info_c * generate_evasions&#40;move_info_c * move_list&#41;;
static int static_exchange_evaluation&#40;int move&#41;;
static void initialize_bitboards&#40;);

inline int move_to&#40;int move&#41; &#123;
  return move & 63;
&#125;

inline int move_from&#40;int move&#41; &#123;
  return &#40;move >> 6&#41; & 63;
&#125;

inline int move_is_enpassant&#40;int move&#41; &#123;
  return &#40;move & IS_ENPASSANT&#41; != 0;
&#125;

inline int make_enpassant&#40;int from, int to&#41; &#123;
  return to | &#40;from << 6&#41; | IS_ENPASSANT;
&#125;

inline int square_to_file&#40;int square&#41; &#123;
  return square & 7;
&#125;

inline int square_to_rank&#40;int square&#41; &#123;
  return square >> 3;
&#125;

inline int file_and_rank_to_square&#40;int file, int rank&#41; &#123;
  return file + &#40;rank << 3&#41;;
&#125;

inline int piece_value_midgame&#40;int piece&#41; &#123;
  int piece_value_mg&#91;7&#93; = &#123;100, 300, 400, 500, 900, 1000, 0&#125;;
  return piece_value_mg&#91;piece&#93;;
&#125;

inline bitboard_t file_and_rank_to_bitboard&#40;int file, int rank&#41; &#123;
  return static_cast<bitboard_t>&#40;1&#41; << static_cast<bitboard_t>&#40;file_and_rank_to_square&#40;file, rank&#41;);
&#125;

inline bitboard_t pawn_attack_bitboard&#40;int color, int square&#41; &#123;
  return pawn_attack_bb&#91;color&#93;&#91;square&#93;;
&#125;

inline int color_on_square&#40;int square&#41; &#123;
  return B.color_on&#91;square&#93;;
&#125;

inline int piece_on_square&#40;int square&#41; &#123;
  return B.piece_on&#91;square&#93;;
&#125;

inline bitboard_t pawn_bitboard&#40;int color&#41; &#123;
  return B.piece_bb&#91;0&#93; & B.color_bb&#91;color&#93;;
&#125;

inline int move_is_capture&#40;int move&#41; &#123;
  return &#40;piece_on_square&#40;move_to&#40;move&#41;) != 6&#41; || move_is_enpassant&#40;move&#41;;
&#125;

inline int move_piece&#40;int move&#41; &#123;
  return piece_on_square&#40;move_from&#40;move&#41;);
&#125;

inline int move_piece_captured&#40;int move&#41; &#123;
  return move_is_enpassant&#40;move&#41; ? 0 &#58; piece_on_square&#40;move_to&#40;move&#41;);
&#125;

void initialize_bitboards&#40;) &#123;
  for &#40;int square = 0; square < 64; square++) &#123;
    int file = square_to_file&#40;square&#41;;
    int rank = square_to_rank&#40;square&#41;;

    pawn_attack_bb&#91;0&#93;&#91;square&#93; = 0;
    pawn_attack_bb&#91;1&#93;&#91;square&#93; = 0;

    if &#40;file - 1 >= 0 && rank + 1 <= 7&#41; pawn_attack_bb&#91;0&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file - 1, rank + 1&#41;;
    if &#40;file + 1 <= 7 && rank + 1 <= 7&#41; pawn_attack_bb&#91;0&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file + 1, rank + 1&#41;;
    if &#40;file - 1 >= 0 && rank - 1 >= 0&#41; pawn_attack_bb&#91;1&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file - 1, rank - 1&#41;;
    if &#40;file + 1 <= 7 && rank - 1 >= 0&#41; pawn_attack_bb&#91;1&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file + 1, rank - 1&#41;;
  &#125;
&#125;

void initialize_fen_position&#40;) &#123;
  for &#40;int square = 0; square < 64; square++) &#123;
    B.color_on&#91;square&#93; = 2;
    B.piece_on&#91;square&#93; = 6;
  &#125;
  B.color_on&#91;28&#93; = 1;
  B.piece_on&#91;28&#93; = 0;
  B.color_on&#91;29&#93; = 0;
  B.piece_on&#91;29&#93; = 0;
&#125;

move_info_c * generate_evasions&#40;move_info_c * move_list&#41; &#123;
  &#40;move_list++)->move = make_enpassant&#40;28, 21&#41;;
  return move_list;
&#125;

int static_exchange_evaluation&#40;int move&#41; &#123;
  int from = move_from&#40;move&#41;;
  int to = move_to&#40;move&#41;;
  int me = color_on_square&#40;from&#41;;
  int you = me ^ 1;
  int piece = piece_on_square&#40;from&#41;;
  int piece_captured = move_piece_captured&#40;move&#41;;

  if (   &#40;pawn_attack_bitboard&#40;me, to&#41; & pawn_bitboard&#40;you&#41;)
      && &#40;piece_value_midgame&#40;piece&#41; > piece_value_midgame&#40;piece_captured&#41;)
      && &#40;piece_value_midgame&#40;piece&#41; > 100&#41;) &#123;
    return piece_value_midgame&#40;piece_captured&#41; - piece_value_midgame&#40;piece&#41;;
  &#125;
  return 0;
&#125;

sort_c&#58;&#58;sort_c&#40;) &#123;
  int temp = 0;

  last_move = generate_evasions&#40;move_list&#41;;

  move_info_c * current = move_list;

  int move = current->move;
  int value = static_exchange_evaluation&#40;move&#41;;

  if &#40;value < 0&#41; &#123;
    current->value = value;
  &#125;
  else if &#40;move_is_capture&#40;move&#41;) &#123;
    temp = 50000 + piece_value_midgame&#40;move_piece_captured&#40;move&#41;) - move_piece&#40;move&#41;;
    current->value = temp;
  &#125;
  else &#123;
    current->value = 0;
  &#125;

  printf_s&#40;"value...........%d\n", current->value&#41;;
  printf_s&#40;"temp............%d\n", temp&#41;;
&#125;

int main&#40;) &#123;
  initialize_bitboards&#40;);
  initialize_fen_position&#40;);
  sort_c s;
  return 0;
&#125;
Dann Corbit
Posts: 12540
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: Compiler Problem

Post by Dann Corbit »

LoopList wrote:Hi

The following code seems to work correct. The code does not make sense, but has an interesting behaviour in Microsoft Visual Studio 2010 as x64 Release with the flag Ob1 or Ob2 (perhaps full optimization).

The variables current->value and temp should have the value 50100, but with x64 Release Ob2 in VC2010 the output is 50000.

I don't know the reason?

Perhaps you can copy the code and see yourself!

Console output:

Code: Select all

value...........50000
temp............50000
Regards
Fritz

Code: Select all

#include <stdio.h>

typedef unsigned __int64 bitboard_t;  // long long

const int IS_ENPASSANT = 1 << 15;

struct move_info_c &#123;
  int move;
  int value;
&#125;;

struct board_c &#123;
  int color_on&#91;64&#93;;
  int piece_on&#91;64&#93;;
  bitboard_t piece_bb&#91;6&#93;;
  bitboard_t color_bb&#91;2&#93;;
&#125;;

class sort_c &#123;
public&#58;
  sort_c&#40;);
  move_info_c * last_move;
  move_info_c move_list&#91;256&#93;;
&#125;;

static bitboard_t pawn_attack_bb&#91;2&#93;&#91;64&#93;;
static board_c B;

static void initialize_fen_position&#40;const char * position, const char * to_move, const char * castle, const char * enpassant&#41;;
static move_info_c * generate_evasions&#40;move_info_c * move_list&#41;;
static int static_exchange_evaluation&#40;int move&#41;;
static void initialize_bitboards&#40;);

inline int move_to&#40;int move&#41; &#123;
  return move & 63;
&#125;

inline int move_from&#40;int move&#41; &#123;
  return &#40;move >> 6&#41; & 63;
&#125;

inline int move_is_enpassant&#40;int move&#41; &#123;
  return &#40;move & IS_ENPASSANT&#41; != 0;
&#125;

inline int make_enpassant&#40;int from, int to&#41; &#123;
  return to | &#40;from << 6&#41; | IS_ENPASSANT;
&#125;

inline int square_to_file&#40;int square&#41; &#123;
  return square & 7;
&#125;

inline int square_to_rank&#40;int square&#41; &#123;
  return square >> 3;
&#125;

inline int file_and_rank_to_square&#40;int file, int rank&#41; &#123;
  return file + &#40;rank << 3&#41;;
&#125;

inline int piece_value_midgame&#40;int piece&#41; &#123;
  int piece_value_mg&#91;7&#93; = &#123;100, 300, 400, 500, 900, 1000, 0&#125;;
  return piece_value_mg&#91;piece&#93;;
&#125;

inline bitboard_t file_and_rank_to_bitboard&#40;int file, int rank&#41; &#123;
  return static_cast<bitboard_t>&#40;1&#41; << static_cast<bitboard_t>&#40;file_and_rank_to_square&#40;file, rank&#41;);
&#125;

inline bitboard_t pawn_attack_bitboard&#40;int color, int square&#41; &#123;
  return pawn_attack_bb&#91;color&#93;&#91;square&#93;;
&#125;

inline int color_on_square&#40;int square&#41; &#123;
  return B.color_on&#91;square&#93;;
&#125;

inline int piece_on_square&#40;int square&#41; &#123;
  return B.piece_on&#91;square&#93;;
&#125;

inline bitboard_t pawn_bitboard&#40;int color&#41; &#123;
  return B.piece_bb&#91;0&#93; & B.color_bb&#91;color&#93;;
&#125;

inline int move_is_capture&#40;int move&#41; &#123;
  return &#40;piece_on_square&#40;move_to&#40;move&#41;) != 6&#41; || move_is_enpassant&#40;move&#41;;
&#125;

inline int move_piece&#40;int move&#41; &#123;
  return piece_on_square&#40;move_from&#40;move&#41;);
&#125;

inline int move_piece_captured&#40;int move&#41; &#123;
  return move_is_enpassant&#40;move&#41; ? 0 &#58; piece_on_square&#40;move_to&#40;move&#41;);
&#125;

void initialize_bitboards&#40;) &#123;
  for &#40;int square = 0; square < 64; square++) &#123;
    int file = square_to_file&#40;square&#41;;
    int rank = square_to_rank&#40;square&#41;;

    pawn_attack_bb&#91;0&#93;&#91;square&#93; = 0;
    pawn_attack_bb&#91;1&#93;&#91;square&#93; = 0;

    if &#40;file - 1 >= 0 && rank + 1 <= 7&#41; pawn_attack_bb&#91;0&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file - 1, rank + 1&#41;;
    if &#40;file + 1 <= 7 && rank + 1 <= 7&#41; pawn_attack_bb&#91;0&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file + 1, rank + 1&#41;;
    if &#40;file - 1 >= 0 && rank - 1 >= 0&#41; pawn_attack_bb&#91;1&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file - 1, rank - 1&#41;;
    if &#40;file + 1 <= 7 && rank - 1 >= 0&#41; pawn_attack_bb&#91;1&#93;&#91;square&#93; |= file_and_rank_to_bitboard&#40;file + 1, rank - 1&#41;;
  &#125;
&#125;

void initialize_fen_position&#40;) &#123;
  for &#40;int square = 0; square < 64; square++) &#123;
    B.color_on&#91;square&#93; = 2;
    B.piece_on&#91;square&#93; = 6;
  &#125;
  B.color_on&#91;28&#93; = 1;
  B.piece_on&#91;28&#93; = 0;
  B.color_on&#91;29&#93; = 0;
  B.piece_on&#91;29&#93; = 0;
&#125;

move_info_c * generate_evasions&#40;move_info_c * move_list&#41; &#123;
  &#40;move_list++)->move = make_enpassant&#40;28, 21&#41;;
  return move_list;
&#125;

int static_exchange_evaluation&#40;int move&#41; &#123;
  int from = move_from&#40;move&#41;;
  int to = move_to&#40;move&#41;;
  int me = color_on_square&#40;from&#41;;
  int you = me ^ 1;
  int piece = piece_on_square&#40;from&#41;;
  int piece_captured = move_piece_captured&#40;move&#41;;

  if (   &#40;pawn_attack_bitboard&#40;me, to&#41; & pawn_bitboard&#40;you&#41;)
      && &#40;piece_value_midgame&#40;piece&#41; > piece_value_midgame&#40;piece_captured&#41;)
      && &#40;piece_value_midgame&#40;piece&#41; > 100&#41;) &#123;
    return piece_value_midgame&#40;piece_captured&#41; - piece_value_midgame&#40;piece&#41;;
  &#125;
  return 0;
&#125;

sort_c&#58;&#58;sort_c&#40;) &#123;
  int temp = 0;

  last_move = generate_evasions&#40;move_list&#41;;

  move_info_c * current = move_list;

  int move = current->move;
  int value = static_exchange_evaluation&#40;move&#41;;

  if &#40;value < 0&#41; &#123;
    current->value = value;
  &#125;
  else if &#40;move_is_capture&#40;move&#41;) &#123;
    temp = 50000 + piece_value_midgame&#40;move_piece_captured&#40;move&#41;) - move_piece&#40;move&#41;;
    current->value = temp;
  &#125;
  else &#123;
    current->value = 0;
  &#125;

  printf_s&#40;"value...........%d\n", current->value&#41;;
  printf_s&#40;"temp............%d\n", temp&#41;;
&#125;

int main&#40;) &#123;
  initialize_bitboards&#40;);
  initialize_fen_position&#40;);
  sort_c s;
  return 0;
&#125;
VC 2008 does not seem to have the problem:

c:\tmp>cl /W4 /Ox /Ob2 t.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

t.cpp
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.

/out:t.exe
t.obj

c:\tmp>t
value...........50100
temp............50100

c:\tmp>

Can't test it with g++:

dcorbit@DCORBIT2008 /c/tmp
$ g++ -Wall -ansi -pedantic -O3 t.cpp
t.cpp:3:1: warning: ISO C++ 1998 does not support 'long long'
t.cpp: In constructor 'sort_c::sort_c()':
t.cpp:167:50: error: 'printf_s' was not declared in this scope
t.cpp: At global scope:
t.cpp:29:13: warning: 'void initialize_fen_position(const char*, const char*, const char*, const char*)' declared 'static' but never defined
Mincho Georgiev
Posts: 454
Joined: Sat Apr 04, 2009 6:44 pm
Location: Bulgaria

Re: Compiler Problem

Post by Mincho Georgiev »

Neither do Intel with /O2, /O3:

Code: Select all

value...........50100
temp............50100
Could it be an aliasing problem ?
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Compiler Problem

Post by bob »

Don't understand the long long problem, g++ compiles it fine on my linux box and gets the right answer. "printf_s" needs to be changed to "printf" and it should run fine. And I get 50100 with any kind of optimization...
LoopList

Re: Compiler Problem

Post by LoopList »

Ok, the mentioned problem only occurs with

Microsoft Visual C++ 2010 Professional 64-Bit Release with inlining (flag Ob1 or Ob2).

On every different setup (64-bit debug, 32-bit release, or VC2008, GCC, Intel, ...) we cannot see this problem.

And for the moment I am sure I have no problem in my code, but I don't understand this phenomenon :)

Regards
Dann Corbit
Posts: 12540
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: Compiler Problem

Post by Dann Corbit »

LoopList wrote:Ok, the mentioned problem only occurs with

Microsoft Visual C++ 2010 Professional 64-Bit Release with inlining (flag Ob1 or Ob2).

On every different setup (64-bit debug, 32-bit release, or VC2008, GCC, Intel, ...) we cannot see this problem.

And for the moment I am sure I have no problem in my code, but I don't understand this phenomenon :)

Regards
It's called a compiler bug.
Send a defect report to Microsoft.

I have found compiler bugs in both the Microsoft and Intel compilers, so they are not all that unusual.
I issued defect reports to them in both cases.
LoopList

Re: Compiler Problem

Post by LoopList »

Dann Corbit wrote:
LoopList wrote:Ok, the mentioned problem only occurs with

Microsoft Visual C++ 2010 Professional 64-Bit Release with inlining (flag Ob1 or Ob2).

On every different setup (64-bit debug, 32-bit release, or VC2008, GCC, Intel, ...) we cannot see this problem.

And for the moment I am sure I have no problem in my code, but I don't understand this phenomenon :)

Regards
It's called a compiler bug.
Send a defect report to Microsoft.

I have found compiler bugs in both the Microsoft and Intel compilers, so they are not all that unusual.
I issued defect reports to them in both cases.
Thank you for your response. Are you shure a compiler bug could be the reason for the wrong console output? Perhaps there are different compiler flags that are mutually incompatible?

When you are right, I would be happy and I can continue to develop Loop's new move ordering algorithm. At the moment I am looking for a proof of our assumption.

Fritz
Gian-Carlo Pascutto
Posts: 1243
Joined: Sat Dec 13, 2008 7:00 pm

Re: Compiler Problem

Post by Gian-Carlo Pascutto »

While a compiler bug is always possible, it's also quite unlikely, less likely than a subtle bug.

Given that you probably cannot step through the code to see where exactly you get a different result, I'd recommend plastering printfs throughout the code and narrowing down to where it diverges.
LoopList

Re: Compiler Problem

Post by LoopList »

Gian-Carlo Pascutto wrote:While a compiler bug is always possible, it's also quite unlikely, less likely than a subtle bug.

Given that you probably cannot step through the code to see where exactly you get a different result, I'd recommend plastering printfs throughout the code and narrowing down to where it diverges.
Did you really have a close look at the above code?

I used printf() and the code is quite simple, as you can see. We tested everything exactly but could'nt find anything. The code above is focussed on the phenomena (which occurred in Loop 2010). There is nothing more to narrow, I suppose.

Fritz
Gian-Carlo Pascutto
Posts: 1243
Joined: Sat Dec 13, 2008 7:00 pm

Re: Compiler Problem

Post by Gian-Carlo Pascutto »

LoopList wrote: Did you really have a close look at the above code?
Yes, I don't see anything wrong, except for the prototype that is incorrect, but that can't cause the problem because this is C++ code and the function will simply get overloaded.
There is nothing more to narrow, I suppose.
You don't seem to have understood what I said. You have 2 compiles of the code, one giving a correct result, one giving a wrong one. The wrong result doesn't pop out magically, it exists because one or multiple statement are giving a wrong result. You want to find this single statement, which makes it obvious what the bug is (code or compiler). If you want to, you can even look at the generated assembler at that point and identify what's wrong.

To find the place where the right and wrong version diverge, you want to look at the intermediate results in the computation. Normally you'd do this with a debugger, but given that the problem only occurs in release mode with heavy optimizations, the alternative is to plaster printfs over all the intermediate computations and then look where they give a different result.

If you're unlucky, the printfs make the problem go away :-P

Without doing this exercise, it's going to be hard to be absolutely sure whether the problem is compiler or code. Given that the code is simple here, I'd say compiler. But problems like this don't always show up in simple code, and by the fact that you posted it here, I'd guess you weren't 100% sure it's a compiler issue either.

Even if it's a compiler issue, having this information makes it much more likely that you can get a fix out of the vendor.