Соглашения о вызовах функции IA-32

Когда функции (подпрограммы) вызывают другие функции (подпрограммы), они, возможно, должны передать параметры им. Вызванные подпрограммы получают доступ к этим параметрам как к параметрам. С другой стороны некоторые подпрограммы передают результат или возвращаемое значение к их вызывающим сторонам. В среде IA-32 большинство параметров передается стеку этапа выполнения; некоторые аргументы вектора передаются в регистрах. Результаты возвращаются в регистрах или в памяти. Для эффективной передачи значений между вызывающими сторонами и вызываемыми GCC соблюдает строгие правила, когда он генерирует объектный код программы.

Эта статья описывает типы данных, которые могут использоваться для управления параметрами и результатами вызовов подпрограммы, как подпрограммы передают параметры подпрограммам, которые они вызывают, и как подпрограммы, обеспечивающие возвращаемое значение, передают результат своим вызывающим сторонам. Эта статья также перечисляет регистры, доступные в архитектуре IA-32 и сохраняется ли их значение после вызова подпрограммы.

Функциональные соглашения о вызовах, используемые в среде IA-32, совпадают с используемыми в System V IA-32 ABI за следующими исключениями:

Содержание этой статьи в основном базируется в Двоичном интерфейсе приложений System V: Дополнение Процессора Архитектуры Intel386, доступное в http://www .sco.com/developers/devspecs/abi386-4.pdf.

Типы данных и выравнивание данных

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

Таблица 1 перечисляет ANSI C скалярные типы данных и их размеры и естественное выравнивание в этой среде.

Табличный 1  Размер и естественное выравнивание скалярных типов данных

Тип данных

Размер (в байтах)

Естественное выравнивание (в байтах)

_Bool, bool

1

1

unsigned char

1

1

char, signed char

1

1

unsigned short

2

2

signed short

2

2

unsigned int

4

4

signed int

4

4

unsigned long

4

4

signed long

4

4

unsigned long long

8

4

signed long long

8

4

float

4

4

double

8

4

long double

16

16

указатель

4

4

Таблица 2 показывает типы векторов, доступные в этой среде.

Табличный 2  Размер и выравнивание типов векторов

Тип вектора

Тип данных элементов

Размер (в байтах)

Выравнивание (в байтах)

__m64

int

8

8

__m128i

int

16

16

__m128

float

16

16

__m128d

double

16

16

Это некоторые важные подробные данные об этой среде:

Это правила выравнивания, соблюденные в этой среде:

  1. Скалярные типы данных используют свое естественное выравнивание.

  2. Составные типы данных (массивы, структуры и объединения) берут выравнивание элемента с самым высоким выравниванием. Массив принимает то же выравнивание как свои элементы. Размер составного типа данных является кратным числом своего выравнивания (дополнение может требоваться).

Вызовы функции

Этот раздел детализирует процесс вызова подпрограммы и передающих параметров к ней, и как возвращаемые значения подпрограмм к их вызывающим сторонам.

Структура штабеля

Среда IA-32 использует штабель, который — при вызовах функции — составляет выровненных 16 байтов, становится нисходящим, и содержит локальные переменные и параметры функции. Каждая подпрограмма может добавить информацию о связи к своему стековому фрейму, но это не требуется, чтобы делать так. Рисунок 1 показывает штабель прежде и во время вызова подпрограммы. (Чтобы помочь предотвратить выполнение вредоносного кода на штабеле, GCC защищает штабель от выполнения.)

  Расположение Штабеля рисунка 1

Указатель вершины стека (SP) указывает на нижнюю часть штабеля. Стековые фреймы содержат следующие области:

  • Область параметра хранит параметры, которые вызывающая сторона передает вызванной подпрограмме. Эта область находится в стековом фрейме вызывающей стороны.

  • Область связи содержит адрес следующей инструкции вызывающей стороны.

  • Сохраненный (дополнительный) указатель кадра содержит базовый адрес стекового фрейма вызывающей стороны.

    Можно использовать gcc -fomit-frame-pointer опция заставить компилятор не сохранить, установите и восстановите указатель кадра в вызовах функции, для которых не нужно один, делая регистр EBP доступным для общего использования. Однако выполнение так может повредить отладку.

  • Локальная область хранения содержит локальные переменные подпрограммы и значения регистров, которые должны быть восстановлены перед вызванными функциональными возвратами. Посмотрите Сохранение Регистра для подробных данных.

  • Сохраненная область регистров содержит значения регистров, которые должны быть восстановлены перед вызванными функциональными возвратами. Посмотрите Сохранение Регистра для подробных данных.

