Сохранены ли регистры rdi и rsi?
Из википедии x86 соглашение о вызовах говорит, что для Microsoft x64 соглашение о вызовах:
Регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14 и R15 считаются энергонезависимыми (сохраненными вызываемыми).
Но для System V AMD64 ABI:
Если вызываемый абонент желает использовать регистры RBX, RBP и R12–R15, он должен восстановить их исходные значения, прежде чем вернуть управление вызывающей стороне.
Там ничего не упоминалось о RDI и RSI.
Я также читал, что % rax,% rcx,% rdx, % rdi,% rsi,% rsp и%r8-r11 считаются регистрами сохранения вызовов (из PDF-файла)
Мой вопрос заключается в том, является ли соглашение о вызовах различным для разных платформ?(Я пытаюсь написать некоторую функцию libc в asm для среды unix)
Я не смог найти ни одной статьи, обсуждающей эту тему, ресурсы по этой теме также будут полезны. Я хотел знать преимущества и недостатки этих конвенций.
1 ответ
Да, во всех соглашениях о вызовах функций, о которых я знаю, регистры прохождения аргументов перекрываются вызовами. (За исключением соглашений о вызовах системного вызова, где обычно сохраняются все регистры, кроме возвращаемого значения, включая передачу аргументов. За исключением этого x86-64 syscall
уничтожает RCX и R11...)
В частности, в x86-64 System V все регистры, отличные от RBX, RBP, RSP и R12-R15, являются замкнутыми. (Это включает регистры xmm0-15, x87/mmx и регистры маски AVX512 zmm0-31 и k0-k7.)
Какие регистры сохраняются при вызове функции linux x86-64, показывает таблицу из документа ABI.
Соглашение о вызовах / ABI определяет статус регистров как сохраненный или закрытый. Различные соглашения могут сделать разные выборы.
И да, Microsoft Windows выбрала другое соглашение о вызовах от всех остальных: почему Windows64 использует другое соглашение о вызовах из всех других ОС на x86-64? В Windows x64 RDI сохраняется, как и в большинстве 32-битных соглашений о вызовах.
Но в x86-64 System V разработчики выбрали регистры с нуля и (как показывает мой ответ на этот связанный вопрос) обнаружили, что использование RDI и RSI для первых двух сохраненных инструкций args (при сборке SPECint с ранним портом x86-64 GCC). Вероятно, потому что GCC в то время любил встроенный memset
или же memcpy
с помощью rep stosd
или реализация библиотеки использовала это.
(Не имеет смысла говорить, что RDI изначально закрыт с помощью вызовов, ISA x86-64 не определяет это. Это зависит от каждой платформы.)
Терминология:
Я ненавижу термины "сохраненный вызывающий абонент" и "сохраненный вызываемый": это сбивает с толку думать с двух разных точек зрения (вызывающий и вызываемый) и ошибочно подразумевает, что каждый регистр действительно сохраняется где-то на каждом call
, Кроме того, имена отличаются только на 1 букву, поэтому не очень визуально различимы при чтении.
"сохраненный" или "забитый" велики; они работают с любой точки зрения. (Что звонящий будет делать с вашими регами, или что вы можете делать с регами звонящего.) Более того, они говорят сами за себя.