2

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

4

2 回答 2

4

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


是的,这称为SIMD。(单指令,多数据) 在 x86-64 上,SSE2(流 SIMD 扩展)保证可用,因此您有 16 个不同的 16 字节寄存器 (xmm0..15)。并且您有指令可以执行 4x 32 位浮点数、2x 64 位双精度的打包 FP add/sub/mul/div/sqrt/cmp、字节、字、双字的打包整数 add/sub/cmp/shift/etc , 和 qword 操作数大小。

(有一些空白;SSE2 不是很正交,例如最窄的移位是 16 位,压缩的最小/最大值仅适用于某些尺寸。其中一些空白由 SSE4.1 填补)。

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

请参阅https://www.felixcloutier.com/x86/p...像这样的指令paddw是压缩整数。 ...ps并且pd是浮点打包单或打包双。

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

例如这个数组的总和:

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

在 Godbolt 编译器资源管理器上使用 GCC9.3 -O3 for x86-64 像这样编译

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 或使用 OpenMP 进行矢量化。


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

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

如何计算 32 位整数中设置的位数?这样做,扩大到 4 位计数器,然后是 8 位,然后使用乘法来移位和相加 4 个不同的移位,并在高字节中产生总和。

于 2020-04-12T01:32:44.530 回答
2

寄存器不倾向于保存指令,而是保存要由指令处理的数据。

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

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

and即使没有这个,您当然可以通过使用按位运算(如、和)和位移操作(如、or、和)将“子寄存器”插入/从寄存器中提取/提取。notxorshlshrrolror

于 2020-04-12T00:07:56.973 回答