Соглашение о вызове - Calling convention

В Информатика, а соглашение о вызовах - это схема уровня реализации (нижнего уровня), показывающая, как подпрограммы получают параметры от вызывающего и как они возвращают результат. Различия в различных реализациях включают параметры where, возвращаемые значения, обратные адреса и ссылки на область видимости помещаются (регистры, стек или память и т. д.), и как задачи по подготовке к вызову функции и последующему восстановлению среды распределяются между вызывающим и вызываемым.

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

Вариации

Соглашения о вызовах могут отличаться:

  • Где находятся параметры, возвращаемые значения и адреса возврата (в регистры, на стек вызовов, сочетание обоих или других структур памяти)
  • Порядок, в котором передаются фактические аргументы для формальных параметров (или части большого или сложного аргумента)
  • Как возвращаемое значение (возможно, длинное или сложное) доставляется от вызываемого обратно вызывающему (в стеке, в регистре или в куче)
  • Как задача настройки и очистки после вызова функции распределяется между вызывающим и вызываемым
  • Ли и как метаданные описание аргументов передается
  • Где хранится предыдущее значение указателя кадра, которое используется для восстановления указателя кадра, когда процедура заканчивается (в кадре стека или в каком-либо регистре)
  • Где размещаются любые ссылки статической области видимости для нелокального доступа к данным подпрограммы (обычно в одной или нескольких позициях в кадре стека, но иногда в общем регистре или, для некоторых архитектур, в регистрах специального назначения)
  • То, как выделяются локальные переменные, иногда также может быть частью соглашения о вызовах (когда вызывающий выделяет для вызываемого)

В некоторых случаях различия также включают следующее:

  • Соглашения, согласно которым регистры могут напрямую использоваться вызываемым пользователем без сохранения (иначе рассматриваются как ABI деталь)
  • Какие регистры считаются изменчивыми и, если они изменчивы, не должны восстанавливаться вызываемым пользователем (часто рассматривается как деталь ABI)

Вариант компилятора

Несмотря на то что немного[который? ] языки программирования может фактически указывать это частично в спецификации языка или основной реализации, различные реализации таких языков (т. е. разные компиляторы ) обычно могут по-прежнему использовать различные соглашения о вызовах, часто выбираемые. Причины этого - производительность, частая адаптация к соглашениям других популярных языков с техническими причинами или без них, а также ограничения или соглашения, налагаемые различными "вычислительные платформы ".

Вариация архитектуры

Архитектура ЦП всегда иметь более одного возможного соглашения о вызовах.[Почему? ] Из-за множества регистров общего назначения и других функций потенциальное количество соглашений о вызовах велико, хотя некоторые[который? ] Архитектуры формально определены для использования только одного соглашения о вызовах, предоставляемого архитектором.

x86 (32-бит)

В архитектура x86 используется с множеством различных соглашений о вызовах. Из-за небольшого количества архитектурных регистров соглашения о вызовах x86 в основном передают аргументы в стеке, а возвращаемое значение (или указатель на него) передается в регистре. Некоторые соглашения используют регистры для первых нескольких параметров, что может улучшить производительность для коротких и простых листовых подпрограмм, которые очень часто вызываются (т.е. подпрограмм, которые не вызывают другие подпрограммы и не должны быть повторно въезжающий ).

Пример вызова:

 толкать EAX            ; передать результат некоторого регистра толкать байт[EBP+20]   ; передать некоторую переменную памяти (синтаксис FASM / TASM) толкать 3              ; передать некоторую константу вызов расчет           ; возвращенный результат теперь в EAX

Типичная структура вызываемого абонента: (некоторые или все (кроме ret) приведенных ниже инструкций можно оптимизировать с помощью простых процедур.)

расчет:  толкать EBP            ; сохранить старый указатель кадра  mov EBP,ESP         ; получить новый указатель кадра  суб ESP,местный размер   ; зарезервировать место для стека для местных  .  .                   ; произвести расчеты, оставить результат в EAX  .  mov ESP,EBP         ; свободное место для местных  поп EBP             ; восстановить старый указатель кадра  Ret размер       ; свободное пространство параметров и возврат

