在Microchip C18中,为什么插入NOP会导致更大的代码?

12

我在中断服务程序中有一些代码。这里提供代码仅供完整性参考,问题只涉及被注释的 __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


为什么会有这么大的差异?- 你尝试过使用 diff 工具比较这两段代码吗? - Mitch Wheat
当然了。我不知道如何在这里以可读的方式呈现结果,所以我希望有兴趣的人能够自己将其复制并粘贴到他们最喜欢的 diff 工具中。我可以放一张部分截图。 - AndreKR
嗯,我不知道。;) 我对这个平台上的汇编和内存布局并不了解。我发现http://www.electro-tech-online.com/microcontrollers/38480-mplab-c18-added-nop-loop-decreased-execution-time.html#post304269 这个网址可能相关,但提供的信息很少。 - AndreKR
1
为什么这个问题被踩了?在我看来,这是一个足够好的问题。 - Praetorian
你是否使用显式的优化标志? - detly
5个回答

10
为了让人们不必猜测,这里有一份来自Microchip C18手册的声明(已经加重了):

一般建议最小限度使用内联汇编。 任何包含内联汇编的函数都将不会被编译器优化。如果要编写大量的汇编代码,请使用MPASM汇编器,并使用MPLINK链接器将模块链接到C模块。

我认为这是内联汇编的常见情况。GCC是一个例外-它将优化内联汇编以及周围的C代码;为了正确地进行这个过程,GCC的内联汇编非常复杂(你必须让它知道哪些寄存器和内存被破坏)。

这是这里最完整的答案。其他答案解决了问题(内联汇编会导致编译器出现问题),但是从nop在代码中的位置来看,即使假设asm破坏了所有寄存器,后续代码也是可优化的。因此,只有引用手册描述任何带有内联汇编的函数都不会被优化才能真正解释这种行为。 - James Greenhalgh
1
我仔细检查了编译器标志,并发现我实际上是在进行优化编译。当我关闭优化(-o-)时,使用和不使用__asm_的清单是相同的。 - AndreKR
由于这是一个ISR,添加函数调用会使情况变得更糟,因此现在我通过第二次写入易失性寄存器来伪造NOP。 - AndreKR

3

内联汇编块 == 没有优化

似乎编译器在访问"分段RAM"之前会发出MOVLB指令。

优化器会将额外的指令移除。(还有其他一些东西。)

当您使用内联汇编时,优化器不会运行。

因此添加内联块就相当于关闭了优化


2

我怀疑这与优化有关。

编译器看到你插入了一段汇编代码,不知道会产生什么影响,所以它更加谨慎地处理。


1

你的编译器似乎在包含汇编语言方面有相对较差的扩展性。基本上,它根本不会向编译器提供任何提示,你正在使用哪个寄存器,可能进行了修改等等。为了生成一致的代码,他所产生的汇编代码必须与之前显著不同。他必须重新初始化所有寄存器以知道的值。

其他编译器,例如gcc,具有asm扩展,让您在这些事情上更具体。特别是,您可以有效地告诉编译器您的汇编代码影响哪些内存和寄存器。对于它们来说,这样一个NOP指令将不会引入比“优化障碍”更多的东西。


0

正如MRAB在他的答案中提到的那样,这很可能是一个优化问题。尝试将汇编指令移动到它们自己的函数中。

函数调用可能会增加比2个NOP更多的开销,因此您可以在确定是否有差异后尝试调整函数。例如,尝试声明函数为inline,或将函数编写为C可调用的汇编函数(假设您的编译器支持此功能)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接