1

The ARM website says that the link register stores the return information for subroutines, function calls, and exceptions (such as interrupts), so what is the stack used for?

The answers to this similar question say that the stack is used to store the return address, and to "push" on local variables that will need to be put back on the core registers after the exception.

But this is what the link register is for, so is why is it needed? What is the difference between the two and how are they both used?

Community
  • 1
  • 1
Blue7
  • 1,750
  • 4
  • 31
  • 55
  • The very same manual describes [exactly how exception entry works](http://infocenter.arm.com/help/topic/com.arm.doc.dui0497a/Babefdjc.html#BABDBGGA) on that architecture (interrupts are just one kind of exception). Note that Cortex-M is rather different from the other ARM architectures in this regard. – Notlikethat Apr 16 '15 at 20:32
  • It says that information is pushed onto the stack, but what information is this? – Blue7 Apr 16 '15 at 20:36
  • What is **it**? The [stack overflow answer](http://stackoverflow.com/questions/1464035/what-is-a-stack-pointer-used-for-in-microprocessors) is referring to a normal stack and not an exception stack. You need to understand the context of information; that is most likely confusing you. – artless noise Apr 16 '15 at 21:09
  • http://stackoverflow.com/a/22500017/1163019 – auselen Apr 17 '15 at 06:10
  • possible duplicate of [ARM: link register and frame pointer](http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer) – artless noise Apr 17 '15 at 14:20

1 Answers1

3

Okay I think I understand your question.

So you have some code in a function call a function

main ()
{
int a,b;
a = myfun0();
b=a+7;
...

So when we call myfun0() the link register basically gets us back so we can do the b = a+7; understanding of course all of this gets compiled to assembly and optimized and such, but this will suffice to understand that the link register is used to return back to just after the call.

But what if

myfun0 ()
{
return(myfun1()+3);
}

when main calls myfun0() the link register points at some code in the main() function it needs to return to. then myfun0() calls myfun1() it needs to come back to myfun0() to do some more math before returning to main(), so when it calls myfun1() the link register is set to come back and add 3. the problem is when we set the link register to return to myfun0() we trash the address in main() we needed to return to. so to prevent that IF the function is going to call another function then as well as local variables that cant all live within the disposable registers the link register must be put on the stack. So now main calls myfun0(), myfun0() is going to call a function (myfun1()) so a copy of the link register (return address into main()) is saved on the stack. myfun0 calls myfun1() myfun1() follows the same rule if calling something else put lr on the stack otherwise you dont have to, myfun1() uses lr to return to myfun0(), myfun0() restores lr from the stack so it can return to main. follow that simple rule per function and you cant go wrong.

Now interrupts which not sure related or not or maybe I misunderstood your question. so arm has banked registers at least for non-cortex-m cores. but in general when an interrupt/exception occurs if the exception handler needs to use any resources/registers that are in use by the foreground task then that handler needs to preserve those on the stack in such a way that the foreground task that was interrupted had no idea this happened since interrupts in general can often occur between any two instructions, so you need to even go so far as to preserve the flags that were set by the instruction before the one you interrupted.

So apply that to arm, you have to look at which architecture you are using and see where it describes the interrupt process what registers you have to preserve and which ones you dont, which stack pointer is used, etc (something you have to setup well ahead of your first exceptions, if you are using an arm with separate interrupt stack and forground stacks).

The cortex-m is designed to do some/all of that work for you it has one stack basically and on interrupt it pushes all the registers on the stack for you so you can simply have a C compiled function just run as a handler and the hardware cleans up after you (from a preserved register perspective). Some other processors families do something like this for you, they might have a return from interrupt instruction separate from a return instruction, one is there because on interrupt the hardware saves the flags and return address but for a simple call you dont need the flags preserved.

The arm is a lot more flexible than some other instruction sets, some others you may not have any instructions that allow you to branch to an address in any register you want you may have a limitation. You might be limited on what register you use as a stack pointer or the stack pointer itself is not accessible as a general purpose register. by convention the sp is 13 in the arm, they allow the pseudo instruction of push and pop which translates into the proper ldmia r13!{blah} and stmdb r13!,{blah} but you could pick your own (if not using a compiler that follows the convention or can change an open source compiler to use a different stack pointer register). the arm doesnt prevent that. the magic of the link register r14 is nothing more than a branch link or branch link exchange automatically modifies r14, but the instruction set allows you to use basically any register to branch/return for normal function calls. The arm has just enough general purpose registers to encourage compilers to do register based parameter passing vs stack only. Some processors lean toward stack only parameter passing and have designed their return address instructions to be strictly stack based avoiding a return register all together and a rule to have to save that if nesting functions.

All of these approaches have pros and cons, in the case of arm register passing was desireable and a register based return address too, but for nesting functions you have to preserve the return address at each nesting level to not get lost. Likewise for interrupts you have to put things back they way you found them as well as be able to get back to where you interrupted the foreground.

old_timer
  • 69,149
  • 8
  • 89
  • 168