Терминология сохранения вызовов / сохранения вызовов основана на довольно неэффективной модели программирования, в которой вызывающие абоненты действительно сохраняют / восстанавливают все регистры с вызовом вызовов (вместо того, чтобы хранить долгосрочные полезные значения в другом месте), и вызываемые абонентына самом деле сохраните / восстановите все регистры, сохраняющие вызовы (вместо того, чтобы просто не использовать некоторые или любой из них).
Или вы должны понимать, что «сохраненный вызывающим абонентом» означает «каким-то образом сохраненный , если *»1004 * вы хотите значение позже ".
В действительности эффективный код позволяет уничтожать значения, когда они больше не нужны.Компиляторы обычно создают функции, которые сохраняют несколько регистров с сохранением вызовов в начале функции (и восстанавливают их в конце).Внутри функции они используют эти регистры для значений, которые должны сохраняться при вызовах функций.
Я предпочитаю "сохраняемый вызов", а не "вызов-забитый" , которые однозначны и самостоятельны- описать, как только вы услышали об основной концепции, и не требовать серьезной умственной гимнастики, чтобы думать с точки зрения вызывающего или с точки зрения вызываемого.(Оба термина с точки зрения одинаковы ).
Кроме того, эти термины отличаются более чем на одну букву.
Термины энергозависимые / энергонезависимые довольно хороши, по аналогии с хранилищем, которое теряет свою ценность при потере мощности или нет (например, DRAM против Flash).Но ключевое слово C volatile
имеет совершенно иное техническое значение, поэтому при описании соглашений о вызовах в Си это недостаток «(non) -volatile».
- Call-clobbered, он же сохраненных вызывающих абонентов или volatile регистры хороши для временных / временных значений, которые не нужны после следующего вызова функции.
С точки зрения вызывающего, ваша функция может свободно перезаписывать (иначе говоря, clobber) эти регистры без сохранения / восстановления.
С точки зрения вызывающего абонента, call foo
уничтожает (так называемые clobbers) все регистры с закрытым вызовом или, по крайней мере,Вы должны предположить, что это так.
Вы можете написать частные вспомогательные функции, которые имеют пользовательское соглашение о вызовах, например, вы знаете, что они не изменяют определенный регистр.Но если все, что вы знаете (или хотите предположить или зависеть), это то, что целевая функция следует обычному соглашению о вызовах, тогда вы должны обрабатывать вызов функции так, как если бы она действительно уничтожала все регистры, вызывающие вызовы.Это буквально то, из-за чего пришло название: вызов блокирует эти регистры.
Некоторые компиляторы, которые выполняют межпроцедурную оптимизацию, могут также создавать определения функций только для внутреннего использования, которые не следуют ABI, используя пользовательскиесоглашение о вызовах.
- Сохранение вызовов , иначе сохранение вызовов или энергонезависимые регистры сохраняют свои значения по всемувызовы функций .Это полезно для переменных цикла в цикле, который выполняет вызовы функций, или вообще для чего-либо в неконечной функции в целом.
С точки зрения вызываемого, эти регистры не могут быть изменены, пока вы не сохранитеисходное значение где-то, чтобы вы могли восстановить его перед возвратом.Или для регистров, таких как указатель стека (который почти всегда сохраняется с сохранением вызовов), вы можете вычесть известное смещение и снова добавить его перед возвратом, вместо сохранения старого значения где-либо.т. е. вы можете восстановить его по безрассудным расчетам, если только вы не выделите переменное во время выполнения количество стекового пространства.Затем, как правило, вы восстанавливаете указатель стека из другого регистра.
Функция, которая может выиграть от использования большого количества регистров, может сохранять / восстанавливать некоторые регистры, сохраняемые при вызове, просто так, чтобы она могла использовать их в качестве большего количества временных файлов, даже если онане делает никаких вызовов функций.Обычно вы делаете это только после исчерпания регистров с ограниченным вызовом для использования, потому что сохранение / восстановление обычно стоит push / pop в начале / конце функции.(Или, если ваша функция имеет несколько путей выхода, в каждом из них pop
.)
Название «сохраненный вызывающим абонентом» вводит в заблуждение: у вас нет , чтобы специально сохранять / восстанавливать их.Обычно в вашем коде есть значения, которые должны выдерживать вызов функции в сохраняемых вызовах регистрах, или где-то в стеке, или в другом месте, откуда вы можете перезагрузить.Разрешить call
уничтожить временные значения нормально.
ABI или соглашение о вызовах определяет, какие именно
См., Например, Какие регистры сохраняются в Linux x86-64 вызов функции для x86-64 System V ABI.
Кроме того, регистры передачи аргументов всегда закрыты вызовом во всех соглашениях о вызовах функций, о которых я знаю.См. Сохраняются ли регистры вызывающих абонентов rdi и rsi или сохраняемые регистры вызываемых абонентов?
Но соглашения о вызовах системного вызова обычно обеспечивают сохранение всех регистров, кроме возвращаемого значения.(Обычно включает даже коды условий / флаги.) См. Каковы соглашения о вызовах для системных вызовов UNIX и Linux на i386 и x86-64