Освобождение стека x87 FPU (ia32)

В моем университете мы только что познакомились с IA32 x87 FPU. Но нам не сообщили, как очистить стек FPU от более невостребованных элементов.

Представьте, что мы выполняем простое вычисление, например (5,6 * 2,4) + (3,9 * 10,3).

.data
        value1: .float 5.6
        value2: .float 2.4
        value3: .float 3.8
        value4: .float 10.3

        output: .string "The result is: %f\n"

.text
.global main

main:
        fld     value1          # Load / Push 5.6 into FPU
        fmul    value2          # Multiply FPU's top (5.6) with 2.4
        fld     value3          # Load / Push 3.8 into FPU
        fmul    value4          # Multiply the top element of the FPU's Stacks with 10.3
        fadd    %st(1)          # Add the value under the top element to the top elements value

.output:
        # Reserve memory for a float (64 Bit)
        subl $8, %esp
        # Pop the FPU's top element to the program's Stack
        fstpl (%esp)
        # Push the string to the stack
        pushl $output
        # Call printf function with the both parameters above
        call printf
        # Free the programs stack from the parameters for printf
        addl $12, %esp

.exit:
        movl $1, %eax
        int $0x80

Проблема в следующем: после извлечения верхнего элемента FPU, который содержит результат вычисления. Как освободить стек FPU от оставшегося нового верхнего элемента, который содержит результат (5,6 * 2,4).

Единственный способ, который я могу себе представить, - это освободить еще немного стека программ и выталкивать элементы из стека FPU до тех пор, пока все ненужные элементы не будут удалены.

Есть ли способ напрямую управлять верхним указателем?


person tmuecksch    schedule 10.11.2013    source источник
comment
Компилятор C обычно хорошо генерирует такой код. Мой использует FMULP вместо FMUL, проблема решена.   -  person Hans Passant    schedule 10.11.2013
comment
Обратите внимание, что вы можете вручную перемещать верхнюю часть с помощью fincstp и fdecstp и помечать regs free с помощью ffree, но лучше этого избегать.   -  person gsg    schedule 10.11.2013
comment
Если вы знаете, сколько элементов у вас есть в стеке FPU, вы можете выполнить ffree st(0) и fincstp в цикле.   -  person Michael    schedule 10.11.2013
comment
@gsg, почему этого лучше избегать, ведь я знаю, сколько элементов я поместил в стек FPU.   -  person tmuecksch    schedule 10.11.2013
comment
@tmuecksch Разумное использование инструкций f<op>p приводит к меньшему количеству инструкций (и, возможно, к более читаемому коду).   -  person gsg    schedule 10.11.2013
comment
(отредактировать, не заметил, что это старый вопрос). Я предполагаю, что x87 является практическим примером для обучения набору регистров на основе стека. Если вы на самом деле пишете новый код FP, используйте SSE2, если не важна обратная совместимость с процессорами AthlonXP 10-летней давности. stackoverflow.com/tags/x86/info содержит ссылки на множество полезных материалов, включая очень хороший учебник/руководство по x87: ray.masmcode.com/tutorial/index.html. Это касается не только отдельных инструкций, но и того, как использовать их вместе. Он также тратит время на объяснение того, как работают регистры стека FP.   -  person Peter Cordes    schedule 07.11.2015


Ответы (4)


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

person Quonux    schedule 10.11.2013
comment
Итак, вы предлагаете хранить предварительный результат в стеке программы? (Поскольку упомянутые операции выдают результат сразу после оценки). - person tmuecksch; 10.11.2013
comment
действительно, используйте стек для своего преимущества, например. чтобы вычислить A * B + C * D, вы нажимаете A; мука Б; нажать С; мульпа Д; доп - person Quonux; 10.11.2013

На случай, если кто-то вроде меня придет сюда в поисках лучшего способа очистки стека, я нашел это простое решение лучшим:

