Почему в Microchip C18 вставка NOP приводит к увеличению кода?

У меня есть код в ISR. Код приведен для полноты, вопрос только по закомментированному блоку __asm_.

Без блока __asm_ это компилируется в 82 инструкции. С блоком __asm_ результат составляет 107 инструкций. Почему большая разница?

Вот код C:

if (PIR1bits.SSPIF)
{
    spi_rec_buffer.read_cursor = 0;
    spi_rec_buffer.write_cursor = 0;

    LATAbits.LATA4 ^= 1;
//      _asm nop nop _endasm
    LATAbits.LATA4 ^= 1;

    while (!PORTAbits.NOT_SS && spi_rec_buffer.write_cursor < spi_rec_buffer.size)
    {
        spi_rec_buffer.data[spi_rec_buffer.write_cursor] = SSPBUF;
        SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
        PIR1bits.SSPIF = 0;
        spi_rec_buffer.write_cursor++;
        spi_out_msg_buffer.read_cursor++;
        if (spi_out_msg_buffer.read_cursor == spi_out_msg_buffer.write_cursor)
            LATAbits.LATA4 = 0;
        LATBbits.LATB1 = 1;
        while (!PORTAbits.NOT_SS && !PIR1bits.SSPIF);
        LATBbits.LATB1 = 0;
    }

    spi_message_locked = true;
    spi_message_received = true;

}

Без NOP:

BTFSS     0x9e,0x3,0x0      if (PIR1bits.SSPIF)
BRA       0x2ba
                            {
MOVLB     0xf                   spi_rec_buffer.read_cursor = 0;
CLRF      0x4,0x1
CLRF      0x5,0x1
CLRF      0x6,0x1               spi_rec_buffer.write_cursor = 0;
CLRF      0x7,0x1
BTG       0x89,0x4,0x0          LATAbits.LATA4 ^= 1;
BTG       0x89,0x4,0x0          LATAbits.LATA4 ^= 1;
MOVF      0x80,0x0,0x0          while (!PORTAbits.NOT_SS && spi_rec_buffer.write_cursor < spi_rec_buffer.size)
ANDLW     0x20
BNZ       0x2b0
MOVLB     0xf
MOVF      0x7,0x0,0x1
XORWF     0x3,0x0,0x1
BTFSS     0xe8,0x7,0x0
BRA       0x254
RLCF      0x3,0x0,0x1
BRA       0x25c
MOVF      0x2,0x0,0x1
SUBWF     0x6,0x0,0x1
MOVF      0x3,0x0,0x1
SUBWFB    0x7,0x0,0x1
BC        0x2b0
BRA       0x240
                                {
MOVF      0x0,0x0,0x1               spi_rec_buffer.data[spi_rec_buffer.write_cursor] = SSPBUF;
ADDWF     0x6,0x0,0x1
MOVWF     0xe9,0x0
MOVF      0x1,0x0,0x1
ADDWFC    0x7,0x0,0x1
MOVWF     0xea,0x0
MOVFF     0xfc9,0xfef
MOVLB     0xf                       SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
MOVF      0x10,0x0,0x1
ADDWF     0x14,0x0,0x1
MOVWF     0xe9,0x0
MOVF      0x11,0x0,0x1
ADDWFC    0x15,0x0,0x1
MOVWF     0xea,0x0
MOVF      0xef,0x0,0x0
MOVWF     0xc9,0x0
BCF       0x9e,0x3,0x0              PIR1bits.SSPIF = 0;
MOVLB     0xf                       spi_rec_buffer.write_cursor++;
INCF      0x6,0x1,0x1
MOVLW     0x0
ADDWFC    0x7,0x1,0x1
MOVLB     0xf                       spi_out_msg_buffer.read_cursor++;
INCF      0x14,0x1,0x1
ADDWFC    0x15,0x1,0x1
MOVF      0x16,0x0,0x1              if (spi_out_msg_buffer.read_cursor == spi_out_msg_buffer.write_cursor)
XORWF     0x14,0x0,0x1
BNZ       0x29e
MOVF      0x17,0x0,0x1
XORWF     0x15,0x0,0x1
BNZ       0x29e
BCF       0x89,0x4,0x0                  LATAbits.LATA4 = 0;
BSF       0x8a,0x1,0x0              LATBbits.LATB1 = 1;
MOVF      0x80,0x0,0x0              while (!PORTAbits.NOT_SS && !PIR1bits.SSPIF);
ANDLW     0x20
BNZ       0x2ac
MOVF      0x9e,0x0,0x0
ANDLW     0x8
BZ        0x2a0
BCF       0x8a,0x1,0x0              LATBbits.LATB1 = 0;
                                }
MOVLB     0xf                   spi_message_locked = true;
MOVLW     0x1
MOVWF     0x18,0x1
MOVLB     0xf                   spi_message_received = true;
MOVWF     0x19,0x1
                            }
