язык ассемблера в вызове функции [дубликат]

Возможно, вы ищете x86_64 ABI?

Если это не совсем то, что вам нужно, используйте «x86_64 abi» в своей предпочтительной поисковой системе, чтобы найти альтернативные ссылки.

2
задан Trillian 1 May 2012 в 03:35
поделиться

3 ответа

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

4
ответ дан hobbs 26 August 2018 в 02:48
поделиться

Преимущество наличия регистров nonvolatile: производительность.

Чем меньше данных перемещается, тем эффективнее процессор.

Чем больше регистров volatile, тем больше больше энергии требуется процессору.

0
ответ дан ncomputers 26 August 2018 в 02:48
поделиться

Соглашение о вызове Windows x86-64 с только 6 списками xmm с записью с записью не очень хорошее, вы правы. Большинство SIMD (и многих скалярных FP) циклов не содержат каких-либо вызовов функций, поэтому они ничего не получают от того, что их данные хранятся в сохраняемых вызовах. Сохранение / восстановление - это чистый недостаток, потому что это редкость, чем любой из их вызывающих пользователей использует это энергонезависимое состояние.

В x86-64 System V все векторные регистры сбрасываются по вызову, что может быть, слишком далеко. Во многих случаях было бы хорошо сохраниться 1 или 2 вызова, особенно для кода, который вызывает некоторые вызовы функций библиотеки. ( Используйте gcc -fno-math-errno, чтобы упростить простые строки , иногда единственная причина, по которой они не делают, - это установить errno на NaN.)

Связано: , как было выбрано соглашение о вызове x86-64 SysV : просмотр размера кода и подсчета команд для gcc-компиляции SPECint / SPECfp.


Для целых регистров, имеющих некоторые из них, определенно хорошо, и все «обычные» соглашения о вызовах (для всех архитектур, а не только x86) действительно имеют микс. Это уменьшает общий объем проделанной работы, выполняемой при размножении / восстановлении в вызываемых и вызываемых абонентах.

Заставляя вызывающего абонента разливать / перезагружать все вокруг каждого вызова функции не подходит для размера кода или производительности. Сохранение / восстановление некоторых сохраняемых вызовов regs в начале / конце функции позволяет не-листовым функциям сохранять некоторые вещи в регистре через call s.

Рассмотрим некоторый код, который вычисляет пару вещей, а затем cout << "result: " << a << "foo" << b*c << '\n'; Это 4 вызова функций std::ostream operator<<, и они обычно не встроены. Сохранение адреса cout и местных жителей, которые вы просто вычислили в энергонезависимых регистрах, означает, что вам нужны только некоторые дешевые mov reg,reg инструкции для настройки аргументов для следующего вызова. (Или push в соглашении о вызове stack-args).

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

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


Еще одна причина для регистры с затухающими звонками - это то, что обычно некоторые из ваших значений «мертвы» после вызова функции: вам нужны только их как аргументы функции. Вычисление их в списках, записанных с помощью call-clobbered, означает, что вам не нужно сохранять / восстанавливать что-либо, чтобы освободить эти регистры, но также и то, что ваш собеседник также может свободно их использовать. Это еще лучше при вызове соглашений, которые передают args в регистры: вы можете вычислять свои входы непосредственно в регистры, проходящие через arg. (И скопируйте все в защищенные вызовом регистры или проливайте их на стеке памяти, если они вам также понадобятся после этой функции.)

(Мне нравятся термины, сохраненные по вызову, по сравнению с call-clobbered, спасенный или спасенный. Последние термины подразумевают, что кто-то должен сохранять регистры вместо того, чтобы просто позволить мертвым значениям умереть. volatile / non-volatile не плохо, но эти термины также имеют другие технические значения как ключевые слова C или в терминах вспышки против DRAM.)

0
ответ дан Peter Cordes 26 August 2018 в 02:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: