1. Assembler / Говнокод #6013

    +240

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    lea    0x0(%esi),%esi
    sub    $0x1,%eax
    cmp    $0xffffffff,%eax
    je     0x8048e07
    mov    (%ecx,%eax,4),%esi
    test   %esi,%esi
    je     0x8048df8

    Чудеса оптимизации -О3 на gcc 4.4
    (код в интелловской аннотации, сначала источник, потом назначение)

    Запостил: SIGSEGV, 17 Марта 2011

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

    • странная какая-то оптимизация -- зачем делать if true goto?)
      Ответить
      • где тут true?
        Ответить
        • test %esi,%esi же сравнивает регистры esi и esi, и прыгает в 0x8048df8 если равны
          нет?

          я писал на асме очень давно и в другом синтаксисе (это был масм кажется) и все уже забыл
          Ответить
          • Нет. Не путай test и cmp

            http://www.codenet.ru/progr/asm/newbee/lesson8.php

            "Ещё одна команда сравнения - test.Команда Test выполняет операцию AND (логическое И) с двумя операндами и в зависимости от результата устанавливает или сбрасывает соответствующие флаги. Результат не сохраняется. Test используется для проверки бит, например в регистре:

            test eax, 100b
            jnz смещение"

            Короче, в данном случае сравнивает esi с нулём.
            Ответить
            • а, она and делает
              тогда логично)))

              я с cmp грешным делом перепутал видимо
              стыдно
              Ответить
    • > 0x0(%esi),%esi

      что это? mov esi, esi?

      > sub $0x1,%eax
      > cmp $0xffffffff,%eax

      какой понт сначала вычитать 1, а потом сравнивать с -1?
      Ответить
      • Это мне тоже понравилось. Но первым делом я обратил внимание на строку

        > sub $0x1,%eax

        Мне одному кажется, что команда dec занимает меньше времени?
        Ответить
        • > Мне одному кажется, что команда dec занимает меньше времени?

          Да.
          Ответить
        • выполняются за одинаковое время, но dec занимает меньше места.
          скорее всего по причине выравнивания команд перед условным переходом gcc решил выбрать sub.
          По причине выравнивания же решено было выбрать пустой lea в качестве первой инструкции.
          Если бы ты указал процессор не ниже i686, то получил бы многобайтный nop
          Ответить
          • Не совсем верно. DEC, согласно Интел, устаревшая команда, в отличие от sub, имеющая очень неприятный побочный эффект - обновляет не все флаги, что создает ложную зависимость от предыдущего значения регистра состояния. Подробности в мануалах по оптимизации. Но да, занимает один байт.
            Ответить
        • Да, dec медленнее на многих процессорах уже лет как 10.
          Ответить
      • В смысле "какой"?

        Пусть у вас есть некий алгоритм, который следует по двум веткам. Ветка выбирается в зависимости от того, равен ли `eax` нулю. Но в начале каждой ветки вам, по стечению обстоятельств, надо вычесть единицу из `eax`. Тут сразу возникает два варианта:

        Первый - реализовать все буквально: разбранчеваться по сравнению `eax` с нулем и начать каждую ветку с `dec eax`.

        Второй - вынести декремент "за скобки": сначала сделать `dec eax`, затем разбранчеваться по сравнению `eax` с `-1`.

        Какой вариант лучше - это уже вопрос логики, которой руководстуется компилятор и массы других факторов. В данном случае он, очевидно, решил пойти по второму пути. Вот и все.
        Ответить
        • я вам не скажу за всю Калькутту, но если в предыдущем шаге выяснили, что a = 0, то совершенно необязательно что-то вычитать, чтобы получить -1
          Ответить
          • Да, не обязательно. Но тем не менее зная, что в `eax` сидит 0, более выгодным с точки зрения оптимальности кода способом получения -1 в `eax` будет именно `dec eax`, а не `mov eax, -1`.

            Опять же, как я уже сказал выше, предварительный `dec` позволяет компилятору вынести эту операцию "за скобки", т.е. обойтись одной операцией на обе ветки, вместо того, чтобы писать отдельную операцию для каждой ветки.
            Ответить
            • сравнивать с нулем или использовать ZF от предыдущей инструкции эффективнее.
              и хватит уже засорять комментарии диакритическими символами, неудобно читать же.
              Ответить
              • Не совсем понимаю, что имеется в виду под "использовать ZF от предыдущей инструкции". У нас в данном случае нет "предыдущей инструкции", ZF от которой мы могли бы использовать.

                А в остальном: все возможно. Я лишь привожу пример формальной логики, которой потенциально мог руководствоваться компилятор.
                Ответить
                • sub $0x1,%eax

                  Она устанавливает ZF=1 если в результате отнимания получился 0.
                  Ответить
          • mov eax,-1 занимает больше байтов, выполняется и то и другое, по-моему, за одинаковое время на большинстве целевых процессоров. поэтому - да, выгоднее вычесть. А вообще, код непонятен без адресов и контекста.
            Ответить
    • >код в интелловской аннотации
      это at&t, а не intel

      >gcc 4.4
      А минорную версию указывать не модно?

      >Чудеса оптимизации -О3
      -O3 воистину чудесно
      Ответить
      • $ gcc --version
        gcc (GCC) 4.4.4 20100630 (Red Hat 4.4.4-10)
        Ответить
        • а... редхат. старые добрые времена - которые меня научили не самым легким образом почему дебьян лучше.

          код сгененированый альфа пререлизом поинт-версии которую РедХат вытянул напрямую из сырцов GCC и послал своим пользователям без консультаций с GCC - не в счет. скажи спасибо что то что на твоей системе называется GCC вообще код генерирует.
          Ответить
    • лень ковырять, но на уровне инструкций говорят у clang/llvm слегка получше кодогенератор.
      Ответить
      • А ещё говорят в gcc 4.6 кодогенерацию улучшили, жду релиза :)
        Ответить
    • ...да уж. Кодогенератор у гнуси гнусный и есть. Что с него взять-то?
      Ответить
    • Не хватает информации, чтобы оценить, почему вы к этому коду придрались. Неизестно, под какой процессор оптимизация, нет адресов и вообще исходного кода. Вполне может быть, что с точки зрения скорости компилятор сделал полностью оптимальный код. Мне почему-то кажется, что для P4 это так. Поймите, вам может казаться, что, скажем, можно было пропустить сравнение и сделать переход по значению флажка знака, но это верно лишь если начальное значение EAX всегда выше нуля. Мне, например, это неизвестно. Возможно, компилятору тоже.
      Поэтому: Где, собственно, говнокод?
      Ответить

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