fstp ST(0) ; just pops top of the stack
person Dan M.    schedule 06.11.2015
comment
Или FNINIT для очистки всех регистров FP, независимо от того, сколько из них использовалось ранее. Но да, fstp st(0) — это самый эффективный способ просто поднять вершину стека, отбрасывая результат. - person Peter Cordes; 07.11.2015
comment
@PeterCordes Я считаю, что даже восемь инструкций FSTP будут быстрее, чем инструкция FNINIT, учитывая, что последняя микрокодирована. Кроме того, с FSTP вам не нужно возвращать контрольное слово FP в то состояние, в котором оно должно быть. - person Ross Ridge; 24.06.2017
comment
@RossRidge: хорошая мысль. На Skylake FNINIT составляет 18 мопов с пропускной способностью один на 78 циклов. FFREE st(0) имеет пропускную способность один на 0,5 цикла. Микрокодирование не означает автоматически непригодную для использования скорость (например, VGATHERQPD — это 5 мопов с пропускной способностью один на 2 цикла в Skylake, а все, что превышает 4 мооп, означает, что оно должно поступать из ПЗУ микрокода и храниться в кэше микроопераций в виде Указатель MS-ROM вместо непосредственно uops), но оказывается, что FNINIT конкретно является плохим предложением для Intel и AMD, за исключением размера кода. - person Peter Cordes; 26.06.2017
comment
На AMD Bulldozer и Ryzen FFREE имеет пропускную способность 0,25c (и такая же производительность, как FSTP st(0) на Intel), поэтому, если вам нужно очистить все 8 регистров из неизвестного состояния, это, вероятно, ваш лучший выбор. Или, как указывает ответ CP Taylor, EMMS устанавливает теги для всех регистров x87 в неиспользуемые. На AMD Bulldozer и Ryzen это всего 1 моп, а на Intel — 10. Таким образом, 8x FFREE или FSTP быстрее на Intel, но EMMS неплох (и достойный компромисс между размером кода и производительностью). - person Peter Cordes; 26.06.2017
comment
Я сейчас работаю над интро 256B, поэтому кодирование для размера я пришел к следующим выводам: я использую либо ffreep, чтобы вытолкнуть st0, либо fcompp, чтобы освободить два слота одной инструкцией (fcom повлияет флаги FP, которые я просто игнорирую и не возражаю). fninit стоит, когда нужно выпустить полный стек, даже если потом нужно настроить CW. Конечно, этот совет совершенно неверен при кодировании для повышения производительности, в таком случае не вижу ничего плохого в ffreep (за исключением случаев, когда вы можете настроить алгоритм, чтобы полностью избежать нежелательной поп-музыки, выталкивая промежуточные значения во время расчета). - person Ped7g; 17.01.2018
comment
Размышляя об этом и о том, что ffreep является своего рода недокументированным, позже принятым, разве fstp st0 на самом деле не тот же код операции, что и ffreep? Я могу ответить, что через 20... Нет, это не так, это DDD8 против DFC1... @PeterCordes какие-нибудь комментарии по поводу производительности этих двух? Просто по названию инструкции ffreep звучит более точно для первоначального замысла программиста, т.е. я бы предпочел его загадочному fstp st0. - person Ped7g; 17.01.2018
comment
@Ped7g: Да, если бы Intel задокументировала ffreep, это был бы канонический способ извлечь стек x87, не делая ничего другого. Можно не использовать исполнительный модуль для копии регистра и просто обновить состояние переименования регистра. Но оказывается, что его внутренняя реализация ffreep не оптимизирована, я думаю, потому что это немного редко. Даже на Pentium III ffreep равнялось 2 моопам, а fstp и ffree — только 1 каждый. В семействе Sandybridge и fstp, и ffreep являются однопроцессорными и имеют одинаковую производительность. Агнер не указывает время для ffreep, только ffree быстрее, чем fstp. - person Peter Cordes; 17.01.2018

emms также можно использовать для обозначения каждого члена f.p. стек как свободный. Это имеет то преимущество перед finit, что не меняет никаких флагов в f.p. слова управления или состояния (маски исключений и т. д.)

person CP Taylor    schedule 24.06.2017
comment
Согласно таблицам Агнера Фога, EMMS на AMD Bulldozer/Ryzen работает так же быстро, как 1 FFREE. Но на Intel он может быть довольно медленным: 31 мооп, один на 18 циклов на Sandybridge. (Лучше на Skylake: 10 мопов, по одному на пропускную способность 6c, так что производительность лишь несколько хуже, чем 8x FFREE st(i) инструкций.) - person Peter Cordes; 26.06.2017

Есть несколько инструкций, которые могут выполнять операции, подобные той, которую вы ищете. FDECSTP уменьшает указатель стека (без каких-либо других действий), FFREE помечает слот как пустой (но не касаясь указателя стека). Однако упомянутое выше решение с FADDP или FMULP часто лучше.

Вам следует рассмотреть возможность загрузки Руководства по архитектуре Intel< /а>. Они содержат полный набор инструкций семейства процессоров Intel.

person PMF    schedule 10.11.2013