向 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 位的 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
则没有。 )
如果指令吞吐量比延迟更重要,那么如果您不能使用 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
这在 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 位的 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
则没有。 )
如果指令吞吐量比延迟更重要,那么如果您不能使用 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