【问题标题】:Save value of xmm registers保存 xmm 寄存器的值
【发布时间】:2017-04-12 00:43:44
【问题描述】:

我有点惊讶,我找不到太多这方面的信息。

如果我们想保存/恢复通用寄存器的值,我们可以使用pushad/popad

是否有类似的方法来保存/恢复 所有 xmm 寄存器?

【问题讨论】:

  • 指令集参考中好像没有写...见FXSAVE。 PS:pusha/popa 在 64 位模式下不再可用。
  • 试试fxsave或更新的xsave
  • 正如@fuz 所写,XSAVE 可能对您有所帮助,因为它确实将几乎所有寄存器保存到内存区域 - 与 PUSHA 非常相似,但范围更广。
  • FXSAVE 和 XSAVE 都不仅仅是保存 XMM 寄存器,而是供操作系统使用。特别是使用 XSAVE 需要操作支持。仅保存和恢复 XMM 寄存器的最快方法是将它们一个一个写入内存(例如sub esp,8*16mov 0[esp],xmm0mov 16[esp],xmm1...)

标签: assembly x86


【解决方案1】:

如果您想将 XMM/YMM 寄存器保存到内存中,您有 2 个选项:

答:使用(F)XSAVE/(F)XRSTOR,见:Intel x86-64 XSAVE/XRSTOR

为此,您需要最新的 CPU。

;save:
mov     rdx, -1              
mov     rax, -1                 ;save all possible regs
xsave   [location]

...some code that alters the registers
;restore:
mov     rdx, -1
mov     rax, -1
xrstor  [location]

B:手动将注册表保存到内存中

另一种选择是将寄存器的内容简单地存储在内存中。 假设您要将数据存储在堆栈中。
这种方法适用于任何 CPU,并且更便携,至少在接下来的十年中,这将是我最喜欢的方法。

快速而肮脏的未对齐保存

;push xmm0
lea rsp,[rsp-16*numOfRegsToSave]          ;reserve space on the stack
vmovdqu [rsp+16*0],xmm0                   ;push a register (note unaligned! write) 
vmovdqu [rsp+16*1],xmm1                   ;keep pushing
.......

;pop xmm0
.......
vmovdqu xmm1,[rsp+16*1]                   ;pop registers in reverse order
vmovdqu xmm0,[rsp+16*0]                   ;first 'pop' the register
lea rsp,[rsp+16*numOfRegsToSave]          ;then update the stack.

如果您有很多寄存器要保存,您会希望在 32 字节边界上对齐堆栈:

稍微快一点的代码(以一个损坏的寄存器为代价)

mov rbp,rsp                       ;save the stack pointer
and rsp,-32                       ;align on a 32 byte boundary
;push ymm0
lea rsp,[rsp-32*numOfRegsToSave] ;reserve space on the stack
vmovdqa [rsp+32],ymm0             ;push a register (note aligned! write)
.......

;pop ymm0
....
vmovdqa ymm0,[rsp+32]
mov rsp,rbp

总是先压入任何 ymm 寄存器,然后再压入任何其他寄存器(按大小递减顺序),这样堆栈将始终以最佳方式对齐。

请注意,x64 不再支持 pushad/popad

【讨论】:

  • 您省略了计算 XSAVE 缓冲区大小的部分,给它 64 字节对齐并初始化 XSAVE 标头。另外,当问题仅询问如何保存 XMM 寄存器时,为什么还要保存所有内容?仅保存 SSE 状态意味着您可以使用固定大小的缓冲区,因为您只使用 XSAVE 缓冲区的遗留部分,因此不需要在运行时计算其大小。
  • @Johan 嘿。要么我填错了剩余的偏移量,要么你的实现中有一个小错误。在我的示例中,我想存储 xmm0、xmm1、xmm2、xmm3。我sub rsp 16 * 4。现在,如果我从 vmovdqu [rsp+16*1],xmm0 开始在堆栈上移动寄存器内容,那么我最终会执行 rsp+16*4 覆盖以前的堆栈内容。我相信这应该是 vmovdqu [rsp+16*0],xmm0 vmovdqu [rsp+16*1],xmm1 等。
  • 请注意,这不适用于任何 CPU,因为 vmovdqu 是 AVX 指令。但是,您可以使用 movdqu,它执行完全相同的操作,但速度稍慢,这是一条 SSE2 指令,保证适用于所有 x-86-64 CPU
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-22
  • 2017-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2017-10-16
相关资源
最近更新 更多