Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

1 курс 1 семестр / лекции / Лекция_5_Функции_и_стековая_память_П

.pdf
Скачиваний:
30
Добавлен:
27.12.2022
Размер:
1.02 Mб
Скачать

2.3. Функция diffofsums, сохраняющая оберегаемые регистры на стеке.

(на стеке сохраняет только регистр $s0, а не оберегаемые регистры не сохраняются.)

Код на языке ассемблера MIPS

# $s0 = result diffofsums:

addi $sp, $sp, −4 # выделение пространства на стеке для сохранения одного регистра.

sw $s0, 0($sp) # сохранениеаняемогоегистра

 

$s0 впамятистека.

add $t0, $a0, $a1

# $t0

= f + g

Выполнение вычислительных операций,

add $t1, $a2, $a3

# $t1

= h + i

sub $s0, $t0, $t1 # result = (f + g) − (h + i)

используя основную память компьютера

 

$v0

add $v0, $s0, $0

# сохранение вычисленнойвеличиныпамятикомпьютерана

lw $s0, 0($sp)

# возвратсох аняемогоегистра

 

$s0 изпамятистека.

addi $sp, $sp, 4 # возвратвыделенногопрост.екаранства

 

 

jr $ra

# возвратквызвафу.нкцииной

 

 

Сохраняемый регистр $s0 – выше указателя стека. Временные регистры: $t0, $t1, $v0 – ниже указателя стека. Вычисления, использующие временные результаты, обычно завершаются до того, как вызывается функция, поэтому эти регистры не оберегаются, а необходимость сохранять их в вызывающей функции возникает крайне редко.

Регистры: $v0–$v1, очевидно, не следует оберегать, потому что в них вызываемая функция возвращает свой результат.

$s0

$p

Функция, которая не вызывает другие функции, называется листовой, или

терминальной, функцией (англ.: leaf function); пример – функция diffofsums.

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

Вызывающая функция сохраняет любые не оберегаемые регистры ($t0–$t9 и $a0–$a3), значения которых будут нужны ей после вызова.

Вызываемая функция сохраняет любые оберегаемые регистры ($s0–$s7 и $ra), которые собирается изменять.

Рекурсивная функция – это не листовая функция, вызывающая сама себя. Функция вычисления факториала может быть реализована в виде рекурсивной функции:

factorial(n) = n х (n – 1) × (n – 2) ×… × 2 × 1. Функция factorial в рекурсивном представлении выглядит как

factorial(n) = n х factorial(n – 1).

Факториал от 1 – это просто 1.

Функция Factorial (n). При n=3 функция имеет вид factorial (3) = nx(n-1)x1=3x2x1

0x90

factorial: addi $sp, $sp, −8

# создание пространства на СТЕКЕ $sp вниз для двух регистров.

0x94

 

sw $a0, 4($sp)

# запись значения n из $a0 в память СТЕКА

 

0x98

 

sw $ra, 0($sp)

# запись регистра возврата $ra =0х94 в СТЕКследующий адрес за функцией

0x9C

 

addi $t0, $0, 2

# во временный регистр $t0 = 2 (запись значения n-1)

0xA0

 

slt $t0, $a0, $t0

# n <= 1 ? $t0 =1 если $a0>= $t0 иначе 0 определение условий проверки

0xA4

 

beq $t0, $0, else

# нет: переход в else

Проверка условий

0xA8

 

addi $v0, $0, 1

# ДА: запись 1 в регистр $v0

0xAC

 

addi $sp, $sp, 8

# возврат адрес указателя СТЕКА $sp

 

0xB0

else:

jr $ra

# возврат по регистру адреса возврата $ra= 0xBC

0xB4

addi $a0, $a0, −1

# n = n − 1 уменьшаем n =$a0 на 1

 

0xB8

 

jal factorial

# рекурсивной вызов функции factorial до тех пор, пока $a0>= $t0

0xBC

 

lw $ra, 0($sp)

# чтение $ra с адресом 0xBC или адресом 0x94 из СТЕКА, как адреса возврата

0xC0

 

lw $a0, 4($sp)

# чтение значения $a0 из СТЕКА

 

0xC4

 

addi $sp, $sp, 8

# возврат адреса указателя СТЕКА $sp перемещением на 8

0xC8

 

mul $v0, $a0, $v0

# n * factorial(n−1) (1) =3х2х1; вычисление значения функции

0xCC

 

jr $ra

# возврат в factorial: по адресу СТЕКА 0xBC или адресу 0x94

Программа работает в 3 этапа:

 

