1. Си / Говнокод #29294

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    #include <stddef.h>
    __attribute__((const)) int f(int a);
    
    void conv(int *restrict in, int *restrict out, size_t sz)
    {
      for(size_t i = 0; i < sz-1; i++)
      {
        out[i] = f(in[i])+f(in[i+1]);
      }
      return;
    }

    Пример, на котором обделывается оптимизатор Clang но не обделывается оптимизатор GCC
    https://godbolt.org/z/W9WshxsKG

    Запостил: j123123, 10 Ноября 2026

    Комментарии (5) RSS

    • "conv":
              cmp     rdx, 1
              je      .L9
              push    r14
              mov     r14, rdi
              push    r13
              push    r12
              mov     r12, rsi
              push    rbp
              lea     rbp, [rdx-1]
              push    rbx
              mov     edi, DWORD PTR [rdi]
              xor     ebx, ebx
              call    "f"
      .L3:
              add     rbx, 1
              mov     r13d, eax
              mov     edi, DWORD PTR [r14+rbx*4]
              call    "f"
              add     r13d, eax
              mov     DWORD PTR [r12-4+rbx*4], r13d
              cmp     rbx, rbp
              jne     .L3
              pop     rbx
              pop     rbp
              pop     r12
              pop     r13
              pop     r14
              ret
      .L9:
              ret

      Как тут можно заметить, компилятор внутри цикла делает лишь один вызов функции "f". Хотя тут мелкие косяки тоже есть, например в конце почему-то две инструкции "ret" непонятно нахуй нужные
      Ответить
      • conv:
                push    rbp
                push    r15
                push    r14
                push    r13
                push    r12
                push    rbx
                push    rax
                mov     rbx, rdx
                mov     qword ptr [rsp], rdi
                dec     rbx
                je      .LBB0_3
                mov     r15, rsi
                mov     rax, qword ptr [rsp]
                mov     ebp, dword ptr [rax]
                xor     r13d, r13d
        .LBB0_2:
                mov     edi, ebp
                call    f@PLT
                mov     r12d, eax
                lea     r14, [r13 + 1]
                mov     rax, qword ptr [rsp]
                mov     ebp, dword ptr [rax + 4*r13 + 4]
                mov     edi, ebp
                call    f@PLT
                add     eax, r12d
                mov     dword ptr [r15 + 4*r13], eax
                mov     r13, r14
                cmp     rbx, r14
                jne     .LBB0_2
        .LBB0_3:
                add     rsp, 8
                pop     rbx
                pop     r12
                pop     r13
                pop     r14
                pop     r15
                pop     rbp
                ret

        Тут же так получается, что Clang в цикле вызывает функцию "f" два раза, хотя можно переиспользовать ее значение из предыдущей итерации цикта т.к. там __attribute__((const))

        https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html
        const

        This attribute applies to functions.

        Calls to functions whose return value is not affected by changes to the observable state of the program and that have no observable effects on such state other than to return a value may lend themselves to optimizations such as common subexpression elimination. Declaring such functions with the const attribute allows GCC to avoid emitting some calls in repeated invocations of the function with the same argument values.
        Ответить
        • А разве шланг обязан понимать атрибусы GCC?
          Кстати, а надо как-то компилятору доказывать, что она чиста?
          Если я петух проткнутый, и помечу атрибутом const функцию, которая читает IO, то я сам себе буратино?

          Хотя конечно оптимиизация достойная Кнута: если я не ветке N вызываю f(N-1) то вероятно у меня она осталась с прошлого вызова, а инта прикопать -- не велика беда

          Хитро!
          Ответить
          • > А разве шланг обязан понимать атрибусы GCC?

            Нет, но вообще говоря он их понимает и делает на основе них некоторые оптимзизации
            __attribute__((const)) int f(int a);
            
            int test(int a)
            {
              return f(a)+f(a);
            }

            Такую функцию компилятор clang и gcc вызовет лишь единожды и результат удвоит. Если же без атрибута const, оно честно вызывает функцию два раза.

            > Кстати, а надо как-то компилятору доказывать, что она чиста?

            Нет. Компилятор может сам себе иногда доказать, что функция чистая, но это надо или чтоб она была в одной единице трансляции, или чтоб какие-то (LTO) линк-тайм оптимизации были врублены. Если от функции объявлен лишь прототип, и не задействовано никаких LTO, он только на основе этого атрибута что-то там может понять. Кстати, если допустим сделать функцию, которой передаешь указатель, и она чего-то туда пишет строго по этому адресу, но более ничего не меняет, никаких атрибутов для этого уже нет. Контракты в C++26 (которые кстати не прошли утверждение в говнокомитете) этого делать не позволяют, там нельзя сказать "вот этой функции передают указатель на хуйню, и она срет только по тому-то адресу, меняя ровно 4 байтика, и больше никаких побочных эффектов не делает"

            > Если я петух проткнутый, и помечу атрибутом const функцию, которая читает IO, то я сам себе буратино?

            Да.
            Ответить
            • Вообще, прикольно было бы если бы API для IO нужно было бы явно получать аргументом, и само-собой никакой глобальщины, ну и семантика const как в С++ или Расте.

              Тогда резонить про функции было бы проще.

              Например назвать IO "ЗашкварАПИ", и если функция не получает ЗашкварАПИ и не получает ничего мутабельного, то она по определинию чистая: она физически ничего не может поменять в этом мире, и свой результат всегда получает от входных параметров.

              Про такую функцию можно вообще таблицу составлять (может даже во время компиляции, если значения удасца посчитать конечно).

              Ебать я кажется изобрел функциональные языки.
              Ответить

    Добавить комментарий