3.3 Simple-MIPS指令(2)
阅读入口
指令总表与控制流
指令总表
| 指令 | 类别 | 语法 | 语义 |
|---|---|---|---|
| load | 数据加载 | lw $rt, offset($rs) | 从地址 $rs + offset读出一个字,存入 $rt |
| store | 数据存储 | sw $rt, offset($rs) | 把 $rt的一个字写到地址 $rs + offset |
| mov | 数据移动 | move $rd, $rs | $rd = $rs |
| sub | 算术运算 | sub $rd, $rs, $rt | $rd = $rs - $rt |
| shiftl | 逻辑左移 | sll $rd, $rt, shamt | $rd = $rt << shamt,右侧补0 |
| shiftr | 逻辑右移 | srl $rd, $rt, shamt | $rd = $rt >> shamt,左侧补0 |
| slt | 比较 | slt $rd, $rs, $rt | $rs < $rt时 $rd = 1,否则 $rd = 0 |
| sle | 比较(伪指令) | sle $rd, $rs, $rt | $rs <= $rt时 $rd = 1,否则 $rd = 0 |
| beqz | 条件分支 | beqz $rs, label | $rs == 0时转到label |
| goto | 无条件跳转 | goto label | 直接转到label |
表中的mov、shiftl、shiftr是教学指令集里的名称,汇编写法分别是move、sll、srl;goto对应真实MIPS的j。读真实MIPS汇编时按真实指令集的助记符理解,不要混用。
访存指令的地址计算
load与store都采用“基址寄存器加偏移”的寻址方式:$rs中存放基址,offset是立即数,实际访问的地址为两者之和。偏移可以为0,此时直接访问 $rs指向的字。这两条指令一次都只移动一个32位的字。
可以借助一个类比记住方向:寄存器像手边的工作台,内存像书架;load把数据从书架搬到工作台,store把工作台的内容放回书架。所以load的 $rt是写入目标,store的 $rt是数据来源。
复制、减法与移位
复制指令把源寄存器的值写到目标寄存器,两个寄存器此后的值相同。Simple-MIPS把复制视为一条基本指令;真实MIPS通常用add或or与 $zero配合实现,例如add $t0, $t1, $zero。
sub把 $rs当作被减数、$rt当作减数,结果写入 $rd。减法由ALU完成,操作数来自寄存器组,写法与slt一样是三个寄存器。例如 $t1为10、$t2为3时,sub $t0, $t1, $t2使 $t0等于7。
shamt给出移动的位数。当左移结果未丢失高位时,左移shamt位相当于乘以 ;对无符号数,右移shamt位相当于除以 并向下取整。这个“相当于除以”的结论只对非负整数成立:srl是逻辑右移,左侧补0,不能用它处理带符号数的除法。
控制流指令的组合方式
slt和sle不改变执行顺序,它们只把比较结果写成0或1;beqz检查一个寄存器是否为0,为0就转到标签;goto无条件转到标签,实质是把PC置为标签对应的地址,例如goto Loop回到循环开头。三者配合,才能让程序离开“顺序执行”:
-
用
slt把条件变成0或1。 -
用
beqz检查这个0或1,决定是否跳出。 -
用标签和
goto回到循环开头。
sle是伪指令,汇编器通常把它展开成与slt相关的指令序列。它的语义完整,可以直接在作业中使用,只是不要把它当成硬件直接实现的一条指令。
本节只新增slt、sle、beqz、goto四条,其余指令在3.2已经用过。读总表时把每条指令的语义和它对PC的影响对上,后面的组合方式就只是这几条指令的拼接。
学习安排
3.2的数据指令只改寄存器或内存中的值,执行顺序仍由PC顺序推进;本节加入slt、sle、beqz、goto后,程序才能改变执行顺序。读这一节时把每条指令落到PC上:beqz和goto可能改写PC,其余指令执行完PC仍加4。
- 先看
slt、sle如何把比较结果变成0或1,再看beqz如何用这个结果决定是否跳转,最后用标签和goto回到循环开头。 sle是伪指令,汇编器会把它展开成与slt相关的指令序列;作业按sle的语义作答。- 比较结果就是0或1,可以像其他整数一样参与后续运算。
课程约定
- 条件分支只有一个方向:
beqz在寄存器为0时跳转。想表达“小于时继续”,要先把条件写成slt的结果,再决定0对应哪条分支。 goto对应真实MIPS的j;教材统一写goto。- 标签是指令地址的符号名,
beqz $t0, Loop中的Loop不是变量,也不保存数据。 slt、sle只写目标寄存器,不修改两个源寄存器。
易错点
beqz只检查是否等于0,不检查正负。判断“大于”时不要直接套beqz,先想清楚哪个比较结果会变成0。sle是伪指令,但语义与普通指令一样可以直接使用;不要因为“伪”就认为它不能出现在作业里。- 写循环时别忘了在循环体末尾用
goto回到开头,否则PC顺序执行会直接离开循环。
读程序的顺序
读含分支的程序时,先标出所有标签和会改写PC的指令,再沿着beqz的两个出口分别走一遍。只看顺序执行的那条路径,容易漏掉循环退出后的分支。