Использование и сохранение регистров во встроенном коде на языке ассемблера
Блок, относящийся только к системам Майкрософт
В общем случае в начале блока __asm
не следует предполагать, что регистр будет иметь какое-либо определенное значение. Сохранение значений регистров между разными блоками __asm
не гарантируется. Когда заканчивается один блок встроенного кода и начинается следующий блок, не следует полагать, что регистры во втором блоке сохраняют свои значения из первого блока. Блок __asm
наследует значения регистров, получающиеся в процессе обычного потока управления.
Если используется соглашение о вызовах __fastcall
, компилятор передает аргументы функций в регистрах, а не в стеке. Это может создавать проблемы в функциях с блоками __asm
, поскольку для функции не существует способа определить, как параметры распределены по регистрам. Если функция получила параметр в регистре EAX и сразу же записала в регистр EAX какое-то другое значение, исходный параметр будет потерян. Кроме того, необходимо сохранять значение регистра ECX в любой функции, объявленной с атрибутом __fastcall
.
Чтобы избежать подобных конфликтов регистров, не используйте соглашение __fastcall
для функций, которые содержат блок __asm
. Если соглашение __fastcall
задано глобально с помощью параметра компилятора /Gr, объявляйте каждую функцию, содержащую блок __asm
, с атрибутом __cdecl
или __stdcall
. (Атрибут __cdecl
сообщает компилятору использовать соглашение о вызовах C для этой функции.) Если вы не компилируетсяе с помощью /Gr, не объявляйте функцию с атрибутом __fastcall
.
При использовании блока __asm
для написания кода на языке ассемблера в функциях C/C++ нет необходимости сохранять значения регистров EAX, EBX, ECX, EDX, ESI и EDI. Например, в POWER2. Пример в написании функций с помощью встроенной сборкиpower2
функция не сохраняет значение в регистре EAX. Однако использование этих регистров влияет на качество кода, поскольку распределитель регистров не может использовать их для хранения значений между блоками __asm
. Кроме того, если во встроенном коде на языке ассемблера используется регистр EBX, ESI или EDI, компилятор вынужден сохранять и восстанавливать значения этих регистров в прологе и эпилоге функции.
Необходимо сохранять значения остальных используемых регистров (например, DS, SS, SP, BP и флаговые регистры) в пределах области блока __asm
. Необходимо сохранять значения регистров ESP и EBP, если только нет определенной причины для их изменения (переключение стека, например). См. также статью "Оптимизация встроенной сборки".
Для некоторых типов SSE требуется 8-байтовое выравнивание стека, в результате чего компилятор вынужден создавать код динамического выравнивания стека. Чтобы иметь возможность доступа к локальным переменным и параметрам функций после выравнивания, компилятор поддерживает два указателя фреймов. Если компилятор выполняет пропуск указателя кадра (FPO), он будет использовать EBP и ESP. Если компилятор не выполняет FPO, он будет использовать EBX и EBP. Чтобы обеспечить правильное выполнение кода, не изменяйте значение регистра EBX в коде на языке ассемблера, если функция требует динамического выравнивания стека, так как при этом может измениться указатель фрейма. Переместите типы с 8-байтовым выравниванием за пределы функции или не используйте регистр EBX.
Примечание.
Если встроенный код на языке ассемблера изменяет флаг направления с помощью инструкций STD или CLD, необходимо восстановить исходное значение этого флага.
Завершение блока, относящегося только к системам Майкрософт