【发布时间】:2020-02-13 17:18:02
【问题描述】:
在我的学习过程中,我开始尝试使用 AVX 指令并编写了一个简单的数组乘法,只是为了让事情正常工作,非常基础。 第一个问题是 xmm0 和 xmm1 的填充,因为 nasm 不接受 XMMWORD 作为大小(yasm 接受它,但由于它不再开发,我不想使用它),我不得不填充 2 个 64 位步骤。 我发现this thread 展示了适合我的解决方案,使用了 MOVQ 和 PINSRQ。 (某种)工作的代码是:
section .data
array1: dd 1.0, 2.0, 3.0, 4.0 ; Declares 2 arrays of 16 bytes
array2: dd 2.0, 3.0, 4.0, 5.0
section .text
global _start
_start:
mov r8, qword array1 ; Stores the address of the 1st element
mov r9, qword array2 ; of each array in the registers
movq xmm0, r8 ; Populates the first half of xmm0
pinsrq xmm0, r8, 1 ; Populates the second half
movq xmm1, r9 ; The same for xmm1
pinsrq xmm1, r9, 1
vmulps xmm0, xmm1 ; Multiplies the arrays and save in xmm0
xor ebx, ebx
mov rax, 1
int 80h
但在我找到这个解决方案之前,我正在尝试:
vmovlps xmm0, qword [r8]
vmovhps xmm0, qword [r8 + 8]
这些应该填充 xmm0 寄存器的低位,然后是高位,但程序在第一个 vmov 中崩溃。那么,你们能解释一下为什么这对 mov 不起作用,而 movq/pinsrq 对却能正常工作吗?如果在这个简单的过程中有任何可以改进的地方,请随时提出建议。
========= 编辑、更新 ========
只是为了尝试将结果放回内存中,以便 rdi 指向 xmm0 中保存的 4 个 32 位值中的第一个,以防万一我想返回 rdi,这会组合输出(由 C++ 程序打印) ) 是垃圾,所以显然是错误的方式:
vmulps xmm0, xmm1 ; Multiplies the arrays and save in xmm0
vmovdqa [rdi], xmm0 ; Assembles and doesn't crash, but no meaningful result
【问题讨论】:
-
nasm与movupd或movapd没有问题,如果您的数组是对齐的。 -
movq xmm0, r8将地址放入 XMM reg 中。vmovlps xmm0, qword [r8]加载指向的 qword。 (效率低下,使用错误的依赖项和合并 uop;使用movq或movsd,而不是movlps,除非您需要 SSE1 兼容性。但是您使用的是 AVX 编码。)顺便说一句,请确保您了解 SSE/ Haswell/Icelake 与 Skylake 中的 AVX 转换惩罚,以确保如果您使用 YMM 寄存器,而不仅仅是 AVX-128,您不会在脚下开枪。 -
感谢两位 cmets。小丑,我将尝试 movupd/movapd(我实际上已经尝试过使用 vmov 变体,但 vmulps 只作用于 2 个元素,所以我认为我的加载不正确)。彼得,我会注意你说的话,因为我不需要任何 SSE 兼容性并且打算只使用 AVX。如果你们中的任何人想提出答案,我很乐意接受,因为我的疑问现在已经澄清。
-
@PeterCordes 如果我可以要求您再澄清一点, movlps/movhps 列在 AVX 指令集中。由于我使用的是 xmm 而不是 ymm,所以您对混合 SSE/AVX 的评论是什么?
-
movhps是 SSE1。vmovhps是 AVX1。 felixcloutier.com/x86/movhps。至于回答这个问题:你所说的一切似乎都是倒退的。您说从[r8]插入不起作用,但插入r8可以。请注意,您链接的答案是将立即常量插入 XMM 寄存器,这就是您对 64 位绝对地址所做的事情。我想知道您是否尝试在来自vmulps/vmovdqa的浮点数上使用printf,但这样做也是错误的,而不是使用调试器。你不能printf一个浮点数,你需要转换成双精度。