ARM (A32)

Стандартный 32-битный РУКА Соглашение о вызовах выделяет 15 регистров общего назначения как:

  • r15: Программный счетчик (согласно спецификации набора команд).
  • r14: Ссылка на регистр. Инструкция BL, используемая в вызове подпрограммы, сохраняет адрес возврата в этом регистре.
  • r13: указатель стека. Команды Push / Pop в режиме работы «Thumb» используют только этот регистр.
  • r12: временный регистр внутрипроцедурного вызова.
  • от r4 до r11: локальные переменные.
  • от r0 до r3: значения аргументов, передаваемые подпрограмме, и результаты, возвращаемые подпрограммой.

Если тип возвращаемого значения слишком велик, чтобы поместиться в r0 - r3, или размер которого не может быть определен статически во время компиляции, то вызывающий должен выделить пространство для этого значения во время выполнения и передать указатель на это пространство в r0.

Подпрограммы должны сохранять содержимое от r4 до r11 и указатель стека (возможно, сохраняя их в стеке в пролог функции, затем используя их как временное пространство, а затем восстанавливая их из стека в эпилог функции ). В частности, подпрограммы, которые вызывают другие подпрограммы должен сохраните адрес возврата в регистре связи r14 в стеке перед вызовом этих других подпрограмм. Однако таким подпрограммам не нужно возвращать это значение в r14 - им просто нужно загрузить это значение в r15, программный счетчик, чтобы вернуться.

Соглашение о вызовах ARM требует использования полного нисходящего стека.[1]

Это соглашение о вызовах заставляет "типичную" подпрограмму ARM:

  • В прологе вставьте r4 в r11 в стек и отправьте адрес возврата из r14 в стек (это можно сделать с помощью одной инструкции STM);
  • Скопируйте все переданные аргументы (от r0 до r3) в локальные временные регистры (от r4 до r11);
  • Разместите другие локальные переменные в оставшихся локальных временных регистрах (с r4 по r11);
  • Выполните вычисления и вызовите другие подпрограммы по мере необходимости с использованием BL, предполагая, что от r0 до r3, r12 и r14 не сохранятся;
  • Результат записываем в r0;
  • В эпилоге вытащите из стека с r4 по r11, а адрес возврата - в программный счетчик r15. Это можно сделать с помощью одной инструкции LDM.

ARM (A64)

64-битный ARM (AArch64 ) соглашение о вызовах выделяет 31 регистр общего назначения как:[2]

  • x31 (SP): указатель стека или нулевой регистр, в зависимости от контекста.
  • x30 (LR): регистр ссылки на процедуру, используемый для возврата из подпрограмм.
  • x29 (FP): указатель кадра.
  • от x19 до x29: спасенный вызываемый.
  • x18 (PR): регистр платформы. Используется для некоторых специальных целей, связанных с операционной системой, или для дополнительного реестра, сохраненного вызывающим абонентом.
  • x16 (IP0) и x17 (IP1): временные регистры внутрипроцедурного вызова.
  • от x9 до x15: локальные переменные, вызывающий сохранен.
  • x8 (XR): адрес косвенного возвращаемого значения.
  • от x0 до x7: значения аргументов, передаваемые подпрограммой, и результаты, возвращаемые из нее.

Все регистры начиная с Икс иметь соответствующий 32-битный регистр с префиксом ш. Таким образом, 32-битный x0 называется w0.

Точно так же 32 регистра с плавающей запятой распределяются как:[3]

  • от v0 до v7: значения аргументов, передаваемые подпрограммой, и возвращаемые ею результаты.
  • от v8 до v15: сохраняется вызываемый, но нужно сохранить только нижние 64 бита.
  • от v16 до v31: локальные переменные, вызывающий абонент сохранен.

PowerPC

