No puedo entender por qué el compilador de GCC corta 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 corta, pero ¿por qué conserva el mismo al final del código?
¿Hay alguna instrucción del compilador para evitar dicha optimización?
Respuestas:
Dado que en un comentario declaras que "cada tic de CPU es digno", sugiero usar un ensamblaje en línea para hacer que tus retrasos se repitan como quieras. Esta solución es superior a las diversas
volatile
o-O0
porque deja en claro cuál es su intención.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" asm se explican por sí mismas, puede usar cualquier registro en lugar de r24, creo que al compilador le gustan los registros inferiores, por lo que es posible que desee usar uno alto. Después de la primera
:
, debe enumerar las variables de salida (lectura y escritura) c, y no hay ninguna, después de la segunda:
, debe enumerar las variables de entrada (ronly) c, nuevamente, no hay ninguna, y el tercer parámetro es una lista separada por comas de registros modificados , en este caso r24. No estoy seguro de si debe incluir también el registro de estado, ya que laZERO
bandera cambia, por supuesto, no lo incluí.edite la respuesta editada según lo solicitado por OP. Algunas notas.
El
"+rm"
antes(i)
significa que usted está dejando que el compilador decide colocar i en m Emory o en un r egistro. Eso es algo 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 forzar a i a ser un registro.fuente
c
variable en lugar del literal10
que mencioné en la respuesta original? Estoy tratando de leer los manuales de GCC sobre el uso adecuado de la construcción de asm, pero ahora estoy un poco oscurecido. ¡Te lo agradecería mucho!Podrías intentar hacer que el ciclo realmente haga algo. Tal como está, el compilador dice con razón: "Este ciclo no está haciendo nada, me deshaceré de él".
Entonces podrías probar una construcción que uso con frecuencia:
Nota: no todos los objetivos para el compilador gcc usan la misma sintaxis de ensamblaje en línea; es posible que deba ajustarlo para su objetivo.
fuente
Sí, puedes asumir eso. Si declaras que la variable i es volátil, le dices al compilador que no optimice en i.
fuente
register unsigned char volatile i __asm__("r1");
tal vez?i
como volátil resuelve todo. Esto está garantizado por el estándar C 5.1.2.3. Un compilador conforme no debe optimizar esos bucles sii
es volátil. Afortunadamente, GCC es un compilador conforme. Desafortunadamente, hay muchos posibles compiladores de C que no se ajustan al estándar, pero eso es irrelevante para esta pregunta en particular. No hay absolutamente ninguna necesidad de ensamblador en línea.Después del primer bucle
i
es una constante. La inicialización dei
y el bucle no hacen más que producir un valor constante. Nada en el estándar especifica que este bucle debe compilarse tal cual. El estándar 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ó bajo el estándar (el tiempo no cuenta).El segundo bucle también debe eliminarse. Considero que es un error (o una optimización que falta) que no lo es. Después del ciclo
i
es constante cero. El código debe reemplazarse con la configuracióni
a cero.Creo que GCC se mantiene
i
únicamente por la razón por la que puede afectar el acceso a un puerto (opaco)i
.Utilizar
engañar a GCC para que crea que el ciclo hace algo.
fuente