weiqi7777

riscv32位架构如何进行64位加减运算

0
阅读(235) 评论(0)

在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位运算,要减去这个借位。