2011-08-14 48 views
15

Como parte de un proyecto de compilación, debo escribir el código ensamblador GNU para x86 para comparar los valores de punto flotante. He intentado encontrar recursos sobre cómo hacer esto en línea y por lo que entiendo funciona así:ensamblador x86: coma flotante comparar

Suponiendo que los valores a que deseo comparar son los únicos valores en la pila de coma flotante, entonces la instrucción fcomi se comparará los valores y configure los indicadores de CPU para que se puedan usar las instrucciones je, jne, jl, ...

Pregunto porque esto solo funciona a veces. Por ejemplo:

.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 

no imprime "Hola" a pesar de que creo que deberían, y si cambia f1 y f2 todavía no se que es una contradicción lógica. je y jne sin embargo, parecen funcionar bien.

¿Qué estoy haciendo mal?

PD: ¿el Fcomip muestra solo un valor o muestra ambos?

Respuesta

34

Todo esto proviene del Volumen 2 de Intel 64 and IA-32 Architectures Software Developer's Manuals.

FCOMI establece solo algunos de los indicadores que CMP hace. Su código tiene %st(0) == 9 y %st(1) == 10. (Dado que se trata de una pila en la que se cargan), consulte la tabla de la página 3-348 del Volumen 2A y verá que este es el caso "ST0 < ST (i)", por lo que borrará ZF y PF y establecerá CF. Mientras tanto en la pág. 3-544 vol. 2A puede leer que JG significa "salto corto si es mayor (ZF = 0 y SF = OF)". En otras palabras, está probando el signo, el desbordamiento y las banderas cero, ¡pero FCOMI no establece el signo ni el desbordamiento!

Dependiendo de las condiciones en las que desea saltar, debería ver los posibles resultados de comparación y decidir cuándo desea saltar.

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

que he hecho esta pequeña mesa para que sea más fácil de averiguar:

 
+--------------+---+---+-----+------------------------------------+ 
| 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 

En otras palabras, los códigos de condición coinciden con los que para el uso de comparaciones sin firmar. Lo mismo ocurre si está usando FMOVcc.

Si uno (o ambos) operando a fcomi es NaN, establece ZF=1 PF=1 CF=1. (Las comparaciones FP tienen 4 resultados posibles: >, <, ==, o no ordenadas). Si le importa lo que hace su código con NaNs, es posible que necesite jp o jnp adicionales. Pero no siempre: por ejemplo, ja solo es verdadero si CF = 0 y ZF = 0, por lo que no se tomará en el caso desordenado. Si desea que el caso desordenado tome la misma ruta de ejecución que la siguiente o igual, entonces ja es todo lo que necesita.


Aquí se debe utilizar JA si desea que se imprima (es decir. if (!(f2 > f1)) { puts("hello"); }) y JBE si no lo hace (corresponde a if (!(f2 <= f1)) { puts("hello"); }). (Tenga en cuenta que esto puede ser un poco confuso debido al hecho de que solo imprimimos si no saltamos).


En cuanto a su segunda pregunta: por defecto fcomi no aparecerá nada. ¿Quieres su primo cercano fcomip que aparece %st0. Siempre debe borrar la pila del registro fpu después de su uso, así que todo en todo su programa termina así asumiendo que desea que se imprima el mensaje:

.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 
+7

Respuesta muy impresionante. Excepcional. Un comentario menor: el opuesto de 'ja' es' jbe', no 'jb'. –

+2

@Ray Toal: Tienes toda la razón. Aunque no hará ninguna diferencia en este caso, cambié el ejemplo, ya que tiene más sentido de esa manera. – user786653

+0

¡agradable! ¡Muchas gracias! – JustMaximumPower

Cuestiones relacionadas