Подтвердить что ты не робот

Что такое регистры, на которые зарегистрированы вызываемые и вызываемые абоненты?

У меня возникли проблемы с пониманием разницы между списками звонящего и вызываемого абонента и когда использовать что.

Я использую MSP430

процедура:

mov.w #0,R7 
mov.w #0,R6 
add.w R6,R7 
inc.w R6 
cmp.w R12,R6 
jl l$loop 
mov.w R7,R12
ret

вышеуказанный код является вызываемым и использовался в примере учебника, поэтому он следует за соглашением. R6 и R7 сохранены, а R12 сохранен. Я понимаю, что сохраненные реестры не являются "глобальными" в том смысле, что изменение его значения в процедуре не повлияет на его значение вне процедуры. Вот почему вам нужно сохранить новое значение в реестре вызываемого абонента в начале.

R12, сохраненный звонящий является "глобальным", из-за отсутствия лучших слов. То, что делает процедура, оказывает продолжительное влияние на R12 после вызова.

Правильно ли я понимаю? Я пропустил другие вещи?

4b9b3361

Ответ 1

Регистры абонентов сохраняемых (AKA Летучие регистры, или колл-затерт) используются для хранения временных величин, которые не должны быть сохранены между вызовами.

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

Впрочем, нормально разрешать call уничтожать временные значения в этих регистрах.

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

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

Ответ 2

Callee vs caller saved - это соглашение о том, кто несет ответственность за сохранение и восстановление значения в регистре по вызову. ВСЕ регистры являются "глобальными", поскольку любой код в любом месте может видеть (или изменять) регистр, и эти изменения будут видны каким-либо более поздним кодом в любом месте. Соглашением о сохранении регистров является то, что код не должен изменять некоторые регистры, поскольку другой код предполагает, что значение не изменяется.

В вашем примере кода NONE из регистров записывается с сохранением, поскольку он не пытается сохранить или восстановить значения регистра. Однако, похоже, это не целая процедура, так как она содержит ветвь с меткой undefined (l$loop). Таким образом, это может быть фрагмент кода с середины процедуры, которая обрабатывает некоторые регистры как спасение вызываемого абонента; вам просто не хватает инструкций сохранения/восстановления.

Ответ 3

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

Или вы должны понимать, что "сохраненный вызывающим абонентом" означает "каким-то образом сохраненный, если вы хотите значение позже".

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

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

Кроме того, эти термины отличаются более чем на одну букву.

Термины volatile/non -volatile довольно хороши, по аналогии с хранилищем, которое теряет свою ценность при потере мощности или нет (например, DRAM против Flash). Но ключевое слово C volatile имеет совершенно другое техническое значение, так что при описании соглашений о вызовах языка Си есть недостаток "(не) -volatile".


  • Регистры с блокировкой вызовов, иначе называемые сохраненными вызывающими или изменчивыми регистрами хороши для временных/временных значений, которые не нужны после следующего вызова функции.

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

С точки зрения вызывающего абонента, call foo уничтожает (иначе говоря, clobbers) все регистры с закрытым вызовом, или, по крайней мере, вы должны предположить, что это так.

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

Некоторые компиляторы, которые выполняют межпроцедурную оптимизацию, могут также создавать определения функций только для внутреннего использования, которые не следуют ABI, используя пользовательское соглашение о вызовах.

  • Регистры с сохраненными вызовами, иначе как с сохраненными вызовами или не -volatile, сохраняют свои значения в вызовах функций. Это полезно для переменных цикла в цикле, который выполняет вызовы функций, или вообще для чего-либо в неконечной функции в целом.

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

Функция, которая может извлечь выгоду из использования большого количества регистров, может сохранять/восстанавливать некоторые регистры, сохраняемые при вызове, просто для того, чтобы она могла использовать их как большее количество временных файлов, даже если она не выполняет никаких вызовов функций. Обычно вы делаете это только после исчерпания регистров с ограниченным вызовом для использования, потому что сохранение/восстановление обычно стоит push/pop в начале/конце функции. (Или, если ваша функция имеет несколько путей выхода, в каждом из них появляется pop.)


Название "сохраненный абонентом" вводит в заблуждение: вам не нужно специально сохранять/восстанавливать их. Обычно в вашем коде есть значения, которые должны выдерживать вызов функции в сохраняемых вызовах регистрах, или где-то в стеке, или в другом месте, откуда вы можете перезагрузить. Это нормально, чтобы позволить call уничтожить временные значения.


ABI или соглашение о вызовах определяет, какие

Посмотрите, например, Какие регистры сохраняются посредством вызова функции linux x86-64 для ABI System V x86-64.

Кроме того, регистры передачи аргументов всегда закрыты во всех соглашениях о вызовах функций, о которых я знаю. См. Сохранены ли регистры вызовов rdi и rsi или сохранены ли вызываемые регистры?

Но соглашения о вызовах системного вызова обычно делают все регистры, кроме возвращаемого значения, сохраняемым вызовом. (Обычно включает даже коды условий/флаги.) См. Каковы соглашения о вызовах для системных вызовов UNIX и Linux на i386 и x86-64

Ответ 4

сохранение вызываемого абонента register- локальные переменные и долговременные переменные сохранение вызывающего абонента register-

Ответ 5

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