LA32R中的用户态整型指令
算术逻辑移位类指令
这一节我们先来看最容易理解掌握的算术逻辑类指令。
算术逻辑移位类指令速查表
| 指令 | 汇编表达 | 功能简释 |
|---|---|---|
| add.w (add word) | add.w $rx, $ry, $rz | GR[x] = GR[y] + GR[z] |
| sub.w (subtract word) | sub.w $rx, $ry, $rz | GR[x] = GR[y] - GR[z] |
| addi.w (add immediate word) | addi.w $rx, $ry, si12 | GR[x] = GR[y] + sext32(si12) |
| mul.w (multiply word) | mul.w $rx, $ry, $rz | GR[x] = (GR[y] * GR[z])[31:0] |
| mulh.w (multiply high word signed) | mulh.w $rx, $ry, $rz | GR[x] = (GR[y] s*s GR[z])[63:32] |
| mulh.wu (multiply high word usigned) | mulh.wu $rx, $ry, $rz | GR[x] = (GR[y] u*u GR[z])[63:32] |
| div.w (divide word signed) | div.w $rx, $ry, $rz | GR[x] = GR[y] /s GR[z] |
| mod.w (modulo word signed) | mod.w $rx, $ry, $rz | GR[x] = GR[y] %s GR[z] |
| div.wu (divide word usigned) | div.wu $rx, $ry, $rz | GR[x] = GR[y] /u GR[z] |
| mod.wu (modulo word usigned) | mod.wu $rx, $ry, $rz | GR[x] = GR[y] %u GR[z] |
| and (and) | and $rx, $ry, $rz | GR[x] = GR[y] & GR[z] |
| or (or) | or $rx, $ry, $rz | GR[x] = GR[y] | GR[z] |
| nor (not or) | nor $rx, $ry, $rz | GR[x] = ~(GR[y] | GR[z]) |
| xor (exclusive or) | xor $rx, $ry, $rz | GR[x] = GR[y] ^ GR[z] |
| andi (and immediate) | andi $rx, $ry, ui12 | GR[x] = GR[y] & zext32(ui12) |
| ori (or immediate) | ori $rx, $ry, ui12 | GR[x] = GR[y] | zext32(ui12) |
| xori (exclusive or immediate) | xori $rx, $ry, ui12 | GR[x] = GR[y] ^ zext32(ui12) |
| sll.w (shift left logic word) | sll.w $rx, $ry, $rz | GR[x] = GR[y] << GR[z][4:0] |
| srl.w (shift right logic word) | srl.w $rx, $ry, $rz | GR[x] = GR[y] >>u GR[z][4:0] |
| sra.w (shift right arithmetic word) | sra.w $rx, $ry, $rz | GR[x] = GR[y] >>s GR[z][4:0] |
| slli.w (shift left logic immediate word) | slli.w $rx, $ry, ui5 | GR[x] = GR[y] << ui5 |
| srli.w (shift right logic immediate word) | srli.w $rx, $ry, ui5 | GR[x] = GR[y] >>u ui5 |
| srai.w (shift right arithmetic immediate word) | srai.w $rx, $ry, ui5 | GR[x] = GR[y] >>s ui5 |
上表中的功能解释借用了C语言中的+、-、*、/、%、>>、<<、&、|、^、~运算符,同时也体现了这些指令与C语言中的算术逻辑操作之间的关联性。此外sext32(IMM)表示将数值IMM有符号扩展至32位,zext32(IMM)表示将数值IMM零扩展(也就是无符号扩展)至32位。
部分操作的进一步说明
变量减一个小常数的操作
汇编编程中你可能需要完成变量减一个小常数N(N < 211)的操作。经常有初学者因为在LA23R的指令集手册或者汇编伪指令中看不到形如subi.w这样的指令,就会使用li.w+sub.w两条指令来实现。这里提示一下,addi.w指令中的12位立即数是有符号扩展至再进行操作,换言之,该指令的12位立即数域中可以存一个负数。加一个负的常数,就是减一个正的常数。所以,只要减去的常数N的大小符合前面讲的条件,是可以用单条指令addi.w $r##, $r##, -N完成的。
乘法操作
本节开头表中符号 s*s表示两个有符号数相乘,符号 u*u表示两个无符号数相乘。为什么乘法要区分有符号和无符号,而加减法就不用这样区分。搞清楚这个问题需要知晓二进制补码算术运算相关知识。如果你没有学过,可以自学《Computer Organization and Design: The Hardware Software Interface》中“计算机算术运算”一章中的相关内容,或者也可以自学《计算机体系结构基础》(第3版)第8章“运算器设计”中的相关内容。
mulh.w和mulh.wu这两个指令的目的寄存器中存放的是乘积的高半部分结果。在32位目标机器上通常的C语言表达不会编译出这两条指令。这两条指令通常在汇编编程中使用。譬如,当你想判断乘法的结果有没有溢出32位可表达范围,或者你开发的应用其数据的数值分布导致乘积必须要使用高半部分结果,等等。
除法操作
本节开头表中符号/s表示两个有符号数相除取商,%s表示两个有符号数相除取余数;/u表示两个无符号数相除取商,%u表示两个无符号数相除取余数。如果觉得不好理解,可以看下面的例子。
int a, b, c, d;
unsigned int e, f, g, h;
c = a / b; //对应 div.w
d = a % b; //对应 mod.w
g = e / f; //对应 div.wu
h = e % f; //对应 mod.wu
LA32R中除法指令自身不进行除0的检查,这个工作交给软件来做。以上面例子中c = a / b来说,编译器会产生类似如下的代码序列:
上面的代码序列中,当除数为0时,bne指令将不跳转,将执行其后的break指令,触发断点例外。特别要指出的是,上面序列中的后两条指令是C编译器插入的,与汇编器无关,也就是说当你在汇编代码中直接写div.w这条指令时,生成的代码也只有div.w这条指令。如果你是将MIPS的汇编代码往LA架构上移植,要注意MIPS汇编中div d, s, t(d不是0号寄存器)其实是一条宏指令,MIPS汇编器会插入除0判断的指令序列,而在LA中这些逻辑就必须由你手动写出来。
移位操作
本节开头表中符号>>u表示逻辑右移(高位补0),符号>>s表示算术右移(高位补符号位)。如果觉得不好理解,可以看下面的例子。
int a, b;
unsigned int c, d;
unsigned char shamt;
b = a >> (shamt & 0x1f); //这里的>>操作将对应 sra.w
d = c >> (shamt & 0x1f); //这里的>>操作将对应 srl.w
比特提取操作以及字节半字扩展操作
LA32R的指令中没有单条指令就能完成比特提取操作的。如果需要从寄存器的数据中提取一串连续的,通常用slli.w和srli.w指令配合在一起完成。如果是若干不连续的比特位提取并拼凑在一期,那就再用移位指令和or指令将使用移位指令提取出的各个连续的短串拼接起来。
尽管说LA32R已经提供了字节、半字的装载(load)指令,汇编编程时仍可能需要对已经在GR中的数据进行字节或半字扩展操作。由于没有LA64/LA32中的位操作指令,LA32R中字节和半字扩展操作可以用下表中的指令序列实现。
| 操作 | 对应指令序列 |
|---|---|
| Byte Signed Extend | slli.w $r##, $r##, 24; srai.w $r##, $r##, 24 |
| Byte Unsigned Extend | slli.w $r##, $r##, 24; srli.w $r##, $r##, 24 |
| Halfword Signed Extend | slli.w $r##, $r##, 16; srai.w $r##, $r##, 16 |
| Halfword Unsigned Extend | slli.w $r##, $r##, 16; srli.w $r##, $r##, 16 |
按位取反操作
LA32R汇编器中没有not(按位取反)这条伪指令,如果汇编编程时需要,可以用nor rd, $zero, rj来达成not rd, rj的功能。特别提醒注意的是,LA32R下不能像RISC-V那样用xori rd, rs, -1来实现。RISC-V中所有的立即数都是有符号扩展,所以xori rd, rs, -1中的-1扩展到32位后就是0xFFFFFFFF,其与任意32位数异或就是按位取反的效果。但是LA32R中xori中的立即数是零扩展的,无法达成等价的效果。