4

Could this be a compiler error? My environment is:

  • Win7 pro (64-bit)
  • VS2012 (update 3)

I compile the tiny console program below. Things work fine for x64 bit release/debug builds. The x32 debug build also works just fine. The x32 release build, however displays 'BUG!'.

If i disable 'Whole Program Optimization' that will fix the issue.

Any ideas?

-

#include <string>
#include <iostream>


int main()
{
    std::string const buffer = "hello, world";
    std::string::size_type pos = 0;
    std::string::size_type previous_pos;


    while (pos != std::string::npos)
    {
        previous_pos = ++pos;
        pos = buffer.find('w', pos);
    } 


    if (previous_pos == std::string::npos)
    {
        std::cout << "BUG!!"<< std::endl;
    }

    return 0;
}
10
  • 1
    If the loop goes around zero times, then previous_pos is uninitialized. Commented Aug 4, 2013 at 23:40
  • 1
    What happens when you initialize previous_pos to zero? Perhaps the compiler is making some assumption based on the fact that it's uninitialized? Commented Aug 5, 2013 at 0:15
  • 2
    also, changing the pre-increment of pos to post-increment seems to make the bug go away. Commented Aug 5, 2013 at 0:31
  • 1
    while (pos != std::string::npos) { previous_pos = ++pos; pos = buffer.find('w', pos); std::cout << "Prev Pos: " << previous_pos << std::endl; } This is interesting. Adding a cout statement using previous_pos before the end of the loop also causes the bug to dissapear... Interesting question! I'm guessing this is something to do with compiler optimization? Commented Aug 5, 2013 at 3:23
  • 1
    Also, changing previous_post to = pos and pos = buffer.find('w', pos++) has some interesting results. In debug previous_pos will alternate between 0 and 7. In release 0 and 8.... Again, I'm using a cout in the loop here. Commented Aug 5, 2013 at 3:37

1 Answer 1

4

I can reproduce this too. When the bug manifests, the code is testing eax to deterimine whether to output "BUG", which is the same register it's using for 'pos'.

17:         previous_pos = ++pos;

013C12E5 inc eax
...

21:     if (previous_pos == std::string::npos)

00301345 cmp eax,eax
00301347 jne main+0F6h (0301366h)

However if you make a change to try to get the optimzer to realise they are distinct then the test is different. If I add ++previous_pos at the end of the loop body then it uses ecx for previous_pos and the bug goes away:

22:     if (previous_pos == std::string::npos)

00361349 cmp ecx,eax
0036134B jne main+0FAh (036136Ah)

If I change the find to 'pos = buffer.find('w', previous_pos);' (searching from previous_pos instead of pos, which has the same value) then it uses ebx, and again the bug goes away:

21:     if (previous_pos == std::string::npos)

00191345 cmp ebx,eax
00191347 jne main+0F6h (0191366h)

So it seems in the original that the optimiser is erroneously deciding that it can use eax for both of those variables, despite the line 'pos = buffer.find('w', pos);' that can set pos to a different value than previous_pos.

Sign up to request clarification or add additional context in comments.

2 Comments

So adding the cout statement causes the programme to realize it cannot use the same register? What about my last comment above about the register alternating between 0 and 7 in debug and 0 and 8 in release?
Thanks to all you guys for having a look at this issue. Seems like everyone is in agreement that this is indeed a compiler bug, so I think i'll mark this as 'answered' now to close it off.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.