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

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

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

Вариации

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

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

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

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

Многие архитектуры имеют только одно широко используемое соглашение о вызовах, часто предлагаемое архитектором. Для RISC, включая SPARC, MIPS и RISC-V , часто используются имена регистров, основанные на этом соглашении о вызовах. Например, MIPS регистры $4через $7иметь «ABI имен» $a0через $a3, отражая их использование для передачи параметров в стандартных вызовах. (У процессоров RISC есть много эквивалентных регистров общего назначения, поэтому обычно нет аппаратной причины давать им имена, кроме чисел.)

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

Архитектура

x86 (32-бит)

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

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

 push EAX            ; pass some register result
 push dword [EBP+20] ; pass some memory variable (FASM/TASM syntax)
 push 3              ; pass some constant
 call calc           ; the returned result is now in EAX

Типичная структура вызываемого объекта: ( некоторые или все (кроме ret) приведенные ниже инструкции могут быть оптимизированы с помощью простых процедур ). Некоторые соглашения оставляют пространство параметров выделенным, используя plain retвместо ret imm16. В этом случае вызывающий абонент может add esp,12в этом примере или иным образом иметь дело с изменением ESP.

calc:
  push EBP            ; save old frame pointer
  mov EBP,ESP         ; get new frame pointer
  sub ESP,localsize   ; reserve stack space for locals
  .
  .                   ; perform calculations, leave result in EAX
  .
  mov ESP,EBP         ; free space for locals
  pop EBP             ; restore old frame pointer
  ret paramsize       ; free parameter space and return.

ARM (A32)

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

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

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

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

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

Это соглашение о вызовах заставляет "типичную" подпрограмму 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 регистр общего назначения как:

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

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

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

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

PowerPC

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

MIPS

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

Для 64-битной версии чаще всего используется N64 ABI (не относящийся к Nintendo 64 ) от 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

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

System V ABI , что большинство современные Unix следует -как система, передает первые шесть аргументов в «в» регистрах% i0 через% i5, оставляя% i6 для указателя кадра и% i7 для обратного адреса.

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

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

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

     LA  1,ARGS      Load argument list address
     L   15,=A(SUB)  Load subroutine address
     BALR 14,15      Branch to called routine1
     ...
ARGS DC A(FIRST)     Address of 1st argument
     DC A(SECOND)
     ...
     DC A(THIRD)+X'80000000' Last argument2

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

SUB  EQU *            This is the entry point of the subprogram

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

     USING *,153
     STM 14,12,12(13) Save registers4
     ST  13,SAVE+4    Save caller's savearea addr
     LA  12,SAVE      Chain saveareas
     ST  12,8(13)
     LR  13,12
     ...

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

     L   13,SAVE+45
     LM  14,12,12(13)
     L   15,RETVAL6
     BR  14          Return to caller
SAVE DS  18F         Savearea7

Заметки:

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

В System / 390 ABI и z / Architecture ABI, используемых в 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.0 gcc Renesas
R0 Возвращаемые значения. Временно для расширения псевдо-инструкций сборки. Неявный источник / место назначения для 8/16-битных операций. Не сохранилось. Возвращаемое значение, вызывающий сохраняет Переменные / временные. Не гарантировано
R1..R3 Служит временными регистрами. Не сохранилось. Абонент сохранил царапину. Структурный адрес (по умолчанию сохранение звонящего) Переменные / временные. Не гарантировано
R4..R7 Первые четыре слова целочисленных аргументов. Область построения аргументов предоставляет пространство, в которое могут помещаться аргументы от R4 до R7. Не сохранилось. Передача параметров, вызывающий сохраняет Аргументы. Не гарантировано.
R8..R13 Служит постоянными регистрами. Сохранилось. Callee сохраняет Переменные / временные. Гарантированно.
R14 Указатель фрейма по умолчанию. (R8-R13 может также служить указателем кадра, а конечные подпрограммы могут использовать R1 – R3 как указатель кадра.) Сохраняется. Указатель кадра, FP, сохранение вызываемого абонента Переменные / временные. Гарантированно.
R15 Служит указателем стека или постоянным регистром. Сохранилось. Указатель стека, SP, сохранения вызываемого абонента Указатель стека. Гарантированно.

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

68 тыс.

Наиболее распространенное соглашение о вызовах для Motorola серии 68000 :

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

IBM 1130

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 subroutine example
     ENT  SUB        Declare "SUB" an external entry point
 SUB DC   0          Reserved word at entry point, conventionally coded "DC *-*"
 *                   Subroutine code begins here
 *                   If there were arguments the addresses can be loaded indirectly from the return addess
     LDX I 1 SUB     Load X1 with the address of the first argument (for example)
 ...
 *                   Return sequence
     LD      RES     Load integer result into ACC
 *                   If no arguments were provided, indirect branch to the stored return address
     B   I   SUB     If no arguments were provided
     END  SUB

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

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

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

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

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

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

PL / I

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

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

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

Внешние ссылки