Ассемблер x86: сравнение с плавающей запятой

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

Предполагая, что значения, которые я хочу сравнить, являются единственными значениями в стеке с плавающей запятой, тогда команда fcomi будет сравнивать значения и устанавливать ЦП-флаги, чтобы можно было использовать команды 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 только одно значение или он всплывает?

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

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

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

 + -------------------- + --- + --- + --- +
 |  Результаты сравнения |  Z |  P |  C |
 + -------------------- + --- + --- + --- +
 |  ST0> ST (i) |  0 |  0 |  0 |
 |  ST0 

Я сделал этот маленький стол, чтобы было легче понять:

 + -------------- + --- + --- + ----- + -------------------- ---------------- +
 |  Тест |  Z |  C |  Jcc |  Примечания |
 + -------------- + --- + --- + ----- + -------------------- ---------------- +
 |  ST0  = ST (i) |  X |  0 |  JAE |  Пока CF ясно, мы хорошие |
 |  ST0> ST (i) |  0 |  0 |  JA |  Оба CF и ZF должны быть четкими |
 + -------------- + --- + --- + ----- + -------------------- ---------------- +
 Обозначения: X: все равно, 0: clear, 1: set

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

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


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


Что касается вашего второго вопроса: по умолчанию fcomi ничего не fcomi . Вы хотите, чтобы его близкий родственник fcomip который pops %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 

Давайте будем гением компьютера.