¿Por qué el compilador no usa directamente LSR?

10

Hola, he estado trabajando en un proyecto utilizando un Arduino Uno (entonces ATmega328p) donde el tiempo es bastante importante y quería ver en qué instrucciones el compilador estaba convirtiendo mi código. Y allí tengo una uint8_tque desplazo un poco a la derecha en cada iteración usando data >>= 1y parece que el compilador tradujo esto en 5 instrucciones ( dataestá en r24):

mov     r18, r24
ldi     r19, 0x00
asr     r19
ror     r18
mov     r24, r18

Pero si miro la documentación del conjunto de instrucciones, veo una instrucción que hace exactamente esto: lsr r24

¿Puedo pasar por alto algo o por qué el compilador no está usando esto también? Los registros r18y r19no se utilizan en ningún otro lugar.

Estoy usando un Ardunio, pero si estoy en lo correcto, solo uso el avr-gcccompilador normal . Este es el código (recortado) que genera la secuencia:

ISR(PCINT0_vect) {
    uint8_t data = 0;
    for (uint8_t i = 8; i > 0; --i) {
//        asm volatile ("lsr %0": "+w" (data));
        data >>= 1;
        if (PINB & (1 << PB0))
            data |= 0x80;
    }
    host_data = data;
}

Por lo que puedo ver, el IDE de Ardunino está utilizando el compilador AVR gcc proporcionado por el sistema, que es la versión 6.2.0-1.fc24. Ambos se instalan a través del administrador de paquetes, por lo que deben estar actualizados.

xZise
fuente
1
El ensamblaje no parece corresponder al código C.
Eugene Sh.
Bueno, lo compilé usando el Ardunio IDE y luego lo usé avr-objdumpen el archivo elfo ... ¿Qué es lo que parece no corresponder?
xZise
1
@Eugene Sh .: Se hace corresponden al código C. Corresponde solo a la líneadata >>= 1;
Cuajada
1
Este es uno de los casos en que "usar turnos en lugar de división" es el consejo equivocado. Si hace / = 2, el compilador generará lsr r24; (consejo: pruebe el explorador gcc para jugar con la generación de código asm)
PlasmaHH
Que compilador Que procesador Realmente debería ser obvio que esta es información necesaria para que la pregunta tenga sentido.
Olin Lathrop

Respuestas:

18

De acuerdo con la especificación del lenguaje C, cualquier valor cuyo tamaño sea menor que el tamaño de int(depende del compilador particular; en su caso intes de 16 bits de ancho) involucrado en cualquier operación (en su caso >>) se convierte en un intantes de la operación.
Este comportamiento del compilador se llama promoción de enteros .

Y eso es exactamente lo que hizo el compilador:

  • r19 = 0 es el MSByte del valor promocionado entero de data.
  • (r19, r18) representa el valor total promovido entero de dataese valor que luego se desplaza un bit hacia la derecha por asr r19y ror 18.
  • El resultado se devuelve implícitamente a su uint8_tvariable data:
    mov r24, r18es decir, se desecha el MSByte en r19.

Editar: por
supuesto, el cumplidor podría optimizar el código.
Intentando reproducir el problema, descubrí que al menos con avr-gcc versión 4.9.2 el problema no ocurre. Crea un código muy eficiente, es decir, C-line data >>= 1;se compila en una sola lsr r24instrucción. Entonces, tal vez esté utilizando una versión compiladora muy antigua.

Cuajada
fuente
2
No es un desperdicio total porque a veces se necesita el código no optimizado para la depuración a nivel de ensamblador. Entonces está muy contento si tiene un código no optimizado.
Cuajada
3
Si recuerdo correctamente -mint8 es la bandera para hacer enteros de 8 bits. Sin embargo, esto tiene muchos efectos secundarios no deseados. Lo siento, no puedo recordar lo que eran ahora, pero nunca usé la bandera por ellos. Pasé mucho tiempo comparando avr-gcc con un compilador comercial hace muchos años.
Jon
1
Ah, sí, el estándar C requiere que los enteros sean de al menos 16 bits, por lo que usar -mint8 rompe todas las bibliotecas.
Jon
99
Nigel Jones dijo en "Efficient C Code for Microcontrollers de 8 bits" algo así como: "... Las reglas de promoción de enteros de C son probablemente el crimen más atroz cometido contra aquellos de nosotros que trabajamos en el mundo de 8 bits" ...
Dirceu Rodrigues Jr
1
@Jonas Wielicki: la mejor solución para el problema es usar un mejor compilador. Por ejemplo, con avr-gcc versión 4.9.2, no puedo reproducir el problema: para la línea de código C d >>= 1;solo recibo una sola lsr r24instrucción. Quizás xZise está utilizando una versión de compilador muy antigua.
Cuajada