Соглашение о вызовах Windows x64 разработано, чтобы упростить реализацию переменных функций (таких как printf и scanf) путем сброса 4 регистровых аргументов в теневое пространство, создавая непрерывный массив всех аргументов. Аргументы, превышающие 8 байтов, передаются по ссылке, поэтому каждый аргумент всегда занимает ровно 1 слот, передающий аргументы.
Учитывая это конструктивное ограничение, для большего количества аргументов регистра потребуется больше теневого пространства, что тратит больше места в стеке для небольших функций, у которых мало аргументов.
Да, большее количество аргументов регистра обычно будет более эффективным. Но если вызываемый объект хочет сразу же вызвать другой вызов функции с другими аргументами, он должен будет сохранить все свои аргументы регистров в стеке, поэтому существует ограничение на количество полезных аргументов регистров.
Вы хотите хорошее сочетание регистров с сохранением и с сохранением вызовов, независимо от того, сколько их используется для передачи аргументов . R10 и R11 - засоренные регистры нуля. Прозрачная функция-обертка, написанная на asm, может использовать их для создания пустого пространства, не нарушая ни одного из аргументов в RCX, RDX, R8, R9, и без необходимости сохранять / восстанавливать регистр, сохраняемый при вызове в любом месте.
R12..R15 - это регистры, сохраняемые при вызове, которые вы можете использовать для чего угодно, до тех пор, пока вы сохраните / восстановите их до возвращения.
Или, если мы можем сделать это в пользовательских функциях
Да, вы можете свободно составлять свои собственные соглашения о вызовах при вызове из asm в asm с учетом ограничений, налагаемых ОС. Но если вы хотите, чтобы исключения могли разматывать стек от до такого вызова (например, если одна из дочерних функций перезванивает в некоторый C ++, который может генерировать), вы должны следовать большему количеству ограничений, таких как создание раскрутить метаданные. Если нет, вы можете сделать почти все.
См. Мое Выберите соглашение о вызовах, чтобы расположить аргументы там, где вы хотите. ответ на вопросы и ответы CodeGolf "Советы по игре в гольф в машинном коде x86 / x64".
Вы также можете вернуться в любой регистр (ы) и вернуть несколько значений. (Например, функция asm strcmp
или memcmp
может возвращать разницу - / 0 / + в несоответствии в EAX, и возвращают позицию несоответствия в RDI, поэтому вызывающая сторона может использовать один или оба. )
Полезным упражнением при оценке дизайна является сравнение его с другими фактическими или возможными проектами
Для сравнения, x86-64 System V ABI передает первые 6 целочисленных аргументов в регистрах, и первые 8 аргументов FP в XMM0..7 . (Windows x64 передает 5-й аргумент в стеке, даже если это FP и первые 4 аргумента были целыми числами.)
Таким образом, другое основное соглашение о вызовах x86-64 использует больше регистров передачи аргументов. Он не использует теневое пространство; он определяет красную зону под RSP, которая защищена от асинхронного замыкания. Маленькие конечные функции могут по-прежнему избегать манипулирования RSP для резервирования пространства.
Забавный факт: R10 и R11 также являются не проходящими через аргументы регистрами с перекрытыми вызовами в x86-64 SysV. Интересный факт # 2: syscall
уничтожает R11 (и RCX), поэтому Linux использует R10 вместо RCX для передачи аргументов системным вызовам, но в остальном использует то же соглашение о передаче регистра-аргумента, что и при вызовах функций пользовательского пространства.
См. Также Почему Windows64 использует соглашение о вызовах, отличное от всех других операционных систем на x86-64? для дополнительной информации и предположений о том, почему Microsoft сделала выбор дизайна, который они сделали с их соглашением о вызовах.
x86-64 System V усложняет реализацию переменных функций (больше кода для индексирования аргументов), но они, как правило, встречаются редко. Большая часть кода не является узким местом при пропускной способности sscanf
. Теневое пространство обычно хуже, чем красная зона. Исходное соглашение Windows x64 не передает векторные аргументы (__m128
) по значению, поэтому существует второе 64-разрядное соглашение о вызовах в Windows, называемое vectorcall
, которое позволяет использовать эффективные векторные аргументы. (Обычно это не имеет большого значения, потому что большинство функций, которые принимают векторные аргументы, являются встроенными, но функции математической библиотеки SIMD выиграют.)
Передача большего количества аргументов в нижние 8 (оригинальные регистры rax..rdi, для которых не требуется префикс REX), и наличие большего количества регистров с закрытыми вызовами, которые не требуют префикса REX, вероятно, хорошо для кода размер в коде, который достаточно встроен, чтобы не вызывать огромное количество вызовов функций. Вы могли бы сказать, что выбор Window для сохранения большего количества регистров не-REX для сохранения вызовов лучше для кода с циклами, содержащими вызовы функций, но если вы делаете много вызовов функций для коротких вызываемых, то они выиграют от большего незаполненные регистры, не требующие префиксов REX. Интересно, сколько мыслей MS вложило в это, или они просто сохраняли сходство с 32-битными соглашениями о вызовах при выборе того, какой из регистров с низким 8 будет сохранен для вызова.
Одна из слабостей x86-64 System V заключается в отсутствии сохраняемых вызовов регистров XMM. Таким образом, любой вызов функции требует разливания / перезагрузки любых переменных FP. Иметь пару, например, младшие 128 или 64 бита xmm6 и xmm7, было бы неплохо.