Stack to the rescue

Last updated at 4 March 2022 at 8:43pm


So, I’m learning assembly. There’s something I’ve been wondering for quite some time now. I think I’ve figured it out.

If I’m wrong, I’d appreciate it if you reach out, :)

We have a finite number of registers at our disposal when working with assembly code.

mov r0, #10
mov r1, #10
add r0, r0, r1

The example above that adds two numbers. The example uses both registers r0 and r1.

The question or thing that I’ve been struggling with is,

Question
If we only have r0 and r1 registers, what happens when we make a function call, and both functions use r0 and r1?

To explain the issue better, consider the following code snippets

void bar()
{
	int a = 10;    // mov r0, #10
	int b = 10;    // mov r1, #10
	int c = a + b; // add r0, r0, r1
	
	...
}
void foo()
{
	int a = 12;    // mov r0, #12
	int b = 14;    // mov r1, #14
	...
	bar();         // branch bar
	int c = a + b; // add r0, r0, r1
	...
}

r0 and r1 are being used by both, bar() and foo().

But something is missing, when bar() returns, that would mean the values of a and b would not be 12 and 14 as foo() expects since the values were tampered with by bar()

This is where the stack comes to the rescue. One of the uses of the stack is to save the values of the registers and restore them later. So before using a register in bar() we would need to push its value to the stack, then once everything in bar() is executed before returning, restore its value by popping the stack and set the registers to the appropriate values.

Here is what that looks like

.foo:
	mov r0, #12
	mov r1, #14

	branch bar

	; Continue execution
	add r0, r0, r1
.bar:
	; Push r0 and r1 to the stack
	push r0
	push r1

	mov r0, #10
	mov r1, #10
	add r0, r0, r1

	; Pop stack and set r0 and r1 values
	pop r1, stack
	pop r0, stack

	; Return
	ret .foo
	

Parts to pay attention to, since bar will use r0 and r1 we push their values to the stack.

.bar:
	push r0
	push r1
	...

And once we are done, just before returning, we pop the stack and set the values to the register. Also note the order of popping, the stack is LIFO(last in first out).

.bar:
	...
	pop r1
	pop r0

	; Return
	ret .foo

pop, branch and push in this case are made up, but there should be similar/appropriate instructions to handle that.

Here is another question, that I’ve still not figured out.

Question
What happens when we use up all our registers, this time in the same function?

Answer is here

References