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

    +76

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    .global	shit
    	.type	shit, @function
    shit:
    /* prologue: function */
    /* frame size = 0 */
    /* stack size = 0 */
    .L__stack_usage = 0
    	mov r30,r24
    	mov r31,r25
    	ldd r18,Z+1
    	ldd r22,Z+2
    	mov r24,r22
    	ldi r25,0
    	ldi r26,0
    	ldi r27,0
    	mov r26,r24
    	mov r27,r25
    	clr r25
    	clr r24
    	or r25,r18
    	ld r18,Z
    	or r24,r18
    	ldd r18,Z+3
    	mov r22,r24
    	mov r23,r25
    	mov r24,r26
    	mov r25,r27
    	or r25,r18
    	ret
    	.size	shit, .-shit

    Вот такое ГЛОБАЛЬНОЕ ГОВНО мне делает GCC под AVR
    Код разворота байтиков:

    unsigned long int shit(unsigned char *a)
    {
    return
    ( unsigned long int)a[0] << 0 |
    ((unsigned long int)a[1] << 8 ) |
    ((unsigned long int)a[2] << 16) |
    ((unsigned long int)a[3] << 24);
    }

    Вот другие попытки это сделать, чтоб компилятор сделал более оптимально http://goo.gl/3D2Lri - ссылка на gcc.godbolt.org

    У меня есть собранный через crosstools-ng более новый gcc под AVR, вот выхлоп с него для тех же примеров, что и в godbolt https://paste.debian.net/378491/

    Там __builtin_bswap32() становится rcall __bswapsi2, но этот __bswapsi2 состоит из двух сраных инструкций
    gcc/libgcc/config/avr/lib1funcs.S
    #if defined (L_bswapsi2)
    ;; swap bytes
    ;; r25:r22 = bswap32 (r25:r22)
    DEFUN __bswapsi2
    bswap r22, r25
    bswap r23, r24
    ret
    ENDF __bswapsi2
    #endif /* defined (L_bswapsi2) */

    ТАК ПОЧЕМУ Б ТУПО НЕ ЗАИНЛАЙНИТЬ ЭТО?

    Запостил: j123123, 04 Февраля 2016

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

    • Еще по теме:
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=27663
      https://electronics.stackexchange.com/questions/153481/avr-gcc-how-do-i-improve-code-optimization
      Ответить
    • А может нехуй ему чары совать, а?

      (a>>24)|((a&0xff0000)>>8)|((a&ff00)<<8)| (a<<24)
      Ответить
      • Получается тоже говно.
        Лучше всего оптимизируется через вот эту вот конструкцию shit_union tmp2 = (shit_union){{tmp.bytes[3],tmp.bytes[2],tmp.bytes[1],tmp.bytes[0]}};
        Ответить
      • о пропустил твой ответ. +1.

        судя по генереной ахинее в ГК, avr бэк-энд на чем-то спотыкается, и пытается в промежуточные вычисления делать аккуратно. что здесь в принципе не нужно.
        Ответить
    • -Os, -O2?
      Ответить
      • Да один хер. Я все варианты оптимизаций пробовал. Везде говно получается для этого кода
        Ответить
      • Короче, надо через говноюнионы это делать, так лучше всего получается
        typedef union
        {   
          unsigned char bytes[4];
          unsigned long int byte4;
        } shit_union;
         
        unsigned long int shit3(unsigned char *a)
        {
          register shit_union tmp2 = (shit_union){{a[3],a[2],a[1],a[0]}};
          return tmp2.byte4;
        }

        и компилятор посвежее взять. Все кто говорят про мегакрутость современных компиляторов и что вот ассмблер нинужно потому что компилятр круче заоптимизирует, должны заткнуться и пойти пилить эти самые компиляторы, чтобы они перестали генерировать говнокод в ответ на ДОСТАТОЧНО ИЗВЕСТУЮ И ШИРОКО ИСПОЛЬЗУЕМУЮ КОНСТРУКЦИЮ. Всякие там htons htonl именно так через сдвиги и написаны, но компилятор делает из этого кода какое-то дичайшее говно на ассемблере
        Ответить
        • "должны заткнуться и пойти пилить эти самые компиляторы, чтобы они перестали генерировать говнокод в ответ на ДОСТАТОЧНО ИЗВЕСТУЮ И ШИРОКО ИСПОЛЬЗУЕМУЮ КОНСТРУКЦИЮ."

          сам виноват что говно-непроцессором пользуешься. гцц сам по себе и не виноват то - ругайся на авторов AVR которые поддержку оного в гцц и добавляли.

          с одной стороны. с другой стороны компилером пользоватся надо уметь. тот огород типов и конверсий который городишь в оригинальном коде указывает что ты просто не дорос еще до почётного звания байтоёба.

          PS авр нету. но на intel & arm, байтсвап как описан ниже генерирует 2-3 инструкции (weak alignment, и может быть дюжину при побайтовом чтении из памяти). твое говно генерит кучу потому что ты идиот ренундантно типы конвертишь.
          Ответить
          • "твое говно генерит кучу потому что ты идиот ренундантно типы конвертишь." кучу которую avr бэк-энд (в сравнении с intel или arm) разгрести очевидно не может.
            Ответить
            • > ренундантно типы конвертишь
              Там int 16-битный.

              P.S. Пишут, что в 2006 году avr-gcc вообще 47 команд генерил в ответ на bswap32... Так что прогресс :)

              P.P.S. Вот и отличный пруф про хуёвые конпеляторы в embedded, о которых я упоминал в недавнем треде.
              Ответить
              • > хуёвые конпеляторы в embedded

                я несколько месяцев с IAR работал и могу это только подтвердить.

                но и приоритеты у них другие: насколько можно более компактный код сгенерить, потому что производительность на далеком втором месте.

                почему гцц на типичной коммерческой встроенщине и подсасывает: фокус у гцц-шников это (поддержка стандартов и) максимальная производительность на популярных процах. то что народ пишет на коммерческой встроенщине это просто ужас - и в каком-то смысле это просто чудо что (на арме) keil & iar это вообще в нечто юзабельное оптимизируют. я видел что народ в кернеле с гцц делает: насколько народ код вылизывает что бы гцц мог оптимальный код генерить. на коммерческих проектах фокус как правило style guide conformance, почему и пользуются кучами неоптимальных копипащиных повсюду конструкций. keil/iar реально в ж оптимизируют эту всю копипасту. даже сложно представить себе какие именно оптимизации они там делают, но навярняка как минимум с O(n^2) производительностью. на встроенщине, с 128К фирмваре это может быть и допустимо, но для десктопного 10-20МБ объектного кода проекта эти оптимизации будут смертельны (с точки зрения времени компиляции).
                Ответить
                • >но и приоритеты у них другие: насколько можно более компактный код сгенерить, потому что производительность на далеком втором месте.

                  Не совсем. Если надо чтоб реалтаймовость, чтоб там кусок кода гарантированно отрабатывал за определенное время, да еще ж можно прерывания по таймеру делать, если не успело. К тому же там может быть очень наркоманская архитектура в этих контроллерах с переключением банков памяти. Это вам не в хаскеле монадки заворачивать
                  Вот cтатья http://www.wasm.ru/article/276 история одного байта
                  Ответить
                  • К слову о реалтаймовости - когда ARM'овские компилеры генерят софтовое деление для старых кристаллов, там есть 2 варианта - точное и реалтаймовое. Первое считает до упора, второе - не больше 45 тактов и возвращает грубое частное.

                    > история одного байта
                    Мне всегда было интересно, под какой чип писал ГГ этого рассказа...
                    Ответить
                • А насчет этих IAR. Хотел бы я посмотреть на оптимизацию этого IAR под arm для тех же функций, что в http://goo.gl/3D2Lri
                  Не думаю что оно там будет сильно круче. Я так прикинул, для некоторых оптимизаций вообще надо чуть ли не какой-то AI городить с системой символьных вычислений.

                  >фокус у гцц-шников это (поддержка стандартов и) максимальная производительность на популярных процах.

                  Следование стандартам часто несовместимо с максимальной производительностью. https://gcc.gnu.org/viewcvs/gcc/trunk/gcc/simplify-rtx.c?view=markup&pathrev=232689#l2549 вот такая штука в GCC есть для какбы-символьных вычислений с плавучкой, которая срабатывает только если отрублено строгое следование стандарту вычислений с плавучкой. Оно там что-то через ассоциативность и коммутативность делает, например оно наверное сможет упростить a*b + a*c до a*(b+c)
                  Ответить
                  • > А насчет этих IAR. Хотел бы я посмотреть на оптимизацию этого IAR под arm для тех же функций, что в http://goo.gl/3D2Lri

                    Disassembly of section .text:
                    
                    00000000 <shit0>:
                       0:	e5900000 	ldr	r0, [r0]
                       4:	e3a01cff 	mov	r1, #65280	; 0xff00
                       8:	e0011420 	and	r1, r1, r0, lsr #8
                       c:	e1811c20 	orr	r1, r1, r0, lsr #24
                      10:	e2002cff 	and	r2, r0, #65280	; 0xff00
                      14:	e1811402 	orr	r1, r1, r2, lsl #8
                      18:	eafffffe 	b	34 <.text_4>
                    			18: R_ARM_JUMP24	.text_4
                    
                    0000001c <shit>:
                      1c:	e5d01000 	ldrb	r1, [r0]
                    			1c: R_ARM_NONE	.text_4
                      20:	e5d02001 	ldrb	r2, [r0, #1]
                      24:	e1811402 	orr	r1, r1, r2, lsl #8
                      28:	e5d02002 	ldrb	r2, [r0, #2]
                      2c:	e5d00003 	ldrb	r0, [r0, #3]
                      30:	e1811802 	orr	r1, r1, r2, lsl #16
                    
                    00000034 <.text_4>:
                      34:	e1810c00 	orr	r0, r1, r0, lsl #24
                      38:	e12fff1e 	bx	lr
                    
                    0000003c <shit2>:
                      3c:	e92d5000 	push	{ip, lr}
                      40:	e1a01000 	mov	r1, r0
                      44:	e3a02004 	mov	r2, #4
                      48:	e1a0000d 	mov	r0, sp
                      4c:	ebfffffe 	bl	0 <__builtin_memcpy>
                    			4c: R_ARM_CALL	__builtin_memcpy
                      50:	e59d0000 	ldr	r0, [sp]
                      54:	ebfffffe 	bl	0 <__builtin_bswap32>
                    			54: R_ARM_CALL	__builtin_bswap32
                      58:	e8bd8002 	pop	{r1, pc}
                    Ответить
                    • насчет извратных оптимизаций. посмотрите на код shit0 (gcc canonical swap) и shit(): IAR один OR и ретурн замержил.
                      Ответить
                    • http://goo.gl/SEf5U7 вот в гцц
                      По-моему это самый лучший вариант:
                      shit2:
                              ldr     r0, [r0]  @ unaligned
                              rev     r0, r0
                              bx      lr

                      что оно инструкцию rev тут втулило в ответ на __builtin_bswap32()

                      IAR походу не знает про эти __builtin т.к. это гццшные экстеншены, и соотвественно не смогло заоптимизировать эти копирования памяти. Попробуй туда memcpy просто втулить, может оно что-то умное сделает
                      Ответить
                      • > IAR походу не знает про эти __builtin т.к. это гццшные экстеншены

                        про какие-то он знает. дока дерьмовая - лень искать какие именно он поддерживает.

                        но memcpy() с константным размером (только что протестил) он в лоб не оптимизирует: на встроенном арме со статической памятью вызов функции относительно дешевый.

                        в добавок под IAR (если пользоватся IAR-specific функциями) твоей проблемы в какой то мере не существует: переменную можно объявить как __big_endian или __little_endian, и все остальное компилер сам сделает за тебя. или же встроенными асм макросами. (я этой магией сам не пользовался, и на простом быстром эксперименте ничего не вышло.)
                        Ответить
                  • 0000005c <shit3>:
                      5c:	e92d4000 	stmfd	sp!, {lr}
                      60:	e24dd00c 	sub	sp, sp, #12
                      64:	e1a01000 	mov	r1, r0
                      68:	e3a02004 	mov	r2, #4
                      6c:	e1a0000d 	mov	r0, sp
                      70:	ebfffffe 	bl	0 <__builtin_memcpy>
                    			70: R_ARM_CALL	__builtin_memcpy
                      74:	e5dd1003 	ldrb	r1, [sp, #3]
                      78:	e5cd1004 	strb	r1, [sp, #4]
                      7c:	e5dd1002 	ldrb	r1, [sp, #2]
                      80:	e5cd1005 	strb	r1, [sp, #5]
                      84:	e5dd1001 	ldrb	r1, [sp, #1]
                      88:	e5cd1006 	strb	r1, [sp, #6]
                      8c:	e5dd1000 	ldrb	r1, [sp]
                      90:	e5cd1007 	strb	r1, [sp, #7]
                      94:	e59d0004 	ldr	r0, [sp, #4]
                      98:	e28dd00c 	add	sp, sp, #12
                      9c:	e8bd8000 	ldmfd	sp!, {pc}
                    
                    000000a0 <shit4>:
                      a0:	e92d5000 	push	{ip, lr}
                      a4:	e1a01000 	mov	r1, r0
                      a8:	e3a02004 	mov	r2, #4
                      ac:	e1a0000d 	mov	r0, sp
                      b0:	ebfffffe 	bl	0 <__builtin_memcpy>
                    			b0: R_ARM_CALL	__builtin_memcpy
                      b4:	e5dd0000 	ldrb	r0, [sp]
                      b8:	e5dd1003 	ldrb	r1, [sp, #3]
                      bc:	e5dd2002 	ldrb	r2, [sp, #2]
                      c0:	e5cd0003 	strb	r0, [sp, #3]
                      c4:	e5dd0001 	ldrb	r0, [sp, #1]
                      c8:	e5cd1000 	strb	r1, [sp]
                      cc:	e5cd2001 	strb	r2, [sp, #1]
                      d0:	e5cd0002 	strb	r0, [sp, #2]
                      d4:	e59d0000 	ldr	r0, [sp]
                      d8:	e8bd8002 	pop	{r1, pc}


                    стоить заметить что новые армы преимущественно strict-alignment и соптимизировать побайтовое чтение невозможно.
                    Ответить
    • А если | на + поменять?
      Ответить
      • Тоже куча бреда генерируется. Кстати если надо "наложить" куски чисел друг на друга, компиляторы обычно лучше догадываются сделать что-то умное, если им | подсовывать вместо +
        Вот например для | шланг смог тут узнать ror, а для + уже не http://goo.gl/Vrcq0S
        Ответить
    • > unsigned long int shit(unsigned char *a)

      AVR - стрикт алаймент?

      каноничейский байт свап на GCC это:
      #define uswap_32(x) \
              ((((x) & 0xff000000) >> 24) | \
               (((x) & 0x00ff0000) >>  8) | \
               (((x) & 0x0000ff00) <<  8) | \
               (((x) & 0x000000ff) << 24))


      читаешь число из памяти, и вот так его байт-свапишь.

      PS читай линуховы и/или u-boot сырцы.
      Ответить
      • > стрикт алаймент
        Он осьмибитный. Ему даже передать unsigned long int - уже боль.
        Ответить
        • 8-бит - делает все проще - однозначно не-strict алайнмент! ;)
          Ответить
      • На такой код там тоже куча мусора выдается
        http://goo.gl/moktyM
        Ответить
        • я поздно заметил что борманд уже это запостил. на большинстве проектов народ не парится и делает это на асмовых вставках.

          по генерируемому коду очевидно что компилер вообще обламался что-то соптимизировать: все операции в асме один ко одному к сишному сырцу. avr бэк-энд не предоставляет достаточное количество информации для оптимизиций или насильственно рубит оптимизации что бы эмуляция 32-бит чисел работала.
          Ответить
        • к слову, 32bit vs 16bit:
          #define uswap_16(x) \
                  ((((x) & 0xff00) >> 8) | \
                   (((x) & 0x00ff) << 8))
          
          
          unsigned int shit16(unsigned int a)
          {
            return uswap_16(a);
          }


          всего 4 инструкции.
          Ответить
          • А можно было бы обойтись двумя
            bswap r24,r25
            ret
            Ответить
            • Кстати, а этот bswap есть на выбранном тобой контроллере? Просто я сейчас в даташите на тиньку, с которой работал, покопался - нету.
              Ответить
              • Походу там его нет. Я просто недостаточно подробно изучил исходник lib1funcs.S из GCC в котором этот bswap используется. Это макрос такой, который через xor меняет. Так что и там и там получается 3 инструкции и ret, но регистров меньше используется
                ;; swap two registers with different register number
                .macro bswap a, b
                    eor \a, \b
                    eor \b, \a
                    eor \a, \b
                .endm
                
                #if defined (L_bswapsi2)
                ;; swap bytes
                ;; r25:r22 = bswap32 (r25:r22)
                DEFUN __bswapsi2
                    bswap r22, r25
                    bswap r23, r24
                    ret
                ENDF __bswapsi2
                #endif /* defined (L_bswapsi2) */

                http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_EOR.html
                Ответить

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