第69部分- Linux x86 64位汇编 FPU之浮点条件分支
浮点的比较不像整数那么容易,在处理整数时候,容易使用CMP指令并且评估EFLAGS寄存器中的值来确定是否大于,等于还是小于。
浮点数,不能使用CMP指令,FPU提供了一些自己的指令来比较浮点值。
FCOM比较指令
比较的结果设置在状态寄存器的C0,C2和C3条件代码位中。
需要使用FSTSW指令把状态寄存器的值复制到AX寄存器,然后使用test指令判断比较结果。
示例
.extern printf ;//调用外部的printf函数
.section .data
fmtg: .asciz "greate \n"
fmtb: .asciz " less \n"
value1:
.float 100.923
value2:
.float 400.5532
.section .text
.globl _start
_start:
nop
flds value1;//加载value1 10.923到st0
fcoms value2;// st0和value2 4.5532对比。
fstsw;//加载状态寄存器到AX
sahf;//加载AH寄存器到EFLAGS中。
ja greater;//大于则跳转到greater
jb lessthan;//小于则跳转到lessthan
greater:
movq $fmtg,%rdi
call printf
jmp exit
lessthan:
movq $fmtb,%rdi
call printf
exit:
movq $60,%rax
syscall
as -g -o fcomtest.o fcomtest_att.s
ld -o fcomtest fcomtest.o -lc -I /lib64/ld-linux-x86-64.so.2
这里有指令:
LAHF(加载状态标志位到 AH)指令将 EFLAGS 寄存器的低字节复制到 AH。
被复制的标志位包括:符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位。
SAHF(保存 AH 内容到状态标志位)指令将 AH 内容复制到 EFLAGS(或 RFLAGS)寄存器低字节。将AH寄存器的第0,2,4,6,7分别传送到进位、奇偶校验、对准、零和符号标志、不影响EFLAGS寄存器的其他位。
FSTSW和SAHF指令组合传送如下:
- C0位传送到EFLAGS的进位标志
- C2位传送到EFLAGS的奇偶校验标志
- C3位传送到EFLAGS的零标志
为后续的JA,JB,JZ指令提供了很好的工作。
FCOMI指令
此外,还可以使用FSTSW和SAHF指令组合效果的指令,就是FCOMI。
只能比较FPU寄存器中的值,不能比较FPU寄存器和内存中的值。
FUCOMI和FUCOMP是确保被比较的值是合法的浮点数。
示例
.extern printf ;//调用外部的printf函数
.section .data
fmtg: .asciz "greate \n"
fmtb: .asciz " less \n"
value1:
.float 10.923
value2:
.float 4.5532
.section .text
.globl _start
_start:
nop
flds value2;//加载value2 4.5532到st0
flds value1;//加载value1 10.923到st0
fcomi %st(1), %st(0);//比较st1,st0
ja greater
jb lessthan
greater:
movq $fmtg,%rdi
call printf
jmp exit
lessthan:
movq $fmtb,%rdi
call printf
exit:
movq $60,%rax
syscall
as -g -o fcomitest.o fcomitest_att.s
ld -o fcomitest fcomitest.o -lc -I /lib64/ld-linux-x86-64.so.2
FCMOV指令
类似整数的CMOV指令,FCMOV指令可以编写浮点值的条件传送。根据EFLAGS寄存器中的值,FCMOV系列的指令把FPU寄存器ST(x)中的源操作数传送到FPU寄存器ST(0)中目标操作数。如果条件为true,就把ST(x)寄存器中值传送到ST(0)寄存器。
常用的方法是在FCMOV之前使用FCOMI
格式是:
Fcmovx source, destination
其中source是ST(x)寄存器,destination是ST(0)寄存器。
示例
.extern printf ;//调用外部的printf函数
.section .data
fmt: .ascii "result is: %f \n"
value1:
.float 20.5
value2:
.float 10.90
.section .text
.globl _start
_start:
nop
finit
flds value1;//加载value1 20.5到st0
flds value2;//加载value2 10.90到st0,20.5移动到st1
fcomi %st(1), %st(0);//如果st0小于st1,设置eflags,然后fcmovb会将st1值复制到st0.
fcmovb %st(1), %st(0)
fstl result;//加载结果到result
movq $fmtb,%rdi
movq result,%xmm0
call printf
exit:
movq $60,%rax
syscall
as -g -o fcmovtest.o fcmovtest_att.s
ld -o fcmovtest fcmovtest.o -lc -I /lib64/ld-linux-x86-64.so.2