2015-05-10 10 views
6

Questo codice (braccio):GCC genera codice differente a seconda schiera di valori di indice

void blinkRed(void) 
{ 
    for(;;) 
    { 
     bb[0x0008646B] ^= 1; 
     sys.Delay_ms(14); 
    } 
} 

... viene compilato in folowing asm-codice:

08000470: ldr r4, [pc, #20]  ; (0x8000488 <blinkRed()+24>) // r4 = 0x422191ac 
08000472: ldr r6, [pc, #24]  ; (0x800048c <blinkRed()+28>) 
08000474: movs r5, #14 
08000476: ldr r3, [r4, #0] 
08000478: eor.w r3, r3, #1 
0800047c: str r3, [r4, #0] 
0800047e: mov r0, r6 
08000480: mov r1, r5 
08000482: bl 0x80001ac <CSTM32F100C6::Delay_ms(unsigned int)> 
08000486: b.n 0x8000476 <blinkRed()+6> 

È ok.

Ma, se mi limito a cambiare indice di array (-0x400) ....

void blinkRed(void) 
{ 
    for(;;) 
    { 
     bb[0x0008606B] ^= 1; 
     sys.Delay_ms(14); 
    } 
} 

... Ho codice non ottimizzato così:

08000470: ldr r4, [pc, #24]  ; (0x800048c <blinkRed()+28>) // r4 = 0x42218000 
08000472: ldr r6, [pc, #28]  ; (0x8000490 <blinkRed()+32>) 
08000474: movs r5, #14 
08000476: ldr.w r3, [r4, #428] ; 0x1ac 
0800047a: eor.w r3, r3, #1 
0800047e: str.w r3, [r4, #428] ; 0x1ac 
08000482: mov r0, r6 
08000484: mov r1, r5 
08000486: bl 0x80001ac <CSTM32F100C6::Delay_ms(unsigned int)> 
0800048a: b.n 0x8000476 <blinkRed()+6> 

La differenza è che in il primo caso r4 viene caricato con l'indirizzo di destinazione immediatamente (0x422191ac) e quindi l'accesso alla memoria viene eseguito con istruzioni a 2 byte, ma nel secondo caso lo r4 viene caricato con un indirizzo intermedio (0x42218000) e quindi l'accesso alla memoria viene eseguito con l'istruzione di 4 byte con offset (+0x1ac) all'indirizzo di destinazione (0x422181ac).

Perché il compilatore lo fa?

che uso: arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -g2 -Wall -O1 -std=gnu++14 -fno-exceptions -fno-use-cxa-atexit -fstrict-volatile-bitfields -c -DSTM32F100C6T6B -DSTM32F10X_LD_VL

bb è:

__attribute__ ((section(".bitband"))) volatile u32 bb[0x00800000]; 

In .ld esso è definito come: in MEMORY sezione:

BITBAND(rwx): ORIGIN = 0x42000000, LENGTH = 0x02000000 

in SECTIONS sezione:

.bitband (NOLOAD) : 
SUBALIGN(0x02000000) 
{ 
    KEEP(*(.bitband)) 
} > BITBAND 
+1

Ummm ... non sono sicuro se questo è rilevante, ma ... anche la versione ottimizzata funzionerà? Forse ci sono alcune condizioni preliminari per il carico più veloce.Queste informazioni indicano se si tratta del problema dell'ottimizzatore o dell'architettura. – luk32

+0

Entrambe le versioni funzionano. Non riesco a immaginare alcun presupposto disponibile in uno dei due casi e non disponibile in altri. Ho modificato l'indice passo dopo passo e ho scoperto che la seconda versione viene compilata se l'indice di array è compreso nell'intervallo da 0x00086020 a 0x000863FF. Se l'indice dell'array è fuori da questo intervallo, viene compilata la prima versione (ottimizzata). – Woodoo

+0

Che tipo di ARM? Probabilmente un problema di allineamento della memoria. – Unimportant

risposta

1

Lo considero un artefatto/opportunità di ottimizzazione mancante di -O1.

Si può comprendere più in dettaglio se guardiamo il codice generato con -O- per caricare bb[...]:

Primo caso:

movw r2, #:lower16:bb 
movt r2, #:upper16:bb 
movw r3, #37292 
movt r3, 33 
adds r3, r2, r3 
ldr r3, [r3, #0] 

Secondo caso:

movw r3, #:lower16:bb 
movt r3, #:upper16:bb 
add r3, r3, #2195456  ; 0x218000 = 4*0x86000 
add r3, r3, #428 
ldr r3, [r3, #0] 

Il codice nel secondo caso è migliore e può essere eseguito in questo modo perché la costante può essere aggiunta con due istruzioni di aggiunta (che non è il caso se l'indice è 0x0008646B).

-O1 esegue solo ottimizzazioni che non richiedono molto tempo. Quindi apparentemente si fonde presto l'add e il ldr in modo che manchi in seguito l'opportunità di caricare l'intero indirizzo con un ldr relativo al pc.

Compilare con -O2 (o -fgcse) e il codice sembra come previsto.