A menudo se nos dice que al hardware no le importa en qué idioma está escrito un programa, ya que solo ve el código binario compilado, sin embargo, esta no es toda la verdad. Por ejemplo, considere el humilde Z80; sus extensiones al conjunto de instrucciones 8080 incluyen instrucciones como CPIR, que es útil para escanear cadenas de estilo C (terminadas en NULL), por ejemplo, para realizar strlen()
. Los diseñadores deben haber identificado que ejecutar programas en C (a diferencia de Pascal, donde la longitud de una cadena está en el encabezado) era algo para lo que probablemente se usaría su diseño. Otro ejemplo clásico es la máquina Lisp .
¿Qué otros ejemplos hay? Por ejemplo, instrucciones, número y tipo de registros , modos de direccionamiento, que hacen que un procesador en particular favorezca las convenciones de un idioma en particular. Estoy particularmente interesado en las revisiones de la misma familia.
sizeof(int)
igual a 1 debe requerir que se firme el tipochar
(ya que unint
debe poder contener todos los valores de tipochar
). He escrito código para una máquina dondechar
yint
son enteros con signo de 16 bits; Las mayores dificultades son que no se pueden usar uniones para la conversión de tipos, y el almacenamiento eficiente de una gran cantidad de bytes requiere el empaquetado y desempaquetado manual. Esos problemas son menores en comparación con la posibilidad en C de que sizeof (int) == sizeof (long), ya que ...unsigned int
valores. C99 mejoró esa situación, pero antes de C99 no había una forma segura y segura de un solo paso para comparar un valor potencialmente negativo con un valor de tipounsigned int
(uno tendría que probar si el número era negativo antes de hacer la comparación).Respuestas:
Las respuestas existentes se centran en los cambios de ISA . También hay otros cambios de hardware. Por ejemplo, C ++ comúnmente usa vtables para llamadas virtuales. Comenzando con el Pentium M , Intel tiene un componente "predictor de rama indirecta" que acelera las llamadas a funciones virtuales.
fuente
El conjunto de instrucciones Intel 8086 incluye una variación de "ret" que agrega un valor al puntero de la pila después de abrir la dirección de retorno. Esto es útil para muchas implementaciones de Pascal donde el llamador de una función empujará argumentos a la pila antes de hacer una llamada a la función, y los sacará después. Si una rutina aceptara, por ejemplo, cuatro bytes de parámetros, podría terminar con "RET 0004" para limpiar la pila. En ausencia de tal instrucción, tal convención de llamada probablemente habría requerido que el código muestre la dirección de retorno en un registro, actualice el puntero de la pila y luego salte a ese registro.
Curiosamente, la mayoría del código (incluidas las rutinas del sistema operativo) en el Macintosh original usaba la convención de llamadas Pascal a pesar de la falta de una instrucción facilitadora en el 68000. El uso de esta convención de llamadas ahorró 2-4 bytes de código en un sitio de llamada típico, pero requirió un extra 4-6 bytes de código en el sitio de retorno de cada función que tomó parámetros.
fuente
ENTER
contraparte en estoRET n
...ENTER
existiera en el 8086 original; vino con procesadores posteriores. Sin embargo, plantea un punto interesante: los modos de direccionamiento basados en BP están claramente diseñados en torno al uso de parámetros apilados y locales a los que se accede a través del puntero de trama. Encuentro esta convención interesante de varias maneras, especialmente considerando que (1) el código de lenguaje ensamblador puro es más apto para usar valores en registros que la pila, pero (2) las ventajas de direccionamiento [BP + nn] sobre [SP + nn] el direccionamiento es más significativo para los programas en lenguaje ensamblador que acceden a cosas en la pila que ...Un ejemplo es MIPS, que tiene ambos
add
yaddu
para atrapar e ignorar el desbordamiento, respectivamente. (Tambiénsub
ysubu
.) Necesitaba el primer tipo de instrucción para lenguajes como Ada (creo que nunca he usado Ada sin embargo) que se ocupan de los desbordamientos explícitamente y el segundo tipo para lenguajes como C que ignoran los desbordamientos.Si no recuerdo mal, la CPU real tiene algunos circuitos adicionales en la ALU para realizar un seguimiento de los desbordamientos. Si el único idioma que a la gente le importara fuera el C, no lo necesitaría.
fuente
nmemb*size+offset
bytes y necesita asegurarse de que no se produce un desbordamiento.addu
ysubu
(las que no comprueban los desbordamientos) fueron las que se agregaron para hacer feliz a C. Por supuesto, no lo sé, solo lo cubrimos vagamente en clase y ciertamente no soy un experto en arquitectura: P.La serie 5000 de Burroughs fue diseñada para soportar eficientemente ALGOL, y el iAPX-432 de Intel fue diseñado para ejecutar Ada de manera eficiente. El Inmos Transputer tenía su propio idioma, Occam. Creo que el procesador Parallax "Propeller" fue diseñado para ser programado usando su propia variante de BASIC.
No es un lenguaje, pero el conjunto de instrucciones VAX-11 tiene una sola instrucción para cargar un contexto de proceso, que fue diseñado después de una solicitud del equipo de diseño de VMS. No recuerdo los detalles, pero ISTR tomó tantas instrucciones para implementar que puso un límite superior serio en la cantidad de procesos que podían programar.
fuente
Algo que nadie parece haber mencionado hasta ahora es que los avances en la optimización del compilador (donde el lenguaje base es en gran medida irrelevante) impulsaron el cambio de los conjuntos de instrucciones CISC (que fueron diseñados en gran medida para ser codificados por humanos) a los conjuntos de instrucciones RISC (que fueron en gran medida diseñado para ser codificado por compiladores).
fuente
La familia Motorola 68000 introdujo un modo de dirección de autoincremento que hizo que la copia de datos a través de la CPU fuera muy eficiente y compacta.
[Ejemplo actualizado]
este fue un código c ++ que influyó en el ensamblador 68000
implementado en ensamblador convencional (pseudocódigo, olvidé los comandos del ensamblador 68000)
con el nuevo modo de dirección se convirtió en algo similar a
solo dos instrucciones por ciclo en lugar de 4.
fuente
El mainframe de la serie Z de IBM es el descendiente del IBM 360 de la década de 1960.
Hubo varias instrucciones que se pusieron allí específicamente para acelerar los programas COBOL y Fortran. El ejemplo clásico es
BXLE
- "Branch on Index Low or Equal", que es la mayoría de unfor
bucle Fortran o un COBOLPERFORM VARYING x from 1 by 1 until x > n
encapsulado en una sola instrucción.También hay una familia completa de instrucciones decimales empaquetadas para admitir la aritmética decimal de punto fijo común en los programas COBOL.
fuente
DO
bucle FORTRAN .Las primeras CPU de Intel tenían las siguientes características, muchas de ellas ahora obsoletas en el modo de 64 bits:
El indicador de signo, que se encuentra en el registro de estado de muchas CPU, existe para realizar fácilmente la aritmética firmada Y sin firmar.
El conjunto de instrucciones SSE 4.1 presenta instrucciones para el procesamiento de cadenas, contadas y terminadas en cero (PCMPESTR, etc.)
Además, podría imaginar que una serie de características a nivel de sistema fueron diseñadas para soportar la seguridad del código compilado (verificación de límite de segmento, puertas de llamada con copia de parámetros, etc.)
fuente
Algunos procesadores ARM, principalmente aquellos en dispositivos móviles, incluyen (d) la extensión Jazelle, que es un intérprete JVM de hardware; interpreta el bytecode de Java directamente. JVM compatible con Jazelle puede usar el hardware para acelerar la ejecución y eliminar gran parte de JIT, pero el respaldo al software VM todavía está asegurado si el código de bytes no se puede interpretar en el chip.
Los procesadores con dicha unidad incluyen la instrucción BXJ, que pone al procesador en un "modo Jazelle" especial, o si la activación de la unidad ha fallado, simplemente se interpreta como una instrucción de bifurcación normal. La unidad reutiliza los registros ARM para mantener el estado JVM.
El sucesor de la tecnología Jazelle es ThumbEE
fuente
Hasta donde yo sé, esto era más común en el pasado.
Hay una sesión de preguntas en la que James Gosling dijo que había personas que intentaban hacer hardware que pudiera manejar mejor el código de bytes JVM, pero luego estas personas encontrarían una manera de hacerlo con intel x86 "genérico" común (tal vez compilando el bytecode de alguna manera inteligente).
Mencionó que hay una ventaja en el uso del chip popular genérico (como Intel) porque tiene una gran corporación que arroja enormes sumas de dinero al producto.
Vale la pena ver el video. Habla de esto en el minuto 19 o 20.
fuente
Hice una búsqueda rápida en la página y parece que nadie ha mencionado las CPU desarrolladas específicamente para ejecutar Forth . El lenguaje de programación Forth está basado en pila, es compacto y se usa en sistemas de control.
fuente
La CPU Intel iAPX fue diseñada específicamente para lenguajes OO. Sin embargo, no funcionó del todo.
fuente
El 68000 tenía MOVEM que era más adecuado para insertar múltiples registros en la pila en una sola instrucción, que es lo que muchos idiomas esperaban.
Si vio MOVEM (MOVE Multiple) antes de JSR (Jump SubRoutine) en todo el código, entonces generalmente sabía que estaba tratando con el código C cumplido.
MOVEM permitió el incremento automático del registro de destino, permitiendo que cada uso continúe apilando en el destino, o eliminándose de la pila en caso de disminución automática.
http://68k.hax.com/MOVEM
fuente
La arquitectura AVR de Atmel está completamente diseñada desde cero para ser adecuada para la programación en C. Por ejemplo, esta nota de aplicación desarrolla más.
En mi opinión, esto está estrechamente relacionado con la excelente respuesta de rockets4kids , con los primeros PIC16-s desarrollados para la programación directa del ensamblador (40 instrucciones en total), con familias posteriores que apuntan a C.
fuente
Cuando se diseñó el coprocesador numérico 8087, era bastante común que los idiomas realizaran todas las matemáticas de punto flotante utilizando el tipo de mayor precisión, y solo redondean el resultado a menor precisión al asignarlo a una variable de menor precisión. En el estándar C original, por ejemplo, la secuencia:
promovería
a
yb
quedouble
, agregarlos, promoverc
adouble
, agregarlo, y luego almacenar el resultado redondeado alfloat
. Aunque en muchos casos hubiera sido más rápido para un compilador generar código que realizaría operaciones directamente en tipofloat
, era más simple tener un conjunto de rutinas de punto flotante que operarían solo en tipodouble
, junto con rutinas para convertir a / desdefloat
, que tener conjuntos separados de rutinas para manejar operaciones enfloat
ydouble
. El 8087 se diseñó en torno a ese enfoque de la aritmética, realizando todas las operaciones aritméticas utilizando un tipo de punto flotante de 80 bits [80 bits probablemente se eligió porque:En muchos procesadores de 16 y 32 bits, es más rápido trabajar con una mantisa de 64 bits y un exponente separado que trabajar con un valor que divide un byte entre la mantisa y el exponente.
Es muy difícil realizar cálculos que sean precisos a la precisión total de los tipos numéricos que uno está usando; si uno está tratando de calcular, por ejemplo, algo como log10 (x), es más fácil y rápido calcular un resultado con una precisión de 100ulp de un tipo de 80 bits que calcular un resultado con una precisión de 1ulp de 64 bits tipo, y redondeando el resultado anterior a una precisión de 64 bits producirá un valor de 64 bits que es más preciso que el segundo.
Desafortunadamente, las versiones futuras del lenguaje cambiaron la semántica de cómo deberían funcionar los tipos de punto flotante; mientras que la semántica 8087 habría sido muy agradable si los lenguajes los hubieran apoyado de manera consistente, si las funciones f1 (), f2 (), etc. devuelven el tipo
float
, muchos autores del compilador se encargarían de crearlong double
un alias para el tipo doble de 64 bits en lugar del tipo de 80 bits del compilador (y no proporcionar otros medios para crear variables de 80 bits), y evaluar arbitrariamente algo como:en cualquiera de las siguientes formas:
Tenga en cuenta que si f3 y f4 devuelven los mismos valores que f1 y f2, respectivamente, la expresión original debería devolver claramente cero, pero muchas de las últimas expresiones pueden no hacerlo. Esto llevó a las personas a condenar la "precisión adicional" del 8087 a pesar de que la última formulación sería generalmente superior a la tercera y, con un código que usara el tipo doble extendido apropiadamente, rara vez sería inferior.
En los años intermedios, Intel ha respondido a la tendencia del lenguaje (en mi humilde opinión) de forzar a que los resultados intermedios se redondeen a la precisión de los operandos diseñando sus procesadores posteriores para favorecer ese comportamiento, en detrimento del código que se beneficiaría con el uso de niveles superiores. precisión en cálculos intermedios.
fuente
## How the stack changed the processor
y## How floating point changed the processor
) para que las personas puedan tener la mentalidad adecuada al leerlo y sean menos propensos a pensar que estaba distraído al responder o volver a publicar el mismas (r similares) respuestas.