Обработка стека с плавающей точкой с включенными исключениями с плавающей точкой - PullRequest
6 голосов
/ 07 октября 2009

У меня проблема с исключениями с плавающей запятой, включенными в Visual Studio 2005. Если у меня есть такой код:

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

и если я зарегистрирую подпрограмму-обработчик SEH, то я могу легко превратить деление на ноль в исключение C ++ и перехватить его. Пока все хорошо.

Однако, когда я делаю это, первый операнд (0.0 в примере выше) остается в стеке регистров FPU. Если я сделаю это восемь раз, то с этого момента я начну получать исключение проверки стека с плавающей запятой с КАЖДОЙ операцией с плавающей запятой.

Я могу справиться с этим, используя блок __asm ​​для выполнения FSTP, таким образом выталкивая случайное значение из стека, и все в порядке.

Однако меня это беспокоит, потому что я нигде не видел, чтобы это обсуждалось . Как я могу быть уверен в количестве значений, которые я должен вытолкнуть? Безопасно ли просто выталкивать все, что находится в стеке во время исключения? Есть ли рекомендуемые лучшие практики в этой области?

Спасибо!

1 Ответ

3 голосов
/ 09 октября 2009

Хотя я тоже ничего не могу найти, я могу дать некоторые объяснения относительно вероятного ответа:

ABI определяет, что при вызове функции стек должен быть пустым, и он должен быть пустым снова при выходе, если только возвращаемое значение не является значением с плавающей запятой, где он будет единственным элементом в стеке.

Поскольку обработчик исключений должен иметь возможность вернуться в любое место, в этих местах должны соблюдаться некоторые критерии. Вопрос в том, знает ли стек разматыватель о стеке FPU функции, имеющей catch ()? Скорее всего, ответ отрицательный, потому что проще и быстрее создать подходящую точку возврата с фиксированными свойствами, чем включать полный стек FPU в разматывание.

Что приводит к вашей проблеме - обычно при возбуждении исключения компилятор заботится о том, чтобы FPU был пустым, но в обработчике SEH компилятор не имеет ни малейшего представления, что он вызвал вход в другую функцию и поэтому не может позаботиться вещей на всякий случай. (кроме того, что это снова ужасно медленно)

Что означает, что, скорее всего, стек FPU должен быть в своем "непротиворечивом" состоянии по возвращении, что означает, что вам, вероятно, нужен эквивалент инструкции EMMS.

Почему EMMS? Ну, если он не поддерживается, он делает следующее:

  • очистить стек (который исправляет все оставшиеся аргументы с плавающей запятой)
  • очищает теги стека (исправляет неиспользуемый стек при выходе из функции с поддержкой MMX)

Если вы хотите поддерживать Pentium 1 или хуже, вы, конечно, можете использовать if () вокруг EMMS и использовать что-то другое.

Конечно, никаких гарантий, но я надеюсь, что достаточно объяснил причину вероятного ответа.

...