x64 significa saber que como assembly x86 mips x86-64 cpu-architecture

assembly - saber - x86 que significa



¿Por qué es x86 feo? ¿Por qué se considera inferior en comparación con otros? (10)

  1. x86 has a very, very limited set of general purpose registers

  2. it promotes a very inefficient style of development on the lowest level (CISC hell) instead of an efficient load / store methodology

  3. Intel made the horrifying decision to introduce the plainly stupid segment / offset - memory adressing model to stay compatible with (at this time already!) outdated technology

  4. At a time when everyone was going 32 bit, the x86 held back the mainstream PC world by being a meager 16 bit (most of them - the 8088 - even only with 8 bit external data paths, which is even scarier!) CPU

For me (and I''m a DOS veteran that has seen each and every generation of PCs from a developers perspective!) point 3. was the worst.

Imagine the following situation we had in the early 90s (mainstream!):

a) An operating system that had insane limitations for legacy reasons (640kB of easily accessible RAM) - DOS

b) An operating system extension (Windows) that could do more in terms of RAM, but was limited when it came to stuff like games, etc... and was not the most stable thing on Earth (luckily this changed later, but I''m talking about the early 90s here)

c) Most software was still DOS and we had to create boot disks often for special software, because there was this EMM386.exe that some programs liked, others hated (especially gamers - and I was an AVID gamer at this time - know what I''m talking about here)

d) We were limited to MCGA 320x200x8 bits (ok, there was a bit more with special tricks, 360x480x8 was possible, but only without runtime library support), everything else was messy and horrible ("VESA" - lol)

e) But in terms of hardware we had 32 bit machines with quite a few megabytes of RAM and VGA cards with support of up to 1024x768

Reason for this bad situation?

A simple design decision by Intel. Machine instruction level (NOT binary level!) compatibility to something that was already dying, I think it was the 8085. The other, seemingly unrelated problems (graphic modes, etc...) were related for technical reasons and because of the very narrow minded architecture the x86 platform brought with itself.

Today, the situation is different, but ask any assembler developer or people who build compiler backends for the x86. The insanely low number of general purpose registers is nothing but a horrible performance killer.

Recientemente, he estado leyendo algunos archivos SO y encontré declaraciones en contra de la arquitectura x86.

y muchos más comentarios como

Intenté buscar pero no encontré ninguna razón. No creo que x86 sea malo, probablemente porque esta es la única arquitectura con la que estoy familiarizado.

¿Puede alguien amablemente darme razones para considerar x86 feo / malo / inferior en comparación con los demás.


Además de las razones que las personas ya han mencionado:

  • x86-16 tenía un esquema de direccionamiento de memoria bastante extraño que permitía abordar una sola ubicación de memoria de hasta 4096 formas diferentes, RAM limitada a 1 MB y programadores forzados para tratar dos punteros de distintos tamaños. Afortunadamente, el cambio a 32 bits hizo innecesaria esta característica, pero los chips x86 todavía llevan la parte de los registros de segmento.
  • Si bien no es una falla de x86 per se , las convenciones de llamadas x86 no estaban estandarizadas como MIPS (principalmente porque MS-DOS no __cdecl ningún compilador), dejándonos con el desastre de __cdecl , __stdcall , __fastcall , etc.

Creo que esta pregunta tiene una suposición falsa. Es principalmente solo académicos obsesionados con RISC que llaman x86 feo. En realidad, el ISA x86 puede hacer en una sola instrucción operaciones que tomarían 5-6 instrucciones en RISC ISA. Los fanáticos de RISC pueden contar que las CPU x86 modernas rompen estas instrucciones "complejas" en microops; sin embargo:

  1. En muchos casos, eso es parcialmente cierto o no verdadero en absoluto. Las instrucciones "complejas" más útiles en x86 son cosas como mov %eax, 0x1c(%esp,%edi,4) es decir, modos de direccionamiento, y estos no están desglosados.
  2. Lo que a menudo es más importante en las máquinas modernas no es la cantidad de ciclos gastados (porque la mayoría de las tareas no están vinculadas a la CPU) sino el impacto del código en la caché de instrucciones. 5-6 instrucciones de tamaño fijo (generalmente 32 bits) afectarán a la memoria caché mucho más que una instrucción compleja que rara vez supera los 5 bytes.

