En los primeros días de la arquitectura de la computadora, los registros eran elementos físicos hechos de chanclas, que podían almacenar palabras de datos de máquinas. El registro del contador del programa contenía la dirección de memoria de la siguiente instrucción. El registro de instrucciones contenía la instrucción obtenida de la memoria. El Registro de direcciones de memoria contenía la dirección para ser leída o escrita. Luego, el registro de datos de memoria contenía los datos a escribir o los resultados de una lectura. El acumulador contenía los resultados de la aritmética y podría servir como un operando para otras cosas.
Este tipo de pensamiento lleva a instrucciones simples y una lógica de control bastante obvia para secuenciar las operaciones de una máquina.
Un poco más tarde, los registros se volvieron más baratos y más rápidos, por lo que los arquitectos decidieron tener más, quizás 4 (Data General Nova) o 16 (IBM 360, PDP-11). Cosas como el registro de instrucciones y la dirección de memoria y los registros de datos aún podrían existir dentro de algún lugar, pero ya no formaban parte de la arquitectura oficial. Son útiles para construir hardware, pero no agregan nada a la comprensión de los programadores. Las instrucciones se convirtieron en cosas como
- ¿Cómo crear documentación para un proyecto de software? ¿Qué debo escribir en la documentación?
- Cómo reparar una computadora portátil que no lee una tarjeta SD
- ¿Qué porcentaje de miembros de la facultad en su departamento de CS se ocupan del aprendizaje automático?
- ¿Qué idioma entiende la computadora?
- ¿Cuál es la diferencia entre las arquitecturas i386 y x86?
MOV R3, (R2) +
que podría cargar R3 con el contenido de la ubicación de memoria contenida en R2, después de lo cual R2 se incrementaría para apuntar a la siguiente dirección de memoria.
Podría haber un registro de dirección de memoria o un registro de datos de memoria allí, pero solo como una conveniencia para los diseñadores lógicos. En cambio, se extendió la idea de que los registros eran de “almacenamiento rápido” y los compiladores trabajaron duro para descubrir cómo poner las cosas importantes en los registros para que no se guardaran y se restauraran de la memoria. Los sistemas operativos evolucionaron para compartir el tiempo y la conmutación de procesos y la conmutación de subprocesos, y guardaron y restauraron registros cuidadosamente para preservar la ilusión de que el contenido de estos registros era parte del estado de la máquina.
Los registros son almacenamiento para programas, pero es un poco extraño, porque un hilo puede decirle a otro hilo “buscar en la ubicación 100” algunos datos compartidos, pero no puede decir “buscar en el registro AX” algunos datos compartidos. En otras palabras, ¡realmente no se puede hablar del registro como una ubicación! Solo puede hablar sobre el valor que tiene un registro. Sean lo que sean, no es la misma idea que la memoria.
Lo que realmente está sucediendo hoy es que los registros mencionados en los programas tienen muy poco que ver con celdas de almacenamiento o flipflops. En cambio, son nombres para valores que están limitados al contexto local de un hilo de ejecución. Pensemos en algunos ejemplos.
Supongamos que tiene una secuencia de instrucciones como
MOV AX, $ 100; cargar AX con el contenido de la ubicación de memoria 100
MUL AX, 3; triplicarlo
AGREGAR AX, # 5; agregar 5
MOV $ 104, AX; poner la suma en la ubicación de memoria 104
MOV AX, # 7; poner un 7 en AX
…
Una máquina moderna fuera de servicio notará que la instrucción de carga en la línea 1 y la instrucción en la línea 5 hablan sobre AX, pero al usar un registro físico diferente para estos dos AX, ambas instrucciones pueden ejecutarse en paralelo. Además, cuando los datos de la instrucción 1 finalmente regresan de la memoria, se pueden alimentar directamente a la ALU para multiplicarse por 3, en realidad no es necesario almacenarlos en ningún lugar. El resultado de la multiplicación se puede alimentar directamente desde la salida del multiplicador a la entrada del ADD, sin almacenarlo en ningún lugar. (Esto se llama omisión). El resultado del ADD puede enviarse directamente al sistema de memoria sin ser almacenado primero en ningún lugar.
Sin duda, si un programador pone un punto de interrupción en la instrucción 3, el valor en “AX” debe ser accesible, o si se produce una interrupción entre las instrucciones 3 y 4, el estado arquitectónico visible para los programadores debe guardarse, pero como un transitorio. De hecho, los valores a los que se refiere “AX” en las instrucciones 1,2,3 y 4 nunca se colocan realmente en un registro.
Dentro de un núcleo moderno, es probable que haya cientos de “registros” de hardware, pero en un momento dado, varios de ellos podrían ser diferentes encarnaciones de los AX en un programa, y más podrían ser los AX para un hyperthread diferente que se ejecuta en el mismo núcleo. Por lo general, estos registros de hardware se escriben cuando se confirman las instrucciones, para preservar el “estado arquitectónico” en caso de interrupciones, pero es posible que nunca se lean. En cambio, los valores que se supone que tienen se pasan directamente entre las unidades funcionales, mientras que un trozo de lógica muy complicado a veces llamado “el búfer de reordenamiento” lleva la cuenta.
Por lo tanto, cada vez que un programa escribe un “registro”, normalmente se asigna uno nuevo y se le asigna temporalmente el nombre dado por la instrucción. Un registro en estos días es un nombre para un valor, no un lugar en particular donde se guardan datos.
Se podría construir una nueva arquitectura que no tuviera registros en absoluto en el conjunto de instrucciones, en lugar de usar direcciones de memoria. La máquina podría funcionar igual de rápido, interpretando las direcciones de memoria como nombres para valores en lugar de celdas de almacenamiento físico. La memoria real solo necesita actualizarse cuando el programa tiene que comunicarse con otros hilos o con el mundo exterior. La lógica sería mucho más complicada, porque los nombres serían mucho más grandes, pero funcionaría.