операнды операций x86 (x87) с плавающей запятой (теория)

#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)