MIPS学习笔记(1)

MIPS学习笔记(1)

文章目录

  • MIPS学习笔记(1)
    • 背景
    • 指令与立即数
    • 数据传输指令
    • MIPS基本决策机制
    • 乘除法指令
    • 算术溢出
    • 位运算指令
    • 移位指令
    • 不等式
    • 伪指令
    • 实现函数
    • 函数调用约定
    • 存储型程序概念
    • R型指令
    • I型指令
    • B类指令
    • J型指令
    • 汇编实战
    • 反汇编实战

背景

CISC(Complex Instruction Set Computing)

RISC(Reduced Instruction Set Computing)

MIPS是RISC的典型代表

RISC基本策略:

  1. 指令字长:所有指令长度都是1个字(32位)
  2. 寻址模式:基址+偏移量
  3. 访存:load与store

主流ISA:

  1. Intel 80x86 —— PC、服务器
  2. ARM(Advanced RISC Machine)—— 手机、平板
  3. PowerPC —— 航空电子设备、网络设备……

寄存器:

  1. 被内置于CPU中
  2. 速度极快(<1ns)
  3. 数量少

指令与立即数

MIPS内的寄存器:

  1. 数量为32个(编号为0~31)
  2. 宽度为32位
  3. 无正负
  4. 表示方法:$x(0<=x<=31)

MIPS指令的一般性语法格式: op dst, src1, src2

​ op:指令的基本功能

​ dst:保存结果的寄存器

​ src1:第一个操作数; src2:第二个操作数

加法操作:add dst, src1, src2

减法操作:sub dst, src1, src2

每条指令只有一种操作,每行一个指令

注释语句以“#”开始

0号寄存器($0 / $zero):0的专属寄存器,其值恒为零

立即数:指令中出现的常量数值

含有立即数的指令:op dst, src, imm

​ op+i表示对立即数的操作(eg:addi $s1, $s2, 5)

数据传输指令

由于MIPS只能对寄存器与立即数进行运算,因此必须有特定的数据传输指令实现主存单元与寄存器的数据交换

​ LOAD类:主存单元→寄存器

​ STORE类:寄存器→主存单元

数据传输的语法格式:op reg, off(base)

​ reg:操作的寄存器

​ base:存储基址的寄存器(因为是地址,base的值为无符号数)

​ off:偏移量(单位为字节,可正可负)—— 偏移量必须为4的倍数

读写的存储单元实际地址为:base+off

加载字:lw reg, off(base)

存储字:sw reg, off(base)

加载字节:lb reg, off(base) lbu为无符号扩展

存储字节:sb reg, off(base)

​ 注意:读入字节最高位视为符号位,向高24位扩展

加载半字:lh reg, off(base) lhu为无符号扩展

存储半字:sh reg, off(base)

如果变量比寄存器多,则把常用的变量保存在寄存器中,其他不常用的保存在存储器中

大印第安:最高有效字节在字内的最低地址

小印第安:最高有效字节在字内的最高地址

MIPS基本决策机制

MIPS没有语句块的概念(不同于C语言的if-else、while等)

每条指令都对应一个word地址,使用标号标记其后的指令的地址 → **“标号名” + ‘:’ **

通过跳转机制调到标号处,实现转移

相等时转移(Branch If Equal):beq reg1, reg2, label

不等时转移(Branch If Not Equal):bne reg1, reg2, label

无条件转移(Jump):j label

C语言中for、while、do-while都可以改为MIPS

乘除法指令

乘除法指令结算结果不是直接写入32个通用寄存器中,而是保存在两个特殊寄存器HI与LO

2条专用指令读写HI/LO:

​ move from high:mfhi dst

​ move from low:mflo dst

乘法操作:mult src1, src2

​ LO保存结果的低32位,HI保存结果的高32位

除法操作:div src1, src2

​ LO保存商,HI保存余数

算术溢出

当计算结果的位数超出计算机硬件的实际可存储位数时,即为溢出

MIPS会检测溢出(溢出时产生错误),但是有unsigned关键字的算术指令忽略溢出

