1

My professor assigned a homework assignment and it went like this. This is ARM Assembly, and imagine this is an Empty Descending stack. This means memory addresses move from higher addresses to lower addresses, and empty means the stack pointer points to the empty space above the stack. In this example, the addresses are in brackets. I'll use | | for empty space. TOS is the top of the stack and SP is the current position of the stack frame.

|___|            (80)  

|___|             (84)

|___|             (88)

|___|  SP      (92)

|___| TOS    (96)

|___|            (100)

Here is the code in question. I'll explain what I think happens after each line

  • STMED sp!, {fp,lr} (FP is R11 and LR is R13. Because lower registers go in lower addresses, the current value FP is stored in 88 and LR is stored in 92. The stack is an ED stack, so SP is at 84, a spot above FP)

  • MOV fp,sp (FP now points to the same location as SP, 84. The previous value of FP is stored at position 88)

  • SUB SP,SP,#4 (SP points to 80)

  • STR R3, [fp, #12] (FP is 84, so R3 is stored in 84+12 which is equal to 96, replacing the old TOS)

  • STR R6, [fp,#-4] (R6 is stored in 84-4 which is 80)

So this is my logic and it makes sense to me, but my professor said I was wrong. She said I should not use the location FP points to, but the value of FP that was put on the stack (which is at position 88). Meaning R3 would be stored at spot 100 and R6 stored at point 84. She was adamant that this is right, and said the frame pointer cannot be changed once it's put on the stack and it is the base of the stack frame. I understand all that, but I don't understand her logic. We are storing the value on the stack then changing it to point to something else. Why are we still using the old value? Can someone explain this to me?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
RogueNin7x
  • 37
  • 4
  • We cannot and should not know the value of FP that was put on the stack from this information. That FP belongs to the caller, who may or may not even have set up an FP. Even if they did, their FP would potentially point much higher up the stack. Further, this function could be called by different callers.. So, the idea the the old FP and new FP are off by only 4 from each other, and that in this context are always off by exactly 4 from each other, doesn't make any sense to me. (The difference of 4 between the two explanations seems more like the difference between ED and FD.) – Erik Eidt Mar 03 '21 at 17:11
  • @ErikEidt The addressing of the stack and values of FP don't really matter in this situation as it is not an actual program but an example to test our understanding of the stack. The difference of 4 doesn't make sense to me either, but I imagined that when we store the old FP in position 88, we are storing potentially a return address to another point on the stack, and then the new SP is the offset we use to navigate the stack. I don't understand too well so I apologize if I'm off. – RogueNin7x Mar 03 '21 at 18:17

1 Answers1

2

Cursorily the professor seems wrong from your explanation. However, there maybe a misunderstanding (Ie, you have misinterpreted the question). That said, it is quite common for compilers to save the previous FP to the stack (prior stack frame) and then use a new FP base on the updated stack. This is the new stack frame. One handles the prior function local variable that spill to memory and the updated value is for new spills, variable sized arrays, etc. I believe this is answered in ARM link register and frame pointer.

  • STMED sp!, {fp,lr} - A typical prologue operation performed to store the prior frame pointer and link register. This must be done in non-leaf functions. A(){B();} B(){C();} C{} function A() or B() with more complex bodies.

  • MOV fp,sp - Updates the frame pointer to a new stack frame (stack space for this routine reserved next).

  • SUB SP,SP,#4 - Reserve space on the stack for new routine.

  • STR R3, [fp, #12] - This is not standard as normally you would not look back so far in the stack history. Of course everything is valid in assembler, but this would generally be a bug.

  • STR R6, [fp,#-4] - This might be done to save a register to a stack frame so it can be reused for other calculations.

Another use of the STM and LDM is to load and store structure and array data. However, it is not combined with fp, lr, etc. I think the purpose of the exercise was to show function prologue and epilogue on a 32bit ARM CPU.


I think your professor might have had a better teaching moment to describe how a compiler uses these mechanics to map common higher/medium level constructs.

Generally if you are coding in assembler, you will try to avoid stack use like the plague. If you are using stack in assembler, it is to interface with higher level languages. 99% of all assembler will be a leaf function and would not contain code like this. Custom assembler will strive to use registers as much as possible.

However, there are often cases where you wish to understand what a compiler is doing and you need to look at the generated assembler. For this use case, the sort of pattern a compiler uses is important.

It is a difficult job to teach people and I think you just have a misunderstanding with concepts the professor was trying to present. So I don't think anyone can answer the question of who is right? Hopefully, the above helps you to understand how a compiler may use the stack, frame pointer and link register. That is a pragmatic aspect of understanding ARM assembler.

artless noise
  • 21,212
  • 6
  • 68
  • 105
  • 1
    Might want to say more clearly that `STR R3, [fp, #12]` would be storing into space owned by the caller, assuming a normal calling-convention. (For ARM that doesn't include shadow space above the incoming SP). – Peter Cordes Mar 03 '21 at 13:14
  • Well I'm not too sure if I misunderstood the question because this was exactly it. It wasn't a code or program we had to run, but we were given this scenario and asked for the location after those instructions were passed. And I agree with what you meant when you said compilers save an FP as a reference to the old function and use a new FP. Usually with FD stacks we use the new FP pointing to the same location we stored the old FP so we have an offset to nabigate the stack and have quick access to the location of the previous function. With ED, I imagined this would be the same, with the new – RogueNin7x Mar 03 '21 at 18:20
  • with the new FP being what we use to navigate the stack and the old FP stored right below it. I definitely don't claim to be right above my professor but I'm just really confused with none of my old professors mentioning this, and I would like to understand this concept. I also know stack use is rare in assembly but this was just to teach us the principle not necessarily encouraging us to use it. And the parts that are not standard were just to test our understanding, ie she said that we were supposed to use the FP stored at 88 to do this calculation, which is why she included [fp,12]. – RogueNin7x Mar 03 '21 at 18:24