寄存器可以一次保存多个值吗?

Can a register hold multiple values at a time?

在 64 位 x86 寄存器的情况下,是否可以在同一个寄存器中一次保存多个值,如果一个值的大小足够小以至于多个指令可以放入一个登记?例如,将两个 32 位整数装入一个寄存器。如果可能的话,这会是一件坏事吗?我一直在阅读寄存器,但我对这个概念还很陌生。

寄存器往往不保存指令,而是保存指令处理的数据。

但是,如果您想要将指令存储为数据,我相信(来自here)最长的 x86 指令大约为 15 个字节,或 120 位。所以,不,它不适合单个 64 位寄存器。

就在单个寄存器中保存多个数据值而言,这当然是可能的。这甚至得到了硬件的支持,即使是最早的 x86 芯片也具有 ahal,它们共同构成了 ax 寄存器。

即使没有那个,你当然可以 insert/extract "sub-registers" into/from 寄存器,通过使用按位运算(如 andornotxor),以及移位操作(如 shlshrrolror)。

寄存器不保存指令,但我假设您的意思是将多个 放入一个寄存器中,这样您就可以用一条指令将它们都添加。


是的,这个叫做SIMD. (Single Instruction, Multiple Data)在x86-64上,SSE2(Streaming SIMD Extensions)保证可用,所以你有16个不同的16字节寄存器 (xmm0..15)。并且您有指令可以对字节、字、双字和 qword 操作数大小执行 4x 32 位浮点数、2x 64 位双精度、打包整数 add/sub/cmp/shift/etc 的打包 FP add/sub/mul/div/sqrt/cmp。

(有一些差距;SSE2 不是很正交,例如最窄的移位是 16 位,打包 min/max 仅适用于某些尺寸。其中一些差距由 SSE4.1 填补)。

以及元素宽度无关的按位布尔值(直到 AVX512 带有掩码寄存器...)

参见https://www.felixcloutier.com/x86/p... 指令如 paddw 是压缩整数。 ...pspd 是浮点 packed-single 或 packed-double。

编译器经常使用 SSE/SSE2 指令,如 movdqa 来清零或复制 16 字节块中的内存,以及 "vectorize"(使用 SIMD 计算)在数组上循环。例如,GCC 7 或 8 及更高版本知道如何使用 RAX 将相邻结构成员或数组元素的 loads/stores 合并为标量加载或存储。

例如这个数组的总和:

int sumarr(const int *arr)
{
    int sum = 0;
    for(int i=0; i < 10240; i++) {
        sum += arr[i];
    }
    return sum;
}

使用 GCC9.3 -O3 for x86-64 像这样编译 on the Godbolt compiler explorer

sumarr:
        lea     rax, [rdi+40960]            # endp = arr + size
        pxor    xmm0, xmm0
.L2:                                        # do {
        movdqu  xmm2, XMMWORD PTR [rdi]        # v = arr[i + 0..3]
        add     rdi, 16                        # p += 4
        paddd   xmm0, xmm2                     # sum += v  // packed addition of 4 elements
        cmp     rax, rdi
        jne     .L2                         # }while(p != endp)
   ... then a horizontal vector sum ...
        MOVD eax, xmm0
        ret

矢量化有点像并行化,对于像这样的缩减(将数组求和为标量)需要关联操作。例如FP 版本只能使用 -ffast-math 或 OpenMP 进行矢量化。


在像 RAX 这样的通用寄存器中,它没有在字节边界之间不进位的情况下进行 SIMD 加法的指令(就像 paddb xmm0, xmm1 那样),它被称为 SWAR(寄存器中的 SIMD)。

这种技术在过去更有用,在没有适当的 SIMD 指令集的 ISA 上,如 Alpha 或 MIPS64。但这仍然是可能的,并且 SWAR 技术可以用作没有 popcnt 指令的 popcount 之类的东西的一部分,例如屏蔽掉所有其他位并进行移位,这样你就可以有效地将 32 个单独的加法(不能相互溢出)添加到 2 位累加器中。

How to count the number of set bits in a 32-bit integer? 中显示的 popcnt bithack 就是这样做的,先扩展到 4 位计数器,然后是 8 位,然后使用乘法进行 4 次不同移位的移位和相加,并在高位产生总和字节.