В предыдущем вопросе я рассматривал простой пример кода, глядя на оптимизацию хвостового вызова.
По этому вопросу я столкнулся с проблемой, что GCC применил слишком много оптимизации, чтобы продемонстрировать принцип, который должен был быть показан в этом примере кода, и устранил цикл/рекурсию.
Как я могу заставить GCC скомпилировать с включенной оптимизацией хвостового вызова, но с минимально возможной другой оптимизацией?
В настоящее время я пытаюсь скомпилировать со следующими параметрами:
gcc -O0 -foptimize-sibling-calls -ftree-tail-merge tail_recursive.c -o tail_recursive.c.exe
gcc -S -O0 -foptimize-sibling-calls -ftree-tail-merge tail_recursive.c
Рассматриваемая рекурсивная функция:
long recursive_tco(long loop, long current) {
return (loop>0) ? recursive_tco(loop-1, current+1): current;
}
и ассемблер, созданный этим:
.globl recursive_tco
.def recursive_tco; .scl 2; .type 32; .endef
.seh_proc recursive_tco
recursive_tco:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 16(%rbp)
movq %rdx, 24(%rbp)
cmpq $0, 16(%rbp)
jle .L2
movq 24(%rbp), %rax
leaq 1(%rax), %rdx
movq 16(%rbp), %rax
subq $1, %rax
movq %rax, %rcx
call recursive_tco
jmp .L3
(обратите внимание, что он вызывает сам себя, а не условный jmp, чего я и ожидал)
Каких параметров мне не хватает, чтобы заставить gcc выполнить оптимизацию хвостового вызова, не удаляя код примера?
Очевидно, что в реальном коде я включил бы просто -O2
, и я предпочитаю писать циклы вместо рекурсии на C.
Подборка на gcc version 4.9.3 (GCC)
Редактировать:
изменение функции на:
__attribute__ ((noinline)) long recursive_tco(long loop, long current) {
return (loop>0) ? recursive_tco(loop-1, current+1): current;
}
приводит к следующему при -O2:
recursive_tco:
.seh_endprologue
movq %rdx, %rax
leaq (%rcx,%rdx), %rdx
testq %rcx, %rcx
cmovg %rdx, %rax
ret
Если я правильно читаю ассемблер, кажется, что он выполняет работу хвостовой рекурсивной функции при компиляции с gcc -O1 -foptimize-sibling-calls -ftree-tail-merge
:
recursive_tco:
.seh_endprologue
movq %rdx, %rax
testq %rcx, %rcx
jle .L2
movq %rcx, %r8
.L3:
subq $1, %r8
jne .L3
leaq (%rcx,%rax), %rax
.L2:
rep ret
К сожалению, ему также удается оптимизировать нехвостовую рекурсивную функцию аналогичным образом, чего я не ожидал:
recursive:
.seh_endprologue
testq %rcx, %rcx
jle .L4
movq %rcx, %rdx
.L3:
subq $1, %rdx
jne .L3
movq %rcx, %rax
ret
.L4:
movl $0, %eax
ret
Я ожидаю, что будут сделаны некоторые дальнейшие оптимизации, и мне нужно предоставить некоторые дополнительные флаги, чтобы отключить их. Какие-либо предложения?
-O2
. Я ищу способ заставить его применять только совокупную стоимость владения, если это возможно. Этот вопрос хочет знать, был ли он применен или нет, и в ответах используется-O2
(где указано), что, к сожалению, не соответствует потребностям здесь. Я нашел этот вопрос и, к сожалению, не смог получить на него ответ. (Спасибо за ваш ответ на другой вопрос, хотя) - person Baldrickk   schedule 13.02.2017__attribute__ ((noinline)) long recursive_tco(long loop, long current)
, чтобы предотвратить встраивание, а затем попробовать разные уровни оптимизации. - person Lundin   schedule 13.02.2017-O0 -f...
была попыткой включить только tco, но если это сработает, то это тоже хорошее решение. - person Baldrickk   schedule 13.02.2017return val ? recursive(val)+1 : 0
, который использовался в качестве примера, на самом деле не был хвостовой рекурсией, и это привело к этому. Как уже отмечалось, в C я предпочитаю циклы рекурсии. Хотя мне было весело с рекурсией, когда я работал со Scheme. - person Baldrickk   schedule 13.02.2017-O1 -foptimize-sibling-calls
, похоже, приближает меня к тому, что мне нужно, мне просто нужно решить, что мне нужно отключить, или если вообще невозможно заставить это скомпилировать, как я этого хочу. - person Baldrickk   schedule 13.02.2017