Relation between endianness and stack-growth direction
Asked Answered
C

2

9

Is there a relation between endianness of a processor and the direction of stack growth?

For example, x86 architecture is little endian and the stack grows downwards (i.e. it starts at highest address and grows towards lower address with each push operation). Similarly, in SPARC architecture, which is big endian, the stack starts at lowest address and grows upwards towards higher addresses.

This relationship pattern is seen in almost all architectures. I believe there must be a reason for this unsaid convention. Can this be explained from computer architecture or OS point of view? Is this for some optimization in the microcode inside the processor? Does this aid the kernel in some way? Or may be other reason?

Thanks in advance!

Campy answered 27/8, 2013 at 15:43 Comment(2)
Note: Embedded PIC24 family of processors appear to be counter examples: “(stack pointer) grows from lower to higher addresses” and integers are little endian.Ossa
The stack on SPARC grows down (from higher addresses to lower addresses), not up. It is big-endian, so at least that is correct.Differentiable
D
6

Stack growth direction is orthogonal to integer endianness.

There is zero connection between the order of bytes within a wider integer (word), and whether a stack push adds or subtracts from the stack pointer. As far as a push is concerned, storing the data is a single operation.

Mapping a register-width integer to bytes in memory uses different hardware from the stack-pointer inc/dec logic; I assume the normal design would be to use the same hardware that non-push/pop stores/loads go through, and just treat the store from a push like any other store of a "word". Not any kind of weird one-byte-at-a-time thing that increments the stack pointer one by one.

This relationship pattern is seen in almost all architectures.

Uhhhh, not really. Many modern RISC ISAs (e.g. MIPS, PowerPC, ARM) have selectable endianness1, and that's not connected to stack-growth direction.

What is the direction of stack growth in most modern systems? shows that on most mainstream systems, the stack growth direction is typically down either by convention or by requirement, including on big-endian systems.

According to the answer on that Q&A, mainstream OSes / ABIs on SPARC choose to grow the stack downward. Upward is an option on SPARC, but the normal choice is downward like other systems.

Can this be explained from computer architecture or OS point of view?

What we can explain is that downward is the de-facto standard. IDK why SPARC bothered to make upward an option. Stack at the top of available memory, with static code/data at fixed addresses at the bottom, is obviously natural without paging / virtual memory. https://softwareengineering.stackexchange.com/questions/137640/why-does-the-stack-grow-downward

So that's how we got here.

On some ISAs, e.g. MIPS, the stack-grown direction is purely determined by software. The hardware doesn't implicitly / asynchronously use the stack, and there aren't push/pop instructions that make it more efficient to go one way or the other.

But the normal choice is still downward.

Other ISAs are like x86 where async interrupts push stuff onto the kernel stack, forcing one direction. Or at least have a bias in one way, by providing efficient push/pop for one direction (like ARM Thumb, and like x86's push/pop). Not to mention x86's call/ret instructions that push/pop a return address instead of just writing a link register.

If there is no choice of direction, the one fixed direction is downward in most ISAs, but @chux comments that PIC24 has upward stack growth.


I'm pretty sure there are big and little-endian examples of all of these, or at least bi-endian systems that can be configured as big or little-endian.


Footnote 1: Some specific ARM or MIPS CPUs have their endianness hard-wired, not runtime selectable, because it's basically a useless feature and waste of silicon for embedded. Not sure about modern POWER hardware; the Godbolt compiler explorer (https://godbolt.org/) has both PowerPC64 and PowerPC64le compilers, but that doesn't tell us whether they're both still relevant.

Donnettedonni answered 1/7, 2019 at 8:2 Comment(0)
B
-1

Endianness is the order of the bytes for a piece of data.

consider the hex value of 0x0A0B0C0D of which can be divided into 4 bytes of 0x0A, 0x0B, 0x0C, 0x0D.

In terms of memory:

let's say we have a memory address of o

For big endian, the order is the first byte is always first:

memory[o] = 0x0A

memory[o+1] = 0x0B

memory[o+2] = 0x0C

memory[o+3] = 0x0D

For little endian, the first byte is put into the farthest offset:

memory[o] = 0x0D

memory[o+1] = 0x0C

memory[o+2] = 0x0B

memory[o+3] = 0x0A

With a stack, it goes the opposite endianness so that, when we pop data from it, it re-assumes the correct endian order. Here's another example but using a stack.

Little endian Stack

unsigned data = 0x0A0B0C0D;

&data = 0x0D

&data+1 = 0x0C

&data+2 = 0x0B

&data+3 = 0x0A

when we push data, data's little endianness is stored as big endian since the stack is LIFO/FILO thus popping retains the endianness, thus:

[ebp] == 0x0A

[ebp-1] == 0x0B

[ebp-2] == 0x0C

[ebp-3] == 0x0D

TLDR: Stacks are the opposite endianness of the system.

Bargainbasement answered 31/7, 2017 at 20:31 Comment(3)
This is total nonsense. Stack grown direction is orthogonal to integer endianness, and your x86 example ([ebp - 0..3] is misaligned, with a 32-bit stored at address [ebp-3]. If we're assuming a traditional stack frame, this is overlapping one byte into the saved EBP value. In reality, a push would leave the data at [ebp-4] = [esp], if done right after a mov ebp,esp as part of stack frame setup.Donnettedonni
@PeterCordes It is not nonsense, it makes more sense to grow a stack in the opposite direction of endianness. If you have a big endian machine and you make the stack grow downwards like x86, what happens if you dereference that part of the stack to get one byte? Secondly, the x86 example is just a visual example, you're pointlessly harping on that.Bargainbasement
What happens is that you get the byte you asked for. e.g. the MSB of an integer you just pushed if you load a byte from [esp]. I don't see your point at all.Donnettedonni

© 2022 - 2024 — McMap. All rights reserved.