В этой среде не фиксируется размер стекового фрейма.

Прожурналы и эпилоги

Вызванная подпрограмма ответственна за выделение ее собственного стекового фрейма. Эта работа выполняется разделом кода, названного Прологом, который компилятор помещает перед организацией функции. После организации функции компилятор помещает эпилог для восстановления процесса к состоянию, которым это было до вызова подпрограммы.

Пролог выполняет следующие задачи:

  1. Продвигает значение указателя стекового фрейма (EBP) на штабель.

  2. Устанавливает указатель стекового фрейма на значение указателя вершины стека (ESP).

  3. Продвигает значения регистров, которые должны быть сохранены (EDI, ESI и EBX) на штабель.

  4. Выделяет площадь в стековом фрейме для локального хранения.

Эпилог выполняет эти задачи:

  1. Освобождает пространство, использованное для локального хранения в штабеле.

  2. Восстанавливает сохраненные регистры (EDI, ESI, EBX, EBP) путем сования значений экономил на штабеле Прологом.

  3. Возвраты.

Перечисление 1 показывает определение simp функция.

  Определение перечисления 1 simp функция

#include <stdio.h>
void simp(int i, short s, char c) {
    printf("Hi!\n");
}

Перечисление 2 показывает возможный Пролог к simp функция.

  Пролог перечисления 2 В качестве примера

pushl   %ebp            ; save EBP
movl    %esp,%ebp       ; copy ESP to EBP
pushl   %ebx            ; save EBX
subl    $0x24,%esp      ; allocate space for local storage

Перечисление 3 показывает возможный эпилог для simp функция.

  Эпилог перечисления 3 В качестве примера

addl    $0x24,%esp      ; deallocate space for local storage
popl    %ebx            ; restore EBX
popl    %ebp            ; restore EBP
ret                     ; return

Передача параметров

Компилятор придерживается соблюдающих правил когда передающие параметры подпрограммам:

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

  2. Вызывающая сторона выравнивает невекторные параметры 4-байтовым границам (на 32 бита).

    Размер каждого параметра является кратным числом 4 байтов с дополнением хвоста при необходимости. Поэтому 8-разрядным и 16-разрядным интегральным типам данных способствуют на 32-разрядный, прежде чем они будут продвинуты на штабель.

  3. Вызывающая сторона помещает параметры в область параметра в обратном порядке в 4-байтовые блоки. Т.е. самый правый параметр имеет самый высокий адрес.

  4. Вызывающая сторона помещает все поля структур (или объединения) без векторных элементов в области параметра. Эти структуры составляют выровненные 4 байта.

  5. Структуры мест вызывающей стороны с векторными элементами на штабеле, 16 байтов выровнялись. Каждый вектор в структуре составляет выровненных 16 байтов.

  6. Вызывающая сторона помещает 64-разрядные векторы (__m64) на области параметра, выровненной к 8-байтовым границам.

  7. Вызывающая сторона помещает векторы 128-разрядные векторы (__m128, __m128d, и __m128i) в регистрах XMM0 через XMM3). Когда применимые регистры XMM исчерпываются, вызывающая сторона помещает 128-разрядные векторы в область параметра. Вызывающая сторона выравнивает 128-разрядные векторы в области параметра к 16-байтовым границам.

В целом вызывающая сторона ответственна за удаление всех параметров, используемых в вызове функции после вызванных функциональных возвратов. Единственное исключение является параметрами, сгенерированными автоматически GCC. Когда функция возвращает структуру или объединение, больше, чем 8 байтов, вызывающая сторона передает указатель на надлежащее хранение как первый параметр функции. GCC добавляет этот параметр автоматически в сгенерированном коде. Посмотрите Результаты Возврата для получения дополнительной информации. Например, компилятор перевел бы код, показанный в Перечислении 4 машинному языку, как будто это было записано как показано в Перечислении 5.

Перечисление 4  Используя большую структуру — исходный код

typedef struct {
    float ary[8];
} big_struct;
big_struct callee(int a, float b) {
    big_struct callee_struct;
    ...
    return callee_struct;
}
caller() {
    big_struct caller_struct;
    caller_struct = callee(3, 42.0);
}

Перечисление 5  Используя большую структуру — интерпретация компилятора

typedef struct {
    float ary[8];
} big_struct;
void callee(big_struct *p, int a, float b)
{
    big_struct callee_struct;
    ...
    *p = callee_struct;
    return;
}
caller() {
    big_struct caller_struct;
    callee(&caller_struct, 3, 42.0);
}

