仅使用 XMM 寄存器的一部分进行数据传输

Using only parts of XMM register for data transfer

我有一个汇编程序,它在内存中处理由 3 个浮点数(3*32 位)的元组组成的数据结构。我想知道是否可以使用 xmm 寄存器优化数据复制。 从内存中读取值不会有太大问题,因为我可以加载 4*32 位,但是有什么方法可以只将 xmm 寄存器的一部分写回内存吗? 我在 ISA 文档中发现的唯一一件事是您可以使用写掩码,但没有解释如何做到这一点。

只有 AVX512 有像 vmovups [rdi]{k1}, xmm0 这样的屏蔽商店。

AVX1 具有 vmaskmovps,其工作方式基本相同,但带有向量 control-mask。 (例如 pcmpeqd xmm1,xmm1 / psrldq xmm1, 4)。但它会花费多个微指令。如果目标扩展到另一个页面,它会 fault-suppression ,但如果它实际上会出错,它可能效率低下。如果 16 字节的目标不跨越页面边界,甚至 cache-line 边界,那很好。 (它可能会触发虚假的缓存未命中或必须在 cache-line 拆分上重放存储 uop,即使它只是接触另一个缓存行的屏蔽部分。我忘记了并且最近没有检查过。)

你不需要 SSE2 maskmovdqu;具有 NT-store 语义,因此它会在写入后从缓存中逐出目标。


没有屏蔽 loads/stores,如果您知道源对象不在页面末尾,您通常可以安全地加载额外数据。

您不太可能存储超过目的地的尽头而不踩到任何重要的东西。 (除非你用一个虚拟元素填充你的结构以允许这样做,或者它在一个数组中并且你无论如何都要存储下一个元素。)

您可以使用 2 个存储来写入 12 个字节,一个 8 字节和一个 4 字节。 (或者两个重叠的 8 字节存储,如果这样更容易混洗的话)。

;; SSE2
    movups   xmm0, [rsi]          ; loads 4 bytes past the end of your object

    movsd    [rdi], xmm0           ; 8 byte store of the low 2 elements
    unpckhpd xmm0, xmm0           ; extract the high half
    movss    [rdi+8], xmm0

如果要连续存储到结构数组,则可以只存储 16 字节并将其与下一个 16 字节存储重叠。请注意最后一个元素:剥离最后一次迭代。

或者用SSE4.1 unpckhpd / movss可以变成

extractps  [rdi+8], xmm0, 2

(extractps has a r/m32 destination:您不能使用它来将标量浮点数提取到另一个 XMM 寄存器,但它对于 FP 存储到内存很有用。)


如果您的代码制作了副本的副本,您可能还想分别执行这 2 次加载,因此 store-forwarding 会起作用。

您甚至可以使用 GP-integer 寄存器,如 8 + 4 字节的 RAX 和 EDX loads/stores。 (最好先进行两次加载,然后进行两次存储,以防重叠或 4k 别名,因此 CPU 不必弄清楚第二次加载是否与第一次存储重叠)。