曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4510|回复: 1
打印 上一主题 下一主题

保存与恢复FPU 环境与状态

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2010-3-13 22:28:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在现在的 IA-32 处理器中,FPU 数据寄存器必须完成双重工作。MMX 技术使用 FPU 数据寄存器作为 MMX 数据寄存器。如果在同一个程序中使用 FPU 和 MMX 功能,那么就有可能“破坏”数据寄存器。

为了防止这种情况,IA-32 平台提供了几个指令,它们用以保存 FPU 处理器的的状态并且在其他处理完成之后恢复先前的状态。

1、保存和恢复 FPU 环境

FSTENV 
指令用于把 FPU 的环境存储到一个内存块中。下面的 FPU 寄存器被存储:
      控制寄存器
      状态寄存器
      标记寄存器
      FPU 指令指针偏移量
      FPU 数据指针
      FPU 最后执行的操作码
上面的这些值存储在一个 28 字节的内存块中。

FLDENV 指令用于把内存块的值加载回 FPU 环境中。

演示程序:
.section .data
value1:
    .float 12.34
value2:
    .float 56.789
rup:
    .byte 0x7f, 0x0b

.section .bss
    .lcomm buffer, 28

.section .text
.global _start
_start:
    nop
    finit
    flds value1
    flds value2
    fldcw rup          #设置状态寄存器并保存
    fstenv buffer      #存储 FPU 环境

    finit
    flds value2
    flds value1       
   
    fldenv buffer     #恢复 FPU 环境
   
    movl $1, %eax
    movl $0, %ebx
    int $0x80
说明
在程序中,在执行 fldcw 指令之前,控制寄存器和状态寄存器中的内容为:
fctrl          0x37f    895
fstat          0x0    0
在执行了 flds value1 后,再看一下相关寄存器:
st0            12.340000152587890625    (raw 0x4002c570a40000000000)
fctrl          0x37f    895
fstat          0x3800    14336
从状态寄存器中的内容可以看到,现在堆栈指针指向 R7 (11-13位为堆栈指针),即 ST0。

再执行 flds value2 后,再看一下相关寄存器:
st0            56.78900146484375    (raw 0x4004e327f00000000000)
st1            12.340000152587890625    (raw 0x4002c570a40000000000)
fstat          0x3000    12288
由上可见,堆栈指针下移了。

在执行 fldcw 指令之后,控制寄存器和中的内容为:
fctrl          0xb7f    2943
控制寄存器中的内容已被改变。

在使用 fstenv 进行 FPU 环境的保存后,查看一下保存后的环境内容为:
(gdb) x/28xb &buffer
0x80490c0 <buffer>:    0x7f    0x0b    0xff    0xff    0x00    0x30    0xff    0xff
0x80490c8 <buffer+8>:    0xff    0x0f    0xff    0xff    0x7e    0x80    0x04    0x08
0x80490d0 <buffer+16>:    0x73    0x00    0x05    0x01    0xb8    0x90    0x04    0x08
0x80490d8 <buffer+24>:    0x7b    0x00    0xff    0xff

再次执行 finit 指令初始化了 FPU 的环境,然后再压入 value2 和 value1 (注意,第一次是先压 value1 再压 value2)。这么做的原因是,打算测试一下 fldenv 指令是否能同时恢复数据寄存器中以及 FPU 的相关环境。

在执行 fldenv 指令后再看一下 FPU 中的相关寄存器:
fctrl          0xb7f    2943
fstat          0x3000    12288
st0            12.340000152587890625    (raw 0x4002c570a40000000000)
st1            56.78900146484375    (raw 0x4004e327f00000000000)
由上可见,FPU 的环境被恢复了,但是数据寄存器中的内容没有得到恢复。这说明了 fldenv 不负责对数据寄存器中的内容进行恢复。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
沙发
 楼主| 发表于 2010-3-13 23:34:11 | 只看该作者

保存和恢复 FPU 状态

上面看到,FSTENV 指令存储了 FPU 环境,但是不能恢复 FPU 数据寄存器。

为了保存包括数据在内的完整 FPU 环境,必须使用 FSAVE 指令。

FSAVE 指令把所有 FPU 寄存器复制到一个 108 字节的内存位置,然后初始化 FPU 状态。使用 FRSTOR 指令恢复 FPU 时,所有 FPU 寄存器 (包括数据寄存器)都会被恢复。
.section .data
value1:
    .float 12.34
value2:
    .float 56.789
rup:
    .byte 0x7f, 0x0b

.section .bss
    .lcomm buffer, 108

.section .text
.global _start
_start:
    nop
    finit
    flds value1
    flds value2
    fldcw rup
    fsave buffer
   
    flds value2
    flds value1

    frstor buffer
   
    movl $1, %eax
    movl $0, %ebx
    int $0x80
程序说明
这里要注意在执行 FSAVE 指令之后的情况。在 GDB 里查看一下寄存器:
... ...
fctrl          0x37f    895
st0            0    (raw 0x00000000000000000000)
st1            0    (raw 0x00000000000000000000)
st2            0    (raw 0x00000000000000000000)
st3            0    (raw 0x00000000000000000000)
st4            0    (raw 0x00000000000000000000)
st5            0    (raw 0x00000000000000000000)
st6            56.78900146484375    (raw 0x4004e327f00000000000)
st7            12.340000152587890625    (raw 0x4002c570a40000000000)
... ...
从上面的显示来看,堆栈的顶部被移动了,原来顶部的值现在位于寄存器堆栈的底部。并且,控制寄存器的值也被复位为默认值。可以在调试器中查看缓冲区内存位置中的值的情况:
(gdb) x/108xb &buffer
0x80490c0 <buffer>:    0x7f    0x0b    0xff    0xff    0x00    0x30    0xff    0xff
0x80490c8 <buffer+8>:    0xff    0x0f    0xff    0xff    0x7e    0x80    0x04    0x08
0x80490d0 <buffer+16>:    0x73    0x00    0x05    0x01    0xb4    0x90    0x04    0x08
0x80490d8 <buffer+24>:    0x7b    0x00    0xff    0xff    0x00    0x00    0x00    0x00
0x80490e0 <buffer+32>:    0x00    0xf0    0x27    0xe3    0x04    0x40    0x00    0x00
0x80490e8 <buffer+40>:    0x00    0x00    0x00    0xa4    0x70    0xc5    0x02    0x40
0x80490f0 <buffer+48>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x80490f8 <buffer+56>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049100 <buffer+64>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049108 <buffer+72>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049110 <buffer+80>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049118 <buffer+88>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049120 <buffer+96>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x8049128 <buffer+104>:    0x00    0x00    0x00    0x00
缓冲区里不仅包含了控制、状态和标记寄存器,还包含了 FPU 数据寄存器的值。执行 FRSTOR 指令后,可以查看所有的寄存器,它们都被恢复位执行 FSAVE 时的状态。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-14 17:40 , Processed in 0.065214 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表