Promoción de enteros en MCU de 8 bits

12

Usando avr-gcc como ejemplo, se especifica que los tipos int son de 16 bits de ancho. La realización de operaciones en operandos de 8 bits en C da como resultado que esos operandos se conviertan a tipos int de 16 bits debido a la promoción de enteros en C. ¿Esto significa que todas las operaciones aritméticas de 8 bits en un AVR tomarán mucho más tiempo si se escriben en C que Si está escrito en ensamblaje debido a la promoción de enteros de C?

    
pregunta pr871

3 respuestas

14

Larga historia corta:

La promoción de enteros a 16 bits siempre tiene lugar, el estándar C lo aplica. Pero al compilador se le permite optimizar el cálculo a 8 bits (los compiladores de sistemas integrados suelen ser bastante buenos en tales optimizaciones), si puede deducir que el signo será el mismo que habría sido si el tipo hubiera sido promovido.

¡Este no es siempre el caso! Los cambios de firma implícitos causados por la promoción de enteros son una fuente común de errores en los sistemas integrados.

Puede encontrar una explicación detallada aquí: Reglas de promoción de tipo implícito .

    
respondido por el Lundin
7

No necesariamente, ya que los compiladores modernos hacen un buen trabajo en la optimización del código generado. Por ejemplo, si escribe z = x + y; donde todas las variables son unsigned char , el compilador debe promoverlas a unsigned int antes de realizar los cálculos. Sin embargo, dado que el resultado final será exactamente el mismo sin la promoción, el compilador generará un código que solo agrega variables de 8 bits.

Por supuesto, esto no siempre es así, por ejemplo, el resultado de z = (x + y)/2; dependería del byte superior, por lo que la promoción tendrá lugar. Aún se puede evitar sin tener que recurrir al ensamblaje volviendo el resultado intermedio a unsigned char .

Algunas de estas ineficiencias pueden evitarse utilizando las opciones del compilador. Por ejemplo, muchos compiladores de 8 bits tienen un pragma o un modificador de línea de comandos para ajustar los tipos de enumeración en 1 byte, en lugar de int como lo requiere C.

    
respondido por el Dmitry Grigoryev
7
unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned char fun2 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned int fun3 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

unsigned char fun4 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

como se espera fun1 es todos los ints también lo hace la matemática de 16 bits

00000000 <fun1>:
   0:   86 0f           add r24, r22
   2:   97 1f           adc r25, r23
   4:   08 95           ret

Aunque técnicamente es incorrecto, ya que es una adición de 16 bits llamada por el código, incluso sin optimizarse, este compilador eliminó el anuncio debido al tamaño del resultado.

00000006 <fun2>:
   6:   86 0f           add r24, r22
   8:   08 95           ret

no está realmente sorprendido de que la promoción ocurra, los compiladores no solían hacer esto, no estaban seguros de qué versión hizo que esto empezara a suceder, me topé con esto al principio de mi carrera y, a pesar de que los compiladores se promocionaron fuera de orden (como en el ejemplo anterior) A pesar de que le dije que hiciera uchar matemáticas, no se sorprendió.

0000000a <fun3>:
   a:   70 e0           ldi r23, 0x00   ; 0
   c:   26 2f           mov r18, r22
   e:   37 2f           mov r19, r23
  10:   28 0f           add r18, r24
  12:   31 1d           adc r19, r1
  14:   82 2f           mov r24, r18
  16:   93 2f           mov r25, r19
  18:   08 95           ret

y lo ideal, sé que es de 8 bits, quiero un resultado de 8 bits, así que simplemente le dije que hiciera 8 bits hasta el final.

0000001a <fun4>:
  1a:   86 0f           add r24, r22
  1c:   08 95           ret

Entonces, en general, es mejor apuntar al tamaño del registro, que es idealmente el tamaño de un (u) int, para un mcu de 8 bits como este, los autores del compilador tuvieron que hacer un compromiso ... No se hacen puntos un hábito de usar uchar para matemáticas que usted sabe que no necesita más de 8 bits como cuando mueve ese código o escribe un nuevo código como ese en un procesador con registros más grandes ahora el compilador tiene que comenzar a enmascarar y extender signos, lo que algunos hacen de forma nativa Algunas instrucciones y otras no.

00000000 <fun1>:
   0:   e0800001    add r0, r0, r1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e0800001    add r0, r0, r1
   c:   e20000ff    and r0, r0, #255    ; 0xff
  10:   e12fff1e    bx  lr

forzar 8 bit cuesta más. Hice trampa un poco / mucho, necesitaría ejemplos un poco más complicados para ver más de esto de una manera justa.

EDITAR basado en comentarios discusión

unsigned int fun ( unsigned char a, unsigned char b )
{
    unsigned int c;
    c = (a<<8)|b;
    return(c);
}

00000000 <fun>:
   0:   70 e0           ldi r23, 0x00   ; 0
   2:   26 2f           mov r18, r22
   4:   37 2f           mov r19, r23
   6:   38 2b           or  r19, r24
   8:   82 2f           mov r24, r18
   a:   93 2f           mov r25, r19
   c:   08 95           ret

00000000 <fun>:
   0:   e1810400    orr r0, r1, r0, lsl #8
   4:   e12fff1e    bx  lr

no es de extrañar. Aunque, ¿por qué el optimizador dejó esa instrucción adicional, no puede usar ldi en r19? (Sabía la respuesta cuando la pregunté).

EDIT2

para avr

avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

para evitar el mal hábito o no la comparación de 8 bits

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

claramente que la optimización estaba activada solo toma un segundo probar con su propio compilador para ver cómo se compara con mi salida, pero de todos modos:

whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o

Y sí, el uso de bytes para las variables de tamaño de byte, ciertamente en un avr, pic, etc., le ahorrará memoria y usted realmente desea intentar conservarlo ... si realmente lo está utilizando, pero como se muestra aquí como poco como sea posible, estará en la memoria, tanto en los registros como sea posible, por lo que el ahorro repentino se produce al no tener variables adicionales, el ahorro de RAM puede o no ser real.

    
respondido por el old_timer

Lea otras preguntas en las etiquetas