MOVLW     0x4            }
SUBWF     0xe1,0x0,0x0
BC        0x2c4
CLRF      0xe1,0x0
MOVF      0xe5,0x1,0x0
MOVWF     0xe1,0x0
MOVF      0xe5,0x1,0x0
MOVFF     0xfe7,0xfd9
MOVF      0xe5,0x1,0x0
MOVFF     0xfe5,0xfea
MOVFF     0xfe5,0xfe9
MOVFF     0xfe5,0xfda
RETFIE    0x1

С NOP:

BTFSS     0x9e,0x3,0x0      if (PIR1bits.SSPIF)
BRA       0x30e
                            {
MOVLB     0xf                   spi_rec_buffer.read_cursor = 0;
CLRF      0x4,0x1
CLRF      0x5,0x1
MOVLB     0xf                   spi_rec_buffer.write_cursor = 0;
CLRF      0x6,0x1
CLRF      0x7,0x1
BTG       0x89,0x4,0x0          LATAbits.LATA4 ^= 1;
NOP                             _asm nop nop _endasm
NOP
BTG       0x89,0x4,0x0          LATAbits.LATA4 ^= 1;
MOVF      0x80,0x0,0x0          while (!PORTAbits.NOT_SS && spi_rec_buffer.write_cursor < spi_rec_buffer.size)
ANDLW     0x20
BNZ       0x302
MOVLB     0xf
MOVF      0x7,0x0,0x1
MOVLB     0xf
XORWF     0x3,0x0,0x1
BTFSS     0xe8,0x7,0x0
BRA       0x27e
RLCF      0x3,0x0,0x1
BRA       0x28c
MOVF      0x2,0x0,0x1
MOVLB     0xf
SUBWF     0x6,0x0,0x1
MOVLB     0xf
MOVF      0x3,0x0,0x1
MOVLB     0xf
SUBWFB    0x7,0x0,0x1
BC        0x302
BRA       0x268
                                {
MOVLB     0xf                       spi_rec_buffer.data[spi_rec_buffer.write_cursor] = SSPBUF;
MOVLB     0xf
MOVF      0x0,0x0,0x1
MOVLB     0xf
ADDWF     0x6,0x0,0x1
MOVWF     0xe9,0x0
MOVLB     0xf
MOVLB     0xf
MOVF      0x1,0x0,0x1
MOVLB     0xf
ADDWFC    0x7,0x0,0x1
MOVWF     0xea,0x0
MOVFF     0xfc9,0xfef
MOVLB     0xf                       SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
MOVLB     0xf
MOVF      0x10,0x0,0x1
MOVLB     0xf
ADDWF     0x14,0x0,0x1
MOVWF     0xe9,0x0
MOVLB     0xf
MOVLB     0xf
MOVF      0x11,0x0,0x1
MOVLB     0xf
ADDWFC    0x15,0x0,0x1
MOVWF     0xea,0x0
MOVF      0xef,0x0,0x0
MOVWF     0xc9,0x0
BCF       0x9e,0x3,0x0              PIR1bits.SSPIF = 0;                           // Interruptflag löschen...
MOVLB     0xf                       spi_rec_buffer.write_cursor++;
INCF      0x6,0x1,0x1
MOVLW     0x0
ADDWFC    0x7,0x1,0x1
MOVLB     0xf                       spi_out_msg_buffer.read_cursor++;
INCF      0x14,0x1,0x1
MOVLW     0x0
ADDWFC    0x15,0x1,0x1
MOVLB     0xf                       if (spi_out_msg_buffer.read_cursor == spi_out_msg_buffer.write_cursor)
MOVF      0x16,0x0,0x1
MOVLB     0xf
XORWF     0x14,0x0,0x1
BNZ       0x2ea
MOVLB     0xf
MOVF      0x17,0x0,0x1
MOVLB     0xf
XORWF     0x15,0x0,0x1
BNZ       0x2ee
BCF       0x89,0x4,0x0                  LATAbits.LATA4 = 0;
BSF       0x8a,0x1,0x0              LATBbits.LATB1 = 1;
MOVF      0x80,0x0,0x0              while (!PORTAbits.NOT_SS && !PIR1bits.SSPIF);
ANDLW     0x20
BNZ       0x2fe
MOVF      0x9e,0x0,0x0
ANDLW     0x8
BNZ       0x2fe
BRA       0x2f0
BCF       0x8a,0x1,0x0              LATBbits.LATB1 = 0;
                                }
MOVLB     0xf                   spi_message_locked = true;
MOVLW     0x1
MOVWF     0x18,0x1
MOVLB     0xf                   spi_message_received = true;
MOVLW     0x1
MOVWF     0x19,0x1
                            }
