¿Por qué las stacks normalmente crecen hacia abajo?

Sé que en las architectures con las que estoy familiarizado (x86, 6502, etc.), la stack normalmente crece hacia abajo (es decir, cada elemento insertado en la stack da como resultado un SP decrementado, no uno incrementado).

Me pregunto sobre la razón histórica para esto. Sé que en un espacio de dirección unificado, es conveniente comenzar la stack en el extremo opuesto del segmento de datos (por ejemplo), por lo que solo hay un problema si los dos lados colisionan en el medio. ¿Pero por qué la stack tradicionalmente obtiene la parte superior? ¿Especialmente dado que esto es lo opuesto al modelo “conceptual”?

(Y tenga en cuenta que en la architecture 6502, la stack también crece hacia abajo, aunque está limitada a una única página de 256 bytes, y esta elección de dirección parece arbitraria).

En cuanto a la lógica histórica, no puedo decirlo con certeza (porque no los diseñé). Mi opinión al respecto es que los primeros CPUs tenían su contador de progtwig original configurado en 0 y era un deseo natural comenzar la stack en el otro extremo y crecer hacia abajo, ya que su código crece naturalmente hacia arriba.

Como un aparte, tenga en cuenta que esta configuración del contador de progtwig a 0 en el reinicio no es el caso para todas las CPUs tempranas. Por ejemplo, el Motorola 6809 0xfffe/f contador del progtwig desde las direcciones 0xfffe/f para que pueda comenzar a ejecutar en una ubicación arbitraria, dependiendo de lo que se suministró en esa dirección (generalmente, pero de ninguna manera limitada, a la ROM).

Una de las primeras cosas que harían algunos sistemas históricos sería escanear la memoria desde la parte superior hasta que encontrara una ubicación que leyera el mismo valor escrito, para que supiera la RAM real instalada (por ejemplo, un z80 con espacio de direcciones de 64K) no necesariamente tenía 64K o RAM, de hecho 64K habría sido masivo en mis primeros días). Una vez que encuentre la dirección real superior, establecerá el puntero de la stack de manera apropiada y luego podrá comenzar a llamar a las subrutinas. Este escaneo generalmente lo haría el código de ejecución de la CPU en la ROM como parte de la puesta en marcha.

Con respecto al crecimiento de las stacks, no todas crecen hacia abajo, vea esta respuesta para más detalles.

Una buena explicación que escuché fue que algunas máquinas en el pasado solo podían tener compensaciones sin firmar, por lo que querría que la stack creciera hacia abajo para poder golpear a los locales sin tener que perder las instrucciones adicionales para falsificar una compensación negativa.

Una posible razón podría ser que simplifica la alineación. Si coloca una variable local en la stack que debe colocarse en un límite de 4 bytes, simplemente puede restar el tamaño del objeto del puntero de stack y luego poner a cero los dos bits inferiores para obtener una dirección alineada correctamente. Si la stack crece hacia arriba, garantizar la alineación se vuelve un poco más complicado.

IIRC la stack crece hacia abajo porque el montón crece hacia arriba. Podría haber sido al revés.

Creo que es puramente una decisión de diseño. No todos crecen a la baja: consulte este hilo SO para tener una buena discusión sobre la dirección del crecimiento de la stack en diferentes architectures.

Creo que la convención comenzó con el IBM 704 y su infame “decrement register”. El habla moderna lo llamaría un campo de desplazamiento de la instrucción, pero el punto es que bajaron , no subieron .

No estoy seguro pero hice algo de progtwigción para el VAX / VMS en los días. Me parece recordar una parte de la memoria (¿el montón?) Que sube y la stack baja. Cuando los dos se conocieron, entonces estabas sin memoria.

Solo 2c más:

Más allá de todas las razones históricas mencionadas, estoy bastante seguro de que no hay ninguna razón válida en los procesadores modernos. Todos los procesadores pueden tomar compensaciones firmadas, y maximizar la distancia de stack / stack es bastante discutible desde que comenzamos a tratar con múltiples hilos.

Personalmente, considero esto un defecto de diseño de seguridad. Si, digamos, los diseñadores de la architecture x64 hubieran invertido la dirección de crecimiento de la stack, se hubieran eliminado los desbordamientos de la memoria tampón de la stack, lo cual es una gran cosa.