我正在创建一个返回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
?
答案 0 :(得分:1)
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