#assembly #x86 #cpu-architecture #fpu #x87
# #сборка #x86 #архитектура процессора #fpu #x87
Вопрос:
Я следую https://en.wikibooks.org/wiki/X86_Assembly/Floating_Point чтобы изучить основные операции FPU.
Для умножения (в данном случае вычисления квадрата) он использует:
fmul st0, st0; //
Но я читал, что FPU реализован как стек и работает на вершине или (top — 1) стека.
Поэтому я предположил, что это должно быть что-то вроде
fld qword [c]
fmul st(0)
fmulp st(1)
Почему FPU разрешает косвенный доступ к регистру через индексы стека?
Такое ощущение, что это противоречит аргументам stack vs register.
Комментарии:
1. Один из аргументов каждой инструкции x87 является неявным
st0
, другим может быть любой регистр стека или операнд памяти.
Ответ №1:
Это не «косвенный», он по-прежнему прямой: с использованием номера регистра, встроенного в машинный код. Использование машинного кода с 1 явным операндом позволяет сделать код более компактным, что было преимуществом для дизайна еще в древние времена, когда был разработан 8087. Исходный или конечный операнд должен быть st0
, при этом код операции подразумевает, является ли он src или dst.
Один номер регистра по-прежнему жестко запрограммирован в большинстве инструкций, и вы можете это сделать fmul st, st6
, но нет fmul st4, st6
. Взгляните на доступные кодировки fmul
:
D8 /1 FMUL m32fp
источник памяти, настройка стека отсутствуетDC /1 FMUL m64fp
то же самое.D8 C8 i FMUL ST(0), ST(i)
— reg, reg с st0 в качестве назначенияDC C8 i FMUL ST(i), ST(0)
— reg, reg с st0 в качестве источникаDE C8 i FMULP ST(i), ST(0)
— reg, reg, а затем извлеките st0 из стекаDE C9 FMULP
— просто частный случай предыдущего,fmulp st1, st0
Таким образом, 3 байта кода операции (и множество кодировок ModRM) позволяют использовать источник памяти или регистра, или reg, reg с более высоким назначением.
«Штабелирование» предназначено для экономии размера машинного кода, а не для превращения его в «чистую» стековую машину. Это отстойно для производительности: потребовалось бы потратить много инструкций, чтобы получить нужные данные в st0, st1. И сделало бы большую часть стека регистров непригодной для использования; почти весь код должен был бы просто сохранять / перезагружать большинство результатов в память вместо того, чтобы держать их выше в стеке регистров.
Кроме того, ваш fmul
пример без операндов нарушен. Если вы соберете это с помощью NASM, fmul
DE C9 fmulp st(1),st
по какой-то странной причине оно будет собираться в, чтобы считывать два верхних регистра из стека x87, но один из них пуст, поэтому вы получите NaN. Вам понадобился бы fld st0
первый, если бы вы хотели использовать fmulp
В руководстве Intel не указана форма fmul
только без явных операндов fmulp
. Чтобы возвести st0 в квадрат дважды, вы бы fmul st0
/ fmul st0
. Это дает вам d8 c8 fmul st,st(0)