已经有a question这个,但它被关闭为“含糊不清”所以我开了一个新的 - 我找到了答案,也许它也会帮助其他人。
问题是:如何编写汇编代码序列来初始化具有128位立即(常量)值的XMM寄存器?
答案 0 :(得分:16)
只是想补充一点,可以在Agner Fog的手册Optimizing subroutines in assembly language,生成常量,第13.4节,第121页中使用汇编来阅读有关生成各种常量的信息。
答案 1 :(得分:10)
只需一条movaps
指令就可以这样做:
.section .rodata # put your constants in the read-only data section
.p2align 4 # align to 16 = 1<<4
LC0:
.long 1082130432
.long 1077936128
.long 1073741824
.long 1065353216
.text
foo:
movaps LC0(%rip), %xmm0
通过数据加载加载它通常比将其嵌入指令流更好,特别是因为它需要多少指令。这是CPU执行的几个额外的uop,用于一个任意常量,无法通过几次移位从全部生成。
如果它更容易,你可以在jit-compile的函数之前或之后放置常量,而不是在单独的部分中。但由于CPU已经拆分了L1d / L1i缓存和TLB,因此通常最好将常量与指令分开组合。
如果你的常数的两半都相同,你可以用SSE3广播加载它
movddup (m64), %xmm0
。
答案 2 :(得分:7)
作为10000种方法之一,请使用SSE4.1 pinsrq
mov rax, first half
movq xmm0, rax ; better than pinsrq xmm0,rax,0 for performance and code-size
mov rax, second half
pinsrq xmm0, rax, 1
答案 3 :(得分:6)
在指令流中有多种嵌入常量的方法:
因此,虽然无法在XMM
寄存器中执行立即加载,但可以从存储的值中进行PC相对加载(64位)“ “到代码执行的地方。这就产生了类似的东西:
.align 4
.val:
.long 0x12345678
.long 0x9abcdef0
.long 0xfedbca98
.long 0x76543210
func:
movdqa .val(%rip), %xmm0
当您拆卸时:
0000000000000000 : 0: 78 56 34 12 f0 de bc 9a 8: 98 ca db fe 10 32 54 76 0000000000000010 : 10: 66 0f 6f 05 e8 ff ff movdqa -0x18(%rip),%xmm0 # 0
完全紧凑,23字节。
其他选项是在堆栈上构造值并再次从那里加载它。在32位x86中,您没有%rip
- 相对内存访问,仍然可以在24字节中执行此操作(假设堆栈指针在条目上对齐;否则,需要未对齐的加载):
00000000 : 0: 68 78 56 34 12 push $0x12345678 5: 68 f0 de bc 9a push $0x9abcdef0 a: 68 98 ca db fe push $0xfedbca98 f: 68 10 32 54 76 push $0x76543210 14: 66 0f 6f 04 24 movdqa (%esp),%xmm0
虽然在64位(ABI保证函数入口处的堆栈指针对齐)需要27字节:
0000000000000000 : 0: 48 b8 f0 de bc 9a 78 56 34 12 movabs $0x123456789abcdef0,%rax a: 50 push %rax b: 48 b8 10 32 54 76 98 ba dc fe movabs $0xfedcba9876543210,%rax 15: 50 push %rax 16: 66 0f 6f 04 24 movdqa (%rsp),%xmm0
如果您将其中任何一个与MOVLHPS
版本进行比较,您会发现它是最长的:
0000000000000000 : 0: 48 b8 f0 de bc 9a 78 56 34 12 movabs $0x123456789abcdef0,%rax a: 66 48 0f 6e c0 movq %rax,%xmm0 f: 48 b8 10 32 54 76 98 ba dc fe movabs $0xfedcba9876543210,%rax 19: 66 48 0f 6e c8 movq %rax,%xmm1 1e: 0f 16 c1 movlhps %xmm1,%xmm0
33字节。
直接从指令内存加载的另一个好处是movdqa
不依赖于之前的任何内容。最有可能的是,@ Paul R给出的第一个版本是你能获得的最快版本。
答案 4 :(得分:5)
最好的解决方案(特别是如果你想坚持SSE2 - 即避免使用AVX)用你的立即值的两个64位半来初始化两个寄存器(比如xmm0和xmm1),做MOVLHPS xmm0,xmm1 为了初始化64位值,最简单的解决方案是使用通用寄存器(例如,AX),然后使用MOVQ将其值传输到XMM寄存器。 所以序列将是这样的:
MOV RAX, <first_half>
MOVQ XMM0, RAX
MOV RAX, <second_half>
MOVQ XMM1, RAX
MOVLHPS XMM0,XMM1