views:

413

answers:

1

I'm running into an issue with floating point exceptions turned on in Visual Studio 2005. If I have code like this:

  double d = 0.0;
  double d2 = 3.0;
  double d3 = d2/d;

and if I register an SEH handler routine, then I can easily turn the div-by-zero into a C++ exception and catch it. So far so good.

However, when I do this, the first operand (0.0 in the example above) is left on the FPU register stack. If I do it eight times, then I'll start getting a floating point stack check exception with EVERY floating point operation from then on.

I can deal with this using an __asm block to execute a FSTP, thus popping the stray value off the stack, and everything is fine.

However, this worries me, because I haven't seen this discussed anywhere. How can I be certain about the number of values that I should pop? Is it safe to just pop everything that's on the stack at the time of the exception? Are there any recommended best practices in this area?

Thanks!

+1  A: 

While I can't find anything either, I can give some explanation as to the likely answer:

The ABI defines that upon a function call the stack should be empty, and it should be empty again on exit unless the return is a floating point value, where it would be the only item on the stack.

Since the exception handler must be able to return to any place, some criteria must hold on those locations. The question here is, does the stack unwinder have any knowledge of the FPU stack of the function having the catch()? Most likely, the answer is no because it is easier and faster to create a suitable return point with fixed properties than to include the full FPU stack in the unwinding.

Which leads to your problem - normally raising an exception has the compiler take care of the FPU being empty, but in an SEH handler, the compiler has no clue it caused an entry into another function, and thus can't take care of things just in case. (other than it being again hideously slow)

Which means that most likely, the FPU stack should be in its "consistent" state upon return, which means you probably want an equivalent of an EMMS instruction.

Why EMMS? Well, unless it is not supported it does the following:

  • clear the stack (which fixes all leftover floating point arguments)
  • clears the stack tags (which fixes an unusable stack when exiting from an MMX-enabled function)

If you want to support Pentium 1 or worse, you may of course if() around the EMMS and use something else instead.

No guarantees of course, but I hope I explained the why of the likely answer sufficiently.

Combuster
That makes sense - I had come to pretty much the same conclusion. Thanks.
Bill