看龙芯3A的 dmtc1 指令有多慢!

龙芯2F和3A处理器都实现了与 x86 MMX 基本兼容的 SIMD,即 MMI,该 ASE 是在浮点部件中的实现的,并且复用了 64-bit 的浮点寄存器(FPR)。在使用 MMI 时不可避免的会使用到通用寄存器向浮点器移动数据的情况,那么 dmtc1 的效率如何呢?

GPR 向 FPR 移动数据的指令共有3种:
mtc1 : 从 GPR 向 FPR 移动 32-bit 的数据,64-bit 平台上目标 FPR 的高 32-bit 清 0。
mthc1 : 从 GPR (低 32-bit)向 FPR 的高 32-bit 移动 32-bit 的数据,目标 FPR 的低 32-bit 数据保留。
dmtc1 : 从 GPR 向 FPR 移动 64-bit 数据。

从上面的说明可以看出, dmtc1 的功能是可以使用 mtc1 与 mthc1 模拟实现的,那么我们就设计个实验程序来验证一下这两条方式的时间开销分别如何吧。
程序的逻辑大致如下:

for (i=0; i<100000000; i++) {
#if 0
    move $2, $3
    mtc1 $3, $f31
    dsra $3, 32
    mthc1 $3, $f31
    move $3, $2
    ....
    ....
#else
    dmtc1 $3, $f31
    dmtc1 $3, $f31
     ....
     ....
#endif
}

结果:
在 MIPS64 系统上,每个循环中做8次GPR2FPR的数据移动,其 dmtc1 实现时间大概为 0m4.463s,而 mtc1 与 mthc1 组合实现为 0m3.857s,后者如不做寄存器的保存恢复,开销仅为 0m1.791s。

Over!

龙芯3的 128-bit 访存指令

龙芯3实现了两组 128-bit 的访存指令 gslq, gssq, gslqc1, gssqc1,分别用于加载、存储 128-bit 数据至通用寄存器和浮点寄存器。这两组指令都要求地址对齐到 16 字节,另外由于指令占用 lwc2, swc2 编码域,所以如果要使用需要启用 CP2。

gslq/gssq

gslq gpr0, gpr1, off(gpr2) // match: 0xc8000020, mask: 0xfc008020
gssq gpr0, gpr1, off(gpr2) // match: 0xe8000020, mask: 0xfc008020
 
gpr0 : 编码域 bit0-bit4,取值 0-32,高 64-bit
gpr1 : 编码域 bit16-bit20,取值 0-32,低 64-bit
off  : 编码域 bit6-bit14,取值 -256-255,实际偏移值需要左移 4 位,即 -4096-4080
gpr2 : 编码域 bit21-bit25,取值 0-32

gslqc1/gssqc1

gslqc1 fpr0, fpr1, off(gpr0) // match: 0xc8008020, mask: 0xfc008020
gssqc1 fpr0, fpr1, off(gpr0) // match: 0xe8008020, mask: 0xfc008020
 
fpr0 : 编码域 bit0-bit4,取值 0-32,高 64-bit
fpr1 : 编码域 bit16-bit20,取值 0-32,低 64-bit
off  : 编码域 bit6-bit14,取值 -256-255,实际偏移值需要左移 4 位,即 -4096-4080
gpr0 : 编码域 bit21-bit25,取值 0-32

Over!