课程笔记

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位相当于乘以 2shamt2^{shamt};对无符号数,右移shamt位相当于除以 2shamt2^{shamt} 并向下取整。这个“相当于除以”的结论只对非负整数成立:srl是逻辑右移,左侧补0,不能用它处理带符号数的除法。

控制流指令的组合方式

slt和sle不改变执行顺序,它们只把比较结果写成0或1;beqz检查一个寄存器是否为0,为0就转到标签;goto无条件转到标签,实质是把PC置为标签对应的地址,例如goto Loop回到循环开头。三者配合,才能让程序离开“顺序执行”:

  1. 用slt把条件变成0或1。

  2. 用beqz检查这个0或1,决定是否跳出。

  3. 用标签和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的两个出口分别走一遍。只看顺序执行的那条路径,容易漏掉循环退出后的分支。