Хранение управляющего слова x87 FPU - PullRequest
0 голосов
/ 12 мая 2018

Я делаю функцию, которая возвращает регистр управляющего слова 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 голосов
/ 14 мая 2018

TL: DR: вы, вероятно, добавили fpuctl: .word 0 в раздел только для чтения .text вместе со своим кодом. Храните на некотором пустом месте в стеке или на BSS, если вы действительно хотите использовать статическое хранилище.


Вы правы, единственная форма fnstcw - это адресат памяти. Более часто используемый fnstsw %ax (слово x87 status ) имеет форму AX-destination, но fnstcw не имеет.

(Конечно, x87 устарел, за исключением случаев, когда вам действительно нужна 80-битная точность; современный x86 имеет как минимум SSE2, так что вы можете и должны выполнять скалярную FP математику в регистрах XMM . Управление и утилита 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. (Для изменения среды FP вы должны использовать стандартные функции C из fenv.h, например fegetenv / fesetenv.)

Если вы собираетесь использовать это из написанного от руки asm, сделайте его макросом, который принимает 2-й аргумент в качестве ячейки памяти, вместо функции. Это слишком мало, чтобы иметь смысл как не встроенная функция; это 2 полезные инструкции (fnstcw и перезагрузка); остальное накладные расходы.


Кстати, AX - это 16-битный регистр. AL - младшие 8 бит EAX.

Кроме того, movzwl fpuctl, %eax будет выполнять загрузку слова с расширением нуля, поэтому вам не нужно будет сначала xor-zero eax.


Вы не предоставили 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

В качестве альтернативы используйте .lcomm, чтобы зарезервировать 2 байта в 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
...