登录后台

页面导航

本文编写于 137 天前,最后修改于 86 天前,其中某些信息可能已经过时。

https://www.anquanke.com/post/id/162992

简介

MIPS(Microprocessor without Interlocked Piped Stages architecture),是一种采取精简指令集(RISC)的处理架构,由MIPS科技公司开发并授权,广泛应用在许多电子产品、网络设备、个人娱乐装置与商业装置上。最早的MPS架构是32位,最新的版本已经变成64位。

指令集

通用寄存器

一共32个,每个四字节,用编号$0~$31表示,也可以用寄存器的名称,如$sp,$t1等

编号名称说明备注
$0$zero和0值作比较运算是常用该寄存器
$1$at汇编保留保留寄存器,不可做其他用途Assembler Temporary
$2~$3$v0~$v1存储函数的返回值。如果返回值为1字节,则只有$v0有效Values
$4~$7$a0~$a3作为函数调用时传递的前4个参数,不够时才使用堆栈传递Arguments
$8~$15$t0~$t7临时寄存器,无需保证函数调用的恢复Temporaries
$16~$23$s0~$s7函数调用后这些寄存器需要被恢复Saved Values
$24~$25$t8~$t9临时寄存器的拓展补充Temporaries
$26~$27$k0~$k1系统保留寄存器,请勿使用Kernel reserved
$28$gp全局指针,静态数据访问时需要保证在gp指定的64kb范围Global Pointer
$29$sp堆栈指针Stack Pointer
$30$s8/$fp Saved value / Frame Pointer
$31$ra存放函数调用返回的地址Return Address

特殊寄存器

MIPS32架构中定义了3个特殊寄存器,分别是PC、HI、和LO。这几个用的比较少,暂时先不介绍啦。

字节序

数据在存储器中是按照字节存放的,处理器也是按照字节访问存储器中的指令或数据的。如果要读出一个WORD(4bytes),那么有两种结果:

大端模式(Big-Endian),也称MSB(Most Significant Byte),低地址存放高字节。
小端模式(Little-Endian),也称LSB(Least Significant Byte),低地址存放低字节。

MIPS指令集

特点

1.MIPS固定4字节指令长度;
2.内存中的数据访问(load/store)必须严格对其(至少4字节对齐);
3.跳转指令只有26位目标地址,加上2位对齐位,可寻址28位的空间,即256MB;
4.条件分支指令只有16位跳转地址,加上2位对齐位,可寻址18位的空间,即256KB;
5.流水线效应。MIPS采用了高度的流水线,其中最重要的就是分支延迟效应。在分支跳转语句后面那条语句叫分支延迟槽。实际上,在程序执行到分支语句时,当他刚把要跳转的地址填充好(填充到代码计数器里),还没有完成本条指令时,分支语句后面的那个指令就已经执行了,其原因就是流水线效应——几条指令同时执行,只是处于不同的阶段。

指令格式

MIPS指令长度为32位(4字节,64位),其中指令位均为6位,其余的26位可以分为R型、I型、J型共3种类型。

R型 Opcode(6) Rd(5) Rs(5) Rt(5) Shamt(5) Funct(6)

I型 Opcode(6) Rd(5) Rs(5) Immediate(16)

J型 Opcode(6) Address(26)

各字段含义如下:

Opcode:指令基本操作,成为操作码;
Rs:第一个源操作数寄存器;
Rt:第二个源操作数寄存器;
Rd:存放操作结果的目的操作数;
Shamt:位移量;
Funct:函数,这个字段选择Opcode操作的某个特定变体。

LOAD/STORE指令

la(Load Address):用于将一个地址或标签存入一个寄存器

语法:la $Rd, Label
示例:la $t0, val_1 //复制val_1表示的地址到$t0寄存器中,其中val_1是一个Label

  li(Load Immediate):用于将一个立即数存入一个寄存器
  

算数运算指令