С т е к

С т е к

 

 

 

 

Адрес Данные

Адрес Данные

1 этап. Функция factorial изменяет регистры $a0 ( значение n) и $ra (адрес

?

?

возврата 0х94), поэтому она сохраняет их на стеке.

 

$a0=3

красные.

 

 

 

$ra=0x98

Функция Factorial (n). При n=3 функция имеет вид factorial (3) = nx(n-1)x1=3x2x1

0x90

factorial: addi $sp, $sp, −8

# создание пространства на СТЕКЕ $sp вниз для двух регистров.

0x94

 

sw $a0, 4($sp)

# запись значения n из $a0 в память СТЕКА

0x98

 

sw $ra, 0($sp)

# запись регистра возврата $ra =0х94 в СТЕК

0x9C

 

addi $t0, $0, 2

# во временный регистр $t0 = 2 (запись значения n-1)

0xA0

 

slt $t0, $a0, $t0

# n <= 1 ? $t0 =1 если $a0>= $t0 иначе 0 определение условий проверки

0xA4

 

beq $t0, $0, else

# нет: переход в else

Проверка условий

0xA8

 

addi $v0, $0, 1

# ДА: запись 1 в регистр $v0

0xAC

 

addi $sp, $sp, 8

# возврат адрес указателя СТЕКА $sp

0xB0

else:

jr $ra

# возврат по регистру адреса возврата $ra= 0xBC

0xB4

addi $a0, $a0, −1

# n = n − 1 уменьшаем n =$a0 на 1

 

0xB8

 

jal factorial

# рекурсивной вызов функции factorial до тех пор, пока $a0>= $t0

0xBC

 

lw $ra, 0($sp)

# чтение $ra с адресом 0xBC или адресом 0x94 из СТЕКА, как адреса возврата

0xC0

 

lw $a0, 4($sp)

# чтение значения $a0 из СТЕКА

 

0xC4

 

addi $sp, $sp, 8

# возврат адреса указателя СТЕКА $sp перемещением на 8

0xC8

 

mul $v0, $a0, $v0

# n * factorial(n−1) (1) =3х2х1; вычисление значения функции

0xCC

 

jr $ra

# возврат в factorial: по адресу СТЕКА 0xBC или адресу 0x94

Программа работает в 3 этапа:

2 этап. Проверка условия n<=2 при не выполнении его переход на метку else: изменение величины n ($a0=3-1=2), рекурсивный вызов factorial, изменение $ra=0хВС как адрес инструкции следующей за инструкцией вызова factorial. Возврат к функции factorial, что обеспечивает дальнейший рост СТЕКА и сохранение на стеке НОВЫХ значений $a0=2 и $ra = 0хВС. (См. рис стека).

При $a0=2 вновь переход на метку else,

изменение $a0=1. Возврат к функции factorial, что обеспечивает снова рост СТЕКА и сохранение на стеке НОВЫХ значений $a0=1 и повторяет $ra = 0хВС.

Этим завершается наполнение СТЕКА что обозначено стрелами синего цвета.

С т е к

С т е к

Адрес Данные

?

Адрес Данные

?

$a0=3

$a0=3

$ra=0х98

$ra=0х98

$a0=2

$a0=2

$ra=0хВС

$ra=0хВС

 

$a0=1

 

$ra=0хВС

Функция Factorial (n). При n=3 функция имеет вид factorial (3) = nx(n-1)x1=3x2x1

0x90

factorial: addi $sp, $sp, −8

# создание пространства на СТЕКЕ $sp вниз для двух регистров.

0x94

 

sw $a0, 4($sp)

# запись значения n из $a0 в память СТЕКА

 

0x98

 

sw $ra, 0($sp)

# запись регистра возврата $ra =0х94 в СТЕК

 

0x9C

 

addi $t0, $0, 2

# во временный регистр $t0 = 2 (запись значения n-1)

 

0xA0

 

slt $t0, $a0, $t0

# n <= 1 ? $t0 =1 если $a0>= $t0 иначе 0 определение условий проверки

0xA4

 

beq $t0, $0, else

# нет: переход в else

 

Проверка условий

0xA8

 

addi $v0, $0, 1

# ДА: запись 1 в регистр $v0

0xAC

 

addi $sp, $sp, 8

# возврат адрес указателя СТЕКА $sp

 

0xB0

else:

jr $ra

# возврат по регистру адреса возврата $ra= 0xBC

 

0xB4

addi $a0, $a0, −1

# n = n − 1 уменьшаем n =$a0 на 1

 

 