В PowerPC архитектура имеет большое количество регистров, поэтому большинство функций могут передавать все аргументы в регистрах для один уровень звонки. Дополнительные аргументы передаются в стек, и пространство для аргументов на основе регистров также всегда выделяется в стеке для удобства вызываемой функции в случае использования многоуровневых вызовов (рекурсивных или иных) и необходимости сохранения регистров. Это также полезно в вариативные функции, Такие как printf (), где к аргументам функции нужно обращаться как к массиву. Для всех процедурных языков используется единое соглашение о вызовах.

MIPS

O32[4] ABI является то наиболее часто используемый ABI из-за его статуса оригинального Система V ABI для MIPS.[5] Он строго основан на стеке, всего с четырьмя регистрами $ a0- $ a3 доступен для передачи аргументов. Эта кажущаяся медлительность, наряду со старинной моделью с плавающей запятой только с 16 регистрами, способствовала распространению многих других соглашений о вызовах. ABI сформировался в 1990 году и никогда не обновлялся с 1994 года. Он определен только для 32-битных MIPS, но GCC создал 64-битную версию под названием O64.[6]

Для 64-битных версий чаще всего используется N64 ABI от Silicon Graphics. Наиболее важным улучшением является то, что теперь доступно восемь регистров для передачи аргументов; Он также увеличивает количество регистров с плавающей запятой до 32. Существует также версия ILP32 под названием N32, которая использует 32-битные указатели для меньшего кода, аналогично x32 ABI. Оба работают в 64-битном режиме ЦП.[6]

Было предпринято несколько попыток заменить O32 32-битным ABI, больше похожим на N32. Конференция 1995 года представила MIPS EABI, для которого 32-разрядная версия была очень похожей.[7] EABI вдохновил MIPS Technologies на предложение более радикального ABI "NUBI", который дополнительно повторно использует регистры аргументов для возвращаемого значения.[8] MIPS EABI поддерживается GCC, но не LLVM; ни один из них не поддерживает NUBI.

Для всех O32 и N32 / N64 адрес возврата хранится в $ ra регистр. Это автоматически устанавливается с помощью JAL (переход и ссылка) или JALR (перейти и связать регистр) инструкции. Стек растет вниз.

SPARC

В SPARC архитектура, в отличие от большинства RISC архитектуры, построен на зарегистрировать окна. В каждом окне регистров есть 24 доступных регистра: 8 - это входящие регистры (% i0-% i7), 8 - это «локальные» регистры (% l0-% l7), а 8 - «выходные» регистры (% о0-% о7). Регистры "in" используются для передачи аргументов вызываемой функции, и любые дополнительные аргументы должны быть помещены в куча. Однако вызываемая функция всегда выделяет пространство для обработки потенциального переполнения окна регистра, локальных переменных и (в 32-битном SPARC) возврата структуры по значению. Чтобы вызвать функцию, нужно поместить аргументы функции, которая должна быть вызвана, в регистры «out»; когда функция вызывается, регистры "out" становятся регистрами "in", а вызываемая функция получает доступ к аргументам в своих регистрах "in". Когда вызываемая функция завершает свою работу, она помещает возвращаемое значение в первый входной регистр, который становится первым выходным регистром, когда вызываемая функция возвращается.

В Система V ABI,[9] который самый современный Unix -подобные системы, передает первые шесть аргументов в регистры "in" от% i0 до% i5, оставляя% i6 для указателя кадра и% i7 для адреса возврата.

IBM System / 360 и последователи

В IBM System / 360 это еще одна архитектура без аппаратного стека. Приведенные ниже примеры иллюстрируют соглашение о вызовах, используемое OS / 360 и последователи до введения 64-битной z / Архитектура; другие операционные системы для System / 360 могут иметь другие соглашения о вызовах.

Вызов программы:

     LA 1, ARGS Загрузить адрес списка аргументов L 15, = A (SUB) Загрузить адрес подпрограммы BALR 14,15 Переход к вызываемой подпрограмме1     ... ARGS DC A (FIRST) Адрес 1-го аргумента DC A (SECOND) ... DC A (THIRD) + X'80000000 'Последний аргумент2

