有符号除数的 % 运算符在 C 中需要更多的程序内存

% operator on signed divisor needs more program memory in C

案例 1:

static uint8_t i;
i % 3;     // 3 is a signed number

案例 2:

static uint8_t i;
i % 3u;    // 3 is a unsigned number

使用Microchip XC8编译器,我发现情况1需要更多的指令(​​使用的程序内存增加很少)。 为什么?

注:8 位 CPU。 XC8编译器符合C89标准

程序集:

225:                   if(KEYbits.I2C == ON && h%5u == 0){
02F2  1ED2     BTFSS KEYbits, 0x5
02F3  2AF5     GOTO 0x2F5
02F4  2AF6     GOTO 0x2F6
02F5  2C75     GOTO 0x475
02F6  3005     MOVLW 0x5
02F7  00F0     MOVWF __pcstackCOMMON
02F8  3000     MOVLW 0x0
02F9  00F1     MOVWF hold
02FA  0850     MOVF h, W
02FB  00DE     MOVWF 0x5E
02FC  01DF     CLRF 0x5F
02FD  085E     MOVF 0x5E, W
02FE  00F2     MOVWF product
02FF  085F     MOVF 0x5F, W
0300  00F3     MOVWF multiplier
0301  318A     MOVLP 0xA            //here
0302  22B4     CALL 0x2B4           //here
0303  3180     MOVLP 0x0
0304  0870     MOVF __pcstackCOMMON, W
0305  0471     IORWF hold, W
0306  1D03     BTFSS STATUS, 0x2
0307  2B09     GOTO 0x309
0308  2B0A     GOTO 0x30A
0309  2C75     GOTO 0x475
226:                       h = 0;

02B4  0834     MOVF TMR4_counter, W
02B5  07DE     ADDWF 0x5E, F
02B6  0835     MOVF 0x35, W
02B7  3DDF     ADDWFC 0x5F, F
02B8  0836     MOVF 0x36, W
02B9  3DE0     ADDWFC 0x60, F
02BA  0837     MOVF 0x37, W
02BB  3DE1     ADDWFC 0x61, F
02BC  0861     MOVF 0x61, W
02BD  00B7     MOVWF 0x37
02BE  0860     MOVF 0x60, W
02BF  00B6     MOVWF 0x36
02C0  085F     MOVF 0x5F, W
02C1  00B5     MOVWF 0x35
02C2  085E     MOVF 0x5E, W
02C3  00B4     MOVWF TMR4_counter

225:                   if(KEYbits.I2C == ON && h%5 == 0){
02F2  1ED2     BTFSS KEYbits, 0x5
02F3  2AF5     GOTO 0x2F5
02F4  2AF6     GOTO 0x2F6
02F5  2C75     GOTO 0x475
02F6  3005     MOVLW 0x5
02F7  00F0     MOVWF __pcstackCOMMON
02F8  3000     MOVLW 0x0
02F9  00F1     MOVWF hold
02FA  0850     MOVF h, W
02FB  00DE     MOVWF 0x5E
02FC  01DF     CLRF 0x5F
02FD  085E     MOVF 0x5E, W
02FE  00F2     MOVWF product
02FF  085F     MOVF 0x5F, W
0300  00F3     MOVWF multiplier
0301  3186     MOVLP 0x6           //here
0302  2663     CALL 0x663          //here
0303  3180     MOVLP 0x0
0304  0870     MOVF __pcstackCOMMON, W
0305  0471     IORWF hold, W
0306  1D03     BTFSS STATUS, 0x2
0307  2B09     GOTO 0x309
0308  2B0A     GOTO 0x30A
0309  2C75     GOTO 0x475
226:                       h = 0;


0663  01F6     CLRF sign
14:             if(dividend < 0) {
0664  1FF3     BTFSS multiplier, 0x7
0665  2E67     GOTO 0x667
0666  2E68     GOTO 0x668
0667  2E70     GOTO 0x670
15:                 dividend = -dividend;
0668  09F2     COMF product, F
0669  09F3     COMF multiplier, F
066A  0AF2     INCF product, F
066B  1903     BTFSC STATUS, 0x2
066C  0AF3     INCF multiplier, F
16:                 sign = 1;
066D  01F6     CLRF sign
066E  0AF6     INCF sign, F
066F  2E70     GOTO 0x670

%的操作数执行usual arithmetic conversions。我猜 i 是有符号类型; i % 3 的结果可能与 i % 3u 的结果不同。

例如,-1 % 3u 通常是 0,因为在转换为 unsigned int 之后,-1 通常会变成比 2 的偶数次方小一,例如655354294967295,始终是 3 的倍数。

但是 -1 % 3 在 C89 中要么是 -1 要么是 2(这是实现定义的)。

按理说,编译器可能必须针对这两种不同的情况发出不同的汇编指令。

对问题的编辑不会改变这一点; uint8_t 被提升为有符号 int,优化器可能没有意识到它可以在这两种情况下使用 "better" 程序集。