Al leer acerca de ensamblador a menudo me encuentro con gente que escribe que empujan un cierto registro del procesador y el pop de nuevo más tarde para restaurar su estado anterior.
- ¿Cómo se puede presionar un registro? ¿Dónde se empuja? ¿Por qué es necesario?
- ¿Esto se reduce a una sola instrucción de procesador o es más complejo?
assembly
x86
stack
terminology
Ars emble
fuente
fuente
b
,w
,l
, oq
para indicar el tamaño de la memoria que se está manipulado. Ej .:pushl %eax
ypopl %eax
%eax
que siempre tiene un tamaño de 32 bits.Respuestas:
presionar un valor (no necesariamente almacenado en un registro) significa escribirlo en la pila.
hacer estallar significa restaurar lo que esté en la parte superior de la pila en un registro. Esas son instrucciones básicas:
fuente
r/m
, no solo registrarse, por lo que puede hacerlopush dword [esi]
. O inclusopop dword [esp]
para cargar y luego almacenar el mismo valor en la misma dirección. ( github.com/HJLebbink/asm-dude/wiki/POP ). Solo menciono esto porque dices "no necesariamente un registro".pop
posible un espacio de memoria:pop [0xdeadbeef]
Así es como empuja un registro. Supongo que estamos hablando de x86.
Se coloca en la pila. El valor del
ESP
registro se reduce al tamaño del valor empujado a medida que la pila crece hacia abajo en los sistemas x86.Es necesario preservar los valores. El uso general es
A
push
es una sola instrucción en x86, que hace dos cosas internamente.ESP
registro por el tamaño del valor empujado.ESP
registro.fuente
¿Dónde se empuja?
esp - 4
. Más precisamente:esp
se resta de 4esp
pop
invierte esto.El System V ABI le dice a Linux que
rsp
señale una ubicación de pila sensible cuando el programa comienza a ejecutarse: ¿Cuál es el estado de registro predeterminado cuando se inicia el programa (asm, linux)? que es lo que debería utilizar habitualmente.¿Cómo se puede presionar un registro?
Ejemplo mínimo de GNU GAS:
Lo anterior en GitHub con aserciones ejecutables .
¿Por qué es necesario?
Es cierto que esas instrucciones podrían implementarse fácilmente a través de
mov
,add
ysub
.La razón por la que existen, es que esas combinaciones de instrucciones son tan frecuentes, que Intel decidió proporcionárnoslas.
La razón por la que esas combinaciones son tan frecuentes es que facilitan guardar y restaurar los valores de los registros en la memoria temporalmente para que no se sobrescriban.
Para comprender el problema, intente compilar algo de código C a mano.
Una dificultad importante es decidir dónde se almacenará cada variable.
Idealmente, todas las variables encajarían en los registros, que es la memoria más rápida para acceder (actualmente alrededor de 100 veces más rápido que la RAM).
Pero, por supuesto, podemos tener fácilmente más variables que 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 a funciones y los retornos encajan en un patrón de pila agradable, que evita la fragmentación de la memoria , esa es la mejor manera de lidiar con eso. Compare eso con la locura de escribir un asignador de montones.
Luego dejamos que los compiladores optimicen la asignación de registros para nosotros, ya que es NP completo y una de las partes más difíciles de escribir un compilador. Este problema se denomina asignación de registros y es isomorfo para colorear los gráficos .
Cuando el asignador del compilador se ve obligado a almacenar cosas en la memoria en lugar de solo registros, eso 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 unapop
instrucción, por lo que son una instrucción en ese sentido.Internamente, podría expandirse a múltiples microcódigos, uno para modificar
esp
y otro para hacer la E / S de memoria, y tomar múltiples ciclos.Pero también es posible que una sola
push
sea más rápida que una combinación equivalente de otras instrucciones, ya que es más específica.Esto está en su mayoría sub (der) documentado:
push
ypop
toman una sola micro operación.fuente
push
/pop
decodificar en uops. Gracias a los contadores de rendimiento, es posible realizar pruebas experimentales, y Agner Fog lo ha hecho y ha publicado tablas de instrucciones . Las CPU Pentium-M y posteriores tienen un solo uoppush
/pop
gracias al motor de pila (consulte el pdf del microarchivo de Agner). Esto incluye CPU AMD recientes, gracias al acuerdo de intercambio de patentes entre Intel / AMD.mov
cargas separadas ). Para las variables no constantes que se derraman, los viajes de ida y vuelta de reenvío a la tienda tienen mucha latencia adicional (un ~ 5c adicional frente al reenvío directo, y las instrucciones de la tienda no son baratas).ocperf.py
script de envoltura para obtener nombres simbólicos fáciles para los contadores.Los registros de empuje y estallido están detrás de escena equivalentes a esto:
Tenga en cuenta que esta es la sintaxis x86-64 de At & t.
Usado como un par, esto le permite guardar un registro en la pila y restaurarlo más tarde. También hay otros usos.
fuente
lea rsp, [rsp±8]
lugar deadd
/sub
para emular mejor el efecto depush
/pop
en las banderas.Casi todas las CPU usan pila. La pila de programas es la técnica LIFO con gestión de hardware compatible.
La pila es la cantidad de memoria de programa (RAM) que normalmente se asigna en la parte superior del montón de memoria de la CPU y crece (en la instrucción PUSH, el puntero de la pila disminuye) en la dirección opuesta. Un término estándar para insertar en la pila es PUSH y para quitar de la pila es POP .
La pila se gestiona a través del registro de la CPU previsto en la pila, también llamado puntero de pila, por lo que cuando la CPU realiza POP o PUSH, el puntero de pila cargará / almacenará un registro o constante en la memoria de pila y el puntero de pila disminuirá automáticamente o aumentará según el número de palabras empujadas o aparecieron en (desde) la pila.
A través de las instrucciones del ensamblador, podemos almacenar para apilar:
fuente