Передача параметров основополагающих типов данных

Примите функцию foo объявляется как это:

void foo(SInt32 i, float f, double d, SInt16 s, UInt8 c);

Рисунок 2 иллюстрирует размещение параметров в области параметра при вызове функции. Отметьте дополнение, добавленное для выравнивания штабеля в 16 байтах.

  Присвоение Параметра рисунка 2 с параметрами основополагающих типов данных

Передающие структуры и векторы

Примите структуру data и функция bar объявляются как это:

struct data {
    float f;
    long long l;
    __m128 vf;
};
void bar(SInt32 i, UInt8 c, struct data b, __m128i vi, void* p);

Рисунок 3 иллюстрирует размещение параметров в области параметра, 16-байтовом дополнении выравнивания штабеля при вызове и регистрах XMM.

  Присвоение Параметра рисунка 3 со структурой и аргументами вектора

Возврат результатов

Это - то, как функции передают результаты своим вызывающим сторонам:

  • Скалярные значения. Скалярные значения включают структуры, содержащие только одно скалярное значение.

    • Вызванная функция помещает интеграл или результаты указателя в EAX.

    • Вызванная функция помещает результаты с плавающей точкой в ST0.

    • Вызывающая сторона удаляет значение из этого регистра, даже когда это не использует значение.

  • Структуры. Вызванная функция возвращает структуры согласно их выровненному размеру.

    • Структуры 1 или 2 байта в размере помещаются в EAX.

    • Структуры 4 или 8 байтов в размере помещаются в: EAX и EDX.

    • Структуры других размеров помещаются в адрес, предоставленный вызывающей стороной. Например, язык C++ иногда вынуждает компилятор возвратить значение в памяти, когда это обычно возвращалось бы в регистрах. Посмотрите Передающие Параметры для получения дополнительной информации.

  • Векторы.

    • Вызванная функция помещает векторы в адресе, предоставленном вызывающей стороной.

Сохранение регистра

Таблица 3 перечисляет регистры архитектуры IA-32, используемые в этой среде и их энергозависимости в вызовах процедуры. Регистры, которые должны сохранить их значение после вызова функции, вызывают энергонезависимые.

Табличный 3  Процессор регистрируется в архитектуре IA-32

Ввести

Имя

Сохраненный

Примечания

Регистр общего назначения

EAX

Нет

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

EDX

Нет

Регистр дивиденда (делят работу). Доступный для общего использования для всех других операций.

ECX

Нет

Регистр количества (операции сдвига и строковые операции). Доступный для общего использования для всех других операций.

EBX

Да

Позиционно-независимый регистр кодовой базы. Доступный для общего использования в непозиционно-независимом коде.

EBP

Да

Указатель стекового фрейма. Дополнительно содержит базовый адрес текущего стекового фрейма. Параметры подпрограммы находятся в предыдущем кадре как положительные смещения значения этого регистра. Локальные переменные находятся при отрицательных смещениях.

ESI

Да

Доступный для общего использования.

EDI

Да

Доступный для общего использования.

Регистр указателя вершины стека

ESP

Да

Содержит адрес нижней части штабеля.

Регистр с плавающей точкой

ST0

Нет

Используемый для возврата значений с плавающей точкой. Когда функция не возвращает значение с плавающей точкой, этот регистр должен быть пустым. Этот регистр должен также быть пустым на функциональной записи.

ST1–ST7

Нет

Доступный для общего использования. Эти регистры должны быть пустыми на стандартном входе и выходе.

64-разрядный регистр

MM0–MM7

Нет

Используемый для выполнения единственной инструкции, операций (SIMD) многократных данных на 64-разрядном упакованном байте, 2 байтах и 4-байтовых целых числах.

128-разрядный регистр

XMM0–XMM7

Нет

Используемый для выполнения 32-разрядной и 64-разрядной арифметики с плавающей точкой. Также используемый для выполнения единственной инструкции, операций (SIMD) многократных данных на 128-разрядной упакованной одинарной точности и скаляре двойной точности и значениях с плавающей точкой, и 128-разрядном упакованном байте, 2 байтах и 4-байтовых целых числах. XMM0–XMM3 используются для передачи первых четырех векторов в вызове функции.

Регистр системных флагов

EFLAGS

Нет

Содержит системные флаги, такие как флаг направления и флаг переноса. Флаг направления должен быть установлен в «прямое» направление (т.е. 0) перед записью в и на выход от подпрограммы. Другие пользовательские флаги не имеют никакой указанной роли в стандартной вызывающей последовательности и не сохраняются.