Использование и сохранение регистров во встроенном коде на языке ассемблера

Блок, относящийся только к системам Microsoft

В общем случае в начале блока __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. Пример C при написании функций с помощью встроенной сборки функция 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, необходимо восстановить исходное значение этого флага.

Завершение блока, относящегося только к системам Майкрософт

См. также

Встроенный ассемблер