overflow detection no overflow detection
add dst, src1, src2 addu dst, src1, src2
addi dst, src1, src2 addiu dst, src1, src2
sub dst, src1, src2 subu dst, src1, src2

位运算指令

instruction C MIPS
and a=b&c and $s1, $s2, $s3
and immediate a=b&0x1 andi $s1, $s2, $s3
or a=b|c or $s1, $s2, $s3
or immediate a=b|0x5 ori $s1, $s2, $s3
not or a=~(b|c) nor $s1, $s2, $s3
exclusive or a=b^c xor $s1, $s2, $s3
exclusive or immediate a=b^0xF xori $s1, $s2, $s3

移位指令

移位的不同种类:

  1. 方向:左移&右移

  2. 性质:逻辑移位&算术移位

    左移位低位补零;右移位:逻辑移位补0,算术移位补符号位

  3. 移位量:对于32位寄存器,移动位数合理最大取值为31,即0x1F

    表示方式:5位的立即数&某寄存器的值(低5位有意义)

指令 功能 示例
sll 逻辑左移 sll $dst, $src, imm/$x
srl 逻辑右移 srl $dst, $src, imm/$x
sra 算数右移 sra $dst, $src, imm/$x
sllv 逻辑可变左移 sllv $dst, $src, imm/$x
srlv 逻辑可变右移 srlv $dst, $src, imm/$x
srav 算数可变右移 srav $dst, $src, imm/$x

不等式

