Включить оптимизацию хвостового вызова только в GCC

В предыдущем вопросе я рассматривал простой пример кода, глядя на оптимизацию хвостового вызова.

По этому вопросу я столкнулся с проблемой, что 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

Я ожидаю, что будут сделаны некоторые дальнейшие оптимизации, и мне нужно предоставить некоторые дополнительные флаги, чтобы отключить их. Какие-либо предложения?


person Baldrickk    schedule 13.02.2017    source источник
comment
comment
@Lundin не в этом случае. Я знаю, что в данном случае это не так, и он "переоптимизирует" с -O2. Я ищу способ заставить его применять только совокупную стоимость владения, если это возможно. Этот вопрос хочет знать, был ли он применен или нет, и в ответах используется -O2 (где указано), что, к сожалению, не соответствует потребностям здесь. Я нашел этот вопрос и, к сожалению, не смог получить на него ответ. (Спасибо за ваш ответ на другой вопрос, хотя)   -  person Baldrickk    schedule 13.02.2017
comment
Хм, хорошо, этот вопрос предполагает, что оптимизация хвоста выполняется при -O2 и -O3. Я полагаю, вы могли бы попробовать __attribute__ ((noinline)) long recursive_tco(long loop, long current), чтобы предотвратить встраивание, а затем попробовать разные уровни оптимизации.   -  person Lundin    schedule 13.02.2017
comment
@Lundin Я попробую, спасибо. - -O0 -f... была попыткой включить только tco, но если это сработает, то это тоже хорошее решение.   -  person Baldrickk    schedule 13.02.2017
comment
В последнее время я несколько раз видел, как это появлялось в C, это артефакт какой-то книги или какая-то проблема реального мира, которая возникает совсем недавно? Это не функция уровня языка в C, как в LISP ... просто любопытно, мой ответ будет снят с эфира.   -  person Grady Player    schedule 13.02.2017
comment
Как задокументировано во многих местах, большинство конкретных флагов оптимизации не действуют при -O0, вам нужно как минимум -O1 или -Og, чтобы вообще включить оптимизацию (и тогда вы можете отключить определенные вещи).   -  person Marc Glisse    schedule 13.02.2017
comment
@GradyPlayer в данном случае это результат встречи команды на работе, на которой обсуждаются подобные мелочи. Я упомянул, что return val ? recursive(val)+1 : 0, который использовался в качестве примера, на самом деле не был хвостовой рекурсией, и это привело к этому. Как уже отмечалось, в C я предпочитаю циклы рекурсии. Хотя мне было весело с рекурсией, когда я работал со Scheme.   -  person Baldrickk    schedule 13.02.2017
comment
@MarcGlisse да, -O1 -foptimize-sibling-calls, похоже, приближает меня к тому, что мне нужно, мне просто нужно решить, что мне нужно отключить, или если вообще невозможно заставить это скомпилировать, как я этого хочу.   -  person Baldrickk    schedule 13.02.2017