Código máquina |
---|
Conceptos generales |
|
Instrucciones |
En informática , una arquitectura de conjunto de instrucciones ( ISA ) es un modelo abstracto que generalmente define cómo el software controla la CPU en una computadora o una familia de computadoras. [1] Un dispositivo o programa que ejecuta instrucciones descritas por esa ISA, como una unidad central de procesamiento (CPU), se denomina implementación de esa ISA.
En general, una ISA define las instrucciones admitidas , los tipos de datos , los registros , el soporte de hardware para gestionar la memoria principal , [ aclaración necesaria ] características fundamentales (como la consistencia de la memoria , los modos de direccionamiento , la memoria virtual ) y el modelo de entrada/salida de las implementaciones de la ISA.
Una ISA especifica el comportamiento del código de máquina que se ejecuta en implementaciones de esa ISA de una manera que no depende de las características de esa implementación, lo que proporciona compatibilidad binaria entre implementaciones. Esto permite múltiples implementaciones de una ISA que difieren en características como el rendimiento , el tamaño físico y el costo monetario (entre otras cosas), pero que son capaces de ejecutar el mismo código de máquina, de modo que una máquina de menor rendimiento y menor costo se puede reemplazar por una máquina de mayor costo y mayor rendimiento sin tener que reemplazar el software. También permite la evolución de las microarquitecturas de las implementaciones de esa ISA, de modo que una implementación más nueva y de mayor rendimiento de una ISA puede ejecutar software que se ejecuta en generaciones anteriores de implementaciones.
Si un sistema operativo mantiene una interfaz binaria de aplicación (ABI) estándar y compatible para una ISA en particular, el código de máquina se ejecutará en futuras implementaciones de esa ISA y sistema operativo. Sin embargo, si una ISA admite la ejecución de varios sistemas operativos, no garantiza que el código de máquina de un sistema operativo se ejecute en otro sistema operativo, a menos que el primer sistema operativo admita la ejecución de código de máquina creado para el otro sistema operativo.
Se puede ampliar una ISA añadiendo instrucciones u otras capacidades, o añadiendo compatibilidad con direcciones y valores de datos más grandes; una implementación de la ISA extendida podrá seguir ejecutando código de máquina para versiones de la ISA sin esas extensiones. El código de máquina que utilice esas extensiones solo se ejecutará en implementaciones que admitan esas extensiones.
La compatibilidad binaria que proporcionan convierte a las ISA en una de las abstracciones más fundamentales de la informática .
Una arquitectura de conjunto de instrucciones se distingue de una microarquitectura , que es el conjunto de técnicas de diseño de procesadores utilizadas, en un procesador en particular, para implementar el conjunto de instrucciones. Los procesadores con diferentes microarquitecturas pueden compartir un conjunto de instrucciones común. Por ejemplo, el Intel Pentium y el AMD Athlon implementan versiones casi idénticas del conjunto de instrucciones x86 , pero tienen diseños internos radicalmente diferentes.
El concepto de arquitectura , distinto del diseño de una máquina específica, fue desarrollado por Fred Brooks en IBM durante la fase de diseño de System/360 .
Antes de NPL [System/360], los diseñadores de computadoras de la compañía habían tenido libertad para cumplir con los objetivos de costo no sólo seleccionando tecnologías sino también diseñando mejoras funcionales y arquitectónicas. El objetivo de compatibilidad SPREAD, en cambio, postulaba una arquitectura única para una serie de cinco procesadores que abarcaban una amplia gama de costos y rendimiento. Ninguno de los cinco equipos de diseño de ingeniería podía contar con poder realizar ajustes en las especificaciones arquitectónicas como forma de aliviar las dificultades para lograr los objetivos de costo y rendimiento. [2] : p.137
Algunas máquinas virtuales que admiten bytecode como su ISA, como Smalltalk , la máquina virtual Java y Common Language Runtime de Microsoft , implementan esto traduciendo el bytecode de las rutas de código de uso común a código de máquina nativo. Además, estas máquinas virtuales ejecutan rutas de código de uso menos frecuente mediante interpretación (consulte: Compilación Just-in-time ). Transmeta implementó el conjunto de instrucciones x86 sobre procesadores VLIW de esta manera.
Una ISA puede clasificarse de varias maneras diferentes. Una clasificación común es por complejidad arquitectónica . Una computadora con conjunto de instrucciones complejas (CISC) tiene muchas instrucciones especializadas, algunas de las cuales pueden usarse solo en raras ocasiones en programas prácticos. Una computadora con conjunto de instrucciones reducidas (RISC) simplifica el procesador al implementar de manera eficiente solo las instrucciones que se usan con frecuencia en los programas, mientras que las operaciones menos comunes se implementan como subrutinas, lo que compensa el tiempo de ejecución adicional del procesador resultante con un uso poco frecuente. [3]
Otros tipos incluyen arquitecturas de palabras de instrucción muy largas (VLIW, por sus siglas en inglés) y las arquitecturas de palabras de instrucción largas (LIW, por sus siglas en inglés) [ cita requerida ] y de computación de instrucciones explícitamente paralelas (EPIC, por sus siglas en inglés). Estas arquitecturas buscan explotar el paralelismo a nivel de instrucción con menos hardware que RISC y CISC al hacer que el compilador sea responsable de la emisión y programación de instrucciones. [4]
Se han estudiado arquitecturas con una complejidad aún menor, como la computadora con conjunto de instrucciones mínimo (MISC) y la computadora con conjunto de instrucciones único (OISC). Estos son tipos teóricamente importantes, pero no se han comercializado. [5] [6]
El lenguaje de máquina se construye a partir de instrucciones o sentencias discretas . En la arquitectura de procesamiento, una instrucción dada puede especificar:
Se crean operaciones más complejas combinando estas instrucciones simples, que se ejecutan secuencialmente o según lo indiquen las instrucciones de flujo de control .
Algunos ejemplos de operaciones comunes a muchos conjuntos de instrucciones incluyen:
Los procesadores pueden incluir instrucciones "complejas" en su conjunto de instrucciones. Una sola instrucción "compleja" hace algo que puede requerir muchas instrucciones en otras computadoras. Dichas instrucciones se caracterizan por las instrucciones que requieren varios pasos, controlan varias unidades funcionales o aparecen en una escala mayor que la mayor parte de las instrucciones simples implementadas por el procesador en cuestión. Algunos ejemplos de instrucciones "complejas" incluyen:
Las instrucciones complejas son más comunes en los conjuntos de instrucciones CISC que en los conjuntos de instrucciones RISC, pero los conjuntos de instrucciones RISC también pueden incluirlas. Los conjuntos de instrucciones RISC generalmente no incluyen operaciones ALU con operandos de memoria o instrucciones para mover grandes bloques de memoria, pero la mayoría de los conjuntos de instrucciones RISC incluyen instrucciones SIMD o vectoriales que realizan la misma operación aritmética en múltiples piezas de datos al mismo tiempo. Las instrucciones SIMD tienen la capacidad de manipular vectores y matrices grandes en un tiempo mínimo. Las instrucciones SIMD permiten una fácil paralelización de algoritmos comúnmente involucrados en el procesamiento de sonido, imagen y video. Varias implementaciones SIMD se han lanzado al mercado bajo nombres comerciales como MMX , 3DNow! y AltiVec .
En las arquitecturas tradicionales, una instrucción incluye un código de operación que especifica la operación a realizar, como agregar contenido de memoria a un registro , y cero o más especificadores de operandos , que pueden especificar registros , ubicaciones de memoria o datos literales. Los especificadores de operandos pueden tener modos de direccionamiento que determinen su significado o pueden estar en campos fijos. En las arquitecturas de palabras de instrucción muy largas (VLIW), que incluyen muchas arquitecturas de microcódigo , se especifican múltiples códigos de operación y operandos simultáneos en una sola instrucción.
Algunos conjuntos de instrucciones exóticos no tienen un campo de código de operación, como las arquitecturas activadas por transporte (TTA), solo operandos.
La mayoría de las máquinas de pila tienen conjuntos de instrucciones de " operando 0 " en los que las operaciones aritméticas y lógicas carecen de campos especificadores de operandos; solo las instrucciones que insertan operandos en la pila de evaluación o que extraen operandos de la pila en variables tienen especificadores de operandos. El conjunto de instrucciones lleva a cabo la mayoría de las acciones de la ALU con operaciones de posfijo ( notación polaca inversa ) que funcionan solo en la pila de expresiones , no en registros de datos o celdas de memoria principal arbitrarias. Esto puede ser muy conveniente para compilar lenguajes de alto nivel, porque la mayoría de las expresiones aritméticas se pueden traducir fácilmente a notación de posfijo. [8]
Las instrucciones condicionales suelen tener un campo de predicado (unos pocos bits que codifican la condición específica para hacer que se realice una operación en lugar de que no se realice). Por ejemplo, una instrucción de bifurcación condicional transferirá el control si la condición es verdadera, de modo que la ejecución continúe a una parte diferente del programa, y no transferirá el control si la condición es falsa, de modo que la ejecución continúe secuencialmente. Algunos conjuntos de instrucciones también tienen movimientos condicionales, de modo que el movimiento se ejecutará y los datos se almacenarán en la ubicación de destino, si la condición es verdadera, y no se ejecutarán y la ubicación de destino no se modificará, si la condición es falsa. De manera similar, IBM z/Architecture tiene una instrucción de almacenamiento condicional. Algunos conjuntos de instrucciones incluyen un campo de predicado en cada instrucción; esto se denomina predicación de bifurcación .
Los conjuntos de instrucciones se pueden clasificar según el número máximo de operandos especificados explícitamente en las instrucciones.
(En los ejemplos que siguen, a , b y c son direcciones (directas o calculadas) que hacen referencia a celdas de memoria, mientras que reg1 y así sucesivamente hacen referencia a registros de máquina).
C = A + B
push a
, push b
, add
, pop c
.C = A+B
necesita cuatro instrucciones . [10] Para las máquinas de pila, los términos "operando 0" y "dirección cero" se aplican a las instrucciones aritméticas, pero no a todas las instrucciones, ya que las instrucciones push y pop de 1 operando se utilizan para acceder a la memoria.load a
, add b
, store c
.C = A+B
Necesita tres instrucciones . [10]move A
a C ; luego add B
a C .C = A+B
Necesita dos instrucciones . Esto efectivamente "almacena" el resultado sin una instrucción de almacenamiento explícita .load a,reg1
; add b,reg1
; store reg1,c
; Esto requiere un par de carga/almacenamiento para cualquier movimiento de memoria, independientemente de si el add
resultado es un aumento almacenado en un lugar diferente, como en C = A+B
, o en la misma ubicación de memoria: A = A+B
.C = A+B
Necesita tres instrucciones .load a,reg1
; load b,reg2
; add reg1,reg2
; store reg2,c
.C = A+B
Necesita cuatro instrucciones .add a,b,c
C = A+B
Necesita una instrucciónmove a,reg1
; add reg1,b,c
;C = A+B
Necesita dos instruccionesload a,reg1
; load b,reg2
; add reg1+reg2->reg3
; store reg3,c
;C = A+B
Necesita cuatro instrucciones .Debido a la gran cantidad de bits necesarios para codificar los tres registros de una instrucción de 3 operandos, las arquitecturas RISC que tienen instrucciones de 16 bits son invariablemente diseños de 2 operandos, como Atmel AVR, TI MSP430 y algunas versiones de ARM Thumb . Las arquitecturas RISC que tienen instrucciones de 32 bits suelen ser diseños de 3 operandos, como las arquitecturas ARM , AVR32 , MIPS , Power ISA y SPARC .
Cada instrucción especifica un número de operandos (registros, posiciones de memoria o valores inmediatos) de forma explícita . Algunas instrucciones proporcionan uno o ambos operandos de forma implícita, por ejemplo, almacenándolos en la parte superior de la pila o en un registro implícito. Si algunos de los operandos se proporcionan de forma implícita, es necesario especificar menos operandos en la instrucción. Cuando un "operando de destino" especifica explícitamente el destino, se debe proporcionar un operando adicional. En consecuencia, el número de operandos codificados en una instrucción puede diferir del número matemáticamente necesario de argumentos para una operación lógica o aritmética (la aridad ). Los operandos se codifican en la representación del "código de operación" de la instrucción o se proporcionan como valores o direcciones después del código de operación.
La presión de registro mide la disponibilidad de registros libres en cualquier momento durante la ejecución del programa. La presión de registro es alta cuando se utiliza una gran cantidad de registros disponibles; por lo tanto, cuanto mayor sea la presión de registro, más a menudo se debe volcar el contenido de los registros en la memoria. Aumentar la cantidad de registros en una arquitectura disminuye la presión de registro, pero aumenta el costo. [12]
Mientras que los conjuntos de instrucciones embebidas como Thumb sufren una presión de registro extremadamente alta porque tienen conjuntos de registros pequeños, las ISA RISC de propósito general como MIPS y Alpha disfrutan de una presión de registro baja. Las ISA CISC como x86-64 ofrecen una presión de registro baja a pesar de tener conjuntos de registros más pequeños. Esto se debe a los muchos modos de direccionamiento y optimizaciones (como direccionamiento de subregistros, operandos de memoria en instrucciones ALU, direccionamiento absoluto, direccionamiento relativo a PC y derrames de registro a registro) que ofrecen las ISA CISC. [13]
El tamaño o la longitud de una instrucción varía ampliamente, desde tan solo cuatro bits en algunos microcontroladores hasta cientos de bits en algunos sistemas VLIW . Los procesadores utilizados en computadoras personales , mainframes y supercomputadoras tienen tamaños mínimos de instrucción entre 8 y 64 bits. La instrucción más larga posible en x86 es de 15 bytes (120 bits). [14] Dentro de un conjunto de instrucciones, diferentes instrucciones pueden tener diferentes longitudes. En algunas arquitecturas, en particular la mayoría de las computadoras con conjunto de instrucciones reducido (RISC),Las instrucciones tienen una longitud fija , que normalmente corresponde al tamaño de palabra de esa arquitectura. En otras arquitecturas, las instrucciones tienen una longitud variable , normalmente múltiplos enteros de un byte o media palabra . Algunas, como ARM con extensión Thumb , tienen una codificación variable mixta , es decir, dos codificaciones fijas, normalmente de 32 y 16 bits, donde las instrucciones no se pueden mezclar libremente, sino que se deben cambiar entre ellas en una rama (o límite de excepción en ARMv8).
Las instrucciones de longitud fija son menos complicadas de manejar que las de longitud variable por varias razones (no tener que verificar si una instrucción se extiende a lo largo de una línea de caché o un límite de página de memoria virtual, [11] por ejemplo), y por lo tanto son algo más fáciles de optimizar para la velocidad.
A principios de los años 60, la memoria principal de los ordenadores era cara y muy limitada, incluso en los mainframes. Minimizar el tamaño de un programa para asegurarse de que cupiera en la memoria limitada era a menudo fundamental. Por ello, el tamaño de las instrucciones necesarias para realizar una tarea en particular, la densidad del código , era una característica importante de cualquier conjunto de instrucciones. Siguió siendo importante en las memorias inicialmente diminutas de los miniordenadores y luego en los microprocesadores. La densidad sigue siendo importante hoy en día, para las aplicaciones de teléfonos inteligentes, las aplicaciones descargadas en los navegadores a través de conexiones lentas a Internet y en las ROM para aplicaciones integradas. Una ventaja más general de una mayor densidad es la mayor eficacia de las cachés y de la precarga de instrucciones.
Las computadoras con alta densidad de código a menudo tienen instrucciones complejas para la entrada de procedimientos, retornos parametrizados, bucles, etc. (por lo tanto, se denominaron retroactivamente Computadoras con Conjunto de Instrucciones Complejo , CISC ). Sin embargo, las instrucciones "CISC" más típicas o frecuentes simplemente combinan una operación ALU básica, como "agregar", con el acceso a uno o más operandos en la memoria (utilizando modos de direccionamiento como directo, indirecto, indexado, etc.). Ciertas arquitecturas pueden permitir dos o tres operandos (incluido el resultado) directamente en la memoria o pueden realizar funciones como el incremento automático del puntero, etc. Los conjuntos de instrucciones implementados por software pueden tener instrucciones aún más complejas y poderosas.
Las computadoras con conjunto de instrucciones reducido , RISC , se implementaron ampliamente por primera vez durante un período de rápido crecimiento de los subsistemas de memoria. Sacrifican la densidad del código para simplificar los circuitos de implementación e intentan aumentar el rendimiento mediante frecuencias de reloj más altas y más registros. Una sola instrucción RISC normalmente realiza una sola operación, como una "adición" de registros o una "carga" desde una ubicación de memoria a un registro. Un conjunto de instrucciones RISC normalmente tiene una longitud de instrucción fija, mientras que un conjunto de instrucciones CISC típico tiene instrucciones de longitud muy variable. Sin embargo, como las computadoras RISC normalmente requieren más instrucciones y, a menudo, más largas para implementar una tarea determinada, inherentemente hacen un uso menos óptimo del ancho de banda del bus y las memorias caché.
Algunas ISA RISC integradas, como Thumb y AVR32, suelen presentar una densidad muy alta debido a una técnica llamada compresión de código. Esta técnica empaqueta dos instrucciones de 16 bits en una palabra de 32 bits, que luego se descomprime en la etapa de decodificación y se ejecuta como dos instrucciones. [15]
Las computadoras con conjunto mínimo de instrucciones (MISC) son comúnmente una forma de máquina de pila , donde hay pocas instrucciones separadas (8-32), de modo que se pueden incluir múltiples instrucciones en una sola palabra de máquina. Estos tipos de núcleos a menudo requieren poco silicio para implementarse, por lo que se pueden realizar fácilmente en un FPGA o en una forma de múltiples núcleos . La densidad de código de MISC es similar a la densidad de código de RISC; la mayor densidad de instrucciones se compensa al requerir más instrucciones primitivas para realizar una tarea. [16] [ verificación fallida ]
Se han realizado investigaciones sobre la compresión de archivos ejecutables como mecanismo para mejorar la densidad del código. Las matemáticas de la complejidad de Kolmogorov describen los desafíos y los límites de esta técnica.
En la práctica, la densidad del código también depende del compilador . La mayoría de los compiladores optimizadores tienen opciones que controlan si se optimiza la generación de código para la velocidad de ejecución o para la densidad del código. Por ejemplo, GCC tiene la opción -Os para optimizar para un tamaño de código de máquina pequeño y -O3 para optimizar para la velocidad de ejecución a costa de un código de máquina más grande.
Las instrucciones que constituyen un programa rara vez se especifican utilizando su forma numérica interna ( código de máquina ); pueden ser especificadas por programadores utilizando un lenguaje ensamblador o, más comúnmente, pueden ser generadas a partir de lenguajes de programación de alto nivel por compiladores . [17]
El diseño de conjuntos de instrucciones es un tema complejo. Hubo dos etapas en la historia del microprocesador. La primera fue el CISC (Complex Instruction Set Computer), que tenía muchas instrucciones diferentes. Sin embargo, en la década de 1970, lugares como IBM investigaron y descubrieron que muchas instrucciones del conjunto podían eliminarse. El resultado fue el RISC (Reduced Instruction Set Computer), una arquitectura que utiliza un conjunto más pequeño de instrucciones. Un conjunto de instrucciones más simple puede ofrecer el potencial de velocidades más altas, un tamaño de procesador reducido y un consumo de energía reducido. Sin embargo, un conjunto más complejo puede optimizar las operaciones comunes, mejorar la eficiencia de la memoria y la caché o simplificar la programación.
Algunos diseñadores de conjuntos de instrucciones reservan uno o más códigos de operación para algún tipo de llamada al sistema o interrupción de software . Por ejemplo, MOS Technology 6502 utiliza 00 H , Zilog Z80 utiliza los ocho códigos C7, CF, D7, DF, E7, EF, F7, FF H [18] mientras que Motorola 68000 utiliza códigos en el rango A000..AFFF H.
Las máquinas virtuales rápidas son mucho más fáciles de implementar si un conjunto de instrucciones cumple con los requisitos de virtualización de Popek y Goldberg . [ aclaración necesaria ]
La diapositiva NOP utilizada en la programación consciente de la inmunidad es mucho más fácil de implementar si el estado "no programado" de la memoria se interpreta como un NOP . [ dudoso – discutir ]
En sistemas con múltiples procesadores, los algoritmos de sincronización sin bloqueo son mucho más fáciles de implementar [ cita requerida ] si el conjunto de instrucciones incluye soporte para algo como " buscar y agregar ", " carga-enlace/almacenamiento condicional " (LL/SC) o " comparación e intercambio atómico ".
Un conjunto de instrucciones determinado se puede implementar de diversas maneras. Todas las formas de implementar un conjunto de instrucciones en particular proporcionan el mismo modelo de programación , y todas las implementaciones de ese conjunto de instrucciones pueden ejecutar los mismos archivos ejecutables. Las diversas formas de implementar un conjunto de instrucciones ofrecen diferentes compensaciones entre costo, rendimiento, consumo de energía, tamaño, etc.
Al diseñar la microarquitectura de un procesador, los ingenieros utilizan bloques de circuitos electrónicos "cableados" (a menudo diseñados por separado), como sumadores, multiplexores, contadores, registros, unidades de lógica de procesamiento (ALU), etc. A menudo, se utiliza algún tipo de lenguaje de transferencia de registros para describir la decodificación y secuenciación de cada instrucción de un ISA que utiliza esta microarquitectura física. Hay dos formas básicas de construir una unidad de control para implementar esta descripción (aunque muchos diseños utilizan vías intermedias o compromisos):
Algunos diseños de CPU microcodificados con un almacén de control escribible lo utilizan para permitir que se cambie el conjunto de instrucciones (por ejemplo, el procesador Rekursiv y el Imsys Cjip). [19]
Las CPU diseñadas para computación reconfigurable pueden utilizar matrices de puertas programables en campo (FPGA).
Una ISA también se puede emular en software mediante un intérprete . Naturalmente, debido a la sobrecarga de interpretación, esto es más lento que ejecutar programas directamente en el hardware emulado, a menos que el hardware que ejecuta el emulador sea un orden de magnitud más rápido. Hoy en día, es una práctica común que los proveedores de nuevas ISA o microarquitecturas pongan emuladores de software a disposición de los desarrolladores de software antes de que la implementación del hardware esté lista.
A menudo, los detalles de la implementación tienen una fuerte influencia en las instrucciones particulares seleccionadas para el conjunto de instrucciones. Por ejemplo, muchas implementaciones de la secuencia de instrucciones solo permiten una única carga o almacenamiento de memoria por instrucción, lo que conduce a una arquitectura de carga y almacenamiento (RISC). Otro ejemplo: algunas de las primeras formas de implementar la secuencia de instrucciones condujeron a una ranura de retardo .
Las demandas de procesamiento de señales digitales de alta velocidad han impulsado la dirección opuesta: obligan a que las instrucciones se implementen de una manera particular. Por ejemplo, para realizar filtros digitales con la suficiente rapidez, la instrucción MAC en un procesador de señales digitales (DSP) típico debe utilizar un tipo de arquitectura Harvard que pueda obtener una instrucción y dos palabras de datos simultáneamente, y requiere un multiplicador de multiplicación-acumulación de un solo ciclo .