Команды вызова подпрограммы call и возврата return



Одно из главных назначений аппаратного стека — поддержка вызовов подпрограмм. При вызове подпрограммы надо сохранить адрес возврата, чтобы подпрограмма могла по окончанию своей работы вернуть управление вызвавшей ее программе. В старых архитектурах, в которых аппаратный стек отсутствовал (например, в компьютерах IBM 360/370), точки возврата сохранялись в фиксированных ячейках памяти для каждой подпрограммы. Это делало невозможнойрекурсию, т.е. повторный вызов той же подпрограммы непосредственно из ее текста или через цепочку промежуточных вызовов, поскольку при повторном вызове старое содержимое ячейки, хранившей адрес возврата, терялось/

Во всех современных архитектурах точка возврата сохраняется в аппаратном стеке, что делает возможным рекурсию, а также параллельное выполнение нескольких легковесных процессов (нитей). Для вызова подпрограммы ƒ служит команда call, которая осуществляет переход к подпрограмме ƒ (т.е. присваивает регистру PC адрес ƒ) и одновременно помещает старое содержимое регистра PC в стек:

call ƒ ~ push PC;        PC:= ƒ;

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

Для возврата из подпрограммы используется команда return. Она извлекает из стека адрес возврата и помещает его в регистр PC:

return ~ pop PC;

Аппаратный стек и локальные переменные подпрограммы

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

Различные нити работают параллельно над общими статическими данными, совершая таким образом некоторую совместную работу. При этом одна и та же подпрограмма может вызываться из разных нитей. В отличие от статических переменных, которые являются общими для всех нитей, для каждой нити выделяется свой отдельный стек. При использовании нитей очень важно, чтобы локальные переменные подпрограммы располагались в стеке. Иначе было бы невозможно параллельно вызывать одну и ту же подпрограмму из разных нитей: повторный вызов подпрограммы, уже работающей в рамках другой нити, разрушил бы статический набор локальных переменных этой подпрограммы. А при использовании стека наборы локальных данных одной и той же подпрограммы, вызываемой из разных нитей, различны, поскольку они располагаются в разных стеках. Таким образом, разные нити работают с разными наборами локальных переменных, не мешая друг другу.

Рассмотрим более подробно, как размещаются локальные переменные подпрограммы в стеке, на примере языка Си. В Си подпрограммы называются функциями. Функция может иметь аргументы и локальные переменные, т.е. переменные, существующие только в процессе выполнения функции. Рассмотрим для примера функцию ƒ, зависящую от двух входных аргументов x и y целого типа, в которой используются три локальные переменные a, b и c также целого типа. Функция возвращает целое значение.

int f(int x, int y) { int a, b, c; ...}Пусть в некотором месте программы вызывается функция ƒ с аргументами x = 222, y = 333: z = f(222, 333);

Вызывающая программа помещает фактические значения аргументов x и y функции ƒ в стек, при этом на вершине стека лежит первый аргумент функции, под ним — второй аргумент. Вызов функции транслируется в следующие команды:

push 333push 222call ƒ

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

В момент начала работы функции ƒ cтек имеет следующий вид:

адрес возврата

<=SP

222
333
....

На вершине стека лежит адрес возврата, под ним — фактическое значение аргумента x, затем фактическое значение аргумента y.

Перед началом работы функция ƒ должна захватить в стеке область памяти под свои локальные переменные a, b, c. В языке Си принято следующее соглашение: адрес блока локальных переменных функции в момент ее работы помещается в специальный регистр процессора, который называется FP, от англ. Frame Pointer — указатель кадра. (В процессоре Intel 80386 роль указателя кадра выполняет регистр EBP.) В первую очередь функция ƒ сохраняет в стеке предыдущее значение регистра FP. Затем значение указателя стека копируется в регистр FP. После этого функция ƒ захватывает в стеке область памяти размером в 3 машинных слова под свои локальные переменные a, b, c. Для этого функция ƒ просто уменьшает значение регистра SP на 12 (три машинных слова равны двенадцати байтам). Таким образом, начало функции ƒ состоит из следующих команд:

push FPFP := SPSP := SP − 12

После захвата кадра локальных переменных стек выглядит следующим образом.

c

<=SP

b
a
старое значение FP

<=FP

адрес возврата
x=222
y=333
...

Аргументы и локальные переменные функции ƒ адресуются относительно регистра FP. Так, аргумент x имеет адрес FP+8, аргумент y - адрес FP+12. Переменная a имеет адрес FP-4, переменная b - адрес FP-8, переменная c - адрес FP-12.По окончании работы функция ƒ сначала увеличивает указатель стека на 12, удаляя таким образом из стека свои локальные переменные a, b, c. Затем старое значение FP извлекается из стека и помещается в FP (таким образом, регистр FP восстанавливает свое значение до вызова функции ƒ). После этого осуществляется возврат в вызывающую программу: адрес возврата снимается со стека и управление передается по адресу возврата. Результат функции ƒ передается через нулевой регистр.

R0 := результат функцииSP := SP +12pop FPreturn

Вызывающая программа удаляет из стека фактические значения аргументов x и y, помещенные в стек перед вызовом функции ƒ.


Дата добавления: 2018-05-30; просмотров: 349; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!