¿Por qué el compilador GCC omite algún código?

9

No puedo entender por qué el compilador de GCC recorta parte de mi código mientras conserva absolutamente el mismo en el vecindario.

El código C:

#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);

ISR(INT0_vect){
    unsigned char i;

    i = 10;
    while(i>0)i--;   // first pause - omitted

    setb_SYNCO;
    setb_GATE;
    i=30;
    clrb_SYNCO;
    while(i>0)i--;  // second pause - preserved
    clrb_GATE;
}

La parte correspondiente de LSS (archivo ensamblador, creado por el compilador):

ISR(INT0_vect){
  a4:   1f 92           push    r1
  a6:   0f 92           push    r0
  a8:   0f b6           in  r0, 0x3f    ; 63
  aa:   0f 92           push    r0
  ac:   11 24           eor r1, r1
  ae:   8f 93           push    r24
    unsigned char i;

    i = 10;
    while(i>0)i--;

    setb_SYNCO;
  b0:   d8 9a           sbi 0x1b, 0 ; 27
    setb_GATE;
  b2:   d9 9a           sbi 0x1b, 1 ; 27
    i=30;
    clrb_SYNCO;
  b4:   d8 98           cbi 0x1b, 0 ; 27
  b6:   8e e1           ldi r24, 0x1E   ; 30
  b8:   81 50           subi    r24, 0x01   ; 1
    while(i>0)i--;
  ba:   f1 f7           brne    .-4         ; 0xb8 <__vector_1+0x14>
    clrb_GATE;
  bc:   d9 98           cbi 0x1b, 1 ; 27
}
  be:   8f 91           pop r24
  c0:   0f 90           pop r0
  c2:   0f be           out 0x3f, r0    ; 63
  c4:   0f 90           pop r0
  c6:   1f 90           pop r1
  c8:   18 95           reti

Podría suponer que el compilador descubre que dicho código es ficticio y lo elimina, pero ¿por qué conserva el mismo al final del código?

¿Hay instrucciones del compilador para evitar dicha optimización?

    
pregunta Roman Matveev

4 respuestas

9

Ya que en un comentario usted declara que "cada tilde de CPU es digno", sugiero usar un ensamblaje en línea para hacer que sus retrasos se repitan como desee. Esta solución es superior a los diversos volatile o -O0 porque deja claro cuál es su intención.

unsigned char i = 10;
__asm__ volatile ( "loop: subi    %0, 0x01\n\t"
                   "      brne    loop"
                   : "+rm" (i)
                   : /* no inputs */
                   : /* no dirty registers to decleare*/);

Eso debería hacer el truco. Lo volátil está ahí para decirle al compilador "Sé que esto no hace nada, solo guárdalo y confía en mí". Las tres "declaraciones" de asm son bastante autoexplicativas, puede usar cualquier registro en lugar de r24, creo que al compilador le gustan los registros inferiores, por lo que es posible que desee utilizar uno alto. Después del primer : debe listar las variables c de salida (lectura y escritura), y no hay ninguna, después del segundo : debe listar las variables de entrada (ronly) c, nuevamente, no hay ninguna, y el tercer parámetro es un Lista separada por comas de registros modificados, en este caso r24. No estoy seguro de si debería incluir también el registro de estado, ya que la marca ZERO cambia, por supuesto, no lo incluí.

editar respuesta editada como OP solicitada. Algunas notas.

El "+rm" antes de (i) significa que está dejando que el compilador decida colocar i en m emory o en un r egister. Eso es bueno en la mayoría de los casos, ya que el compilador puede optimizar mejor si es gratis. En su caso, creo que desea mantener solo la restricción r para obligar a i a ser un registro.

    
respondido por el Vladimir Cravero
13

Podrías intentar hacer que el bucle realmente haga algo. Tal como está, el compilador está diciendo con razón: "Este bucle no hace nada, me desharé de él".

Para que puedas probar una construcción que uso frecuentemente:

int i;
for (i = 0; i < 10; i++) {
    asm volatile ("nop");
}

Nota: no todos los destinos para el compilador gcc usan la misma sintaxis de ensamblaje en línea, es posible que debas modificarlo para tu objetivo.

    
respondido por el Majenko
6

Sí, podrías asumir eso. Si declara la variable i como volátil, le dice al compilador que no optimice en i.

    
respondido por el Ambiorix
1

Después del primer bucle i es una constante. La inicialización de i y el bucle no hacen más que producir un valor constante. Nada en el estándar especifica que este bucle debe compilarse como está. La norma tampoco dice nada sobre el tiempo. El código compilado debe comportarse como si el bucle estuviera presente y lo hace. No se puede decir de manera confiable que esta optimización se realizó según el estándar (el tiempo no cuenta).

El segundo bucle también debe eliminarse. Considero que es un error (o una optimización faltante) que no lo es. Después del bucle i es constante a cero. El código debe reemplazarse con la configuración de i en cero.

Creo que GCC mantiene i alrededor únicamente porque el acceso al puerto (opaco) puede afectar a i .

Utilizar

asm volatile ("nop");

para engañar a GCC para que crea que el bucle hace algo.

    
respondido por el usr

Lea otras preguntas en las etiquetas