¿Existe un procesador que haga operaciones aritméticas en una pila y no en registros? Por supuesto, para mantener el rendimiento, ese procesador debe almacenar en caché el bloque superior de una pila en el mismo tipo de memoria que se usa para los registros.
Leí en un artículo (David R. Ditzel, HR McLellan. Register Allocation for Free: The C Machine Stack Cache. ) que un caché es 2 veces más lento que los registros debido a:
El papel es viejo. ¿Quizás aparecieron mejoras en el diseño del procesador que hacen viable la caché de pila? Siento que reducirá la complejidad de los compiladores y optimizará la copia entre registros y el resto de la memoria.
Actualización 2012-10-18. Como este concepto era bien conocido (no para mí), cambio la pregunta a "... ¿Procesadores modernos?"
Actualización 2012-10-18. Siento que debo decir explícitamente que no estoy hablando de una "máquina de dirección cero". El almacenamiento en caché y la "dirección cero" son ortogonales. Mi procesador hipotético puede tener incluso una suma de 5 arios como "r3 := r0+r2+r11+r5+r8". “rn” significa la celda de memoria en sp+n, donde sp es un puntero de pila. sp cambia antes y después de un bloque de código. Un programa muy inusual cambia sp en cada operación aritmética.
Sí, toda la línea de computadoras centrales de Burroughs a partir de 1961 con la B5000 utilizó una arquitectura de pila.
En esta arquitectura, administrar el flujo de datos hacia y desde la pila en realidad no es un gran cuello de botella para el rendimiento. Un problema mayor es el hecho de que una máquina de "dirección cero" necesita muchas más instrucciones para completar una tarea determinada que una máquina de una, dos o tres direcciones. La decodificación de instrucciones y la tubería de ejecución se convierten en el cuello de botella principal.
Cuando trabajé allí a principios de la década de 1980, se hizo un esfuerzo por construir una CPU que pudiera precargar secuencias relativamente grandes de instrucciones de dirección cero y traducirlas sobre la marcha a operaciones de tres direcciones que se alimentarían a la canalización de ejecución. (Piense en un compilador Java JIT implementado en hardware). Se volvió bastante complejo, especialmente para las tecnologías de implementación disponibles en ese momento, y no sé si esta estrategia finalmente tuvo éxito.
En caso de que se lo pregunte, la terminología de "dirección N" se refiere a la cantidad de operandos que se pueden especificar en una sola instrucción. Todas las operaciones en una máquina de pila están implícitamente en una o dos ubicaciones superiores en la pila, por lo que no hay operandos en las instrucciones. Una máquina que tiene un acumulador que se usa para todas las operaciones junto con otro registro o ubicación de memoria es una máquina de una sola dirección. Una máquina de dos direcciones puede especificar un operando de origen y destino arbitrario en una instrucción, y una máquina de tres direcciones puede especificar dos operandos de origen y poner el resultado en un destino independiente.
movf src / addwf dest,f
es más rápido que ldr r0,[src+r13] / ldr r1,[dest+r13] / add r0,r0,r1 / str [src+r13]
(y realiza su actualización de destino atómicamente). Lástima que agregar un número a otro mientras el valor de W
se necesita para otra cosa cuesta cuatro ciclos (uno para guardar W, uno para cargar un operando, uno para hacer la operación y uno para restaurar W). Algo como usefw
podría reducir eso a dos.Recuerdo haber leído un artículo similar (quizás el mismo) hace unos 17 años. Tal enfoque podría ser bueno si uno estuviera desarrollando un procesador para ejecutar una instrucción a la vez rápidamente. Desafortunadamente, no funciona bien con la programación de instrucciones desordenadas. Si uno tiene un código como:
ldr r1,[r0] ... hacer algunas cosas que no involucren a r1, r2 o [r2] cadena r1,[r2]
Un programador de instrucciones es libre de cambiar esas dos instrucciones como mejor le parezca. Si bien puede ser difícil para el programador de instrucciones saber si una escritura en alguna ubicación de memoria podría ser una escritura en [r2], muchos lenguajes compilados requieren que los programadores indiquen qué cosas pueden o no tener un alias.
Por el contrario, las instrucciones eran más como:
mov.l [r0],[--sp] ; Empuje [r0] en la pila ... hacer algunas cosas, lo que afecta sp mov.l [sp++],[r2] ; Pop [r2] de la pila
Sería mucho más difícil para un motor de ejecución fuera de orden determinar si el operando de origen para la última instrucción siempre sería el mismo que el operando de destino de la primera, y si alguna instrucción intermedia podría afectarlo.
En el pasado trabajé con el Saab Ericsson Space Thor, un microprocesador para aplicaciones espaciales. Funcionó, pero tenía algunos inconvenientes serios. Solo uno: se expuso la canalización de instrucciones: la instrucción que cargó una palabra de la memoria utilizada como dirección hace 2 instrucciones en la parte superior de la pila . Escribí una rutina de copia de memoria rápida para él, pero Saab dijo que no podía usarse porque las interrupciones causarían problemas...
Había procesadores Forth dedicados que solían usarse en el procesador de arranque para máquinas Sun/Sparc cuya arquitectura dedicada se asignaba al idioma. Pero generalmente no está disponible.
El x86 es casi uno de esos :-) (y la parte x87 fp aún más cerca)
Sin embargo, en los sistemas modernos, la pila es terrible, porque puede crear alias entre núcleos o incluso nodos NUMA, por lo que puede estar involucrada una gran cantidad de señalización lenta y de larga distancia. O, como mínimo, más enclavamientos de los que obtiene con un archivo de registro y cambio de nombre de registro.
Considere que ni siquiera las CPU, pero otros dispositivos pueden incluir datos DMA en su pila, ¡piense en leer búferes!
Super gato
Super gato
david tweed
berroal
berroal
berroal