views:

146

answers:

3

I've a peculiar issue here, which is happening both with VS2005 and 2010. I have a for loop in which an inline function is called, in essence something like this (C++, for illustrative purposes only):

inline double f(int a)
{
  if (a > 100)
  {
    // This is an error condition that shouldn't happen..
  }

  // Do something with a and return a double
}

And then the loop in another function:

for (int i = 0; i < 11; ++i)
{
  double b = f(i * 10);
}

Now what happens is that in debug build everything works fine. In release build with all the optimizations turned on this is, according to disassembly, compiled so that i is used directly without the * 10 and the comparison a > 100 turns into a > 9, while I guess it should be a > 10. Do you have any leads as to what might make the compiler think that a > 9 is the correct way? Interestingly, even a minor change (a debug printout for example) in the surrounding code makes the compiler use i * 10 and compare that with the literal value of 100.

I know this is somewhat vague, but I'd be grateful for any old idea.

EDIT:

Here's a hopefully reproducable case. I don't consider it too big to be pasted here, so here goes:

__forceinline int get(int i)
{
  if (i > 600)
    __asm int 3;

  return i * 2;
}

int main()
{
  for (int i = 0; i < 38; ++i)
  {
    int j = (i < 4) ? 0 : get(i * 16);
  }

  return 0;
}

I tested this with VS2010 on my machine, and it seems to behave as badly as the original code I'm having problems with. I compiled and ran this with the IDE's default empty C++ project template, in release configuration. As you see, the break should never be hit (37 * 16 = 592). Note that removing the i < 4 makes this work, just like in the original code.

+2  A: 

First, it'd help if you could post enough code to allow us to reproduce the issue. Otherwise you're just asking for psychic debugging.

Second, it does occasionally happen that a compiler fails to generate valid code at the highest optimization levels, but more likely, you just have a bug somewhere in your code. If there is undefined behavior somewhere in your code, that means the assumptions made by the optimizer may not hold, and then the compiler can end up generating bad code.

But without seeing your actual code, I can't really get any more specific.

jalf
A: 

The only famous bug I know with optimization (and only with highest optimization level) are occasional modifications of the orders of priority of the operations (due to change of operations performed by the optimizer, looking for the fastest way to compute). You could look in this direction (and put some parenthesis even though they are not strictly speaking necessary, which is why more parenthesis is never bad), but frankly, those kind of bugs are quite rare.

As stated, it difficult to have any precise idea without more code.

Raveline
A: 

Firstly, inline assembly prevents certain optimizations, you should use the __debugbreak() intrinsic for int3 breakpointing. The compiler sees the inline function having no effect other than a breakpoint, so it divides the 600 by 16(note: this is affected by integer truncation), thus it optimizes to debugbreak to trigger with 38 > i >= 37. So it seems to work on this end

Necrolis
This was stripped down from a version which actually did something with the return result. This reproduced even with that and using something else than inline assembly to signal that the value of i was unexpected. I might've done something else wrong though.
Stockhausen
The problem here is the integer truncation of 600 / 16, which makes it trigger at 37 instead of 37.75. If the function was not inlined, this wouldn't happen, as the compiler wouldn't predivide. It might work correctly if floats are used though(need to check this)
Necrolis