MIPS编程

1.各部分寄存器的功能

  • $t0 ~ $t9: 临时变量 (调用者保存,否则容易丢失)
  • $s0 ~ $s7: 保存变量(被调用者保存)
  • $0 :常量0,存不了数据
  • $at : 保留给汇编器(不可以随便用)
  • $v0 ~ $v1 : 函数调用返回值
  • $a0 ~ $a3 : 函数调用参数
  • $sp : 栈指针
  • $ra : 返回地址,用于子程序的调用

2.调用子过程时对于s,t寄存器的操作

1.被调用者维护s寄存器

​ 对于s寄存器而言,被调用者需要保证s寄存器中的值在调用前后不能发生改变, 实际操作中,如果想要编写一个子函数,那么在这个子函数中使用的所有s寄存器,都必须要在函数的开头入栈、在函数的结尾出栈,确保s寄存器的值在函数调用前后不会发生变化。

2.调用者维护t寄存器

​ 对于t寄存器,编写子函数中用到t寄存器的地方无需做任何保存,维护t寄存器是上层函数(调用者维护),调用者将t寄存器压入栈中,函数调用结束之后再弹回来,只需要借助$sp指针。

需要注意的是,对于s,t寄存器的维护都要通过在数据区开辟栈空间来实现!

3.调用关键字

  • jal:跳转到子过程

  • jr:跳转到父过程

4.栈的使用

  1. 过程自身需要满足栈的结构
  2. 过程调用子过程时需满足栈的结构
  3. 子过程执行前后移动栈指针 $sp
  4. MIPS中栈由高地址向低地址延申,即优先使用高地址,父过程栈帧高,子过程栈帧低
子过程的栈帧图
  • 高地址 临时变量
  • ​ 返回地址
  • ​ 需要保存的寄存器
  • ​ 其他变量
  • 低地址 参数0~3,传给子子过程($a0~$a3)

叶子函数:可以省去参数和返回地址(无子子函数)

栈的具体使用
  1. 计算好栈帧大小 即保存这些变量需要的字节大小
  2. 栈指针减少表示向低地址移动,栈指针增加表示向高地址移动
  3. 栈指针始终指向栈顶,栈指针初始时在高地址
  4. 过程开始时分配栈空间 addiu $sp,$sp,-32(需要32字节,将栈指针向低地址移动32字节)
  5. 过程结束时回收栈空间 addiu $sp,$sp,32
  6. 以栈指针为基址进行存取 sw $t0,24($sp) ,偏移量的单位是字节 偏移量为正向高地址偏移,偏移量为负向低地址偏移

5.nop延时槽

在MIPS编程中,延时槽是指指令执行的时间间隔。当某个条件分支指令(如条件跳转、函数调用等)的目标指令紧跟在该分支指令后面时,需要插入一个延时槽指令,以免出现错误的指令结果,常用```nop```.

细说:条件分支指令的目标指令紧跟在该分支指令后面:意味着这个目标指令会在条件分支指令执行之后立即执行。

beq $t0, $t1, label #条件分支指令
add $t2, $t3, $t4   #目标指令紧跟在条件分支指令之后

增加nop

beq $t0, $t1, label 
nop
add $t2, $t3, $t4   

延迟槽的作用是保持流水线的同步,当条件分支指令的目标指令紧跟在该分支指令后面时,由于流水线执行的并行性,目标指令可能会在分支指令判断之前就开始执行。为了避免这种错误的发生,增加延迟槽指令来保证分支指令正确判断。

6.流水线执行

流水线执行是指一种通过将指令的执行过程拆分为多个阶段并以并行的方式执行,从而提高指令执行效率,每一个阶段处理特定的任务,指令每次在不同阶段执行。我们使用的MIPS架构是一种采用了流水线执行的指令集架构。

MIPS架构中,一般将指令的执行过程划分为取指令、译码、执行、访存、写回等阶段。每条指令都会经过上述过程最后完成执行,MIPS流水线中,多条指令可以处于不同的阶段,这样就可以利用处理器的并行性提高执行速度。

​ 延迟槽是在流水线执行中为了解决指令间可能产生的数据冲突或分支预测错误等问题而引入的技术。延迟槽是指在分支指令之后执行的一条指令,它位于分支指令之后但在实际分支发生之前。延迟槽的作用是填充流水线中由于分支发生而产生的空槽,使流水线保持正确的执行状态。在延迟槽中可以插入与分支指令无关的指令,以充分利用流水线的吞吐能力。

7.分支-循环模板

1.分支模板
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.text
li $t1, 100             #t1 = 100
li $t2, 200             #t2 = 200
slt $t3, $t1, $t2       #if(t1 < t2) t3 = 1 
beq $t3, $0, if_1_else
nop
#do something
j if_1_end              #jump to end
nop
if_1_else:
#do something else

if_1_end:
li $v0, 10
syscall
2.循环模板
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.text
li $t1, 100             #n = 100
li $t2, 0               #i

for_begin1:             #for (int i = 0; i < n; i++)
slt $t3, $t2, $t1       #{
beq $t3, $0, for_end1  
nop        
#do something
addi $t2, $t2, 1        #i++
j for_begin1
nop                     #}    

for_end1:
li $v0, 10
syscall