1. Pascal / Говнокод #12375

    +90

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    program Project1;
    Var
      i,j : Integer;
    begin
        i := 300001; j := 300002;
        asm
         MOV EAX, I;
         XCHG EAX,j
         MOV I, EAX;
        end;
        Write(i,' ',j); Readln;
    end.

    Ещё один кулхацкерный метод перестановки значений двух чисел местами.

    Запостил: Govnocoder#0xFF, 30 Декабря 2012

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

    • Откуда в людях желание делать простые вещи через жопу?
      Ответить
    • На что только не готовы программисты ради экономии на спичках... и им насрать, что компилятор код со временой переменной превратит именно в такой код, и что принудительный асм портит оптимизации вокруг себя.
      Ответить
      • Компилятор такую х*ню писать не будет. Зачем он выставляет #LOCK в многоядерной среде?

        Компилятор заменит XCHG EAX,j как-то так:
        mov e*x, j
        mov j, eax
        mov eax, e*x


        Соответственно весь код ,будет выглядеть примерно как:
        MOV EAX, I;
        mov e*x, j
        mov j, eax
        mov I, e*x
        Ответить
        • > Компилятор заменит XCHG EAX,j как-то так
          Компилятор не будет ничего заменять. Раз уж программист написал в асмовставке xchg - значит так тому и быть. Если я не прав - пруфлинк в студию.

          А если не выябываться с асмовставками, и написать tmp=x; x=y; y=tmp;, то при удачных обстоятельствах (к примеру свопают 2 локальных переменных и не в цикле), то компилятор вообще избавится от этого шлака, и вообще не будет генерить для него асм. А в самом неудачном случае да, получится твой нижний код.
          Ответить
          • >Если я не прав - пруфлинк в студию.
            Обоснуйте!
            Ответить
            • > Обоснуйте!
              Как выше правильно заметил LispGovno, xchg имеет неявный lock префикс, в случаях, если одним из операндов является память. If a memory operand is referenced, the processor’s locking protocol is automatically implemented for the duration of the exchange operation, regardless of the presence or absence of the LOCK prefix or of the value of the IOPL. (IA-32 Intel Architecture, том 2) Поэтому компилятор не имеет права поменять xchg eax, j в асмовставке на mov ebx, j; mov j, eax; mov eax, ebx.

              Да и вообще - зачем компилятору менять содержимое асмовставки? В конце концов программист ее не от хорошей жизни писал.
              Ответить
              • Плюс! :)
                То я высказывался по поводу требований пруфлинка от возражателей. Ведь доказывать должен тот, кто утверждает, а не тот, кто отрицает (Дигесты и Кодекс Юстиниана, книга 22, титул 3, правило 2, http://www.e-reading.org.ua/chapter.php/21741/258/Dushenko_-_Mysli_i_izrecheniya_drevnih_s_ukazaniem _istochnika.html)
                Ответить
                • Утверждал тут как раз LispGovno: "Компилятор заменит XCHG EAX,j как-то так:". А своей фразой Компилятор не будет ничего заменять. Раз уж программист написал в асмовставке xchg - значит так тому и быть. я отрицал его утверждение, заодно потребовав пруфлинк.

                  Так что правило "Ведь доказывать должен тот, кто утверждает, а не тот, кто отрицает" я не нарушал :P
                  Ответить
          • Дайте пожалуйста ссылку, где можно узреть как это хитрец компилятор обходится без свопа. Как я понимаю, он просто инвертирует переменные далее при использовании, не обменивая ячейки памяти значениями?
            Ответить
            • > просто инвертирует переменные далее при использовании
              Да, точно так же, как он избавляется от ненужных переменных, временных переменных, присваиваний одной и той же переменной два раза подряд и т.п.

              На этом этапе компилятор еще не задумывается о ячейках памяти и регистрах. Лучше всего представить это как набор виртуальных регистров, по одному на каждый промежуточный результат. Причем если результат протащили через 30 переменных, он все равно останется в одном виртуальном регистре.

              А вот ближе к концу компиляции он займется распределением настоящих регистров, и выделением ячеек в стеке под те виртуальные регистры, которые не помещаются в физические. Несколько виртуальных регистров могут попасть в одну ячейку стека или один и тот же регистр, если, конечно, они не требуются одновременно.

              Поэтому у качественного оптимизирующего компилятора переменные в стеке и регистры имеют мало общего с переменными в исходном коде...

              P.S. swap с кучей и глобальными переменными ему так искоренить не удастся, лучше всего оптимизируются именно локальные переменные.
              Ответить
              • > Поэтому у качественного оптимизирующего компилятора переменные в стеке и регистры имеют мало общего с переменными в исходном коде...

                Я понял. То есть переменные в коде - это смысловое разграничение данных, а результирующее разграничение по памяти и регистрам - физическое, направленное на оптимальность.

                Вы читали что-то по компиляторам, кроме документации к конктретным продуктам? Кнут вроде ещё не сваял свой томик. Дайте линков?
                Ответить
                • > Вы читали что-то по компиляторам, кроме документации к конктретным продуктам?
                  В основном как раз документацию по LLVM и GCC, специализированных книг по компилерам не читал, т.к. свой пока писать не собираюсь...
                  Ответить
            • mov eax, 30001 ; это i
              mov ebx, 30002 ; это j
              ; делаем что-то с eax, ebx
              ...
              ; swap выкинут нахрен, но с этой точки ebx это i, а eax это j
              ...
              ; делаем еще что-то
              Ответить
              • На самом деле, нужно было сделать девятибайтный своп, который мог бы менять адреса в памяти.
                Ответить
                • Тогда, если уж перепиливать формат в котором в командах хранятся операнды, то не только ради xchg, но и ради всей арифметики и mov... сложение и сравнение произвольных переменных было бы удобным
                  Ответить
          • > Раз уж программист написал в асмовставке xchg - значит так тому и быть.
            Лол, как ты мои слова извратил, тролль. Я тебе сказал, как-бы поступил компилятор на месте программиста. Сказал, что он в целях оптимизации без нужды выставлять #LOCK не будет на строчку кеша или шину в многоядерной среде.
            Ответить
    • А как в Паскале сделать clobber list? Ну чтобы паскаль гарантировал, что он не запишет в EAX важные данные, и их не попортит asm-код?
      Ответить
      • Да как бы оно не рубило к хуям все оптимизации, и не заставляло паскаль вообще ничего не предполагать о значениях регистров после асмовставки, спихивать все что было в регистрах в переменные перед вставкой, и перезагружать их заново, если понадобятся... Ну кроме разве что esp и ebp, которые по понятным причинам в асмовставках портить нежелательно.

        Реквестирую в тред Тараса.
        Ответить
      • Честно говоря, не знаю. Может, завернуть асмовставку в pusha/popa?
        Ответить
        • Да не, я думаю он догадается о существовании вставки, и просто не будет генерить вокруг нее код, которому важно значение регистров после нее (ну кроме разве что esp/ebp).

          Зачем компилятор генерит код, что-то держащий в регистрах? Да только ради эффективности, чтобы каждый раз не дергать их из оперативки.

          В случае с асмовставкой ему придется от такой идеи отказаться (асмовставка может очень-очень косвенным путем потрогать и изменить переменную в стеке), и по-честному поместить переменную в стек, а после асмовставки, если ему вновь понадобится эта переменная, заново ее загрузить в регистр.

          Имхо паскаль использует именно этот не самый эффективный, зато очень надежный способ (раз у него нет тонкой настройки попорченных регистров, как в gcc).
          Ответить
    • Обычная перестановка с помощью третьей переменной в ассемблерном коде будет через стек, и ни каких трюков здесь не надо.
      Ответить
      • Обычную перестановку с помощью третьей переменной любой нормальный компилятор замутит через 2 регистра, примерно как написал выше LispGovno, или, при возможности, выкинет совсем. И никаких трюков с асмом тут, действительно, не надо.
        Ответить
        • P.S. Нук анонимный минусятор, обоснуй свое мнение.
          Ответить
        • Чтобы не рабрасываться словами, рассмотрим вот такой простенький пример - вычисление чисел фибоначчи:
          #include <stdio.h>
          #include <stdlib.h>
          
          void swap(int *a, int *b) {
              int tmp = *a;
              *a = *b;
              *b = tmp;
          }
          
          int main(int argc, char **argv) {
              int a = 1, b = 1, n = argc > 1 ? atoi(argv[1]) : 0, i;
              for (i=0; i<n; i++) {
                  printf("%d\n", a);
                  swap(&a, &b);
                  b += a;
              }
              return 0;
          }
          Своп намеренно написан самым угрёбищным способом - его параметры передаются по указателю, и, казалось бы, его оптимизнуть нельзя... Смотрим листинг, и видим там вот такой прекрасный код:
          ; парсинг аргумента поскипан
                  movl    $1, %edi ; это b
                  movl    $1, %esi ; это a
                  xorl    %ebx, %ebx ; это i
                  jmp     .L4
          .L5:
                  movl    %edi, %esi ; a = b
                  movl    %eax, %edi ; b = tmp
          .L4:
                  movl    %esi, 8(%esp) ; аргумент для принтфа
                  addl    $1, %ebx ; i++
                  movl    $.LC0, 4(%esp) ; строка формата для принтфа
                  movl    $1, (%esp) ; stdout
                  call    __printf_chk
                  leal    (%esi,%edi), %eax ; tmp = a+b
                  cmpl    28(%esp), %ebx ; если i <n продолжаем цикл
                  jne     .L5
          Как видим - gcc отлично справилось с задачей, и выкинуло swap нахер. Ну что, мой юный друг анонимный минусятор, у тебя остались какие-то вопросы?
          Ответить

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