我正在做一个函数,返回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?
发布于 2018-05-13 23:33:52
TL:您可能会将fpuctl: .word 0与代码一起放在只读.text部分。存储到堆栈上的一些划痕空间,或者如果您真的想使用静态存储,则存储到BSS。
你说得对,fnstcw是记忆的目的地.更常用的fnstsw %ax ( x87状态词)有一个AX目的地形式,但fnstcw没有。
(当然,x87是过时的,除非您实际上需要80位精度;现代x86至少有SSE2,所以您可以并且应该在XMM寄存器中进行标量FP计算。。SSE的控件和状态位都在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作为一个划痕内存位置,而不是一个函数。作为一个非内联函数,这太小了;它是两个有用的指令(fnstcw和重新加载);其余的都是开销。
顺便说一句,AX是一个16位寄存器。AL是EAX的低8位。
此外,movzwl fpuctl, %eax将执行零扩展字加载,这样您就不需要先xor-零eax。
您还没有提供MCVE,但是fpuctl: .word 0 可能会将与代码一起放在“只读”一节中,这样就可以得到与使用mov %eax, getFPUControlState相同的错误。
相反,将其放入BSS (零init静态存储,其中零没有存储在您的可执行文件中,只是总大小)。
.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作为另一种选择,将在BSS中保留2个字节和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或者,如果您宁愿以存储转发摊档为代价来优化代码大小,那么在一个单词存储之后再加载一个dword:
push $0
fnstcw (%esp)
pop %eax # upper 2 bytes are zero from the PUSHed data
rethttps://stackoverflow.com/questions/50308366
复制相似问题