【问题标题】:Intel VCVTTPD2QQ loads 4 numbers, not 8英特尔 VCVTTPD2QQ 加载 4 个数字,而不是 8 个
【发布时间】:2020-02-04 21:06:04
【问题描述】:

英特尔软件开发人员手册将 VC​​VTTPD2QQ 描述为:

Convert eight packed double-precision floating-point values
from zmm2/m512 to eight packed quadword integers in zmm1
using truncation with writemask k1.

我正在使用VCVTTPD2QQ将8个双精度64位浮点数加载到zmm1中,编码如下:

mov rax,18446744073709551615
KMOVQ k1,rax
EVEX.512.66.0F.W1 VCVTTPD2QQ zmm1 {k1}{z},[r11+r15]

我用全 1 填充 k1 以指示移动所有 8 个数字。

如果我这样编码,我仍然会得到相同的结果:

VCVTTPD2QQ zmm1 {k1}{z},[r11+r15]

英特尔手册通过寄存器名称区分三种可能的编码;如果名字是一个zmm寄存器,那么它应该移动8。

为什么我只有 4 个而不是 8 个数据点加载到 zmm1 中?

编辑:

我消除了写掩码,正如 Peter Cordes 在下面所说的那样,这里没有区别。我还创建了一个内存数组并将其访问为:

test_array:  dq 24.0, 93.0, 43.0, 28.0, 86.0, 143.0, 17.0, 129.0, 33.0, 67.0, 55.0

mov rdi,test_array
EVEX.512.66.0F.W1 VCVTTPD2QQ zmm1,[rdi]

调试器显示的内容与访问从外部文件(4 个元素,而不是 8 个)读取的数组相同。这是 gdb 输出:

(gdb) i r zmm1
zmm1 {v16_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v8_double = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0}, v64_int8 = {0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1c, 0x0 <repeats 39 times>}, v32_int16 = {0x18, 0x0, 0x0, 0x0, 0x5d,
0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x1c, 0x0 <repeats 19 times>},    v16_int32 = {0x18, 0x0, 0x5d, 0x0, 0x2b, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0,     0x0, 0x0, 0x0, 0x0}, v8_int64 = {0x18, 0x5d, 0x2b, 0x1c, 0x0, 0x0, 0x0,
0x0}, v4_int128 = {0x5d0000000000000018, 0x1c000000000000002b, 0x0, 0x0}}

在 v8_int64 部分中,我们看到前四个值(正确),但后四个为零且输入数据不为零。

【问题讨论】:

  • 包含一个可以重现此问题的最小、完整且可验证的示例:stackoverflow.com/help/minimal-reproducible-example
  • 你为什么要使用遮罩?如果您想要全部 8 个,只需使用未屏蔽的。另外,你为什么要用十进制写你的常数,而不是像普通人那样写-10xffffffffffffffff?或者更好,kxnorw k1, k0, k0 如果您出于某种原因确实想要一个全为掩码(而不是根本不使用掩码)。并不是说这些都会改变结果,只是你把它复杂化了。显示调试器输出,您在 ZMM 寄存器中看到“只有 4 个”值,即使指向的内存有 8 个非零 doubles。
  • 而MCVE可以像.rodata中的arr: times 4 dq 3.0, 2.0一样简单,并且这条指令带有内存源。
  • 嗯。 GDB 在这里可能有问题。尝试将 ZMM1 存储到内存并检查内存。顺便说一句,您可以p $zmm1.v8_int64 仅显示矢量注册的视图。为什么在你的 asm 源代码中包含 EVEX.512.66.0F.W1?不过,这不是问题。我从 VCVTTPD2QQ zmm1,[rdi] 获得的机器代码与使用该覆盖时相同。
  • 你有任何证据表明它真的只加载 32 字节吗?例如如果您在未映射的页面之前加载最后 32 个字节,这不会出错吗? (这极不可能;更有可能这是一个 GDB 错误,将向量的高 256 位打印为 0,而不是指令实际上执行错误。)因此,更好的标题可能是将前 4 个元素归零。

标签: assembly gdb x86-64 nasm avx512


【解决方案1】:

正如 Peter Cordes 和 Michael Petch 在他们最后的 cmets 中所怀疑的那样,这个问题是由 gdb 中的错误引起的。显然 gdb 无法显示 zmm 寄存器的高 256 位。

正如 Peter Cordes 所说,简单的测试是在内存中声明一个测试数组:

section .data
test_array2: times 8 dq 0

section .text
VCVTTPD2QQ zmm1,[r11+r15]

mov rdi,test_array2
vmovdqu64 [rdi],zmm1

mov rax,[rdi+0]
mov rax,[rdi+8]
mov rax,[rdi+16]
mov rax,[rdi+24]
mov rax,[rdi+32]
mov rax,[rdi+40]
mov rax,[rdi+48]
mov rax,[rdi+56]

使用 gdb 遍历每一行 rax,[rdi+xxx] 并查看 rax 中的值。在我的例子中,所有 8 个值都与输入数据匹配,即使 gdb 将 zmm1 的高 256 位显示为零。

如果我在原始问题中发布了 MCR 示例,其他人可能会更早发现这一点。

感谢所有 cmets。

【讨论】:

  • 您应该考虑使用内在函数而不是汇编。编译器会知道它可以做到vmovdqu64 [test_array2], zmm1。您还可以使用调试器直接显示内存区域,而无需将其逐个加载到寄存器中。
猜你喜欢
  • 1970-01-01
  • 2019-11-19
  • 2017-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
  • 2023-03-11
  • 2017-03-04
相关资源
最近更新 更多