Вызывается программа:

SUB EQU * Это точка входа в подпрограмму

Стандартная последовательность ввода:

     ИСПОЛЬЗОВАНИЕ *, 153     STM 14,12,12 (13) Сохранение регистров4     ST 13, SAVE + 4 Сохранить адрес сохраненной области вызывающего абонента LA 12, SAVE Chain saveareas ST 12,8 (13) LR 13,12 ...

Стандартная последовательность возврата:

     L 13, СОХРАНИТЬ + 45     LM 14,12,12 (13) L 15, RETVAL6     BR 14 Вернуться к вызывающему СОХРАНИТЬ DS 18F Сохранить область7

Примечания:

  1. В BALR Инструкция сохраняет адрес следующей инструкции (адрес возврата) в регистре, заданном первым аргументом - регистром 14, - и выполняет переход ко второму адресу аргумента в регистре 15.
  2. Вызывающий передает адрес списка адресов аргументов в регистр 1. Последний адрес имеет бит высокого порядка, установленный для обозначения конца списка. Это ограничивает программы, использующие это соглашение, 31-битный адресация.
  3. Адрес вызываемой процедуры находится в регистре 15. Обычно он загружается в другой регистр, а регистр 15 не используется в качестве базового регистра.
  4. В СТМ инструкция сохраняет регистры 14, 15 и от 0 до 12 в 72-байтовой области, предоставленной вызывающей стороной, называемой зона сохранения указывается регистром 13. Вызываемая подпрограмма предоставляет свою собственную область сохранения для использования подпрограммами, которые она вызывает; адрес этой области обычно сохраняется в регистре 13 на протяжении всей процедуры. Следующие инструкции СТМ обновить прямую и обратную цепочки, связывающие эту область сохранения с областью сохранения вызывающего абонента.
  5. Последовательность возврата восстанавливает регистры вызывающего абонента.
  6. Регистр 15 обычно используется для передачи возвращаемого значения.
  7. Статическое объявление области сохранения в вызываемой подпрограмме делает ее невозвращающийся и нерекурсивный; программа с повторным входом использует динамическую область сохранения, полученную либо из операционной системы и освобождаемую при возврате, либо в памяти, передаваемой вызывающей программой.

в Система / 390 ABI[10] и z / Архитектура ABI,[11] используется в Linux:

  • Регистры 0 и 1 изменчивы
  • Регистры 2 и 3 используются для передачи параметров и возврата значений
  • Регистры 4 и 5 также используются для передачи параметров.
  • Регистр 6 используется для передачи параметров и должен быть сохранен и восстановлен вызываемым пользователем.
  • Регистры с 7 по 13 предназначены для использования вызываемым пользователем и должны быть сохранены и восстановлены им.
  • Регистр 14 используется для обратного адреса.
  • Регистр 15 используется как указатель стека
  • Регистры с плавающей запятой 0 и 2 используются для передачи параметров и возврата значений.
  • Регистры с плавающей запятой 4 и 6 предназначены для использования вызываемым пользователем и должны быть сохранены и восстановлены им.
  • В z / Architecture регистры с плавающей запятой 1, 3, 5 и с 7 по 15 предназначены для использования вызываемым пользователем.
  • Регистр доступа 0 зарезервирован для использования системой
  • Регистры доступа с 1 по 15 предназначены для использования вызываемым пользователем.

SuperH