x86 realmente absorbió todos los aspectos buenos de RISC hace unos 10-15 años, y las cualidades restantes de RISC (en realidad la definición , el conjunto mínimo de instrucciones) son dañinas e indeseables.

Además del costo y la complejidad de fabricar CPU y sus requisitos de energía, x86 es la mejor ISA . Cualquiera que te diga lo contrario está dejando que la ideología o la agenda se interpongan en su razonamiento.

Por otro lado, si está apuntando a dispositivos integrados donde el costo de la CPU cuenta, o dispositivos integrados / móviles donde el consumo de energía es una preocupación principal, ARM o MIPS probablemente tengan más sentido. Sin embargo, ten en cuenta que aún tendrás que lidiar con el tamaño extra de memoria ram y binario necesario para manejar código que es fácilmente 3-4 veces más grande, y no podrás acercarte al rendimiento. Si esto importa depende mucho de lo que se ejecutará en él.


Creo que llegarás a una parte de la respuesta si alguna vez intentas escribir un compilador dirigido a x86, o si escribes un emulador de máquina x86, o incluso si tratas de implementar el ISA en un diseño de hardware.

Aunque entiendo que "¡x86 es feo!" argumentos, todavía creo que es más divertido escribir ensambles x86 que MIPS (por ejemplo) - el último es simplemente tedioso. Siempre pretendía ser agradable para los compiladores en lugar de para los humanos. No estoy seguro de que un chip sea más hostil a los escritores del compilador si lo intenta ...

La parte más fea para mí es la forma en que funciona la segmentación en modo real: cualquier dirección física tiene 4096 segmentos: alias de desplazamiento. ¿Cuándo fue lo último que necesitabas ? Las cosas hubieran sido mucho más simples si la parte del segmento fuera estrictamente bits de orden superior de una dirección de 32 bits.


El lenguaje ensamblador x86 no es tan malo. Cuando se llega al código máquina, comienza a ponerse realmente feo. Las codificaciones de instrucciones, los modos de direccionamiento, etc. son mucho más complicados que los de la mayoría de las CPU RISC. Y hay diversión adicional incorporada para fines de compatibilidad con versiones anteriores: cosas que solo se activan cuando el procesador se encuentra en cierto estado.

En modos de 16 bits, por ejemplo, el direccionamiento puede parecer francamente extraño; hay un modo de direccionamiento para [BX+SI] , pero no uno para [AX+BX] . Cosas como esa tienden a complicar el uso del registro, ya que necesita asegurarse de que su valor esté en un registro que puede usar cuando lo necesite.

(Afortunadamente, el modo de 32 bits es mucho más sensato (aunque sigue siendo un poco extraño a veces, por ejemplo, segmentación) y el código x86 de 16 bits ya no es relevante fuera de los cargadores de arranque y algunos entornos integrados).

También están las sobras de los viejos tiempos, cuando Intel intentaba convertir a x86 en el mejor procesador. Instrucciones de un par de bytes de longitud que realizaban tareas que ya nadie hace, porque francamente eran demasiado lentas o complicadas. Las instrucciones ENTER y LOOP , para dos ejemplos, tenga en cuenta que el código del cuadro de la pila C es como "push ebp; mov ebp, esp" y no "enter" para la mayoría de los compiladores.


El principal golpe contra x86 en mi mente es su origen CISC: el conjunto de instrucciones contiene muchas interdependencias implícitas. Estas interdependencias hacen que sea difícil hacer cosas como reordenamiento de instrucciones en el chip, porque los artefactos y la semántica de esas interdependencias deben preservarse para cada instrucción.

Por ejemplo, la mayoría de las instrucciones de suma y resta de entero x86 modifican el registro de banderas. Después de realizar un sumar o restar, la siguiente operación es a menudo mirar el registro de banderas para verificar el desbordamiento, bit de signo, etc. Si hay otro agregado después de eso, es muy difícil determinar si es seguro comenzar la ejecución del segundo agregado. antes de que se conozca el resultado del primer agregado.