MOVLW     0x4            }
SUBWF     0xe1,0x0,0x0
BC        0x318
CLRF      0xe1,0x0
MOVF      0xe5,0x1,0x0
MOVWF     0xe1,0x0
MOVF      0xe5,0x1,0x0
MOVFF     0xfe7,0xfd9
MOVF      0xe5,0x1,0x0
MOVFF     0xfe5,0xfea
MOVFF     0xfe5,0xfe9
MOVFF     0xfe5,0xfda
RETFIE    0x1

Вот скриншот частичного различия (щелкните, чтобы увеличить): Diff


person AndreKR    schedule 02.07.2011    source источник
comment
Почему большая разница? - Вы пытались сравнить два фрагмента кода с помощью инструмента сравнения?   -  person Mitch Wheat    schedule 02.07.2011
comment
Конечно, я сделал. Я не знаю, как представить результат в удобочитаемом виде, поэтому я надеялся, что любой заинтересованный скопирует и вставит его в свой любимый инструмент сравнения. Я мог бы выложить скриншот части этого.   -  person AndreKR    schedule 02.07.2011
comment
Ну, я не знаю. ;) Я не очень разбираюсь в сборке и распределении памяти на этой платформе. Я нашел electro-tech-online.com/microcontrollers/, который может быть связан, но в любом случае дает мало информации.   -  person AndreKR    schedule 02.07.2011
comment
Почему за это проголосовали? Мне кажется, достаточно хороший вопрос.   -  person Praetorian    schedule 02.07.2011
comment
Используете ли вы явные флаги оптимизации?   -  person detly    schedule 02.07.2011


Ответы (5)


Чтобы людям не приходилось гадать, вот утверждение из руководства по Microchip C18 (выделение добавлено):

Обычно рекомендуется свести использование встроенного ассемблера к минимуму. Любые функции, содержащие встроенный ассемблер, не будут оптимизированы компилятором. Чтобы написать большие фрагменты ассемблерного кода, используйте ассемблер MPASM и свяжите модули с модулями C с помощью компоновщика MPLINK.

Я думаю, что это обычная ситуация с inline asm. GCC является исключением - он оптимизирует встроенную сборку вместе с окружающим кодом C; чтобы сделать это правильно, встроенная сборка GCC довольно сложна (вы должны сообщить ей, какие регистры и память затираются).

person Michael Burr    schedule 02.07.2011
comment
Это самый полный ответ здесь. Другие ответы правильно понимают проблему (Inline asm вызывает проблемы для компиляторов), но, глядя на положение nop в коде, кажется, что даже если предположить, что asm затер все регистры, последующий код можно было бы оптимизировать. Таким образом, только цитата из руководства, описывающая, что любая функция со встроенным ассемблером не будет оптимизирована, может действительно объяснить поведение. - person James Greenhalgh; 02.07.2011
comment
Я перепроверил флаги компилятора и обнаружил, что компилирую с оптимизацией. Когда я отключаю оптимизацию (-o-), листинг с _asm и без него идентичен. - person AndreKR; 02.07.2011
comment
Поскольку это ISR, добавление вызова функции усугубило бы ситуацию, поэтому теперь я подделал NOP, записав второй раз в volatile-регистр. - person AndreKR; 02.07.2011

Встроенный блок asm == без оптимизации

Похоже, что компилятор выдает инструкции MOVLB перед любым доступом к "резервной оперативной памяти".

Оптимизатор уберет лишнее. (И некоторые другие вещи.)

Оптимизатор не запускается, когда у вас есть встроенная сборка.

Таким образом, добавление этого встроенного блока равносильно отключению оптимизации.

person DigitalRoss    schedule 02.07.2011

Подозреваю, что дело в оптимизации.

Компилятор видит, что вы вставляете кусок языка ассемблера, он не знает, какой эффект это произведет, поэтому он просто действует более осторожно.

person MRAB    schedule 02.07.2011

Ваш компилятор, похоже, имеет относительно плохое расширение для включения ассемблера. По сути, это не дает компилятору никакого намека на то, какой регистр вы используете, возможно, изменяете и т. Д. Для создания согласованного кода ассемблер, который он производит, должен значительно отличаться от этого. Он должен заново инициализировать все свои регистры известными значениями.

Другие компиляторы, например, gcc, имеют расширение asm, которое позволяет вам более точно определить эти вещи. В частности, у вас есть эффективные способы сообщить компилятору, на какую память и регистры влияет ваш ассемблерный код. Для них такая инструкция NOP введет не более чем «оптимизационный барьер».

person Jens Gustedt    schedule 02.07.2011

Как упомянул MRAB в своем ответ, вероятно, это проблема оптимизации. Попробуйте переместить инструкции по сборке в отдельную функцию.

Вызов функции, вероятно, добавит больше накладных расходов, чем 2 NOPs, поэтому вы можете попробовать возиться с функцией, как только выясните, имеет ли это значение. Например, попробуйте объявить функцию inline или напишите функцию как вызываемую ассемблерную функцию C (при условии, что это возможно с вашим компилятором).

person Praetorian    schedule 02.07.2011