При просмотре вспомогательного выхода следующего кода (без оптимизации, -O2 и -O3 дают очень похожие результаты):
int main(int argc, char **argv)
{
volatile float f1 = 1.0f;
volatile float f2 = 2.0f;
if(f1 > f2)
{
puts("+");
}
else if(f1 < f2)
{
puts("-");
}
return 0;
}
GCC делает что-то, что у меня есть трудное время:
.LC2:
.string "+"
.LC3:
.string "-"
.text
.globl main
.type main, @function
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
subq $32, %rsp
.LCFI2:
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movl $0x3f800000, %eax
movl %eax, -4(%rbp)
movl $0x40000000, %eax
movl %eax, -8(%rbp)
movss -4(%rbp), %xmm1
movss -8(%rbp), %xmm0
ucomiss %xmm0, %xmm1
jbe .L9
.L7:
movl $.LC2, %edi
call puts
jmp .L4
.L9:
movss -4(%rbp), %xmm1
movss -8(%rbp), %xmm0
ucomiss %xmm1, %xmm0
jbe .L4
.L8:
movl $.LC3, %edi
call puts
.L4:
movl $0, %eax
leave
ret
почему Разве GCC перемещает значения поплавка в XMM0 и XMM1 дважды, а также запускают UCOMESS дважды?
не будет бы быстрее сделать следующее?
.LC2:
.string "+"
.LC3:
.string "-"
.text
.globl main
.type main, @function
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
subq $32, %rsp
.LCFI2:
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movl $0x3f800000, %eax
movl %eax, -4(%rbp)
movl $0x40000000, %eax
movl %eax, -8(%rbp)
movss -4(%rbp), %xmm1
movss -8(%rbp), %xmm0
ucomiss %xmm0, %xmm1
jb .L8 # jump if less than
je .L4 # jump if equal
.L7:
movl $.LC2, %edi
call puts
jmp .L4
.L8:
movl $.LC3, %edi
call puts
.L4:
movl $0, %eax
leave
ret
Я не совсем вообще реальный монтажный программист, но это просто казалось Не странно мне иметь дублирующие инструкции. Есть ли проблема с моей версией кода?
Обновление
Если вы удалите волатильный, который у меня был изначально и заменил его с помощью SCANF (), вы получаете те же результаты:
int main(int argc, char **argv)
{
float f1;
float f2;
scanf("%f", &f1);
scanf("%f", &f2);
if(f1 > f2)
{
puts("+");
}
else if(f1 < f2)
{
puts("-");
}
return 0;
}
и соответствующий ассемблер:
.LCFI2:
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
leaq -4(%rbp), %rsi
movl $.LC0, %edi
movl $0, %eax
call scanf
leaq -8(%rbp), %rsi
movl $.LC0, %edi
movl $0, %eax
call scanf
movss -4(%rbp), %xmm1
movss -8(%rbp), %xmm0
ucomiss %xmm0, %xmm1
jbe .L9
.L7:
movl $.LC1, %edi
call puts
jmp .L4
.L9:
movss -4(%rbp), %xmm1
movss -8(%rbp), %xmm0
ucomiss %xmm1, %xmm0
jbe .L4
.L8:
movl $.LC2, %edi
call puts
.L4:
movl $0, %eax
leave
ret
Окончательное обновление
После рассмотрения некоторых из следующих комментариев, кажется, HAN (который прокомментировал по почте Джонатана Лейфлера) прибил эту проблему. GCC не делает оптимизацию не потому, что она не может, но потому что я не сказал это. Кажется, все сводится к правилам плавучей точке IEEE и обрабатывать строгие условия GCC, не могут просто сделать прыжок, если выше или прыгать, если ниже после первого Ucomiss, поскольку он должен обрабатывать все особые условия количества плавающих точек. При использовании рекомендации HAN Оптимизатора -FAST-MATH (ни один из флагов -Ox не включает в себя --fast-math, поскольку она может сломаться некоторые программы) GCC имеет именно то, что я искал:
Следующая сборка была изготовлена с использованием GCC 4.3 .2 "GCC -S -S -O3 -FAST-MATH TEST.C"
.LC0:
.string "%f"
.LC1:
.string "+"
.LC2:
.string "-"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB25:
subq $24, %rsp
.LCFI0:
movl $.LC0, %edi
xorl %eax, %eax
leaq 20(%rsp), %rsi
call scanf
leaq 16(%rsp), %rsi
xorl %eax, %eax
movl $.LC0, %edi
call scanf
movss 20(%rsp), %xmm0
comiss 16(%rsp), %xmm0
ja .L11
jb .L12
xorl %eax, %eax
addq $24, %rsp
.p2align 4,,1
.p2align 3
ret
.p2align 4,,10
.p2align 3
.L12:
movl $.LC2, %edi
call puts
xorl %eax, %eax
addq $24, %rsp
ret
.p2align 4,,10
.p2align 3
.L11:
movl $.LC1, %edi
call puts
xorl %eax, %eax
addq $24, %rsp
ret
Обратите внимание на два инструкции Ucomiss теперь заменяются одним комиссетом, сопровождаемым JA (скачок, если выше) и JB (прыжок, если ниже). GCC может придерживаться этой оптимизации, если вы позволите ему использовать --ffast-math!
Ucomiss VS Comiss (http://www.softeng.rl.ac.uk/st/archive/softeng/sex/html/softwaretools/vtune/users_guide/mergedprojects/Analyzer_ec/mergedProjects/Reference_olh/mergedProjects/instructions/instruct32_hh /vc315.htm): «Инструкция Ucomiss отличается от комассов в том, что она сигнализирует о недействительном исключительном источнике с плавающей запятой SIMD, только тогда, когда источник операнда является Snan. Комиссные сигналы инструкции Comiss недействительны, если операнд источника является либо QNAN или Снань. "
снова спасибо всем за полезное обсуждение.