2011-01-03 346 views
48

Al leer acerca del ensamblador, a menudo me encuentro con personas que escriben que presionan un cierto registro del procesador y pop de nuevo para restaurar su estado anterior.¿Cuál es la función de las instrucciones push/pop usadas en los registros del ensamblaje x86?

  • ¿Cómo se puede presionar un registro? ¿Dónde se empuja? ¿Por qué es esto necesario?
  • hace esto se reducen a una instrucción de procesador único o es más compleja?
+1

Advertencia: todas las respuestas actuales se proporcionan en la sintaxis de ensamblaje de Intel; push-pop en la sintaxis de AT & T, por ejemplo, usa una corrección posterior como 'b',' w', 'l' o' q' para indicar el tamaño de la memoria que se está manipulando. Ej: 'pushl% eax' y' popl% eax' – Hawken

+4

@hawken En la mayoría de los ensambladores capaces de tragar la sintaxis de AT & T (notablemente gas) el tamaño del sufijo puede ser omitido si el tamaño del operando puede deducirse del tamaño del operando. Este es el caso de los ejemplos que ha dado, ya que '% eax' siempre tiene un tamaño de 32 bits. – hirschhornsalz

Respuesta

82

empujando un valor (no necesariamente almacenado en un registro) significa la escritura hasta la pila.

apareciendo medios de recuperación que se encuentre encima de la pila en un registro. Esas son las instrucciones básicas:

push 0xdeadbeef  ; push a value to the stack 
pop eax    ; eax is now 0xdeadbeef 

; swap contents of registers 
push eax 
mov eax, ebx 
pop ebx 
+0