特点:算数运算指令的所有操作数都是寄存器,不能直接使用RAM地址或间接寻址,操作数的大小都为word(4 Byte)
add $t0, $t1, $t2 //$t0 = $t1 + $t2,带符号数相加
sub $t0, $t1, $t2 //$t0 = $t1 - $t2,带符号数相减
addi $t0, $t1, 5 //$t0 = $t1 + 5
addu $t0, $t1, $t2 //$t0 = $t1 + $t2,无符号数相加
subu $t0, $t1, $t2 //$t0 = $t1 - $t2,无符号数相减
mult $t3, $t4 //(Hi, Lo) = $t3 * $t4
div $t5, $t6 //$Lo = $t5 / $t6 $Lo为商的整数部分, $Hi为商的余数部分
mfhi $t0 //$t0 = $Hi
mflo $t1 //$t1 = $Lo

类比较指令

在MIPS寄存器中没有标志寄存器,但是再MIPS指令中有一种指令——SLT系列指令,可以通过比较设置某个寄存器后与分支跳转指令联合使用。

在MIPS寄存器中没有标志寄存器,但是再MIPS指令中有一种指令——SLT系列指令,可以通过比较设置某个寄存器后与分支跳转指令联合使用。

slt(Set on Less Than):有符号比较
slt $Rd, $Rs, $Rt //if ($Rs < $Rt):$Rd=1 else $Rd=0
slti(Set on Less Than Immediate):有符号比较
slti $Rd, $Rs, imm //if ($Rs < imm):$Rd=1 else $Rd=0
sltu(Set on Less Than Unsigned):无符号比较
sltu $Rd, $Rs, $Rt //if ($Rs < $Rt):$Rd=1 else $Rd=0
sltiu(Set on Less Than Immediate Unsigned):有符号比较
slt $Rd, $Rs, $Rt //if ($Rs < $Rt):$Rd=1 else $Rd=0

分支跳转指令

SYSCALL可以产生一个软中断,从而实现系统调用。

语法:

//系统调用号存放在$v0中
//参数存放在$a0~$a3中(如果参数过多,会有另一套机制来处理)
//系统调用的返回值通常放在$v0中
//如果系统调用出错,则会在$a3中返回一个错误号
syscall

跳转指令

j target
//直接跳转到target标签处

jr $Rs
//跳转到$Rs寄存器指向的地址处
//常用于子函数返回
//如果子函数内又调用了其他子函数,那么$Rs的值应该被保存到堆栈中

jal target
//跳转到target标签处,并保存返回地址到$ra
//常用于子函数的调用
//1、复制当前PC值(即子函数的返回地址)到$ra寄存器中
//2、程序跳转到子程序标签target处

MIPS函数调用

基本栈帧结构

1.参数段(Argument Section)。参数段旨在函数调用子函数(非叶子函数)时出现。
编译器在生成这个段的时候保证了它的大小足够所有的参数存放。
参数段最小4*4 byte 大小。
参数寄存器$a0~$a3用于传递子函数的前4个参数。这几个参数将不会复制到栈帧空间中去(注意:栈帧空间仍将为其预留等额空间),额外的参数将复制进入参数段(偏移为16,20,24等等)。
参数端在栈帧的低地址端(栈顶位置)。

2.寄存器保存段(Saved Registers Section)。当函数需要使用到$s0~$s7中的寄存器时出现。
寄存器保存端将为每个需要保存的寄存器准备4 byte大小的的空间。
函数的开场白(Prologue)会在函数入口处将每一个需要保存的寄存器的值保存到寄存器保存段中,然后在返回前将其恢复到对应的寄存器中。
如果函数本身没有对寄存器的内容进行改变,则不需要进行保存/恢复这个过程。
寄存器保存段在参数段后面(栈底方向)。

3.返回地址段(Return Address Section)。当函数调用了其他子函数时出现。
返回地址段只有1字节大小,即用1字节来保存返回地址。
返回地址寄存器$ra将在函数入口处被复制到返回地址段,再函数返回前恢复到$ra中。
返回地址段在寄存器保存段后面(栈底方向)。

4.填充段(Pad)。当参数段+寄存器保存段+返回地址段的大小不是8的倍数时出现。
填充段只有1字节大小(要么就没有)。
填充段不存数据。
填充段在返回地址段后面(栈底方向)。

5.局部数据存储段(Local Data Storage Section)。当函数使用了局部参数或必须保存寄存器参数时出现。
局部数据存储段的大小是由子程序对于局部数据存储的需求决定的,它总是8的倍数。
局部数据存储段的数据是函数私有的,是被调用函数和函数的子函数都无法访问的数据。
局部数据存储段在填充段后面(栈底方向),是栈帧的最后一部分