Assembly x64 ymm/SIMD/向量指令,其中ymm寄存器在寄存器中指定?

Assembly x64 ymm/SIMD/向量指令,其中ymm寄存器在寄存器中指定?,assembly,x86-64,simd,Assembly,X86 64,Simd,如果ymm寄存器在通用寄存器(或SIMD寄存器)中指定,而不是在指令本身中指定,是否存在任何SIMD/向量寄存器指令 基本上,我要做的是编写一个函数,将任何一系列连续的ymm寄存器保存到本地帧上。这里的想法是,除了我发明了我认为半可信的虚构语法的指令,我正在寻找。 .text .align 64 funcname: orq %rcx, %rcx # is register count <= 0 ??? jle f

如果ymm寄存器在通用寄存器(或SIMD寄存器)中指定,而不是在指令本身中指定,是否存在任何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
另一种选择是
%rsi
中的位0到15对应于向量寄存器
ymm00
ymm15
,并保存对应于最低设置位的寄存器(或保存所有“设置位”ymm寄存器)

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

说到“SIMD加载/存储”,有两种不同的方法:

  • 执行“宽负载”,从同一地址的连续数据填充多个向量寄存器
  • 进行“分散-聚集”,在多个地址之间填充向量成分
ARM和/或m68k始终执行前者-这些平台上的“移动多个”操作码(ARM上的
[v]ldm
,m68k上的
movem
)允许指定一个掩码,该掩码枚举从给定地址的(连续)数据填充的寄存器

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

Intel与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 ]
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, ... ] 
另一方面,x86
VGATHER
指令的ARM等价物(请参阅)类似于:

VGATHERDD YMM0, [ RAX + YMM1 ]   ; YMM1[0..8] has 32bit offsets on RAX
需要将多个加载到向量寄存器的单个元素,并在末尾使用“合并”或子寄存器加载;它会变成一个序列:

VMOVAPD YMM1, [ RAX ]
VMOVAPD YMM3, [ RAX + 32 ]
VMOVAPD YMM5, [ RAX + 64 ]
VMOVAPD YMM7, [ RAX + 96 ]
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, ... ] 

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

保存的特定状态组件对应于中设置的位 请求的功能位图(RFBM),它是EDX:EAX的逻辑AND 和XCR0

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

对于寄存器(寄存器指定哪个寄存器),没有其他指令具有额外级别的间接寻址。这将是一个大的复杂化的无序机制的实施。即使ARM加载多条指令(参见另一个答案),也会将寄存器位掩码嵌入到指令中,因此在解码指令时(而不是在以后填充指令时)可以使用寄存器位掩码


您可能会更好地使用任何您想要使用的向量寄存器的明显存储/重新加载,但在您正在设计的调用约定中,这些都是保留的调用


请注意,将来扩展到更宽的向量意味着您将只保留所选向量regs中较低的256b,而上面的位将被删除。(当被叫方不接触它们时,不管是否为零,而不是保存/恢复)。

您不能这样做。你可以使用一个跳转表(或者仅仅是一堆分支)来实现它,但它会很糟糕。也许如果你解释一下这实际上是为了什么(在比“保存一系列ymm注册表”更高的层次上),我/其他人可以想出一些其他的方法ideas@harold当前位置考虑到这是为了什么,我确信解释我自己会让我因为提出明显不恰当的问题而受到残酷的打击!我去过那里,以前做过。这里有些人对我的品味太敏感了。是的,如果没有什么好主意,跳转表是必要的。你可以做地址算术(并使用一些填充词来完成)来跳转表,但这并没有多大好处。@harold:我猜你只是说“测量实现每个保存的字节数”,然后适当地调整调用地址(而不是有一个地址数组)。如果是这样,我怀疑跳转表更有效。但我知道你要去哪里…基本上使代码段适合各种类型的代码数组,每个代码块的长度与所有其他代码块的长度相同。这很恶心,但这必须是尽可能快的,所以…我也会研究一下。PS:新的64位ABI支持吗t“智能功能协议”用于32*512位zmm寄存器。忽略此!:-)@honestann您是通过ARM更改的吗?ARM上的程序集(以及在此之前的m68k)一直都知道“多寄存器存储/加载”操作,即在一条指令中加载/存储通用寄存器或向量寄存器的任意子集的能力。唉,这不是英特尔在他们的体系结构中实现的东西……谢谢。是的,这些指令非常适合编写许多向量处理例程。对我来说不幸的是,我正在寻找一些不同的东西。有些人希望[某些]加法16向量寄存器“保留”,而另一些人希望它们像当前的16向量寄存器一样“从头开始”。我在研究一个schere,其中函数标签前面的32/64位[bitmask或count]值告诉我哪个寄存器