MIPS32指令集与编程

官方信息技术老师·10 页·深入(追求细节与边界)·0 次浏览·3 天前
指令集汇编编程寄存器约定流水线

MIPS32指令集与编程

从指令编码到流水线机制再到函数调用约定,建立可推导的MIPS汇编思维框架

按 空格/→ 演示下一步

1 / 10 页

全部页面点击任意一页,跳回舞台从这页播放

指令集汇编编程寄存器约定流水线

MIPS32指令集与编程

从指令编码到流水线机制再到函数调用约定,建立可推导的MIPS汇编思维框架

1第 1 页 · MIPS32指令集与编程

MIPS32指令集与编程-1

学汇编的人绕不开MIPS32——教科书讲它、面试问它、考研考它。但你有没有想过:为什么偏偏是它?答案藏在一个缩写里:RISC——精简指令集计算机。

RISC理念
只保留最常用的简单指令,复杂操作交给编译器组合
固定长度指令
所有指令都是32位长,译码简单、流水线友好
Load/Store结构
只有load和store能访问内存,其他指令只操作寄存器
32个通用寄存器
$0~$31,其中$0恒为0(可直接当常量用)
典型应用领域
教材经典、Cisco路由器、PS2/PSP游戏机、嵌入式
厨房的基础调料对应 →MIPS32指令集

调料种类少但每种功能标准明确,组合起来能做出任何菜;MIPS指令简单但能组合出任意程序

2第 2 页 · MIPS32指令集与编程-1

MIPS32指令集与编程-2

上一页我们认识了32个寄存器——它们像32个抽屉,每条指令都得说清「对哪个抽屉做什么」。但指令本身是怎么用32个比特把这事讲明白的?

R型指令
寄存器间ALU运算,6字段:op+rs+rt+rd+shamt+funct
I型指令
立即数运算与访存,4字段:op+rs+rt+imm(16位)
J型指令
跳转专用,2字段:op+addr(26位),目标地址由PC高4位拼成
统一32位长度
三种格式字段数不同但总长都是32位,硬件可一次取指并行解码
三栏宽窄不一的表格对应 →MIPS三种指令格式

op=表头标题决定表种类;rs/rt/rd/imm各占若干格;总行宽固定32格

3第 3 页 · MIPS32指令集与编程-2

MIPS32指令集与编程-3

MIPS32指令集与编程-3:定义、要点与典型应用

MIPS32指令集与编程-3
MIPS32指令集与编程-3:定义、要点与典型应用
4第 4 页 · MIPS32指令集与编程-3

MIPS32指令集与编程-4

前页我们用 I 型格式描述分支:操作码 + 两寄存器 + 16 位立即数。但分支目标地址怎么算?早期 MIPS 程序里那条「总是会执行」的指令是什么?这一页讲清楚。

条件分支指令
beq/bne 比较两寄存器相等/不等;blez/bgtz/bltz/bgez 与 $zero 比较
PC 相对寻址
目标地址由 PC 算出来,位置无关,无需链接器重定位
延迟槽
分支指令的下一条无论是否跳转都会被执行——早期流水线的副产物
编译器处理
GCC 用 NOP 或调度有用指令填充延迟槽;MIPS R6 已彻底删除延迟槽
厨师喊「换菜」对应 →延迟槽机制

助手必须切完手头洋葱才能切下一道——分支跳走前,紧跟的指令总是先跑完

target=(PC+4)+(sign_extend(offset)2)\text{target} = (PC + 4) + (\text{sign\_extend}(\text{offset}) \ll 2)
5第 5 页 · MIPS32指令集与编程-4

MIPS32指令集与编程-5

前面我们用 lw/sw 把数据搬进搬出寄存器,但程序不能只会搬数据——它得会判断、会循环、会调子程序,这全靠「控制转移」指令:让 PC 不再机械地顺序加 4。

条件分支 beq/bne
beq/bne 比较两寄存器相等或不等;blez/bgtz 把寄存器与 0 比大小
slt 比较族
slt 族把「小于」编为 0/1;配合 beq/bne 可拼出全部 6 种比较
无条件跳转 j/jal
j 用 26 位直接地址跳;jal 把 PC+8 存 $ra;jr 让寄存器当跳转目标
分支延迟槽
分支/跳转指令的「下一条」无论跳不跳都会先执行;编译器用 nop 或可挪动的指令填充这条槽位
铁轨岔道调度对应 →分支与延迟槽

调度员变道前必先安排好下一节车厢走哪——这条「无论变不变道都得先驶过去」的车厢就是延迟槽

6第 6 页 · MIPS32指令集与编程-5

MIPS32指令集与编程-6

MIPS32指令集与编程-6:定义、要点与典型应用

MIPS32指令集与编程-6
MIPS32指令集与编程-6:定义、要点与典型应用
7第 7 页 · MIPS32指令集与编程-6

MIPS32指令集与编程-7

前面写过 `beq $t0, $zero, loop`——你以为 CPU 会立刻跳到 loop?错了。分支指令后面那条,它照样会执行一次。这一格就叫「分支延迟槽」。

延迟槽定义
分支指令之后、跳转目标之前那条「无论跳不跳都会执行」的指令位置
物理根源
取指阶段已预取下一条,扔掉浪费,索性让它跑一次
编译器调度
把分支前一条不依赖结果的有用指令挪进延迟槽,掩盖开销
nop 填充
找不到可挪指令时,编译器插入空操作占位
编程陷阱
手写汇编按直觉顺序写,常漏掉这一拍,程序就跑偏
火车进站刹车后还会滑行对应 →分支延迟槽

跳转决定已发出,但下一节车厢已挂上,必须跑完这一段才真正分岔

8第 8 页 · MIPS32指令集与编程-7

本节要点

  • 三种指令格式的字段划分决定寻址模式与操作数来源
  • Load/Store是唯一访存通路,R型指令不触碰内存
  • $sp/$ra/$a0-$a3等寄存器约定是调用契约
  • 延迟槽是流水线遗留,编译器需用nop或重排填充
  • 伪指令是汇编器语法糖,需还原为真实机器码
延伸主题:MIPS异常与中断处理流水线冒险与互锁机制MIPS到RISC-V演化路径
9第 9 页 · 本节要点

课后思考

先遮住参考答案写下自己的思路,再对照 a1/a2/a3 看方向对不对。

1为什么MIPS要把访存指令(lw/sw)和寄存器运算分开,而不是像x86那样统一?

参考答案这是Load/Store架构的体现:ALU只对寄存器操作,只有lw/sw能访存。好处是长度规整、流水线更容易并行。

2要装入一个任意32位常数,lui+ori组合会有什么边界陷阱?

参考答案ori把立即数做无符号扩展,装载高位全1的负数时,ori会先清零高位再相加,结果错误。常见做法是改用addi。

3MIPS的分支延迟槽在现代超标量处理器里还存在吗?移植老代码会遇到什么坑?

参考答案现代CPU用分支预测取代延迟槽。移植老代码时,原本假设'延迟槽必执行'的优化会失效,关键指令可能被跳过。

10第 10 页 · 课后思考