Имеет ли значение, какие регистры вы используете при написании ассемблера? - PullRequest
4 голосов
/ 23 января 2020

Если вы пишете сборку, имеет ли значение, каким регистрам вы назначаете значения? Скажем, вы храните накопленное / промежуточное значение в% ebx вместо% eax, которое традиционно использовалось для этой цели. Это плохая практика? Повлияет ли это на производительность?

Другими словами, можете ли вы одинаково относиться к ним как к месту хранения, или вам следует придерживаться их для определенных c целей?

Ответы [ 4 ]

5 голосов
/ 23 января 2020

Везде, где работает только ваш код, вы можете использовать любые регистры, которые вы хотите, для любых целей, которые вы хотите. Однако есть два основных случая, когда предпосылка ложна:

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

    Ваше соглашение о вызовах также позволит функциям уничтожать некоторые регистры без сохранения: регистры volatile и non volatile. Например, обычно FLAGS, EAX, ECX и EDX являются нестабильными в 32-битных соглашениях о вызовах x86, в то время как остальные целочисленные регистры сохраняются через call в ABI-совместимой функции. См. Каковы соглашения о вызовах для системных вызовов UNIX & Linux на i386 и x86-64 для соглашений о системных вызовах и вызовах функций в пространстве пользователя.

4 голосов
/ 23 января 2020

Если вы пишете сборку, имеет ли значение, каким регистрам вы назначаете значения?

Для 80x86; случаи, когда может иметь значение, какой регистр / ы вы используете, включают:

  • соблюдение чужих соглашений о вызовах (передача значений в правильные регистры, избегая использования стека для «сохраненного вызываемого абонента», предпочитая « "сохраненные регистры" регистров)

  • с использованием инструкции, которая подразумевает регистры (MUL, DIV, MOVSQ / D / W / B, STOSQ / D / W / B, XLATB, AAA, CWD , ... - их много)

  • пытаясь избежать стоимости префикса регистра сегмента, когда сегменты не совпадают (например, mov [ds:bp], ... против mov [bx],... ).

  • избегая вычислений адресов, которые не могут быть закодированы из-за ограничений полей «MOD / RM» (например, mov [di+si], ...)

  • избегая префиксов REX в 64-битном коде (например, mov rbx,1 против mov r8,1)

Скажем, вы сохраняете накопленное / промежуточное значение в% ebx вместо% eax, который традиционно использовался для этой цели. Это плохая практика? Повлияет ли это на производительность?

В целом; это не имеет значения (не плохая практика и не повлияет на производительность); однако это может зависеть от окружающего кода (как значение используется позже) и может улучшить производительность или снизить производительность.

Более конкретно, оптимальное распределение регистров трудно достичь (проблема полного NP), даже когда все регистры одинаковые; и 80x86 (где все регистры не совпадают в некоторых случаях) значительно усложняет достижение оптимального распределения регистров.

3 голосов
/ 23 января 2020

Допустим, вы храните накопленное / промежуточное значение в% ebx вместо% eax, которое традиционно использовалось для этой цели. Это плохая практика? Повлияет ли это на производительность?

В редких случаях это может повлиять на производительность. Например, для инструкции adc eax, imm32 существует специальная кодировка, которая короче, чем кодировка для других регистров (см. https://www.felixcloutier.com/x86/adc); ассемблеры обычно используют эту более короткую кодировку.

Однако на последних процессорах Intel более короткое кодирование требует большего количества мопов и имеет большую задержку; см. Какая микроархитектура Intel представила специальный случай AD C reg, 0 single-uop?

3 голосов
/ 23 января 2020

Прежде всего, у вас есть для использования регистров, которые поддерживают инструкции, которые вы хотите использовать. Многие инструкции в x86 (и других архитектурах, хотя и в меньшей степени) имеют некоторые ограничения на то, как поддерживаются регистры.

Возьмите, например, определенные команды умножения и деления двойных регистров, которые, в частности, включают eax и edx в определенных случаях использования.

Далее вы хотите использовать эффективные регистры, т.е. регистры:

  • , для которых кодировки короче ( здесь Хорошая дискуссия на x64 по поводу длины инструкции). Короткие кодировки позволяют лучше использовать ресурсы кэша, что позволяет более эффективным программам более крупных программ работать.

  • , которые не обременены, то есть из-за соглашений о вызовах, то есть они не используют не несут дополнительных (определенных программным обеспечением / соглашением о вызовах) накладных расходов за их использование - если только эти накладные расходы уже не оплачены!

  • , которые являются конечными получателями создаваемых значений: например, если второй параметр, затем регистр, который соответствует второму значению, которое должно быть передано (снова в соответствии с соглашением о вызовах). Если мы можем поместить значение в правильный регистр (если это необходимо для передачи или возврата значений), тогда мы можем для go инструкции перемещения данных (или копирования).

...