регистрWindows CE 5.0gccRenesas
R0Возвращаемые значения. Временно для расширения псевдо-инструкций сборки. Неявный источник / назначение для 8/16-битных операций. Не сохранилось.Возвращаемое значение, вызывающий сохраняетПеременные / временные. Не гарантировано
R1..R3Служит временными регистрами. Не сохранилось.Абонент сохранил царапину. Структурный адрес (по умолчанию сохранение звонящего)Переменные / временные. Не гарантировано
R4..R7Первые четыре слова целочисленных аргументов. Область построения аргументов предоставляет пространство, в которое могут распространяться аргументы от R4 до R7. Не сохранилось.Передача параметров, вызывающий сохраняетАргументы. Не гарантировано.
R8..R13Служит постоянными реестрами. Сохранилось.Callee сохраняетПеременные / временные. Гарантированно.
R14Указатель фрейма по умолчанию. (R8-R13 может также служить указателем кадра, а конечные подпрограммы могут использовать R1 – R3 как указатель кадра.) Сохраняется.Указатель кадра, FP, сохранение вызываемогоПеременные / временные. Гарантированно.
R15Служит указателем стека или постоянным регистром. Сохранилось.Указатель стека, SP, сохранения вызываемого абонентаУказатель стека. Гарантированно.

Примечание: «сохраненные» резервы для сохранения вызываемых абонентов; то же самое и с «гарантированным».

68 тыс.

Наиболее распространенное соглашение о вызовах для Motorola 68000 серии является:[12][13][14][15]

  • d0, d1, a0 и a1 - временные регистры
  • Все остальные регистры сохраняются вызываемыми
  • a6 - указатель кадра, который можно отключить с помощью параметра компилятора
  • Параметры помещаются в стек справа налево.
  • Возвращаемое значение сохраняется в d0

IBM 1130

В IBM 1130 была маленькой 16-битной машиной с адресной памятью. У него было всего шесть регистров плюс индикаторы состояния и не было стека. Регистры Регистр адресов инструкций (IAR), Аккумулятор (ACC), Расширение аккумулятора (EXT), и три индексных регистра X1 – X3. Вызывающая программа отвечает за сохранение ACC, EXT, X1 и X2.[16] Есть два псевдооперации для вызова подпрограмм, ВЫЗОВ для кодирования неперемещаемых подпрограмм, напрямую связанных с основной программой, и LIBF для вызова подпрограмм перемещаемой библиотеки через вектор передачи.[17] Обе псевдооперации сводятся к Филиал и магазин IAR (BSI) машинная инструкция, которая сохраняет адрес следующей инструкции по ее эффективному адресу (EA) и переходит к EA + 1.

Аргументы следуют BSI‍ - «обычно это адреса аргументов, состоящие из одного слова» - «вызываемая процедура должна знать, сколько аргументов ожидать, чтобы она могла пропустить их при возврате. В качестве альтернативы аргументы можно передавать в регистрах. Функциональные подпрограммы возвращали результат в ACC для реальных аргументов или в области памяти, называемой Псевдоаккумулятор вещественных чисел (FAC). Аргументы и адрес возврата были адресованы с использованием смещения значения IAR, хранящегося в первом местоположении подпрограммы.

  * Пример подпрограммы 1130 ENT SUB Объявление «SUB» внешней точки входа SUB DC 0 Зарезервированное слово в точке входа, условно кодируемое как «DC * - *» * Код подпрограммы начинается здесь * Если были аргументы, адреса могут быть загружены косвенно из возврата addess LDX I 1 SUB Загрузить X1 с адресом первого аргумента (например) ... * Последовательность возврата LD RES Загрузить целочисленный результат в ACC * Если аргументы не были предоставлены, косвенный переход к сохраненному адресу возврата BI SUB Если аргументов нет были предоставлены END SUB

Подпрограммы в IBM 1130, CDC 6600 и PDP-8 (все три компьютера были представлены в 1965 году) сохраняют обратный адрес в первом месте подпрограммы.[18]

Соображения по реализации

Эту вариативность необходимо учитывать при объединении модулей, написанных на нескольких языках, или при вызове операционной системы или библиотеки. API с языка, отличного от того, на котором они написаны; в этих случаях необходимо уделять особое внимание согласованию соглашений о вызовах, используемых вызывающим и вызываемым объектами. Даже программа, использующая один язык программирования, может использовать несколько соглашений о вызовах, выбранных компилятором для оптимизации кода или указанных программистом.

Резьбовой код

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