slt dst, src1, src2: Set on Less Than(如果src1

slt与bne、beq和$0组合可以实现所有的比较方式

slt的三种情况:

  1. sltu dst, src1, src2: 无符号数比较
  2. slti dst, src, imm: 与常量比较
  3. sltiu dst, src, imm:与无符号数常量比较
位扩展 溢出检测 符号数
Signed lb:扩展 add, addi, sub, mult, div: 检测 slt, slti: 符号数
Unsigned lbu:无扩展 addu, addiu, subu, multu, divu: 不检测 sltu, sltiu:无符号数

伪指令

MIPS定义了一组伪指令,从而使得程序更可读更易编写

注意:伪指令知识增加了可读性,最终要转换为实际指令

  1. move dst, src:把src赋值给dst
  2. la dst, label:(Load Address)加载特定标号对应的地址至dst
  3. li dst, imm:(Load Immediate)加载一个32位立即数至dst

出现的问题:将一条伪指令转换成真实指令时,可能需要多条真实指令,指令之间必须通过寄存器传递信息,若果任意 使用寄存器,则寄存器的内容可能被误写

解决方案:保留**$1 ($at)**作为汇编器的专用寄存器

TAL(True Assembly Language):真实指令,计算机能理解和执行

MAL(MIPS Assembly Language):提供给汇编程序员使用的指令(含伪指令)

实现函数

实现函数的六个步骤

  1. 调用者把参数放置在某个地方以便函数访问
  2. 调用者转移控制给被调用的函数
  3. 函数获取局部变量对应的空间
  4. 函数执行具体功能
  5. 函数把返回值放置在某个地方,然后恢复使用的资源
  6. 返回控制给调用者

寄存器分配:

  1. **$a0$a3**:(编号23)4个传递参数的寄存器
  2. **$v0$v1**:(编号47)2个传递返回值的寄存器
  3. $ra:(编号31)返回地址寄存器,保留着调用者的地址

函数调用指令:

  1. jal label:(Jump and Link)把jal的下一条指令地址保存在$ra,然后跳转到 label*(用于调用函数)*
  2. jr src:(Jump Register)无条件跳转保存在src的地址(通常指$ra)(用于从函数返回)

PC(Program Counter)是一个特殊寄存器,用于保存当前正在执行的指令的地址(对程序员不可见,但是可以通过jal访问)

注:$ra中保存的地址为PC+4而不是PC,否则从函数返回时再次返回jal

保存和恢复寄存器:

  1. 原因:如果被调用函数继续调用函数,$ra被覆盖,且由于寄存器数量少不可能只用寄存器编写实用程序
  2. 寄存器的保存:栈

$sp:栈指针寄存器,指针指向栈底

栈帧:函数为获得保存寄存器而将$sp向0方向调整的空间(栈帧容量=要保存的寄存器数量×4)

​ eg: addi $sp, $sp, -8

注意:栈增长方向为向0增长,堆增长方向为背0增长

局部变量和数组:

  1. 由于寄存器只有32个,因此编译器绝大多数下不可能把函数需要的所有局部变量都分配在寄存器
  2. 局部变量存放在函数自己的栈帧中(函数返回时随着$sp的回调,局部变量随之释放)
  3. 其他局部变量(数组、结构等)同样存储在栈帧中

函数调用约定

寄存器的保护分析

$s0~$s7:程序员变量

  1. 所有函数都要使用程序员变量,程序员变量的生命周期与函数生命周期相同,即进入函数有效,退出函数无效
  2. 保护前提:用哪些,保护哪些
  3. 保护动作:刚进入函数时保存,退出函数前恢复

$ra:函数返回地址

  1. 保护前提:如果继续调用了子函数,则必须保护
  2. 保护动作:刚进入函数时保存,退出函数前恢复

$t0~$t9:临时变量(前提:MIPS约定其服务于表达式计算)

  1. 无需保护的情况:表达式中不调用子函数,故不存在被子函数修改的可能

    需要保护的情况:表达式中调用子函数,故存在被子函数修改的可能

  2. 保护前提:如果其值在子函数调用前后必须保持一致

  3. 保护动作:调用子函数前保存,调用子函数后恢复

$a0~$a3, $v0~$v1:参数,返回值

  1. $a0~$a3:传递给了子函数,故子函数可自由使用

    $v0~$v1:因为子函数会设置返回值,故子函数可自由使用

  2. 保护前提:如果其值在子函数调用前后必须保持一致

  3. 保护动作:调用子函数前保存,调用子函数后恢复

$at:汇编器使用,无需保护

$sp:栈切换(通常属于操作系统范畴)时才需要保护

$fp、$gp:生成复杂存储布局时使用(属于便于范畴)

小结

序号 名称 MIPS汇编约定的用途 分类 保护的前提条件 何时保护与恢复
16~23 $s0~$s7 程序员变量 强保护 如果需要使用 进入函数时保存,退出函数前恢复
31 $ra 函数返回地址 强保护 如果调用子函数 进入函数时保存,退出函数前恢复
———— ———— —————— ———— —————— ————————————
2~3 $v0~$v1 函数返回值 弱保护 如果要求其值在子函数调用前后必须保持一致 调用子函数前保存,调用子函数后恢复
4~7 $a0~$a3 函数参数 弱保护 如果要求其值在子函数调用前后必须保持一致 调用子函数前保存,调用子函数后恢复
8~15,24~25 $t0~$t9 临时变量 弱保护 如果要求其值在子函数调用前后必须保持一致 调用子函数前保存,调用子函数后恢复

Preserved Register:保护寄存器($s0~$s7、$ra)

Non-preserved Register:非保护寄存器($t0~$t9、$a0~$a3、$v0~$v1)

存储型程序概念

存储型程序:

  1. 指令以二进制方式编码
  2. 程序存储在存储器中,可以从存储器中读取程序也可以写入程序(存储方式与数据存储完全相同)
  3. 简化了计算机系统的软件/硬件设计
  4. 存储器技术既可以存储数据,也可以存储程序
  5. 由于存储在存储器单元中,因此指令和数据都有地址

二进制兼容:

  1. 程序是以二进制形式发布的(指令集与程序之间是强相关)
  2. 新机器不仅能运行基于新指令编译产生的新程序,同样也最好能运行老程序
  3. 上述特性被称为向后兼容(Backward Compatible)

把指令当做数看待:

  1. 数据以字为单位,因此MIPS的所有指令的二进制编码宽度均为32位
  2. 指令的32位被划分为若干域(域:占据若干位置,代表特定含义),指令的各个域分别表示指令的不同信息

MIPS的3类指令格式:

  1. I型指令:指令中包含立即数

    lw / sw 的偏移是立即数;beq/bne同样包含有偏移

    srl等移位指令也有4位立即数(移位位数),但不属于I型指令

  2. J型指令:j和jal(jr不是J型指令)

  3. R型指令:所有其他指令

R型指令

指令中包含6个域:

opcode rs rt rd shamt funct
316 5 5 5 5 60

每个域都被视为无符号整数:

  1. 5位域表示范围:0~31
  2. 6位域表示范围:0~63
含义 说明
opcode(6) 指令操作 R型指令的opcode固定为0b000000
funct(6) 与opcode组合,精确定义指令的具体操作 主要服务于R型指令(由于opcode为0,因此根据6位funct,R型指令最多有64个)
rs(5) 指定1st操作数(source寄存器)
rt(5) 指定2nd操作数(target寄存器)
rd(5) 指定结果回写的寄存器(destination寄存器) eg: add rd, rs, rt
shamt(5) 移位指令中的移位位数 移位位数大于31没有意义;除了移位指令,该域恒为0

NOP(0x00000000):即”sll $0, $0, 0“,主要用于解决流水线冲突

I型指令

指令包括6个域:

opcode rs rt immediate
316 5 5 160

具体含义:

含义 说明
opcode(6) 指令操作 I型指令的opcode为非零编码,可有264条指令
rs(5) 指定1st操作数(source寄存器)
rt(5) 指定2nd操作数(target寄存器)
immediate(16) 无符号或有符号数 1. 无符号数:位运算指令(and/or/nor……)、小于置位指令(slti……) 2. 有符号:分支指令(beq/bne……)、访存指令(lw/sw……)

注意

  1. zero_ext():运算前需要进行无符号扩展
  2. sign_ext():运算前需要进行符号扩展

如何计算32位立即数:(addi/ slti/ andi/ ori/……)

  1. 解决方案:不改变指令格式,而是增加一条指令
  2. 增加的指令:lui reg, imm;(Load Upper Immediate),reg高16位写入imm,低16位写入0

通过增加lui,MIPS可以用16位立即数处理任意大小的数据

B类指令

B类指令:分支指令(beq与bne),主要用于构造if-else、while、for,同样具有:opcode,rs,rt,immediate结构。比较rs和rt,根据比较结果决定能够是否转移。

注意:并非所有的B类指令的rt域都是寄存器,需要指定要转移的地址。转移的范围通常很小(<50指令),函数调用和无条件跳转用J型指令

用immediate表示地址:

  • 由于指令存储在主存中,而主存单元可以由基地址+偏移的方式定位
  • B类指令的基地址就是PC

PC相对寻址:PC为基地址,immediate为偏移(二进制补码)

下一条指令的PC值计算方法

  • 比较结果为真:PC = PC + 4
  • 比较结果为假:PC =(PC + 4)+(immediate * 4)

immediate为16位符号数,范围为±215,意味着转移的指令数最大为215条。按照1行C代码对应10条指令,则可转移约3000行C代码

J型指令

J型指令只定义了2个域:

opcode(31~26) instr_index(25~0)
6 26

要点:

  • opcode的位置及位数与R型、I型指令保持一致
  • 其他域合并起来构造大的地址范围

PC的高4位来自当前指令的高4位,J型指令的转移范围是256MB(只有jr指令可以跳转到4GB内任意地址

汇编实战

每条指令的汇编基本步骤:

  1. 标识出指令类型
  2. 标识出正确的域
  3. 用10进制表示各个域的值
  4. 把各个域的10进制转换为2进制
  5. 用16进制表示整个机器码

反汇编实战

反汇编是汇编的逆过程,即将指令二进制代码转换为汇编代码

反汇编基本步骤:

  1. 用2进制表示指令
  2. 根据opcode标识出指令类型
  3. 用10进制标识出各个域的值
  4. 用标识符表示各个域,并添加相应的表号
  5. 用汇编格式数学代码
  6. 将汇编代码翻译为C

你可能感兴趣的:(MIPS,mips)