En una arquitectura RISC, la instrucción add especificaría los operandos de entrada y los registros de salida, y todo sobre la operación se llevaría a cabo utilizando solo esos registros. Esto hace que sea mucho más fácil desacoplar operaciones de adición que están cerca unas de otras porque no hay registros de apertura de registros que obliguen a todo a alinearse y a ejecutar un solo archivo.

El chip DEC Alpha AXP, un diseño RISC estilo MIPS, fue dolorosamente espartano en las instrucciones disponibles, pero el conjunto de instrucciones fue diseñado para evitar dependencias de registro implícitas entre las instrucciones. No hubo registro de pila definido por hardware. No hubo registro de banderas definidas por hardware. Incluso el puntero de la instrucción estaba definido por el sistema operativo: si quería volver con la persona que llamaba, tenía que averiguar cómo le iba a decir la persona que llama a qué dirección volver. Esto fue generalmente definido por la convención de llamadas del sistema operativo. En el x86, sin embargo, está definido por el hardware del chip.

De todos modos, durante 3 o 4 generaciones de diseños de chip Alpha AXP, el hardware pasó de ser una implementación literal del conjunto de instrucciones espartano con 32 registros int y 32 registros float a un motor de ejecución masivamente fuera de servicio con 80 registros internos, registro de nombres, reenvío de resultados (donde el resultado de una instrucción previa se envía a una instrucción posterior que depende del valor) y todo tipo de potenciadores de rendimiento loco y loco. Y con todos esos detalles, el chip de chip AXP era considerablemente más pequeño que el dado de chip Pentium comparable de la época, y el AXP era muchísimo más rápido.

No se ve ese tipo de ráfagas de rendimiento que aumentan las cosas en el árbol de la familia x86, en gran parte porque la complejidad del conjunto de instrucciones x86 hace que muchos tipos de optimizaciones de ejecución sean prohibitivamente costosas, si no imposibles. La genialidad de Intel fue renunciar a la implementación del conjunto de instrucciones x86 en el hardware: todos los chips x86 modernos son en realidad núcleos RISC que hasta cierto punto interpretan las instrucciones x86, traduciéndolas en microcódigo interno que conserva toda la semántica del x86 original. instrucción, pero permite un poco de ese RISC fuera de orden y otras optimizaciones sobre el microcódigo.

He escrito mucho ensamblador x86 y puedo apreciar completamente la conveniencia de sus raíces CISC. Pero no aprecié completamente lo complicado que era x86 hasta que pasé un tiempo escribiendo ensamblador Alpha AXP. Me sorprendió la simplicidad y la uniformidad de AXP. Las diferencias son enormes y profundas.


La arquitectura x86 data del diseño del microprocesador 8008 y sus familiares. Estas CPU se diseñaron en un momento en que la memoria era lenta y, si se podía hacerlo en la CPU, a menudo era mucho más rápido. Sin embargo, el espacio de CPU también era costoso. Estas dos razones explican por qué hay solo un pequeño número de registros que tienden a tener propósitos especiales, y un conjunto de instrucciones complicado con todo tipo de problemas y limitaciones.

Otros procesadores de la misma época (por ejemplo, la familia 6502) también tienen limitaciones y caprichos similares. Curiosamente, tanto la serie 8008 como la serie 6502 fueron pensadas como controladores integrados. Incluso en aquel entonces, se esperaba que los controladores embebidos se programaran en ensamblador y de muchas maneras se atendieran al programador de ensamblaje en lugar de al escritor del compilador. (Mire el chip VAX para ver lo que ocurre cuando atiende la escritura del compilador). Los diseñadores no esperaban que se convirtieran en plataformas informáticas de uso general; para eso estaban las cosas como los predecesores del archivo de POWER. La revolución de la computadora doméstica cambió eso, por supuesto.


No soy un experto, pero parece que muchas de las características por las que a la gente no le gusta pueden ser las razones por las que funciona bien. Hace varios años, tener registros (en lugar de una pila), marcos de registros, etc., se veían como buenas soluciones para hacer que la arquitectura pareciera más sencilla para los humanos. Sin embargo, hoy en día, lo que importa es el rendimiento del caché, y las palabras de longitud variable de x86 le permiten almacenar más instrucciones en el caché. La "decodificación de instrucciones", que creo que los oponentes señalaron una vez tomó la mitad del chip, ya casi no es así.

