В информатике соглашение о вызовах - это уровень реализации (низко- level) схема того, как подпрограммы получают параметры от вызывающей стороны и как они возвращают результат. Различия в различных реализациях заключаются в том, где размещаются параметры, возвращаемые значения, адреса возврата и ссылки области (регистры, стек или память и т. Д.), А также то, как задачи подготовки к вызову функции и последующее восстановление среды делятся между вызывающим и вызываемым.
Соглашения о вызовах могут быть связаны с стратегией оценки конкретного языка программирования, но чаще всего не считаются ее частью (или наоборот), поскольку стратегия оценки обычно определяется на более высоком уровне. уровень абстракции и рассматривается как часть языка, а не как деталь низкоуровневой реализации компилятора.
Соглашения о вызовах могут отличаться:
В некоторых случаях различия также включают следующее:
Хотя некоторые языки программирования могут частично указывать это в спецификации языка или в основной реализации, в различных реализациях таких языков (т.е. разные компиляторы ) обычно могут по-прежнему использовать различные соглашения о вызовах, часто выбираемые. Причины этого - производительность, частая адаптация к соглашениям других популярных языков с техническими причинами или без них, а также ограничения или соглашения, налагаемые различными «вычислительными платформами ».
Архитектура ЦП всегда имеет более одного возможного соглашения о вызовах. При наличии множества регистров общего назначения и других функций потенциальное количество соглашений о вызовах велико, хотя некоторые архитектуры формально определены для использования только одного соглашения о вызовах, предоставленного архитектором.
архитектура x86 используется с множеством различных соглашений о вызовах. Из-за небольшого количества архитектурных регистров соглашения о вызовах x86 в основном передают аргументы в стеке, а возвращаемое значение (или указатель на него) передается в регистре. Некоторые соглашения используют регистры для первых нескольких параметров, что может улучшить производительность для коротких и простых листовых подпрограмм, которые очень часто вызываются (то есть подпрограмм, которые не вызывают другие подпрограммы и не должны быть повторно входимыми ).
Пример вызова:
push EAX; передать некоторый байт толчка результата регистра [EBP + 20]; передать некоторую переменную памяти (синтаксис FASM / TASM) push 3; передать некоторый постоянный вызов calc; возвращенный результат теперь находится в EAX
Типичная структура вызываемого объекта: (некоторые или все (кроме ret) приведенных ниже инструкций могут быть оптимизированы с помощью простых процедур)
calc: push EBP; сохранить указатель старого кадра mov EBP, ESP; получить новый указатель кадра sub ESP, localsize; зарезервировать место в стеке для местных жителей.. ; произвести расчеты, оставить результат в EAX. mov ESP, EBP; свободное место для местных жителей поп EBP; восстановить старый указатель кадра ret paramsize; свободное пространство для параметров и возврат
Стандартное 32-битное соглашение о вызовах ARM выделяет 15 регистров общего назначения как:
Если тип возвращаемого значения слишком велик, чтобы поместиться в r0 - r3, или размер которого не может быть определен статически во время компиляции, то вызывающий должен выделить пространство для этого значения во время выполнения и передать указатель на это пространство в r0.
Подпрограммы должны сохранять содержимое от r4 до r11 и указатель стека (возможно, сохраняя их в стеке в прологе функции , затем используя их как рабочее пространство, а затем восстанавливая их из стек в эпилоге функции ). В частности, подпрограммы, которые вызывают другие подпрограммы, должны сохранять адрес возврата в регистре связи r14 в стек перед вызовом этих других подпрограмм. Однако таким подпрограммам не нужно возвращать это значение в r14 - им просто нужно загрузить это значение в r15, программный счетчик, чтобы вернуться.
Соглашение о вызовах ARM требует использования полного нисходящего стека.
Это соглашение о вызовах приводит к тому, что "типичная" подпрограмма ARM выполняет следующие действия:
Соглашение о вызовах 64-битного ARM (AArch64 ) выделяет 31 регистр общего назначения как:
Все регистры, начинающиеся с x, имеют соответствующий 32-битный регистр с префиксом w. Таким образом, 32-битный x0 называется w0.
Архитектура PowerPC имеет большое количество регистров, поэтому большинство функций могут передавать все аргументы в регистры для одноуровневых вызовов. Дополнительные аргументы передаются в стек, и пространство для аргументов на основе регистров также всегда выделяется в стеке для удобства вызываемой функции в случае использования многоуровневых вызовов (рекурсивных или иных) и необходимости сохранения регистров. Это также используется в вариативных функциях, таких как printf ()
, где к аргументам функции нужно обращаться как к массиву. Для всех процедурных языков используется единое соглашение о вызовах.
O32 ABI является наиболее часто используемым ABI благодаря своему статусу исходного ABI System V для MIPS. Он строго основан на стеке, и для передачи аргументов доступны только четыре регистра $ a0- $ a3
. Эта кажущаяся медлительность, наряду со старинной моделью с плавающей запятой только с 16 регистрами, способствовала распространению многих других соглашений о вызовах. ABI сформировался в 1990 году и никогда не обновлялся с 1994 года. Он определен только для 32-битных MIPS, но GCC создал 64-битный вариант под названием O64.
Для 64-битных версий. бит, чаще всего используется N64 ABI от Silicon Graphics. Наиболее важным улучшением является то, что теперь доступно восемь регистров для передачи аргументов; Это также увеличивает количество регистров с плавающей запятой до 32. Существует также версия ILP32 под названием N32, которая использует 32-битные указатели для меньшего кода, аналогично x32 ABI. Оба работают в 64-битном режиме ЦП.
Было предпринято несколько попыток заменить O32 32-битным ABI, больше напоминающим N32. Конференция 1995 года представила MIPS EABI, для которого 32-разрядная версия была очень похожей. EABI вдохновил MIPS Technologies на предложение более радикального ABI "NUBI", который дополнительно повторно использует регистры аргументов для возвращаемого значения. MIPS EABI поддерживается GCC, но не LLVM; ни один из них не поддерживает NUBI.
Для всех O32 и N32 / N64 адрес возврата сохраняется в регистре $ ra
. Это автоматически устанавливается с использованием инструкций JAL
(переход и ссылка) или JALR
(регистр перехода и связи). Стек растет вниз.
Архитектура SPARC, в отличие от большинства архитектур RISC, построена на окнах регистров. В каждом окне регистров есть 24 доступных регистра: 8 - это входящие регистры (% i0-% i7), 8 - это «локальные» регистры (% l0-% l7) и 8 - это регистры «выхода» (% о0-% о7). Регистры "in" используются для передачи аргументов вызываемой функции, и любые дополнительные аргументы должны быть помещены в стек . Однако вызываемая функция всегда выделяет пространство для обработки потенциального переполнения окна регистров, локальных переменных и (на 32-битном SPARC) возврата структуры по значению. Чтобы вызвать функцию, нужно поместить аргументы функции, которая должна быть вызвана, в регистры «out»; когда функция вызывается, регистры "out" становятся регистрами "in", а вызываемая функция получает доступ к аргументам в своих регистрах "in". Когда вызываемая функция завершает свою работу, она помещает возвращаемое значение в первый входной регистр, который становится первым выходным регистром, когда вызываемая функция возвращается.
Система System V ABI, которой следуют большинство современных Unix -подобных систем, передает первые шесть аргументов в регистры "in"% i0 через% i5, зарезервировав% i6 для указателя кадра и% i7 для адреса возврата.
IBM System / 360 - это еще одна архитектура без аппаратного стека. Приведенные ниже примеры иллюстрируют соглашение о вызовах, используемое OS / 360 и его преемниками до введения 64-битной z / Architecture ; другие операционные системы для System / 360 могут иметь другие соглашения о вызовах.
Вызывающая программа:
LA 1, ARGS Загрузить адрес списка аргументов L 15, = A (SUB) Загрузить адрес подпрограммы BALR 14,15 Переход к вызываемой подпрограмме... ARGS DC A (FIRST) Адрес 1-го аргумента DC A (SECOND)... DC A (THIRD) + X'80000000 'Последний аргумент
Вызываемая программа:
SUB EQU * Это точка входа подпрограмма
Стандартная последовательность ввода:
USING *, 15 STM 14,12,12 (13) Сохранение регистров ST 13, SAVE + 4 Сохранение адреса сохраненной области вызывающего абонента LA 12, SAVE Chain saveareas ST 12, 8 (13) LR 13,12...
Стандартная последовательность возврата:
L 13, SAVE + 4 LM 14,12,12 (13) L 15, RETVAL BR 14 Return вызывающему SAVE DS 18F Savearea
Примечания:
BALR
сохраняет адрес следующей инструкции (адрес возврата) в регистре, заданном первым аргументом - регистром 14 - и переходит ко второму адресу аргумента в регистре 15.STM
сохраняет регистры 14, 15 и 0–12 в 72-байтовой области, предоставленной вызывающей стороной, называемой областью сохранения, на которую указывает регистр 13. Вызываемый процедура предоставляет свою собственную область сохранения для использования вызываемыми ею подпрограммами; адрес этой области обычно сохраняется в регистре 13 на протяжении всей процедуры. Инструкции, следующие за STM
обновляют прямую и обратную цепочки, связывая эту область сохранения с областью сохранения вызывающего абонента.В System / 390 ABI и z / Архитектура ABI, используемый в Linux:
Регистр | Windows CE 5.0 | gcc | Renesas |
---|---|---|---|
R0 | Возвращаемые значения. Временно для расширения псевдо-инструкций сборки. Неявный источник / назначение для 8/16-битных операций. Не сохраняется. | Возвращаемое значение, вызывающий сохраняет | переменные / временные. Не гарантируется |
R1..R3 | Служит временными регистрами. Не сохранилось. | Абонент сохранил царапину. Адрес структуры (сохранение вызывающего абонента по умолчанию) | Переменные / временные. Не гарантируется |
R4..R7 | Первые четыре слова целочисленных аргументов. Область построения аргументов предоставляет пространство, в которое могут попадать аргументы от R4 до R7. Не сохраняется. | Передача параметров, вызывающий сохраняет | аргументы. Не гарантировано. |
R8..R13 | Служит как постоянные регистры. Сохранено. | Вызываемый сохраняет | переменные / временные. Гарантированно. |
R14 | Указатель кадра по умолчанию. (R8-R13 может также служить указателем кадра, а конечные процедуры могут использовать R1 – R3 как указатель кадра.) Сохраняется. | Указатель кадра, FP, вызываемый объект сохраняет | переменные / временные. Гарантированно. |
R15 | Служит указателем стека или постоянным регистром. Сохранено. | Указатель стека, SP, вызываемый объект сохраняет | указатель стека. Гарантированно. |
Примечание: «сохраненные» резервы для сохранения вызываемого абонента; то же самое и с «гарантированным».
В Викиучебнике есть книга на тему: Сборка 68000 |
Наиболее распространенное соглашение о вызовах для Motorola серии 68000 :
IBM 1130 было маленьким 16-битным словом -адресная машина. У него было всего шесть регистров плюс индикаторы состояния и не было стека. Регистры - это регистр адреса инструкции (IAR), накопитель (ACC), расширение накопителя (EXT) и три индексных регистра X1 – X3. Вызывающая программа отвечает за сохранение ACC, EXT, X1 и X2. Существуют две псевдооперации для вызова подпрограмм, CALL
для кодирования неперемещаемых подпрограмм, напрямую связанных с основной программой, и LIBF
для вызова подпрограмм перемещаемой библиотеки через вектор передачи. Обе псевдооперации разрешаются в машинную инструкцию Branch and Store IAR (BSI
), которая сохраняет адрес следующей инструкции по ее эффективному адресу (EA) и выполняет переход к EA + 1.
Аргументы следуют за BSI
- «обычно это адреса аргументов из одного слова» - «вызываемая подпрограмма должна знать, сколько аргументов ожидать, чтобы она могла пропустить их при возврате. В качестве альтернативы аргументы можно передавать в регистрах. Функциональные подпрограммы возвращали результат в ACC для реальных аргументов или в области памяти, называемой псевдоаккумулятором вещественных чисел (FAC). Аргументы и адрес возврата были адресованы с использованием смещения значения IAR, хранящегося в первом местоположении подпрограммы.
* Пример подпрограммы 1130 ENT SUB Объявление «SUB» внешней точки входа SUB DC 0 Зарезервированное слово в точке входа, обычно кодируемое как «DC * - *» * Код подпрограммы начинается здесь * Если были аргументы, адреса могут загружаться косвенно из адреса возврата LDX I 1 SUB Загрузить X1 с адресом первого аргумента (например)... * Последовательность возврата LD RES Загрузить целочисленный результат в ACC * Если аргументы не были предоставлены, косвенный переход к сохраненному возврату адрес BI SUB Если аргументы не были предоставлены END SUB
Подпрограммы в IBM 1130, CDC 6600 и PDP-8 (все три компьютера были представлены в 1965 году) сохраняют адрес возврата в первом месте подпрограммы.
Эту изменчивость необходимо учитывать при объединении модулей, написанных на нескольких языках, или при вызове операционной системы или библиотеки API с другого языка, кроме того, на котором они написаны; в этих случаях необходимо уделять особое внимание согласованию соглашений о вызовах, используемых вызывающим и вызываемым абонентами. Даже программа, использующая один язык программирования, может использовать несколько соглашений о вызовах, выбранных компилятором для оптимизации кода или указанных программистом.
Потоковый код возлагает всю ответственность за настройку и очистку после вызова функции в вызываемом коде. Вызывающий код ничего не делает, кроме списка вызываемых подпрограмм. Это помещает весь код настройки функции и очистки в одно место - пролог и эпилог функции - а не во многие места, где функция вызывается. Это делает многопоточный код наиболее компактным соглашением о вызовах.
Потоковый код передает все аргументы в стеке. Все возвращаемые значения возвращаются в стек. Это делает наивные реализации медленнее, чем соглашения о вызовах, которые хранят больше значений в регистрах. Однако реализации потокового кода, которые кэшируют несколько верхних значений стека в регистрах, в частности адрес возврата, обычно работают быстрее, чем соглашения о вызове подпрограмм, которые всегда помещают адрес возврата в стек.
Соглашение о вызовах по умолчанию для программ, написанных на языке PL / I, передает все аргументы по ссылке, хотя при желании могут быть указаны другие соглашения. Аргументы обрабатываются по-разному для разных компиляторов и платформ, но обычно адреса аргументов передаются через список аргументов в памяти. Может быть передан окончательный скрытый адрес, указывающий на область, содержащую возвращаемое значение. Из-за большого разнообразия типов данных, поддерживаемых PL / I, дескриптор данных также может быть передан для определения, например, длины символьных или битовых строк, размеров и границ массивов (допинговые векторы ) или макет и содержимое структуры данных . Фиктивные аргументы создаются для аргументов, которые являются константами или не согласуются с типом аргумента, который ожидает вызываемая процедура.
У Wikibook Embedded Systems есть страница по теме: Смешанное программирование на C и ассемблере |
В Wikibook X86 Disassembly есть страница по теме: Соглашения о вызовах |
| 1 =
()