El operando explícito para push y pop es 'r/m', no solo para registrarse, por lo que puede' push dword [esi] '. O incluso 'pop dword [esp]' para cargar y luego almacenar el mismo valor en la misma dirección. (https://github.com/HJLebbink/asm-dude/wiki/POP). Solo menciono esto porque dices "no necesariamente un registro". –

30

Aquí es cómo se presiona un registro. Supongo que estamos hablando de x86.

push ebx 
push eax 

Se empuja en la pila. El valor del registro ESP se reduce al tamaño del valor presionado a medida que la pila crece hacia abajo en los sistemas x86.

es necesario para preservar los valores. El uso general es

push eax   ; preserve the value of eax 
call some_method ; some method is called which will put return value in eax 
mov edx, eax  ; move the return value to edx 
pop eax   ; restore original eax 

Un push es una sola instrucción en x86, que hace dos cosas internamente.

  1. tienda el valor empujado en la dirección actual de ESP registro.
  2. Disminuir el Registro ESP al tamaño del valor empujado.
+3

1. y 2. deben ser reorganizados – vavan

11

Casi todas las CPU utilizan la pila. La pila de programas es LIFO técnica con administración compatible con hardware.

La pila es la cantidad de memoria de programa (RAM) normalmente asignada en la parte superior del montón de memoria de la CPU y crece (en la instrucción PUSH disminuye el puntero de la pila) en dirección opuesta. Un término estándar para insertar en la pila es PUSH y para eliminar de la pila es POP.

pila se gestiona a través de la pila destinada registro de la CPU, también llamado puntero de pila, por lo que cuando la CPU realice POP o PUSH el puntero de pila se carga/guardar un registro o constante en la memoria de pila y el puntero de pila será automática disminuyó xor aumentó según el número de palabras empujadas o apiladas en (desde) la pila.

través de las instrucciones de ensamblador podemos almacenar registros de pila:

  1. CPU y también constantes.
  2. direcciones de retorno para las funciones o procedimientos
  3. funciones/procedimientos de entrada/salida las variables
  4. funciones/procedimientos locales variables.
15

¿Dónde se empuja?

esp - 4. Más precisamente:

  • esp consigue resta por 4
  • el valor es empujado a esp

pop invierte este.

El Sistema V ABI dice a Linux para hacer rsp punto a una ubicación pila sensata cuando el programa se pone en marcha: https://stackoverflow.com/a/32967009/895245 que es lo que debe utilizar normalmente.

¿Cómo se puede presionar un registro?

Minimal GNU ejemplo GAS:

.data 
    /* .long takes 4 bytes each. */ 
    val1: 
     /* Store bytes 0x 01 00 00 00 here. */ 
     .long 1 
    val2: 
     /* 0x 02 00 00 00 */ 
     .long 2 
.text 
    /* Make esp point to the address of val2. 
    * Unusual, but totally possible. */ 
    mov $val2, %esp 

    /* eax = 3 */ 
    mov $3, %ea 

    push %eax 
    /* 
    Outcome: 
    - esp == val1 
    - val1 == 3 
    esp was changed to point to val1, 
    and then val1 was modified. 
    */ 

    pop %ebx 
    /* 
    Outcome: 
    - esp == &val2 
    - ebx == 3 
    Inverses push: ebx gets the value of val1 (first) 
    and then esp is increased back to point to val2. 
    */ 

Lo anterior with assertions.

¿Por qué es necesario?

Es cierto que esas instrucciones podrían implementarse fácilmente a través de mov, add y sub.

Ellos razonan que existen, es que esas combinaciones de instrucciones son tan frecuentes, que Intel decidió proporcionarlas para nosotros.

La razón por la que esas combinaciones son tan frecuentes, es que hacen que sea fácil de guardar y restaurar los valores de los registros de la memoria temporal para que no se sobreescriben.

Para comprender el problema, intente compilar código C a mano.

Una dificultad importante es decidir dónde se almacenará cada variable.

Lo ideal es que todas las variables quepan en los registros, que es la memoria más rápida para acceder (actualmente es 100x faster que la RAM).

Pero, por supuesto, podemos tener fácilmente más variables que los registros, especialmente para los argumentos de funciones anidadas, por lo que la única solución es escribir en la memoria.

Podríamos escribir en cualquier dirección de memoria, pero dado que las variables locales y los argumentos de las llamadas de función y devoluciones encajan en un buen patrón de pila, que previene memory fragmentation, esa es la mejor manera de manejarlo. Compare eso con la locura de escribir un asignador de montón.

Luego dejamos que los compiladores optimicen la asignación de registros para nosotros, ya que es NP completa, y una de las partes más difíciles de escribir un compilador. Este problema se llama register allocation, y es isomorfo a graph coloring.

Cuando el asignador del compilador se ve obligado a almacenar cosas en la memoria en lugar de solo registros, se conoce como derrame.

¿Esto se reduce a una sola instrucción de procesador o es más complejo?

Todo lo que sabemos con certeza es que Intel documenta una push y una instrucción pop, por lo que son una instrucción en ese sentido.

Internamente, podría expandirse a múltiples microcódigos, uno para modificar esp y uno para hacer la memoria IO, y tomar varios ciclos.

Pero también es posible que un solo push sea más rápido que una combinación equivalente de otras instrucciones, ya que es más específico.

Esto es sobre todo de las Naciones Unidas (der) documentado:

+1

@Downvoters por favor explique para que pueda aprender y mejorar. –

+1

No necesita adivinar cómo 'push' /' pop' decodificar en uops.Gracias a los contadores de rendimiento, es posible realizar pruebas experimentales y [Agner Fog lo ha hecho y publicado las tablas de instrucciones] (http://agner.org/optimize/). Las CPU Pentium-M y posteriores tienen '' push'/'pop' de single-uop gracias al motor de la pila (ver el pdf de microarmen de Agner). Esto incluye CPU recientes de AMD, gracias al acuerdo de intercambio de patentes de Intel/AMD. –

+0

@PeterCordes ¡increíble! Entonces, ¿los contadores de rendimiento están documentados por Intel para contar las microoperaciones? –

8

empujar y hacer estallar los registros están detrás de la escenas equivalente a esto:

push reg <= same as =>  sub $8,%rsp  # subtract 8 from rsp 
           mov reg,(%rsp)  # store, using rsp as the address 

pop reg <= same as=>  mov (%rsp),reg  # load, using rsp as the address 
           add $8,%rsp  # add 8 to the rsp 

Nota esto es x86-64 En & t sintaxis

Utilizado como un par, esto le permite guardar un registro en la pila y restaurarlo más tarde. Hay otros usos, también.

+3

Sí, esas secuencias emulan correctamente push/pop. (excepto que push/pop no afecta a los indicadores). –

Cuestiones relacionadas