Optimization level breaks the C code order

Viewed 117

I have the following 5 lines of code and want the this lines are executed in exact this order with O2 or O3:

    PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
    TC3->COUNT16.COUNT.reg =  (volatile uint16_t) 0;
    TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
    SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
    DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;

If I optimize with O2 or O3 the code breaks at line 264 because this line must be executed before line 265:

  261:      PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
200001EE   ldr  r1, [pc, #84]        
   263:         TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
200001F0   ldr  r5, [pc, #84]        
   264:         SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
200001F2   ldr  r4, [pc, #88]        
   265:         DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;
200001F4   ldr  r0, [pc, #88]        
   261:         PORT->Group[GPIO_PORTB].OUTCLR.reg = (volatile uint32_t) 1 << 9;
200001F6   mov.w    r6, #512         
200001FA   str.w    r6, [r1, #148]       
   262:         TC3->COUNT16.COUNT.reg =  (volatile uint16_t) 0;
200001FE   strh r2, [r3, #20]        
   263:         TC3->COUNT16.CC[0].reg = (volatile uint16_t) vusb_driver->in_data->bitlength;
20000200   ldr  r2, [r5]         
20000202   ldr  r2, [r2, #20]        
20000204   ldrh.w   r2, [r2, #72]        
20000208   strh r2, [r3, #28]        
   264:         SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
2000020A   movs r5, #84      
   265:         DMAC->Channel[USB_SEND_SD_DMA_CH].CHCTRLA.reg = (volatile uint8_t) DMAC_CHCTRLA_ENABLE;
2000020C   movs r2, #2       
   264:         SERCOM0->SPI.DATA.reg = (volatile uint32_t) 0x54;
2000020E   str  r5, [r4, #40]   

 
2 Answers

Your use of volatile is incorrect, you should define the destination objects as volatile to ensure they are written to exactly in the order of the program.

The compiler is - in the regular case - allowed to reorder instructions if the effect of the execution is the same (this is the "as-if rule"). So you must do one of the following:

  • Indicate to it that the instructions will have a different effect (e.g. by making the relevant .reg variables volatile; or through aliasing of pointers etc.)
  • Use some sort of compiler-specific directives to control its behavior.
  • Not compile, i.e. generate your machine code in a different manner.

Specifically, if you choose the first option, you must explain - to the compiler and perhaps to yourself, why is it that

line [264] must be executed before line 265

In what sense "must" it be executed before 265? Who would notice? It's likely that a concrete answer to this question is something you could use to force the desired order of execution.

Related