在rv32架构下,一个64位变量,是要用2个寄存器保存的。在c程序中,我们是可以直接对64位变量进行加减操作的。
那么rv32下,又是如何实现64位加减运算的了?下面来讨论一下。
一、64位加
测试程序:
int main() { int64_t a = 0xbbbbbbbb; int64_t b = 0xcccccccc; int64_t c = a + b; printf("%016llx\n", c); }
使用riscv32-unknown-elf-gcc编译器编译,使用riscv32-unknown-elf-objdump查看反汇编:
0001016e <main>: 1016e: 7179 addi x2,x2,-48 10170: d606 sw x1,44(x2) 10172: d422 sw x8,40(x2) 10174: 1800 addi x8,x2,48 10176: bbbbc7b7 lui x15,0xbbbbc 1017a: bbb78793 addi x15,x15,-1093 #bbbbbbbb <__BSS_END__+0xbbb9c143> 1017e: 4801 li x16,0 10180: fef42423 sw x15,-24(x8) 10184: ff042623 sw x16,-20(x8) 10188: ccccd7b7 lui x15,0xccccd 1018c: ccc78793 addi x15,x15,-820 #cccccccc <__BSS_END__+0xcccad254> 10190: 4801 li x16,0 10192: fef42023 sw x15,-32(x8) 10196: ff042223 sw x16,-28(x8) 1019a: fe842683 lw x13,-24(x8) 1019e: fec42703 lw x14,-20(x8) 101a2: fe042583 lw x11,-32(x8) 101a6: fe442603 lw x12,-28(x8) 101aa: 00b687b3 add x15,x13,x11 101ae: 853e mv x10,x15 101b0: 00d53533 sltu x10,x10,x13 101b4: 00c70833 add x16,x14,x12 101b8: 01050733 add x14,x10,x16 101bc: 883a mv x16,x14 101be: fcf42c23 sw x15,-40(x8) 101c2: fd042e23 sw x16,-36(x8) 101c6: fd842603 lw x12,-40(x8) 101ca: fdc42683 lw x13,-36(x8) 101ce: 67f9 lui x15,0x1e 101d0: d4078513 addi x10,x15,-704 # 1dd40 <__clzsi2+0x40> 101d4: 22a5 jal 1033c <printf> |
这里就需要riscv的反汇编知识了。
在rv32下,64位变量,是要使用2个寄存器来保存的。另外rv32下,没有load/store 64位指令,因此保存或读取64位变量,需要两次32位仿存操作。
下面一段一段进行分析:
10176: bbbbc7b7 lui x15,0xbbbbc 1017a: bbb78793 addi x15,x15,-1093 #bbbbbbbb <__BSS_END__+0xbbb9c143> 1017e: 4801 li x16,0 10180: fef42423 sw x15,-24(x8) 10184: ff042623 sw x16,-20(x8) |
x15的值为0xbbbbbbbb,x16的值为0,刚好组合得到变量a,将x15和x16,写入到栈-24和-20位置,所以可以知道,变量a的首地址为sp-24。
10188: ccccd7b7 lui x15,0xccccd 1018c: ccc78793 addi x15,x15,-820 #cccccccc <__BSS_END__+0xcccad254> 10190: 4801 li x16,0 10192: fef42023 sw x15,-32(x8) 10196: ff042223 sw x16,-28(x8) |
x15的值为0xcccccccc,x16的值为0,刚好组合得到变量b,将x15和x16,写入到栈-32和-28位置,所以可以知道,变量b的首地址为sp-32。
1019a: fe842683 lw x13,-24(x8) 1019e: fec42703 lw x14,-20(x8) 101a2: fe042583 lw x11,-32(x8) 101a6: fe442603 lw x12,-28(x8) |
从之前分析,可以得知
-
x13:变量a的低32位
-
x14,:变量a的高32位
-
x11:变量b的低32位
-
x12:变量b的高32位
后面就开始进行真正的运算了:
101aa: 00b687b3 add x15,x13,x11 101ae: 853e mv x10,x15 101b0: 00d53533 sltu x10,x10,x13 101b4: 00c70833 add x16,x14,x12 101b8: 01050733 add x14,x10,x16 101bc: 883a mv x16,x14 |
首先将x11和x13相加,其实就是将变量a的低32位和变量b的低32位相加,得到的结果,保存到x15中。
将x15保存到x10中,在将x10和x13进行无符号比较。如果x10无符号小于x13,x10更新1,否则更新为0。 这里为什么要进行这样操作呢?
原因就是 2个32位数相加,是可能会有进位的,如果有进位,那么高32位相加的时候,要加上这个进位。 riscv没有arm有运算进位指令,只能通过其他的方式来获取进位。
x10是低32位相加的结果,如果结果小于一个加数,那么表示是有进位的,否则就没有。因此通过sltu指令,就可以得到低32位的进位结果。
x12和x14相加,其实就是将变量a的高32位和变量b的高32位相加,得到的结果,保存到x16中。
还需要考虑低32位相加进位,因此还要将x16加上x10,写入到x14。最后将x14保存到x16中。
所以最终
-
x15:变量c的低32位
-
x16:变量c的高32位
通过以上,我们就知道了rv32下,如何进行64位变量加运算。
二、64位减
测试程序:
int main() { int64_t a = 0xbbbbbbbb; int64_t b = 0xcccccccc; int64_t c = a - b; printf("%016llx\n", c); }
汇编代码如下:
0001016e <main>: 1016e: 7179 addi x2,x2,-48 10170: d606 sw x1,44(x2) 10172: d422 sw x8,40(x2) 10174: 1800 addi x8,x2,48 10176: bbbbc7b7 lui x15,0xbbbbc 1017a: bbb78793 addi x15,x15,-1093 #bbbbbbbb <__BSS_END__+0xbbb9c143> 1017e: 4801 li x16,0 10180: fef42423 sw x15,-24(x8) 10184: ff042623 sw x16,-20(x8) 10188: ccccd7b7 lui x15,0xccccd 1018c: ccc78793 addi x15,x15,-820 #cccccccc <__BSS_END__+0xcccad254> 10190: 4801 li x16,0 10192: fef42023 sw x15,-32(x8) 10196: ff042223 sw x16,-28(x8) 1019a: fe842683 lw x13,-24(x8) 1019e: fec42703 lw x14,-20(x8) 101a2: fe042583 lw x11,-32(x8) 101a6: fe442603 lw x12,-28(x8) 101aa: 40b687b3 sub x15,x13,x11 101ae: 853e mv x10,x15 101b0: 00a6b533 sltu x10,x13,x10 101b4: 40c70833 sub x16,x14,x12 101b8: 40a80733 sub x14,x16,x10 101bc: 883a mv x16,x14 101be: fcf42c23 sw x15,-40(x8) 101c2: fd042e23 sw x16,-36(x8) 101c6: fd842603 lw x12,-40(x8) 101ca: fdc42683 lw x13,-36(x8) 101ce: 67f9 lui x15,0x1e 101d0: d4078513 addi x10,x15,-704 # 1dd40 <__clzsi2+0x40> 101d4: 22a5 jal 1033c <printf> |
从反汇编知道,也是使用sltu指令,获取低32位减法借位。然后高32位运算,要减去这个借位。