Переполнение стека FPU?

Тема в разделе "WASM.ASSEMBLER", создана пользователем bers, 23 июн 2009.

  1. bers

    bers New Member

    Публикаций:
    0
    Регистрация:
    16 сен 2005
    Сообщения:
    139
    Адрес:
    Russia
    День добрый всем.

    Столкнулся с такой неприятной вещью:
    есть нетривиальное выражение (выражения) на С, одно из них:
    Код (Text):
    1. erfc_arg = fabs( V - 2.0*n*pl*(1 - pl)) / (2.0*pl*(1 - pl)*sqrt(2*n));
    и что имеем в результате - erfc_arg равен 1#IND - то есть, NaN.
    Происходит это вроде как вследствие переполнения стека FPU - для
    данного выражения требуются 2 регистра FPU (по крайней мере, компилятор
    выдал такой код), а их вроде бы как и нет - есть один свободный регистр.
    Разбил выражение на нескольких простых - все заработало так, как надо.

    Регистры FPU перед "сбоем" имеют значения:
    ST0 +1.9990000000000000 e+6
    ST1 +1.0000000000000000 e+0
    ...
    ST7 +1.0000000000000000 e+0

    TAGS = 0x0000 (!) - т.е. вроде бы как ни одного свободного регистра на стеке.
    статусный регистр = 0x1261 - есть переполнение.

    Вопрос в следующем - как вообще бороться с такой "дурью", т.е. есть ли методы
    кроме как разбиения выражения на более простые?
    И вообще - глюк ли это компилятора (использую компилер из VS 2003)?
     
  2. bers

    bers New Member

    Публикаций:
    0
    Регистрация:
    16 сен 2005
    Сообщения:
    139
    Адрес:
    Russia
    Сорри, приведенное выше значение статусного регистра - конечно, после сбоя.

    P.S. Да, плохо без редактирования...
     
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.787
    bers
    Не знаю, может быть команда finit перед вычислениями поможет...
     
  4. bers

    bers New Member

    Публикаций:
    0
    Регистрация:
    16 сен 2005
    Сообщения:
    139
    Адрес:
    Russia
    Нет, finit - это, конечно, хорошо, но извините - в данном случае речь идет о чистом C, без
    "засирания" его асм-вставками. Или без этого никак? - в этом-то и вопрос...
     
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.787
    bers
    Но попробовать finit то можно, кроме того в ассемблере применяются команды типа FDIVP, FMULP, FSUBP и т.д. -- выполнение деления, умножения, вычитания и т.д. с последующим выталкиванием из стека FPU -- это также бы препятствовало скорому заполнению st0-st7
     
  6. bers

    bers New Member

    Публикаций:
    0
    Регистрация:
    16 сен 2005
    Сообщения:
    139
    Адрес:
    Russia
    Ну finit, конечно, помог, но мне все же кажется, что это не решение для
    тех, кто пишет на C/C++...
     
  7. PaCHER

    PaCHER New Member

    Публикаций:
    0
    Регистрация:
    25 мар 2006
    Сообщения:
    852
    bers
    Да ну :) прям тут С супер безбажный компилятор, ну забыли они сами вставить команду finit перед начало мат выражений. Я не с одним таким багом уже сталкивался.
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    bers
    finit это не решение совсем, точнее решение через ж.... даже если пишешь на чистом асме finit без крайней нужды применять не следует, а уж тем более перед обычным вычислением очередной формулы.
    Однозначно имеет смысл заглянуть в асемблерный код который слепил С компилятор, чтобы понять чего он там намутил и откуда берётся переполнение стека. Раз finit помог, то скорее всего дело не в этой формуле, а в предшествующих вычислениях и в любом случае нужно анализировать асм код в листинге или отладчике на уровне блока формул - от момента когда он в последний раз был пуст.
    Если найдёшь настоящий источник переполнения попробуй разбить длинные формулы на несколько более коротких - почти наверняка поможет.

    ЗЫ: а асм вариант FPU вычислений это не "замусоривание", а как раз очистка полезного кода от компиляторного мусора и глюков :))
     
  9. bers

    bers New Member

    Публикаций:
    0
    Регистрация:
    16 сен 2005
    Сообщения:
    139
    Адрес:
    Russia
    Короче, после более внимательного просмотра асм-кода дело оказалось в следующем...
    Обо всем по порядку.
    В стандартной вижуаловской библиотеке не оказалось весьма нужных функций,
    именно - erf, erfc, erfcf. По стандартну C99 они все должны быть в библиотеке,
    поэтому были выдраны из mingw. Файл math.h был поправлен (прототипы), но...
    не до конца - забыл вставить прототип erf, как самой редкой из вызываемой троицы.
    Ну а дальше все как по маслу - поскольку собираю C-шным (не плюсами) компилером,
    получаю следующую картину:
    компилятор не находит прототипа для erf, поэтому решает, что она возвращает int (на
    самом деле - double erf(double)). А поскольку сама erf знает лучше, что ей возвращать,
    то оставляет на стеке по регистру FPU на каждый ее вызов - оберточный код не попит
    возвращаемое значение (ST0), а берет eax. Отсюда получаем переполнение на 9ый вызов erf.
    Сорри, ошибка моя.
    Но думаю, что многим такой печальный опыт может пригодиться...