Ассемблер x86: сравнение с плавающей точкой - PullRequest
15 голосов
/ 14 августа 2011

Как часть проекта компилятора, я должен написать код ассемблера GNU для x86, чтобы сравнить значения с плавающей запятой.Я попытался найти ресурсы о том, как сделать это в Интернете, и насколько я понимаю, это работает следующим образом:

Если предположить, что значения to, которые я хочу сравнить, являются единственными значениями в стеке с плавающей запятой, то fcomi инструкция сравнит значения и установит флаги CPU, чтобы можно было использовать инструкции je, jne, jl, ...

Я спрашиваю, потому что это работает только иногда.Например:

.section    .data
msg:    .ascii "Hallo\n\0"
f1:     .float 10.0
f2:     .float 9.0

.globl main
    .type   main, @function
main:
    flds f1
    flds f2
    fcomi
    jg leb
    pushl $msg
    call printf
    addl $4, %esp
leb:
    pushl $0
    call exit

не будет печатать «Hallo», хотя я думаю, что это должно произойти, и если вы переключите f1 и f2, это не будет, что является логическим противоречием.Однако je и jne работают нормально.

Что я делаю не так?

PS: fcomip выдает только одно значение или оба?

1 Ответ

38 голосов
/ 14 августа 2011

Это все из тома 2 Руководства разработчика программного обеспечения для архитектуры Intel 64 и IA-32 .

FCOMI устанавливает только некоторые из флагов, которые CMP делает.Ваш код имеет %st(0) == 9 и %st(1) == 10.(Поскольку они загружаются в стек), обращаясь к таблице на стр. 3-348 в томе 2А, ​​вы можете видеть, что это случай "ST0 JG означает «Прыжок с короткой стороны, если больше (ZF = 0 и SF = OF)».Другими словами, он проверяет флаги знака, переполнения и нуля, но FCOMI не устанавливает знак или переполнение!

В зависимости от того, в каких условиях вы хотите прыгнуть, вы должны посмотреть на возможные результаты сравнения и решитькогда вы хотите прыгнуть.

+--------------------+---+---+---+
| Comparison results | Z | P | C |
+--------------------+---+---+---+
| ST0 > ST(i)        | 0 | 0 | 0 |
| ST0 < ST(i)        | 0 | 0 | 1 |
| ST0 = ST(i)        | 1 | 0 | 0 |
+--------------------+---+---+---+

Я сделал эту небольшую таблицу, чтобы было легче понять:

+--------------+---+---+-----+------------------------------------+
| Test         | Z | C | Jcc | Notes                              |
+--------------+---+---+-----+------------------------------------+
| ST0 < ST(i)  | X | 1 | JB  | ZF will never be set when CF = 1   |
| ST0 <= ST(i) | 1 | 1 | JBE | Either ZF or CF is ok              |
| ST0 == ST(i) | 1 | X | JE  | CF will never be set in this case  |
| ST0 != ST(i) | 0 | X | JNE |                                    |
| ST0 >= ST(i) | X | 0 | JAE | As long as CF is clear we are good |
| ST0 > ST(i)  | 0 | 0 | JA  | Both CF and ZF must be clear       |
+--------------+---+---+-----+------------------------------------+
Legend: X: don't care, 0: clear, 1: set

Другими словами, коды условий соответствуют кодам для использования без знакасравнения.То же самое происходит, если вы используете FMOVcc.

Если один из операндов (или оба) в fcomi равен NaN, он устанавливает ZF=1 PF=1 CF=1.(Сравнение FP имеет 4 возможных результата: >, <, == или неупорядоченный).Если вам небезразлично, что ваш код делает с NaN, вам могут потребоваться дополнительные jp или jnp.Но не всегда: например, ja имеет значение true, только если CF = 0 и ZF = 0, поэтому он не будет принят в неупорядоченном случае.Если вы хотите, чтобы в неупорядоченном случае использовался тот же путь выполнения, что и ниже, или равный, тогда ja - это все, что вам нужно.


Здесь вы должны использовать JA, если хотите, чтобы он печатался (т.е.. if (!(f2 > f1)) { puts("hello"); }) и JBE, если вы этого не сделаете (соответствует if (!(f2 <= f1)) { puts("hello"); }).(Обратите внимание, что это может немного сбить с толку из-за того, что мы печатаем, только если мы не прыгаем).


Относительно вашего второго вопроса: по умолчанию fcomi ничего не появляетсяВы хотите его близкого кузена fcomip, который появляется %st0.Вы должны всегда очищать стек регистров fpu после использования, так что в целом ваша программа заканчивается таким образом, если вы хотите, чтобы сообщение было напечатано:

.section    .rodata
msg:    .ascii "Hallo\n\0"
f1:     .float 10.0
f2:     .float 9.0 

.globl main
    .type   main, @function
main:
    flds   f1
    flds   f2
    fcomip
    fstp   %st(0) # to clear stack
    ja     leb # won't jump, jbe will
    pushl  $msg
    call   printf
    addl   $4, %esp
leb:
    pushl  $0
    call   exit
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...