#c #ассемблер
есть функция int foo(int num) { if(num) return 1; else return 3; } мне понятен выхлоп без оптимизаций: foo(int): pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) cmpl $0, -4(%rbp) je .L2 movl $1, %eax jmp .L3 .L2: movl $3, %eax .L3: popq %rbp ret но совершенно не понятно что происходит с O2: foo(int): cmpl $1, %edi sbbl %eax, %eax andl $2, %eax addl $1, %eax ret зачем вообще использовать SuBtract with Borrow... к тому же если заменить возвращаемое значение return 3 на return 2, то вообще как-то странно все отрабатывает foo(int): xorl %eax, %eax testl %edi, %edi sete %al addl $1, %eax ret проясните немного что куда кладется при оптимизациях... а то что-то не могу въехать никак
Ответы
Ответ 1
Компилятор не обязан генерировать понятный и/или простой для понимания код. С другой стороны если провести все вычисления на бумажке для всех входов и выходов, то логика работы функции будет точно такая же: foo(int): cmpl $1, %edi # устанавливает CF, если %edi-1<0 т.е. если %edi==0 sbbl %eax, %eax # %eax = CF ? 0xFFFFFFFF : 0 andl $2, %eax # %eax &= 2 т.е. в зависимости от CF: %eax=={2|0} addl $1, %eax # %eax += 1 т.е. %eax=={3|1} ret Во втором случае всё проще, можно переписать в примерном псевдокоде Си: foo(int): xorl %eax, %eax # int rv=0; testl %edi, %edi # if(num==0) sete %al # rv = 1; addl $1, %eax # rv++; ret # return rv; Идея этих оптимизаций в том чтобы избавиться от команд условного перехода, которые на современных (i586+) ЦП, если блок предсказаний не угадывает, вызывают сброс конвейера, а следовательно значительно замедляют вычисления.Ответ 2
Если вы поэкспериментируете с разными константами в качестве возвращаемых значений в вашей функции int foo(int num) { if(num) return A; else return B; } то можно заметить, что в общем случае в режиме -O2 компилятор в качестве оптимальной стратегии вычисления результата выбирает следующий подход int foo(int edi) { int eax = edi ? 0 : 0xFFFFFFFF; eax &= B - A; // `B - A` - константа return eax + A; } Комбинация cmpl $1, %edi sbbl %eax, %eax это ни что иное, как эффективный способ вычислить значение оператора ?:. А дальнейший код тривиально соответствует вышенаписанному. В случае же, когда возвращаемые константы отличаются не более чем на 1, компилятор выбирает другой подход - через sete.
Комментариев нет:
Отправить комментарий