GCC X86-64 Субоптимальная сборка выхода, почему?

При просмотре вспомогательного выхода следующего кода (без оптимизации, -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 или Снань. "

снова спасибо всем за полезное обсуждение.

8
задан Brandon 20 September 2011 в 23:25
поделиться