placas mrelberni microcontroladores linea isp comandos caracteristicas c++ gcc assembly optimization arm

c++ - mrelberni - GCC genera código diferente según el valor del índice de matriz



mrelberni avr (1)

Lo consideraría una oportunidad de optimización faltante / artefacto de -O1.

Se puede entender con más detalle si miramos el código generado con -O- para cargar bb[...] :

Primer caso:

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

Segundo caso:

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

El código en el segundo caso es mejor y se puede hacer de esta manera porque la constante se puede agregar con dos instrucciones adicionales (que no es el caso si el índice es 0x0008646B).

-O1 solo optimizaciones que no consumen mucho tiempo. Así que aparentemente fusiona temprano el add y el ldr así que luego pierde la oportunidad de cargar toda la dirección con un pc relativo ldr.

Compile con -O2 (o -fgcse) y el código se parece a lo esperado.

Este código (brazo):

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

... está compilado para seguir el código asm:

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>

Está bien.

Pero, si cambio el índice de matriz ( -0x400 ) ...

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

... No tengo un código tan optimizado:

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 diferencia es que en el primer caso r4 se carga con la dirección de destino inmediatamente ( 0x422191ac ) y luego el acceso a la memoria se realiza con instrucciones de 2 bytes, pero en el segundo caso r4 se carga con alguna dirección intermedia ( 0x42218000 ) y luego accede a la memoria se realiza con instrucciones de 4 bytes con desplazamiento ( +0x1ac ) a la dirección de destino ( 0x422181ac ).

¿Por qué el compilador lo hace?

Yo 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 es:

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

En .ld se define como: en la sección MEMORY :

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

en la sección de SECTIONS :

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