Tomé un curso sobre compiladores en mis estudios universitarios en el que escribimos un compilador que compila programas fuente en un lenguaje similar a Java de juguete a un lenguaje ensamblador de juguetes (para el cual teníamos un intérprete). En el proyecto hicimos algunas suposiciones sobre la máquina de destino estrechamente relacionada con los ejecutables nativos "reales", que incluyen:
- una pila en tiempo de ejecución, rastreada por un registro de puntero de pila dedicado ("SP")
- un montón para la asignación dinámica de objetos, seguido por un registro dedicado de puntero de montón ("HP")
- un registro de contador de programa dedicado ("PC")
- la máquina de destino tiene 16 registros
- Las operaciones sobre datos (en oposición a, por ejemplo, saltos) son operaciones de registro a registro
Cuando llegamos a la unidad sobre el uso de la asignación de registros como optimización, me pregunté: ¿Cuál es el número mínimo teórico de registros para una máquina de este tipo? Puede ver por nuestras suposiciones que utilizamos cinco registros (SP, HP, PC, más dos para usar como almacenamiento para operaciones binarias) en nuestro compilador. Si bien las optimizaciones como la asignación de registros sin duda pueden hacer uso de más registros, ¿hay alguna forma de sobrevivir con menos mientras se conservan estructuras como la pila y el montón? Supongo que con el direccionamiento de registros (operaciones de registro a registro) necesitamos al menos dos registros, pero ¿necesitamos más de dos?
fuente
Respuestas:
Si permite el acceso directo a la memoria por dirección de memoria, entonces no necesita ningún "registro" porque puede usar ubicaciones de memoria en su lugar. Por ejemplo, la memoria en la ubicación 0 puede ser el contador del programa, en la ubicación 1 tenemos el puntero de la pila, etc. Pero eso es trampa.
Entonces, para evitar hacer trampa, supongamos que no hay acceso directo a la memoria, porque podríamos usar ubicaciones de memoria fija como registros. Entonces podemos salir con dos registros, un contador de programa y un puntero de pila, como se explica en el artículo de Wikipedia sobre máquinas de pila . Solo se puede acceder a la pila a través del puntero de la pila, y solo se puede acceder al programa a través del contador del programa.
Otra posibilidad es utilizar máquinas de mostrador. Una máquina de dos contadores está completa en Turing, es decir, puede calcular cualquier máquina de Turing. Esto nuevamente se explica muy bien en el artículo de Wikipedia sobre máquinas de mostrador .
fuente
La arquitectura PIC que fue introducida por General Instruments en la década de 1970 y que todavía se usa hoy en día tenía los siguientes registros:
Una instrucción típica leerá un registro, realizará un cálculo utilizando el valor leído y W, y luego almacenará el resultado del cálculo en W o en el registro que se leyó. Uno de los cálculos disponibles produce "el valor leído, ignorando W"; otro es "tomar W, ignorando el valor leído". Los patrones de bits que corresponderían a "leer XX, luego tomar W, ignorar el valor leído y almacenar el resultado en W" se usan para NOP, así como una variedad de instrucciones especiales.
Para permitir cálculos de dirección, la unidad de ejecución del procesador buscará instrucciones que codifiquen una dirección de 00, y sustituirá el contenido del Registro de selección de archivo por la dirección.
Aunque tener que alimentar todos los valores a través del registro W puede ser un cuello de botella, la arquitectura PIC tiene un conjunto de trabajo más grande que otras arquitecturas que usan la misma palabra de instrucción de longitud. En el PIC16C54 (todavía fabricado hoy, y muy similar a los PIC de la década de 1970) las instrucciones tienen 12 bits de longitud. En muchas otras partes 16Cxx o 16Fxx, las instrucciones tienen una longitud de 14 bits y pueden acceder directamente a un espacio de direcciones de 128 bytes. Si el conjunto de trabajo de un programa encaja bien con el conjunto de trabajo del conjunto de instrucciones, una declaración como "total + = valor", donde "total" y "valor" son de tipo
unsigned char
, se compilaría para:En algo como el ARM, incluso si uno tiene un registro precargado con la dirección base de las variables, el código sería más parecido a:
En muchos casos, un compilador podría evitar cargar y almacenar con cada operación, pero en algo como el PIC, los beneficios del conjunto de trabajo más grande a veces superan las limitaciones de tener que pasar por W todo el tiempo.
fuente