¿Cómo puedo vincular una variable local al registro rXX de AVR?

Necesito tener pausa programable con la mayor precisión posible. Para lograr esto tengo el siguiente código GCC:

void delay(unsigned char d){
  volatile unsigned char i=d;
  while(i>0) i--;
}

Que se compila a:

1cc:    89 81           ldd r24, Y+1    ; 0x01
1ce:    81 50           subi    r24, 0x01   ; 1
1d0:    89 83           std Y+1, r24    ; 0x01
1d2:    89 81           ldd r24, Y+1    ; 0x01
1d4:    81 11           cpse    r24, r1
1d6:    fa cf           rjmp    .-12        ; 0x1cc <__vector_1+0x2c>

(Estoy mostrando solo el código del núcleo del bucle). Esto lleva al hecho de que la precisión es de 7 ciclos lo cual no es muy aceptable. Sin embargo, veo que el compilador no hizo su trabajo lo más rápido posible: si la ivariable fuera registro r24, ahorraré 3 operaciones y el código sería casi el doble de rápido.

Entonces, ¿cómo puedo decirle al compilador que quiero que esta variable esté en un registro?

PD. Consideraría hacer una pausa con un número programable en nop's. Pero no puedo imaginar cómo se puede lograr esto. AVR no tiene instrucciones para bifurcarse a la dirección calculada. Por lo que sé, no se puede acceder directamente a la pila en AVR (si pudiera, podría enviar el valor necesario a la pila y ejecutar la retinstrucción para saltar a la dirección del programa necesaria; también es un trabajo complicado, pero sería al menos considerable).

ACTUALIZAR Después de cambiar volatilea registerpalabra clave (como se describió en una de las respuestas), obtuve el siguiente código:

 14e:   81 50           subi    r24, 0x01   ; 1
 150:   f1 f7           brne    .-4         ; 0x14e <__vector_6+0x1c>

Así que reduje el ciclo de 7 a 2 ciclos. Lo cual es mucho mejor de lo que podía esperar.

¿Por qué lo hiciste volatile? Eso prácticamente obliga al compilador a usar una ubicación de memoria y acceder a ella en cada iteración de bucle. Prueba registeren su lugar.
¡@DaveTweed eso ayudó! El ciclo se convierte en sólo 2 instrucciones de largo. Solía volatile​​​​evitar que el compilador "optimizara" el código parecía no hacer nada. ¿Escribirías una respuesta que pudiera votar y aceptar?
Volatile no suele ser una buena palabra clave para evitar que un compilador optimice un código como ese. Muchos compiladores tienen directivas #pragma para deshabilitar la optimización de pequeñas secciones de código.

Respuestas (2)

No declare su variable como volatile. Eso prácticamente obliga al compilador a usar una ubicación de memoria y acceder a ella en cada iteración del ciclo. Utilice registeren su lugar.

No funcionará como retraso, porque el compilador optimizará el bucle por completo.
@TurboJ: El OP dice que estás equivocado. Además, puede controlar el nivel de optimización utilizado y, en algunos compiladores, puede variarlo para funciones individuales. Pero la mejor solución en general es usar temporizadores de hardware para una sincronización precisa.

La forma convencional de lograr lo que desea es codificar a mano la subrutina de retardo en el lenguaje ensamblador nativo. Casi todos los conjuntos de herramientas de desarrollo que son compiladores de C tendrán la capacidad de incluir módulos de lenguaje ensamblador en la compilación.

Un truco común utilizado por programadores experimentados es construir primero la rutina de retardo en código C como lo ha hecho. Luego, al mirar el código de máquina compilado, obtiene una plantilla de cómo escribir la misma rutina en código ensamblador y luego le permite optimizar las secciones críticas. La ventaja de este enfoque es que el compilador "le muestra cómo" configurar la entrada y salida a/desde la subrutina para que sea compatible con la llamada desde el código C principal.

Sería consciente del hecho de que mi código de ensamblador se equivocará si el compilador hace su trabajo de otra manera por alguna razón, como: agregar otra variable local, cambiar los métodos de optimización u otra cosa.
@RomanMatveev - De hecho, esto es cierto. Cambiar un programa esperar cambios en el comportamiento. Pero la misma consideración entra en juego dejando todo el código de retardo en el código C también. Cambiar el diseño del código podría cambiar la forma en que el compilador ve el uso del registro en la subrutina. También cambie las versiones del compilador, los niveles de optimización o incluso el puerto a un compilador diferente y es muy probable que el comportamiento esperado de la rutina de retraso del código C cambie de lo que vio hoy. Confirme la rutina en ensamblador y luego el código no cambiará a menos que decida cambiarlo.
Tenga en cuenta que los conjuntos de herramientas particulares definen procesos estándar para pasar parámetros a subrutinas y para devolver valores de retorno. Es poco probable que los métodos para hacer esto cambien con las versiones del compilador o los niveles de optimización porque si cambiara, la base de clientes se molestaría mucho cuando sus rutinas de lenguaje ensamblador fallaran o las bibliotecas precompiladas no funcionaran correctamente cuando se vincularan a una compilación actual.