0xB8

 

jal factorial

# рекурсивной вызов функции factorial до тех пор, пока $a0>= $t0

0xBC

 

lw $ra, 0($sp)

# чтение $ra с адресом 0xBC или адресом 0x94 из СТЕКА, как адреса возврата

0xC0

 

lw $a0, 4($sp)

# чтение значения $a0 из СТЕКА

 

 

0xC4

 

addi $sp, $sp, 8

# возврат адреса указателя СТЕКА $sp перемещением на 8

0xC8

 

mul $v0, $a0, $v0

# n * factorial(n−1) (1) =3х2х1; вычисление значения функции

0xCC

 

jr $ra

# возврат в factorial: по адресу СТЕКА 0xBC или адресу 0x94

Программа работает в 3 этапа:

 

 

 

 

 

С т е к

 

3 этап. Третья проверка условия n<=2 – т.к. теперь $a0=1, что меньше числа

Адрес Данные

$v0=6

регистре $t0=2, то выполняется переход к оператору addi $v0, $0, 1, уменьшение

?

положения указателя СТЕКА на 2 ячейки (addi $sp, $sp, 8 ) и переход по адресу

 

возврата ($ra = 0хВС) на адрес 0хВС. Чтение из СТЕКА значений $ra = 0хВС и

$a0=3

$a0=3

$a0=2, уменьшение СТЕКА еще на 2 ячейки. Вычисление функции в регистре

$ra=0х94

$v0=6

$v0 вновь переход на адрес возврата $ra = 0хВС. Чтение первого адреса

$a0=2

$a0=2

возврата $ra = 0х94 т.к. СТЕК уменьшился до

этого

адреса возврата и нового

значения $a0=3. Вычисление нового значения функции в регистре $v0 =3х2=6 и

$ra=0хВС $v0=2

возврат к начальному адресу верхушки СТЕКА, определяемый адресом

$a0=1

$a0=1

программы

0х94. Этим завершается

выполнение функции

factorial,

что

$ra=0хВС $v0=1

обозначено

стрелами красного цвета

(повторное обращение

к адресу

0хВС

 

 

показано пунктирными стрелами). Толстая красная стрелка выход на исходное значение СТЕКА с вычисленным результатом функции.

Функция Factorial (n). При n=3 функция имеет вид factorial (n=3) = nx(n-1)x1=3x2x1

0x90

factorial: addi $sp, $sp, −8

# создание пространства на СТЕКЕ $sp вниз для двух регистров.

0x94

 

sw $a0, 4($sp)

# запись значения n из $a0 в память СТЕКА

0x98

 

sw $ra, 0($sp)

# запись регистра возврата $ra =0х94 в СТЕК

0x9C

 

addi $t0, $0, 2

# во временный регистр $t0 = 2 (запись значения n-1)

0xA0

 

slt $t0, $a0, $t0

# n <= 1 ? $t0 =1 если $a0>= $t0 иначе 0 определение условий проверки

0xA4

 

beq $t0, $0, else

# нет: переход в else

Проверка условий

0xA8

 

addi $v0, $0, 1

# ДА: запись 1 в регистр $v0

0xAC

 

addi $sp, $sp, 8

# возврат адрес указателя СТЕКА $sp

0xB0

else:

jr $ra

# возврат по регистру адреса возврата $ra= 0xBC

0xB4

addi $a0, $a0, −1

# n = n − 1 уменьшаем n =$a0 на 1

 

0xB8

 

jal factorial

# рекурсивной вызов функции factorial до тех пор, пока $a0>= $t0

0xBC

 

lw $ra, 0($sp)

# чтение $ra с адресом 0xBC или адресом 0x94 из СТЕКА, как адреса возврата

0xC0

 

lw $a0, 4($sp)

# чтение значения $a0 из СТЕКА

 

0xC4

 

addi $sp, $sp, 8

# возврат адреса указателя СТЕКА $sp перемещением на 8

0xC8

 

mul $v0, $a0, $v0

# n * factorial(n−1) (1) =3х2х1; вычисление значения функции

0xCC

 

jr $ra

# возврат в factorial: по адресу СТЕКА 0xBC или адресу 0x94

Программа работает в 3 этапа:

1 этап. Функция factorial изменяет регистры $a0 ( значение n) и $ra (адрес возврата), поэтому она сохраняет их на стеке.

