В 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

Ответ 1

Так что людям не нужно угадывать, вот инструкция из руководство Microchip C18 (выделено мной):

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

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

Ответ 2

Inline asm block == без оптимизации

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

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

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

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

Ответ 3

Я подозреваю, что это связано с оптимизацией.

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

Ответ 4

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

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

Ответ 5

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

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