Creo que el paralelismo es uno de los factores más importantes hoy en día, al menos para los algoritmos que ya se ejecutan lo suficientemente rápido como para ser utilizables. La expresión de alto paralelismo en el software permite que el hardware amortice (o a menudo oculte por completo) las latencias de memoria. Por supuesto, el futuro de la arquitectura de mayor alcance probablemente sea algo así como la computación cuántica.

Escuché de nVidia que uno de los errores de Intel fue que mantuvieron los formatos binarios cerca del hardware. PTX de CUDA hace algunos cálculos rápidos de uso de registro (coloreado de gráficos), por lo que nVidia puede usar una máquina de registro en lugar de una máquina de apilar, pero todavía tiene una ruta de actualización que no rompe todo el software anterior.


Tengo algunos aspectos adicionales aquí:

Considere la operación "a = b / c" x86 implementaría esto como

mov eax,b xor edx,edx div dword ptr c mov a,eax

Como una ventaja adicional de la instrucción div, edx contendrá el resto.

Un procesador RISC requeriría primero cargar las direcciones de byc, cargar byc desde la memoria a los registros, hacer la división y cargar la dirección de a y luego almacenar el resultado. Dst, src sintaxis:

mov r5,addr b mov r5,[r5] mov r6,addr c mov r6,[r6] div r7,r5,r6 mov r5,addr a mov [r5],r7

Aquí normalmente no habrá un resto.

Si se van a cargar variables a través de punteros, ambas secuencias pueden alargarse, aunque esto es menos posible para el RISC porque puede tener uno o más punteros ya cargados en otro registro. x86 tiene menos registros, por lo que la probabilidad de que el puntero esté en uno de ellos es menor.

Pros y contras:

Las instrucciones RISC pueden mezclarse con el código circundante para mejorar la programación de instrucciones, esta posibilidad es menor con x86, que en su lugar funciona (más o menos bien según la secuencia) dentro de la CPU. La secuencia RISC anterior generalmente tendrá 28 bytes de longitud (7 instrucciones de 32 bits / 4 bytes de ancho cada uno) en una arquitectura de 32 bits. Esto hará que la memoria fuera del chip funcione más cuando se busquen las instrucciones (siete recuperaciones). La secuencia x86 más densa contiene menos instrucciones y, aunque sus anchos varían, probablemente también esté buscando un promedio de 4 bytes / instrucción. Incluso si tiene cachés de instrucciones para acelerar esto, siete recuperaciones significa que tendrá un déficit de tres en otro lugar para compensar en comparación con el x86.

La arquitectura x86 con menos registros para guardar / restaurar significa que probablemente ejecutará interruptores de hilos y manejará las interrupciones más rápido que RISC. Más registros para guardar y restaurar requiere más espacio de pila de RAM temporal para hacer interrupciones y más espacio de pila permanente para almacenar estados de subprocesos. Estos aspectos deberían hacer que x86 sea un mejor candidato para ejecutar RTOS puros.

En una nota más personal, me resulta más difícil escribir el ensamblaje RISC que x86. Resuelvo esto escribiendo la rutina RISC en C, compilando y modificando el código generado. Esto es más eficiente desde el punto de vista de la producción de código y probablemente menos eficiente desde el punto de vista de la ejecución. Todos esos 32 registros para seguir. Con x86 es al revés: 6-8 registros con nombres "reales" hacen que el problema sea más manejable e infunde más confianza de que el código producido funcionará como se espera.

¿Feo? Eso está en el ojo del espectador. Prefiero "diferente".


