Соглашения о вызовах функции IA-32
Когда функции (подпрограммы) вызывают другие функции (подпрограммы), они, возможно, должны передать параметры им. Вызванные подпрограммы получают доступ к этим параметрам как к параметрам. С другой стороны некоторые подпрограммы передают результат или возвращаемое значение к их вызывающим сторонам. В среде IA-32 большинство параметров передается стеку этапа выполнения; некоторые аргументы вектора передаются в регистрах. Результаты возвращаются в регистрах или в памяти. Для эффективной передачи значений между вызывающими сторонами и вызываемыми GCC соблюдает строгие правила, когда он генерирует объектный код программы.
Эта статья описывает типы данных, которые могут использоваться для управления параметрами и результатами вызовов подпрограммы, как подпрограммы передают параметры подпрограммам, которые они вызывают, и как подпрограммы, обеспечивающие возвращаемое значение, передают результат своим вызывающим сторонам. Эта статья также перечисляет регистры, доступные в архитектуре IA-32 и сохраняется ли их значение после вызова подпрограммы.
Функциональные соглашения о вызовах, используемые в среде IA-32, совпадают с используемыми в System V IA-32 ABI за следующими исключениями:
Различные правила для возврата структур
Штабель составляет 16 байтов, выровненных при вызовах функции
Большие типы данных (больше, чем 4 байта) сохранены при их естественном выравнивании
Большинство операций с плавающей точкой выполняется с помощью модуля SSE вместо x87 FPU, кроме тех случаев, когда работа на
long double
значения. (Значения по умолчанию среды IA-32 к 64-разрядной внутренней точности для x87 FPU.)
Содержание этой статьи в основном базируется в Двоичном интерфейсе приложений System V: Дополнение Процессора Архитектуры Intel386, доступное в http://www .sco.com/developers/devspecs/abi386-4.pdf.
Типы данных и выравнивание данных
Используя правильные типы данных для Ваших переменных помогает максимизировать производительность и мобильность Ваших программ. Выравнивание данных указывает, как данные размечаются в памяти. Естественное выравнивание типа данных указывает выравнивание по умолчанию значений того того типа.
Таблица 1 перечисляет ANSI C скалярные типы данных и их размеры и естественное выравнивание в этой среде.
Тип данных | Размер (в байтах) | Естественное выравнивание (в байтах) |
---|---|---|
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 2 | 2 |
| 2 | 2 |
| 4 | 4 |
| 4 | 4 |
| 4 | 4 |
| 4 | 4 |
| 8 | 4 |
| 8 | 4 |
| 4 | 4 |
| 8 | 4 |
| 16 | 16 |
указатель | 4 | 4 |
Таблица 2 показывает типы векторов, доступные в этой среде.
Тип вектора | Тип данных элементов | Размер (в байтах) | Выравнивание (в байтах) |
---|---|---|---|
|
| 8 | 8 |
|
| 16 | 16 |
|
| 16 | 16 |
|
| 16 | 16 |
Это некоторые важные подробные данные об этой среде:
Эта среда не требует 8-байтового выравнивания для значений двойной точности.
Эта среда требует 16-байтового выравнивания для 128-разрядных векторных элементов.
Это правила выравнивания, соблюденные в этой среде:
Скалярные типы данных используют свое естественное выравнивание.
Составные типы данных (массивы, структуры и объединения) берут выравнивание элемента с самым высоким выравниванием. Массив принимает то же выравнивание как свои элементы. Размер составного типа данных является кратным числом своего выравнивания (дополнение может требоваться).
Вызовы функции
Этот раздел детализирует процесс вызова подпрограммы и передающих параметров к ней, и как возвращаемые значения подпрограмм к их вызывающим сторонам.
Структура штабеля
Среда IA-32 использует штабель, который — при вызовах функции — составляет выровненных 16 байтов, становится нисходящим, и содержит локальные переменные и параметры функции. Каждая подпрограмма может добавить информацию о связи к своему стековому фрейму, но это не требуется, чтобы делать так. Рисунок 1 показывает штабель прежде и во время вызова подпрограммы. (Чтобы помочь предотвратить выполнение вредоносного кода на штабеле, GCC защищает штабель от выполнения.)
Указатель вершины стека (SP) указывает на нижнюю часть штабеля. Стековые фреймы содержат следующие области:
Область параметра хранит параметры, которые вызывающая сторона передает вызванной подпрограмме. Эта область находится в стековом фрейме вызывающей стороны.
Область связи содержит адрес следующей инструкции вызывающей стороны.
Сохраненный (дополнительный) указатель кадра содержит базовый адрес стекового фрейма вызывающей стороны.
Можно использовать
gcc -fomit-frame-pointer
опция заставить компилятор не сохранить, установите и восстановите указатель кадра в вызовах функции, для которых не нужно один, делая регистр EBP доступным для общего использования. Однако выполнение так может повредить отладку.Локальная область хранения содержит локальные переменные подпрограммы и значения регистров, которые должны быть восстановлены перед вызванными функциональными возвратами. Посмотрите Сохранение Регистра для подробных данных.
Сохраненная область регистров содержит значения регистров, которые должны быть восстановлены перед вызванными функциональными возвратами. Посмотрите Сохранение Регистра для подробных данных.
В этой среде не фиксируется размер стекового фрейма.
Прожурналы и эпилоги
Вызванная подпрограмма ответственна за выделение ее собственного стекового фрейма. Эта работа выполняется разделом кода, названного Прологом, который компилятор помещает перед организацией функции. После организации функции компилятор помещает эпилог для восстановления процесса к состоянию, которым это было до вызова подпрограммы.
Пролог выполняет следующие задачи:
Продвигает значение указателя стекового фрейма (EBP) на штабель.
Устанавливает указатель стекового фрейма на значение указателя вершины стека (ESP).
Продвигает значения регистров, которые должны быть сохранены (EDI, ESI и EBX) на штабель.
Эпилог выполняет эти задачи:
Освобождает пространство, использованное для локального хранения в штабеле.
Восстанавливает сохраненные регистры (EDI, ESI, EBX, EBP) путем сования значений экономил на штабеле Прологом.
Возвраты.
Перечисление 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 |
Передача параметров
Компилятор придерживается соблюдающих правил когда передающие параметры подпрограммам:
Вызывающая сторона гарантирует, что штабель составляет 16 байтов, выровненных при вызове функции.
Вызывающая сторона выравнивает невекторные параметры 4-байтовым границам (на 32 бита).
Размер каждого параметра является кратным числом 4 байтов с дополнением хвоста при необходимости. Поэтому 8-разрядным и 16-разрядным интегральным типам данных способствуют на 32-разрядный, прежде чем они будут продвинуты на штабель.
Вызывающая сторона помещает параметры в область параметра в обратном порядке в 4-байтовые блоки. Т.е. самый правый параметр имеет самый высокий адрес.
Вызывающая сторона помещает все поля структур (или объединения) без векторных элементов в области параметра. Эти структуры составляют выровненные 4 байта.
Структуры мест вызывающей стороны с векторными элементами на штабеле, 16 байтов выровнялись. Каждый вектор в структуре составляет выровненных 16 байтов.
Вызывающая сторона помещает 64-разрядные векторы (
__m64
) на области параметра, выровненной к 8-байтовым границам.Вызывающая сторона помещает векторы 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 байтах.
Передающие структуры и векторы
Примите структуру 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.
Возврат результатов
Это - то, как функции передают результаты своим вызывающим сторонам:
Скалярные значения. Скалярные значения включают структуры, содержащие только одно скалярное значение.
Вызванная функция помещает интеграл или результаты указателя в EAX.
Вызванная функция помещает результаты с плавающей точкой в ST0.
Вызывающая сторона удаляет значение из этого регистра, даже когда это не использует значение.
Структуры. Вызванная функция возвращает структуры согласно их выровненному размеру.
Структуры 1 или 2 байта в размере помещаются в EAX.
Структуры 4 или 8 байтов в размере помещаются в: EAX и EDX.
Структуры других размеров помещаются в адрес, предоставленный вызывающей стороной. Например, язык C++ иногда вынуждает компилятор возвратить значение в памяти, когда это обычно возвращалось бы в регистрах. Посмотрите Передающие Параметры для получения дополнительной информации.
Векторы.
Вызванная функция помещает векторы в адресе, предоставленном вызывающей стороной.
Сохранение регистра
Таблица 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 | Нет | Содержит системные флаги, такие как флаг направления и флаг переноса. Флаг направления должен быть установлен в «прямое» направление (т.е. |