这在 AVX 中使用 VBROADCASTS 命令很容易,或者在 SSE 中如果值是双精度值或浮点数。
如何将单个 8 位值广播到 Delphi ASM 中 XMM 寄存器中的每个插槽?
迈克尔的回答会奏效。作为替代方案,如果您可以假设SSSE3
指令集,那么使用Packed Shuffle Bytes pshufb
也可以。
AL
假设 (1) (例如)中的 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
是的,德尔福的 BASM 理解 SSSE3。
您的意思是您在 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
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
因为有些 CPU 只有 64 位 shuffle 单元。(包括 Merom 和 K8)。在这样的 CPU 上,pshuflw
速度很快,punpcklqdq
但在小于 64 位的粒度下速度很慢pshufd
。punpck
所以这个序列只使用一个“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
没有。)
如果指令吞吐量比延迟更重要,那么pmuludq
如果您不能使用pshufb
,则值得考虑,即使它在大多数 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