Un par de posibles razones para ello:

  1. x86 es un ISA relativamente antiguo (sus progenitores fueron 8086, después de todo)
  2. x86 ha evolucionado significativamente varias veces, pero se requiere hardware para mantener la compatibilidad con versiones anteriores de binarios antiguos. Por ejemplo, el hardware x86 moderno todavía contiene soporte para ejecutar código de 16 bits de forma nativa. Además, existen varios modelos de direccionamiento de memoria para permitir que el código anterior interopere en el mismo procesador, como el modo real, el modo protegido, el modo 8086 virtual y el modo largo (amd64). Esto puede ser confuso para algunos.
  3. x86 es una máquina CISC. Durante mucho tiempo, esto significó que era más lento que las máquinas RISC como MIPS o ARM, porque las instrucciones tienen interdependencia de datos e indicadores que dificultan la implementación de la mayoría de las formas de paralelismo del nivel de instrucción. Las implementaciones modernas traducen las instrucciones x86 en instrucciones similares a RISC llamadas " micro-ops " bajo las cubiertas para hacer que este tipo de optimizaciones sean prácticas para implementar en el hardware.
  4. En algunos aspectos, el x86 no es inferior, es simplemente diferente. Por ejemplo, la entrada / salida se maneja como mapeo de memoria en la gran mayoría de las arquitecturas, pero no en el x86. (NB: las máquinas x86 modernas suelen tener algún tipo de soporte DMA , y se comunican con otro hardware a través del mapeo de memoria, pero el ISA todavía tiene instrucciones de E / S como IN y OUT )
  5. El ISA x86 tiene muy pocos registros arquitectónicos, lo que puede obligar a los programas a realizar un viaje de ida y vuelta a través de la memoria con más frecuencia de la que sería necesario. Las instrucciones adicionales necesarias para hacer esto toman los recursos de ejecución que se podrían gastar en trabajo útil, aunque el reenvío de tienda eficiente mantiene la latencia baja. Las implementaciones modernas con el cambio de nombre de registro en un archivo de registro físico grande pueden mantener muchas instrucciones en vuelo, pero la falta de registros de arquitectura todavía era una debilidad significativa para 32-bit x86. El aumento de x86-64 de 8 a 16 registros de números enteros y vectores es uno de los factores más importantes en el código de 64 bits que es más rápido que 32 bits (junto con el registro ABI más eficiente), no el ancho incrementado de cada registro. Un aumento adicional de 16 a 32 registros enteros ayudaría a algunos, pero no tanto. (AVX512 aumenta a 32 registros vectoriales, porque el código de coma flotante tiene una mayor latencia y a menudo necesita más constantes). ( Ver comentario )
  6. El código de ensamblaje x86 es complicado porque x86 es una arquitectura complicada con muchas características. Una lista de instrucciones para una máquina MIPS típica cabe en una hoja de papel de una sola letra. La lista equivalente de x86 ocupa varias páginas, y las instrucciones solo hacen más, por lo que a menudo necesita una explicación más amplia de lo que hace que una lista puede proporcionar. Por ejemplo, la instrucción MOVSB necesita un bloque relativamente grande de código C para describir lo que hace:

    if (DF==0) *(byte*)DI++ = *(byte*)SI++; else *(byte*)DI-- = *(byte*)SI--;

    Es una instrucción única que realiza una carga, una tienda y dos sumas o restas (controladas por una entrada de bandera), cada una de las cuales sería instrucciones separadas en una máquina RISC.

    Si bien la simplicidad de MIPS (y arquitecturas similares) no necesariamente los hace superiores, para enseñar una introducción a la clase de ensamblador, tiene sentido comenzar con un ISA más simple. Algunas clases de ensamblaje enseñan un subconjunto ultra simplificado de x86 llamado y86 , que se simplifica más allá del punto de no ser útil para el uso real (por ejemplo, no hay instrucciones de desplazamiento), o algunos enseñan solo las instrucciones básicas de x86.

  7. El x86 usa códigos de operación de longitud variable, que agregan complejidad de hardware con respecto al análisis de las instrucciones. En la era moderna, este costo se está volviendo insignificantemente pequeño ya que las CPU se vuelven cada vez más limitadas por el ancho de banda de la memoria que por el cálculo en bruto, pero muchos artículos y actitudes de "ataque x86" provienen de una era en la que este costo era comparativamente mucho mayor.
    Actualización 2016: Anandtech ha publicado un debate sobre los tamaños de código de operación en x64 y AArch64 .

EDITAR: ¡Esto no se supone que sea un bash el x86! fiesta. No tuve más remedio que hacer una cierta cantidad de ataques dado el modo en que se redactó la pregunta. Pero con la excepción de (1), todas estas cosas se hicieron por buenas razones (ver comentarios). Los diseñadores de Intel no son estúpidos: querían lograr algunas cosas con su arquitectura, y estos son algunos de los impuestos que tenían que pagar para hacer que esas cosas se hicieran realidad.