No puedo entender por qué el compilador GCC elimina 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 se da cuenta de 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?
Dado que en un comentario afirma que "cada tic de la CPU vale la pena", sugiero usar algún ensamblaje en línea para hacer que sus retrasos se reproduzcan como desee. Esta solución es superior a las varias volatile
o -O0
porque deja en 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árdelo y confíe en mí". Las tres "declaraciones" de asm se explican por sí mismas, puede usar cualquier registro en lugar de r24, creo que al compilador le gustan los registros más bajos, por lo que es posible que desee usar uno alto. Después del primero :
, debe enumerar las variables c de salida (leer y escribir), y no hay ninguna, después del segundo :
debe enumerar las variables c de entrada (solo), 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 la ZERO
bandera cambia, por supuesto, no lo incluí.
edite la respuesta editada como OP solicitó. Algunas notas.
Lo "+rm"
anterior (i)
significa que está dejando que el compilador decida colocar i en la memoria o en un registro. Eso es bueno en la mayoría de los casos, ya que el compilador puede optimizar mejor si es gratuito. En su caso, creo que desea mantener solo la restricción r para obligar a i a ser un registro.
c
variable en lugar del literal 10
que mencioné en la respuesta original? Estoy tratando de leer los manuales de GCC con respecto al uso adecuado de la construcción de ASM , pero ahora está un poco oscuro para mí. te lo agradeceria mucho!Podría intentar hacer que el bucle realmente haga algo. Tal como está, el compilador dice con razón "Este bucle no hace nada, me desharé de él".
Así que podrías probar una construcción que uso con frecuencia:
int i;
for (i = 0; i < 10; i++) {
asm volatile ("nop");
}
Nota: no todos los objetivos para el compilador gcc usan la misma sintaxis de ensamblaje en línea; es posible que deba modificarlo para su objetivo.
Sí, podrías suponer eso. Si declara la variable i como volátil, le dice al compilador que no optimice en i.
i
en la RAM para que mi código se vuelva significativamente más grande y más lento. En mi caso, cada marca de la CPU vale la pena, por lo que me gustaría encontrar una forma de controlar la optimización en línea si es posible.i
un registro ya es una optimización, por lo tanto, el primer comentario de @Roman es un malentendido.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 si i
es volátil. Afortunadamente, GCC es un compilador conforme. Desafortunadamente, hay muchos posibles compiladores de C que no cumplen con el 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 ciclo i
es una constante. La inicialización de i
y del ciclo no hace más que producir un valor constante. Nada en el estándar especifica que este bucle debe compilarse tal cual. La norma tampoco dice nada sobre el tiempo. El código compilado debe comportarse como si el bucle estuviera presente y lo hace. No 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 faltante) que no lo es. Después del bucle i
es cero constante. El código debe ser reemplazado con el ajuste i
a cero.
Creo que GCC se mantiene i
simplemente por la razón de que puede afectar el acceso a un puerto (opaco) i
.
Utilizar
asm volatile ("nop");
engañar a GCC para que crea que el ciclo hace algo.
vladimir cravero
romano matveev
i
se asignó var en RAMvladimir cravero
vladimir cravero
romano matveev
Cort Amón
ilmari karonen
romano matveev
Aleatorio832
romano matveev
marc glisse
Stefano Sanfilippo
i
de10
a0
, pero el valor actualizado nunca se usa: después del bucle hay una definición (i = 30
) pero no se usa (me gustay = i + 1
). Entonces, cualquier valor quei
tuviera antes de lai = 30
parte no es relevante. Elwhile
bucle se marca como inútil y no se emite el código correspondiente.vladimir cravero