首页 > 解决方案 > 可以在不使用通用寄存器的情况下将 8 位从 XMM 寄存器移动到内存吗?

问题描述

我需要在不使用通用寄存器的情况下将 1 个字节从 xmm 寄存器移动到内存。而且我也不能使用 SSE4.1。有可能的?

=(

标签: assemblynasmsse

解决方案


通常,您首先要避免这种情况。例如,您可以进行更广泛的加载和合并(pand/pandn/por如果没有的话pblendvb),而不是进行单独的字节存储,然后将合并结果存储回来吗?

这不是线程安全的(未修改字节的非原子 RMW),但只要您知道要 RMW 的字节不会超出数组或结构的末尾,并且没有其他线程在做同样的事情对于同一数组/结构中的其他元素,这是执行诸如字符串中每个小写字母大写之类的常规方法,同时保留其他字节不变。


单 uop 存储只能来自 4、8、16、32 或 64 字节大小的向量寄存器,但仅 1 个未屏蔽元素的AVX-512BW 屏蔽存储除外。更窄的存储,如pextrb涉及一个 shuffle uop 以提取要存储的 2 或 1 个字节。

在没有 GP 整数 regs 的情况下真正存储 1 个字节的唯一好方法是使用SSE4.1pextrb [mem], xmm0, 0..150即使在当前 CPU 上立即运行,这仍然是一个 shuffle + store 。如果您可以在目标位置安全地写入 2 个字节,则 SSE2pextrw是可用的。

可以使用SSE2maskmovdqu字节掩码存储(带有0xff,0,0,...掩码),但您不想这样做,因为它比movd eax, xmm0/慢得多mov [mem], al。例如,在 Skylake 上,10 微指令,每 6 个循环吞吐量 1 个。

如果您想在之后重新加载字节,情况会更糟,因为(与 AVX / AVX-512 屏蔽存储不同)maskmovdqu具有 NT 语义,例如movntps(绕过缓存,或者如果之前很热则驱逐缓存行)。


如果您的要求完全是人为的,并且您只想玩愚蠢的计算机技巧(避免将数据保存在寄存器中),您还可以在堆栈上设置暂存空间并使用movsb它来复制它:

;; with destination address already in RDI
    lea  rsi, [rsp-4]          ; scratch space in the red zone below RSP on non-Windows
    movd  [rsi], xmm0
    movsb                   ; copy a byte, [rdi] <- [rsi], incrementing RSI and RDI

这显然比正常方式慢,并且需要一个额外的寄存器 (RSI) 用于 tmp 缓冲区地址。而且您需要 RDI 中的确切目标地址,而不是[rel foo]静态存储或任何其他灵活的寻址模式。

pop也可以复制 mem-to-mem,但仅适用于 16 位和 64 位操作数大小,因此无法省去您对 RSI 和 RDI 的需求。

由于上述方式需要一个额外的寄存器,因此几乎在所有方面都比正常方式更糟糕:

   movd  esi, xmm0            ; pick any register.
   mov   [rdi], sil           ; al..dl would avoid needing a REX prefix for low-8


;; or even use a register where you can read the low and high bytes separately
   movd  eax, xmm0
   mov   [rdi], al            ; no REX prefix needed, more compact than SIL
   mov   [rsi], ah            ; scatter two bytes reasonably efficiently
   shr   eax, 16              ; bring down the next 2 bytes

(在当前的 Intel CPU 上读取 AH 有一个额外的延迟周期,但这对于吞吐量来说很好,而且我们无论如何都在这里存储,所以延迟并不是一个很大的因素。)

xmm -> GP 整数传输在大多数 CPU 上并不慢。(Bulldozer 系列是异常值,但它仍然与存储/重新加载的延迟相当;Agner Fog 在他的微架构指南(https://agner.org/optimize/)中说,他发现 AMD 的优化手册建议存储/重新加载不是快点。)

很难想象movsb会有更好的情况,因为您已经需要一个免费的注册器,并且movsb是多个微指令。movd r32, xmm如果当前英特尔 CPU上的端口 0 uops 出现瓶颈,可能会出现瓶颈?( https://uops.info/ )


推荐阅读