Electronic – Why GCC compiler omitting some code

avravr-gcccgccoptimization

I can not understand why GCC compiler cutting out part of my code while it preserve absolutely the same one in the neighborhood?

The C code:

#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);

ISR(INT0_vect){
    unsigned char i;

    i = 10;
    while(i>0)i--;   // first pause - omitted

    setb_SYNCO;
    setb_GATE;
    i=30;
    clrb_SYNCO;
    while(i>0)i--;  // second pause - preserved
    clrb_GATE;
}

The corresponding part of LSS (assembler file, created by the compiler):

ISR(INT0_vect){
  a4:   1f 92           push    r1
  a6:   0f 92           push    r0
  a8:   0f b6           in  r0, 0x3f    ; 63
  aa:   0f 92           push    r0
  ac:   11 24           eor r1, r1
  ae:   8f 93           push    r24
    unsigned char i;

    i = 10;
    while(i>0)i--;

    setb_SYNCO;
  b0:   d8 9a           sbi 0x1b, 0 ; 27
    setb_GATE;
  b2:   d9 9a           sbi 0x1b, 1 ; 27
    i=30;
    clrb_SYNCO;
  b4:   d8 98           cbi 0x1b, 0 ; 27
  b6:   8e e1           ldi r24, 0x1E   ; 30
  b8:   81 50           subi    r24, 0x01   ; 1
    while(i>0)i--;
  ba:   f1 f7           brne    .-4         ; 0xb8 <__vector_1+0x14>
    clrb_GATE;
  bc:   d9 98           cbi 0x1b, 1 ; 27
}
  be:   8f 91           pop r24
  c0:   0f 90           pop r0
  c2:   0f be           out 0x3f, r0    ; 63
  c4:   0f 90           pop r0
  c6:   1f 90           pop r1
  c8:   18 95           reti

I could assume that compiler figure out that such code is dummy and cuts it out but why is it preserve the same one in the end of the code?

Is there any compiler instructions to prevent from such optimization?

Best Answer

Since in one comment you state that "each CPU tick is worthy" I suggest using some inline assembly to make your delays loop just as you want. This solution is superior to the various volatile or -O0 because it makes clear what your intent is.

unsigned char i = 10;
__asm__ volatile ( "loop: subi    %0, 0x01\n\t"
                   "      brne    loop"
                   : "+rm" (i)
                   : /* no inputs */
                   : /* no dirty registers to decleare*/);

That should do the trick. The volatile thing is there to tell the compiler "I know this does not do anything, just keep it and trust me". The three asm "statements" are quite self explanatory, you can use any register instead of r24, I believe the compiler likes lower registers so you might want to use a high one. After the first : you should list output (read and write) c variables, and there's none, after the second : you should list input (ronly) c variables, again, there is none, and the third parameter is a comma separated list of modified registers, in this case r24. I am not sure if you should include also the status register since the ZERO flag changes of course, I did not include it.

edit edited answer as OP requested. Some notes.

The "+rm" before (i) means that you are letting the compiler decide to place i in memory or in a register. That's a good thing in most cases since the compiler can optimize better if it's free. In your case I believe you want to keep only the r constraint to force i to be a register.