2 этап. Проверка условия n<=2 при выполнении его переход на метку else: изменение $a0=3-1=2, рекурсивный вызов factorial, изменение $ra=0хВС как адрес инструкции следующей за инструкцией вызова factorial. Возврат к функции factorial, что обеспечивает дальнейший рост СТЕКА и сохранение на стеке НОВЫХ значений $a0=2 и $ra = 0хВС. При $a0=2 вновь переход на метку else, изменение $a0=1. Возврат к функции factorial, что обеспечивает снова рост СТЕКА и сохранение на стеке НОВЫХ значений $a0=1 и повторяет $ra = 0хВС. Этим завершается наполнение СТЕКА что обозначено стрелами синего цвета.

3 этап. Третья проверка условия n<=2 – т.к. теперь $a0=1, что меньше числа регистре $t0=2, то выполняется переход к оператору addi $v0, $0, 1, уменьшение положения указателя СТЕКА на 2 ячейки (addi $sp, $sp, 8 ) и переход по адресу возврата ($ra = 0хВС) на адрес 0хВС. Чтение из СТЕКА значений $ra = 0хВС и $a0=2, уменьшение СТЕКА еще на 2 ячейки. Вычисление функции в регистре $v0 вновь переход на адрес возврата $ra = 0хВС. Чтение нового адреса возврата $ra = 0х94 и нового значения $a0=3. Вычисление нового значения функции в регистре $v0 =3х2=6 и возврат к начальному адресу верхушки СТЕКА, определяемый адресом программы 0х94. Этим завершается выполнение функции factorial, что обозначено стрелами красного цвета (повторное обращение к адресу 0хВС показано пунктирными стрелами). Толстая красная стрелка выход на исходное значение СТЕКА с вычисленным результатом функции.

ВЫВОДЫ: 1-й блок (1и 2 этапы) – заполнение СТЕКА требуемыми для вычисления значениями функции и увеличение СТЕКА - стрелы синие. 2-й блок ( 3 этап) рекурсивное вычисление функции значениями из СТЕКА и уменьшение СТЕКА – стрелы красные.

С т е к

Адрес Данные

С т е к

С т е к

 

 

?

Адрес Данные

Адрес Данные

?

?

 

 

 

 

$a0=3

$a0=3

 

$ra=0х98

$ra=0х98

 

 

$a0=2

 

 

$ra=0хВС

С т е к

С т е к

 

Адрес Данные

 

Адрес

Данные

 

 

 

?

 

 

$v0=6

 

?

 

$a0=3

 

 

 

$a0=3 $a0=3

$ra=0х98

 

 

$ra=0х98 $v0=6

$a0=2

 

$a0=2

$a0=2

 

 

$ra=0хВС

 

 

 

$ra=0хВС $v0=2

$a0=1

 

$a0=1

$a0=1

$ra=0хВС

 

 

 

$ra=0хВС $v0=1

Блок 1: этпа1 и этап2

Блок 2: этпа3

Стек во время формирования памяти переменных для вычислений функции

Вывод по стеку: после вычислений и возврата функцией factorial управление вызвавшей ее функции, указатель стека $sp находится в своём изначальном положении (0xFC), содержимое стека выше значения указателя стека не менялось и все оберегаемые регистры содержат свои изначальные значения. Регистр $v0 содержит результат вычисления ($v0=6).

Изменение стека при вычислении значения функции, которое сохраняется в регистре $v0

Алгоритм работы программы функции factorial (n=3) = nx(n-1)x1=3x2x1

Вычисленное значения функции factorial и запись ее в регистр v0

Освобождение пространства памяти Стека

ra1

 

Значение

ra2

 

адреса ra ?

 

Возврат по адресу ra

прочитанному из стека

Вычисление значения функции factorial и запись ее в регистр v0

Уменьшение пространства памяти Стека на два регистров

Чтение значения аргумента n функции из Стека

Вызов функции factorial

Увеличение пространства памяти Стека для двух регистров

Заполнение регистра стека значением аргумента n

Заполнение регистра стека значением адреса возврата ra функции

Подготовка условий контроля значения n (запись значения 2 в $t0)

Выполнение условия n <= 1 ?

Блок 1

Изменение значения адреса возврата ra2 функции

Вызов функции factorial в factorial

нет

Изменение значения n

да

 

Запись в регистр v0 единицы v0=1

 

 

Блок 2

 

 

 

 

 

 

 

 

 

 

Уменьшение пространства

 

 

 

 

 

 

 

 

 

Вызов функции factorial в factorial

 

 

памяти Стека на два регистров

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Чтение значения адреса

 

 

 

 

 

 

возврата ra функции из Стека

 

 

 

адреса возврата ra функции