Потоковый код передает все аргументы в стек. Все возвращаемые значения возвращаются в стек. Это делает наивные реализации медленнее, чем соглашения о вызовах, которые хранят больше значений в регистрах. Однако реализации многопоточного кода, которые кэшируют несколько верхних значений стека в регистрах, в частности адрес возврата, обычно работают быстрее, чем соглашения о вызове подпрограмм, которые всегда отправляют адрес возврата в стек.[19][20][21]

PL / I

Соглашение о вызовах по умолчанию для программ, написанных на PL / I язык передает все аргументы по ссылке, хотя при желании могут быть указаны другие соглашения. Аргументы обрабатываются по-разному для разных компиляторов и платформ, но обычно адреса аргументов передаются через список аргументов в памяти. Может быть передан окончательный скрытый адрес, указывающий на область, содержащую возвращаемое значение. Благодаря большому разнообразию типов данных, поддерживаемых PL / I a дескриптор данных также может передаваться для определения, например, длины символьных или битовых строк, размера и границ массивов (допинг векторы ) или макет и содержимое структура данных. Фиктивные аргументы создаются для аргументов, которые являются константами или не соответствуют типу аргумента, ожидаемого вызываемой процедурой.

Смотрите также

Рекомендации

  1. ^ «Стандарт вызова процедур для архитектуры ARM» (PDF). 2012.
  2. ^ «Параметры в регистрах общего назначения». Руководство программиста серии ARM Cortex-A для ARMv8-A. Получено 12 ноября 2020.
  3. ^ «Параметры в регистрах NEON и с плавающей запятой». developer.arm.com. Получено 13 ноября 2020.
  4. ^ "Краткое руководство по набору инструкций MIPS32".
  5. ^ Sweetman, Доминик. Смотрите MIPS Run (2-е изд.). Издательство Morgan Kaufmann. ISBN  0-12088-421-6.
  6. ^ а б "История MIPS ABI".
  7. ^ Кристофер, Эрик (11 июня 2003 г.). "документация mips eabi". [email protected] (Список рассылки). Получено 19 июн 2020.
  8. ^ «НУБИ».
  9. ^ Двоичный интерфейс приложения System V для процессора SPARC (3-е изд.).
  10. ^ "Приложение к двоичному интерфейсу приложения S / 390 ELF".
  11. ^ «Дополнение к двоичному интерфейсу приложения zSeries ELF».
  12. ^ Смит, доктор Майк. «Сравнение регистров SHARC (21k) и 68k».
  13. ^ XGCC: языковая система Gnu C / C ++ для встраиваемой разработки (PDF). Корпорация встроенных средств поддержки. 2000. с. 59.
  14. ^ "COLDFIRE / 68K: ThreadX для семейства Freescale ColdFire". Архивировано из оригинал 2015-10-02.
  15. ^ Мошовос, Андреас. «Продолжение подпрограмм: передача аргументов, возврат значений и выделение локальных переменных». все регистры, кроме d0, d1, a0, a1 и a7, должны сохраняться во время вызова.
  16. ^ Корпорация IBM (1967). Система IBM 1130 Disk Monitor, версия 2 - Введение в систему (C26-3709-0) (PDF). п. 67. Получено 21 декабря 2014.
  17. ^ Корпорация IBM (1968 г.). Язык ассемблера IBM 1130 (C26-5927-4) (PDF). С. 24–25.
  18. ^ Смотерман, Марк (2004). «Поддержка вызова подпрограмм и процедур: ранняя история».
  19. ^ Родригес, Брэд. «Движение вперед, часть 1: проектные решения в ядре Forth». На 6809 или Zilog Super8 DTC быстрее, чем STC.
  20. ^ Эртл, Антон. «Скорость выполнения различных приемов устного перевода».
  21. ^ Залески, Мэтью (2008). «Глава 4: Разработка и внедрение эффективной интерпретации». YETI: ПОСТЕПЕННО расширяемый интерпретатор трассировки. Хотя известно, что у прямопоточных интерпретаторов плохие свойства предсказания ветвлений ... задержка вызова и возврата может быть больше, чем косвенный переход.

внешняя ссылка