1

case 1:

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

case 2:

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

With Microchip XC8 compiler, I found that case 1 needs more instruction(the program memory used is little increased). Why?

Note: 8-bit CPU. The XC8 compiler conforms to C89 standard.

assembly:

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
Cœur
  • 37,241
  • 25
  • 195
  • 267
Andy Lin
  • 397
  • 1
  • 2
  • 21
  • 8
    It would improve the question to also show the assembly generated in each case. It could be a compiler bug. – M.M Apr 24 '18 at 01:44
  • 3
    What does “needs more program memory in … compiler” mean? The instruction(s) generated take more memory? The instruction(s) require more memory to execute? The compiler uses more memory when compiling it? How much more? How did you determine this? – Eric Postpischil Apr 24 '18 at 01:47
  • 2
    I don't know about XC8 but in other architectures generally signed divisions are slower than unsigned ones [Why A / is faster when A is unsigned vs signed](https://stackoverflow.com/q/49477554/995714), [performance of unsigned vs signed integers](https://stackoverflow.com/q/4712315/995714), [Is there a difference in term of performance between “unsigned int” and “int” on the IPhone?](https://stackoverflow.com/q/410982/995714) – phuclv Apr 24 '18 at 01:49
  • 1
    C89 does not strictly define behavior of signed integer division. Two behaviors are possible. The final decision is left to implementation. This could be a factor here. What type is `i`? – AnT stands with Russia Apr 24 '18 at 01:50
  • 1
    Which line is line 1? Your title says the signed divisor takes more, but it is on the second line, and your text says line 1 takes more. Are the lines numbered 0 and 1? – Eric Postpischil Apr 24 '18 at 01:50
  • Sorry, everybody. The content is changed! – Andy Lin Apr 24 '18 at 07:17
  • 1
    You still should post the assembly – M.M Apr 24 '18 at 07:38
  • assembly added, if not clear enough, plz tell me – Andy Lin Apr 25 '18 at 07:22
  • You need to be careful about promotion. I think i is promoted to an int (possibly a unsigned int in the second case.) This promotion may be part of the difference. (sorry, not understood the assembler after a glance) – William J Bagshaw Nov 12 '18 at 17:11

1 Answers1

1

The usual arithmetic conversions are performed on the operands of %. I am guessing that i is a signed type; the result of i % 3 may differ from the result of i % 3u.

For example, -1 % 3u is usually 0 because after conversion to unsigned int, -1 typically becomes one less than an even power of two, e.g. 65535 or 4294967295, which is always a multiple of 3.

But -1 % 3 is either -1 or 2 in C89 (it's implementation-defined which).

It stands to reason that the compiler might have to issue different assembly instructions for these two different cases.

The edit to the question doesn't change this; the uint8_t is promoted to signed int, and the optimizer might not realize that it could use the "better" assembly for both cases.

M.M
  • 138,810
  • 21
  • 208
  • 365