【问题标题】:x64 ymm/SIMD/vector instructions where ymm register is specified in a register?x64 ymm/SIMD/vector 指令,其中 ymm 寄存器在寄存器中指定?
【发布时间】:2014-02-26 18:22:09
【问题描述】:

是否存在任何 SIMD/向量寄存器指令,其中 ymm 寄存器是在通用寄存器(或 SIMD 寄存器)而不是指令本身中指定的?

基本上我要做的是编写一个函数,将任何一系列连续的 ymm 寄存器保存到本地帧上。这是我的想法,除了我正在为我正在寻找的指令发明我认为半似是而非的虚构语法。

.text
.align 64
funcname:
    orq         %rcx, %rcx                # is register count <= 0 ???
    jle         funcdone                  # # of registers to save is <= 0

    xorq        %rax, %rax                # rax = 0 == vector save element #

funcloop:
    vmovapd     %ymm(%rsi), (%rdi, %rax)  # save ymm?? to address rdi + rax
    addq        $32, %rax                 # each ymm vector is 32-bytes
    loop        funcloop                  # --rcx; if (rcx > 0) { goto funcloop }

funcdone:
    ret

vmovapd 指令是我正在寻找的奇怪指令。我敢肯定我从未见过这样的指令,但这并不意味着没有一些不寻常的指令可以完成我需要做的事情。

或者该指令可能看起来像以下之一:

    vmovapd     %rsi, (%rdi, %rax)
    vmovapd     (%rsi), (%rdi, %rax)

另一种选择是%rsi 中的位 0 到 15 对应于向量寄存器 ymm00ymm15,并且与最低设置位对应的寄存器被保存(或所有“设置位”ymm 寄存器都被保存) .

顺便说一句,对于我需要完成的事情,自我修改代码不是一种选择。

【问题讨论】:

  • 你不能那样做。您可以使用跳台(或只是一大堆分支)并实现它,但它会很糟糕。也许如果你解释这实际上是为了什么(在比“保存一系列 ymm regs”更高的层次上)我/其他人可以想出一些其他的想法
  • @harold:考虑到这是为了什么,我敢肯定,我解释自己会因为提出公然不恰当的问题而受到残酷的打击!去过那里,以前做过。这里的一些人对我的口味的规则过于敏感。但是,是的,如果没有什么绝妙的主意,跳表是必要的。
  • 你可以做地址算术(并使用一些填充物来解决它)来跳过表格,但这并没有(如果有的话)更好。
  • @harold:我猜你只是在说“测量每次保存需要多少字节”,然后适当地调整调用地址(而不是有一个地址数组)。如果是这样,我怀疑跳转表效率更高。但我知道你要去哪里......基本上使代码段适合各种代码数组,每个代码块的长度与所有其他代码块相同。这太恶心了,但这肯定是尽可能快的,所以......我也会调查一下。 PS:新的 64 位 ABI 支持 32 * 512 位 zmm 寄存器的“智能功能协议”。忽略这个! :-)
  • @honestann 你是来自 ARM 的变化吗? ARM 上的程序集(以及在此之前的 m68k)一直都知道“多寄存器存储/加载”操作 - 即能够在单个指令中加载/存储通用或向量寄存器的任意子集。唉,这不是英特尔在他们的架构中实现的东西......

标签: assembly x86-64 simd


【解决方案1】:

当涉及到“SIMD 加载/存储”时,可能有两种不同的方法:

  • 执行“宽负载”,从同一地址的连续数据填充多个向量寄存器
  • 进行“分散收集”,将向量成分填充到/来自多个地址

ARM 和/或 m68k 一直都是前者——这些平台上的“移动多个”操作码(ARM 上的[v]ldm,m68k 上的movem)允许指定一个掩码,该掩码枚举要填充的寄存器(连续) 给定地址的数据。

英特尔的 x86 在其历史上从未有过这种情况,除了 32 位的 PUSHA / POPA 将无条件/不可屏蔽地将通用寄存器保存/恢复到堆栈中/从堆栈中恢复,并且该指令已退役64位模式。

