向 Delphi ASM 中的所有 16 个 XMM 插槽广播一个字节值

Broadcast a byte value to all 16 XMM slots in Delphi ASM

这在 AVX 中使用 VBROADCASTS 命令很容易,或者在 SSE 中,如果值是双精度或浮点数。

如何将一个 8 位值广播到 Delphi ASM 中 XMM 寄存器中的每个槽?

您的意思是您在 XMM 寄存器的 LSB 中有一个字节并且想要在该寄存器的所有通道中复制它?我不知道 Delphi 的内联汇编语法,但在 Intel/MASM 语法中可以这样做:

punpcklbw xmm0,xmm0    ; xxxxxxxxABCDEFGH -> xxxxxxxxEEFFGGHH
punpcklwd xmm0,xmm0    ; xxxxxxxxEEFFGGHH -> xxxxxxxxGGGGHHHH
punpckldq xmm0,xmm0    ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH
punpcklqdq xmm0,xmm0   ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH

Michael 的回答有效。作为替代方案,如果您可以假设 SSSE3 指令集,那么使用 Packed Shuffle Bytes pshufb 也可以。

假设 (1) AL 中的一个 8 位值(例如)和 (2) 所需的广播目的地是 XMM1,以及 (3) 另一个寄存器,比如说 XMM0,是可用的,这就可以了:

movd   xmm1, eax  ;// move value in AL (part of EAX) into XMM1
pxor   xmm0, xmm0 ;// clear xmm0 to create the appropriate mask for pshufb
pshufb xmm1, xmm0 ;// broadcast lowest value into all slots of xmm1

是的,Delphi 的 BASM 理解 SSSE3。

最快的选项是 pshufb 的 SSSE3(如果可用的话)。

; SSSE3
pshufb      xmm0,  xmm1       ; where xmm1 is zeroed, e.g. with pxor xmm1,xmm1

否则你通常应该使用这个:

; SSE2 only
punpcklbw   xmm0, xmm0        ; xxxxxxxxABCDEFGH -> xxxxxxxxEEFFGGHH
pshuflw     xmm0, xmm0, 0     ; xxxxxxxxEEFFGGHH -> xxxxxxxxHHHHHHHH
punpcklqdq  xmm0, xmm0        ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH

这比 punpckl bw / wd -> pshufd xmm0, xmm0, 0 更好,因为有 some CPUs with only 64-bit shuffle units. (Including Merom and K8)。在这样的 CPU 上,pshuflw 很快,punpcklqdq 也很快,但是粒度小于 64 位的 pshufdpunpck 很慢。所以这个序列只使用一个 "slow shuffle" 指令,而 bw / wd / pshufd 使用 3 个指令。

在所有后来的 CPU 上,这两个 3 指令序列之间没有区别,因此在这种情况下,我们无需为旧 CPU 进行调优。另见 http://agner.org/optimize/ 指令表。

这是迈克尔回答的序列,中间两条指令被 pshuflw 替换。


如果您的字节开始于整数寄存器,您可以使用乘以 0x01010101 将其广播为 4 个字节。例如

; movzx   eax, whatever

imul   edx, eax, 0x01010101    ; edx = al repeated 4 times

movd   xmm0, eax
pshufd xmm0, xmm0, 0

请注意,imul 的非立即源操作数可以是内存,但它必须是 32 位内存位置,您的字节零扩展为 32 位。


如果您的数据从内存开始,首先加载到整数寄存器可能不值得。只需 movd 到 xmm 寄存器。 (或者可能 pinsrb 如果您需要避免更大的负载以避免跨越页面或缓存行。但这对寄存器的旧值有错误的依赖性,而 movd 则没有。 )

如果指令吞吐量比延迟更重要,那么如果您不能使用 pshufb,则值得考虑 pmuludq,即使它在大多数 CPU 上有 5 个周期的延迟。

; low 32 bits of xmm0 = your byte, **zero extended**
pmuludq xmm0, xmm7        ; xmm7 = 0x01010101 in the low 32 bits
pshufd  xmm0, xmm0, 0