Використання і збереження регістрів у вбудованій збірці

У загальному випадку на початку блоку __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, необхідно відновити вихідне значення цього прапора.

Схожі статті