英特尔通过 AVX 创造了同时从多个地址加载的能力,即“分散-收集”。

是的,x86 替代了 ARM 上的某些东西,例如:

VLDM.64 r0, {d1, d3, d5, d7}    ; load odd-numbered vector regs from mem @r0

将是一个序列:

VMOVAPD YMM1, [ RAX ]
VMOVAPD YMM3, [ RAX + 32 ]
VMOVAPD YMM5, [ RAX + 64 ]
VMOVAPD YMM7, [ RAX + 96 ]

另一方面,x86 VGATHER 指令的 ARM 等效项(参见 ARM docs, "indirect addressing")如下:

VGATHERDD YMM0, [ RAX + YMM1 ]   ; YMM1[0..8] has 32bit offsets on RAX

需要对向量寄存器的单个元素进行多次加载,并在末尾加上“组合” - 或子寄存器加载;它会变成一个序列:

VLD1.32 {d0}, [r0]                    ; r0..r3 have the [32bit] addresses
VLD1.32 {d1}, [r1]
VLD1.32 {d2}, [r2]
VLD1.32 {d3}, [r3]
VTBL d0, { d0 - d3 }, d4         ; d4 is [ 0, .., 7, ..., 15, ..., 31, ... ] 

【讨论】:

  • 谢谢。是的,这些指令非常适合编写许多矢量处理例程。对我来说不幸的是,我正在寻找一些不同的东西。有些人希望 [一些] 添加的 16 个向量寄存器“保留”,而另一些人希望它们像当前的 16 个一样“从头开始”。我正在研究一个 32/64 位 [位掩码或计数]函数标签之前的值告诉被调用者修改了哪些寄存器,并且可能还让调用者在调用之前在 r11 中放置一个类似的值。那么保存/恢复的次数总是只是需要的。
  • 这可以通过几种方式完成,但是这个方案看起来比我想要的 x86-64 指令集更奇怪。我会在一段时间内寻找更好的方法,但在这一点上,我们可能不得不接受将现有/传​​统方法扩展到额外的 16 个寄存器。这些额外的 16 个向量寄存器应该被指定为“scratch”,但不幸的是,不了解所有问题的人会施加压力,要求将它们中的一部分或全部保留下来。表面上看起来不错,但更仔细、更详细的分析表明这是不明智的。谢谢。
【解决方案2】:

x86 的状态保存指令 (xsave/xrstor)edx:eax 中使用掩码来控制要保存/恢复的状态。这真的很复杂,并且 insn 参考手册只是将您指向另一手册的一个完整单独的部分。但是,如果您可以在单个向量寄存器级别进行选择,则 IDK。 “所有 16 个向量 reg 的低 128”更有可能只有一个位,但 ymm0-7 与其余部分是分开的,以避免在 32 位代码无法影响它们时保存/恢复 ymm8-15。

保存的具体状态分量对应于设置的位 请求特征位图 (RFBM),它是 EDX:EAX 的逻辑与 和 XCR0。

对于在函数序言/尾声中保存/恢复一些 ymm regs,它不太可能有用。我没有调查过。 xsavec 执行“压缩”:CPU 跟踪哪些状态段实际被修改。

没有其他指令具有额外的寄存器间接级别(寄存器指定哪个寄存器)。对于无序机器的实施来说,这将是一个很大的复杂性。甚至 ARM 加载多条指令(请参阅另一个答案)也将寄存器位掩码嵌入到指令中,因此它在指令被解码时可用(而不是稍后填充)。


您可能最好对要使用的任何向量寄存器进行明显的存储/重新加载,但在您设计的调用约定中保留调用。

请注意,未来对更宽向量的扩展意味着您最终将只保留所选向量 reg 的低 256b 位,而高于该位的位将被破坏。 (归零与否,当被调用者不触摸它们时,而不是保存/恢复)。

【讨论】:

    猜你喜欢
    • 2017-06-08
    • 2017-05-30
    • 2018-06-16
    • 2020-12-26
    • 1970-01-01
    • 2019-01-02
    • 2017-03-30
    • 2020-02-25
    • 1970-01-01
    相关资源
    最近更新 更多