1 курс 1 семестр / лекции / Лекция_5_Функции_и_стековая_память_П
.pdf2.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 функции |
|
|
|
|
|
|
|
|
|
|