存储x87 FPU控制字

时间:2018-05-12 16:23:31

标签: assembly x86 att fpu x87

我正在创建一个返回FPU控制字寄存器(16位)的功能 根据文档,我必须使用fstcw和记忆位置。

我在记忆中的位置是:

fpuctl: .word 0

我的功能是:

.global getFPUControlState
    .type getFPUControlState, function
    getFPUControlState:
    pushl %ebp
    movl %esp, %ebp 
    xorl %eax, %eax #clear eax (ax too)

    fnstcw fpuctl #store in fpuCTL
    mov fpuctl, %ax #put it in 8bit %ax

    leave
    ret

控制台说:

  

内存保护违规。

如何正确使用fnstcw

1 个答案:

答案 0 :(得分:1)

TL:DR:您可能会将fpuctl: .word 0与您的代码一起放入只读.text部分。存储到堆栈上的某个临时空间,如果您真的想使用静态存储,则存储到BSS。

你是对的,the only form of fnstcw是记忆目的地。   较常用的fnstsw %ax(x87 状态字)具有AX目标表单,但fnstcw不具有。

(当然,x87已经过时了,除非你实际需要80位精度; modern x86 has at least SSE2 so you can and should do scalar FP math in XMM registers。SSE FPU的控制和状态位都在MXCSR中,与x87控制和状态分开。)

还要注意如果你从C调用它并转到修改 x87控制字,你需要告诉编译器所以它不是假设舍入模式仍然是舍入到最接近的,或者精度仍然是80位(64位尾数)。这对于编译时常量传播和其他优化很重要。例如,gcc有-frounding-math-fsignaling-nans。它也可能支持C99 #pragma STDC FENV ACCESS ON,但我不确定gcc或clang是否完全符合标准。另见https://gcc.gnu.org/wiki/FloatingPointMath。 (您应该使用fenv.h中的标准C函数来修改FP环境,例如fegetenv / fesetenv。)

如果您要从手写的asm中使用它,请将其作为一个宏,将第二个arg作为临时内存位置,而不是函数。这个太小而不能作为非内联函数;它是2个有用的指令(fnstcw和重新加载);其余的都是开销。

BTW,AX是一个16位寄存器。 AL是EAX的低8位。

此外,movzwl fpuctl, %eax将执行零扩展字加载,因此您不需要首先对xax进行xor-zero。

您尚未提供MCVE,但可能您将fpuctl: .word 0部分与您的代码一起放入只读.text部分,因此您会收到与如果您使用mov %eax, getFPUControlState

相反,将其放入BSS (零初始化静态存储,其中零不存储在您的可执行文件中,只是总大小)。

.bss
fpuctl: .zero 2         # 2 bytes of zeros in the BSS

.text                   # switch back to the .text section
.globl getFPUControlState         # GAS does accept .global as a synonym for .globl
.type getFPUControlState, function
getFPUControlState:
       # leave out the EBP stack-frame stuff, you're not using it for anything
    fnstcw   fpuctl
    movzwl   fpuctl, %eax       # zero-extending word load into EAX

    ret
.size getFPUControlState, . - getFPUControlState

作为替代方案,use .lcomm to reserve 2 bytes in the BSS并使用fpuctl将其标记为非导出标签:

.lcomm  fpuctl, 2     # puts stuff in the BSS regardless of current section

但实际上,您根本不需要静态存储

因此,只需使用堆栈就可以为自己节省潜在的页面错误。

.globl getFPUControlState
.type getFPUControlState, function
getFPUControlState:

    sub      $4, %esp          # reserve space on the stack
    fnstcw   (%esp)
    movzwl   (%esp), %eax
    add      $4, %esp          # restore ESP to point at the return address

    ret

或者,如果你想在单词存储之后以双字加载的商店转发停顿为代价来优化代码大小:

    push     $0
    fnstcw   (%esp)
    pop      %eax         # upper 2 bytes are zero from the PUSHed data
    ret