Confused by return address

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Confused by return address

riverfish
I'm a bit confused by return address.

...
argument 0
argument n-1
return-address
saved LCL
saved ARG
saved THIS
saved THAT
local 0
local n-1
              <--- SP


This is what the stack looks like just after a function is called but before it's returned.

After it's return, the return value replaces argument 0 in the stack when *ARG = pop() is called.

So what's exactly stored in return address then?

Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

ybakos
This post was updated on .
Careful, don't confuse the "return value" with the "return address."

Upon returning, the return value of the function should be on the top of the stack, and PC should be set to the return address.

Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

riverfish
In that case, what does the return address refer to?

I thought after the function is returned, the return value goes where argument 0 is, and SP is set to the next address:

// BEFORE
....
argument 0
argument n-1
return addr
saved LCL
saved ARG
saved THIS
saved THAT
local 0
local n-1
               <---- SP


// AFTER
....
return value
               <---- SP

If so, what's the point of saving return address? Isn't it just easier to set SP to *(argument 0  + 1)?
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

ybakos
riverfish, I made a typo: PC should be set to the return address (not SP)

Chew on that and see if it makes sense now.
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

riverfish
Sorry bakos, still not making any sense to me!

I'm playing with NestedCall in the VMEmulator.

Inside function Sys.init, it calls Sys.main 0; when it does, the value saved in the 'return address' is 6.

And inside Sys.main, it calls Sys.add12 1; when does, the value saved in the 'return address' is 22.

However, after running the simulation dozens of times I still can't understand the relevancy of these two numbers and how they're used. There doesn't seem to be anything significant stored in either RAM[6] or RAM[22] during this simulation.

If it's suppose to act as a program counter, then doesn't it make logical sense to start at 1 and increment each time a function returns?

Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

cadet1620
Administrator
First, let me describe how this works in assembly language programs, because that is a published specification. I will describe what I think the VM Emulator does later.

For an assembly language program, program counter means the ROM address of the currently executing instruction. When an ASM program calls a subroutine, the called subroutine needs to know what instruction in the calling program should be jumped to when the called subroutine returns.

For example the ASM code for Sys.init's call to Sys.main might look something like this:
ROM Addr    Code

        // call Sys.main 0
   96       @$RET_ADDR$2
   97       D=A
            ... [ASM code to push D, build the frame, and jump to Sys.main]
  157   ($RET_ADDR$2)
  
        // pop temp 1
  157       @SP
  158       AM=M-1
            ...
In this case, the return address pushed on the stack will be 157, and the return instruction at the end of Sys.main will get that number off the stack and jump to the next instruction in Sys.init.
 

The return addresses pushed on the stack by the VM Emulator are a bit confusing.

In the VM emulator, the program counter is a VM instruction number. Unfortunately, those numbers are not visible to users when the VM Emulator is running. They appear to be a running count of the instructions shown in the "Program" panel.

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

riverfish
Making sense now and finally got my tests passing. Thanks a lot guys
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

niilz
My problem is somewhat related.

(I'm working on NestedCalls)

After researching here for quite a while I understand now, how you first have to create a label, at the beginning of a function call, like "@return_add_1" and save the PC-value in a general purpose register (e.g. R15).

Then, after the function-call, I have to insert the label like "(return_add_1)"

And when RETURN is called, I have to jump to the PC-value, currently stored in R15.



My Problem:
R15 get's overwritten at some point with the wrong destination. So unfortunately my code jumps to the wrong address. It's almost right. It jumps to pop temp 0, but should jump to pop temp 1 and then enter the infinite loop.
Do I have to use different general purpose registers to prevent that?
Or is my label injection most probably wrong?

I would be very happy for any advice.

Thanks
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

WBahn
Administrator
niilz wrote
My problem is somewhat related.

(I'm working on NestedCalls)

After researching here for quite a while I understand now, how you first have to create a label, at the beginning of a function call, like "@return_add_1" and save the PC-value in a general purpose register (e.g. R15).

Then, after the function-call, I have to insert the label like "(return_add_1)"

And when RETURN is called, I have to jump to the PC-value, currently stored in R15.

My Problem:
R15 get's overwritten at some point with the wrong destination. So unfortunately my code jumps to the wrong address. It's almost right. It jumps to pop temp 0, but should jump to pop temp 1 and then enter the infinite loop.
Do I have to use different general purpose registers to prevent that?
Or is my label injection most probably wrong?

I would be very happy for any advice.

Thanks
Yes, the registers R13 through R15 are for general use by the VM translator, but you need to be very careful that if you use one of them for some purpose, that it is not possible for any of the code generated by your translator to use that register for anything else until after you are completely done using it for the current purpose.

But if you are storing something there at the beginning of a function call and expecting it to still be there at the end of the function call, then how can that happen if your function calls any other function since THAT function call with change it's value in between when the first function starts and when it stops.

Or, another way to think of it, when Main.main() is called at the very beginning of the program execution, it will store something in R15 and expect that value to still be there at the end of the program when Main.main() ends. Unless you have a mechanism to save and restore the value in R15 when other functions are called, then no other function can use R15.

Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

niilz
First of all thank you for the quick reply.

You have confirmed what I thought would be the problem.

BUT, how do I inform the write_return(), (the implementation which "creates-ASM-for-RETURN"), to which PC it has to jump to. Because write_return() just performs it's algorithm. Where it puts all frame pieces (derived from LCL), back into place. Only problem: write_return() does not know to which function it belongs, right?

So I cannot just jmp to some label, because I wouldn't know it's name...

At least that is what I think and why I don't find an out right now.
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

WBahn
Administrator
Look at the pseudocode in Figure 8.5. What is the very first thing that happens for the 'call' VM command?

One of the things that is stored in the stack frame is the return address -- the instruction address that you need to return to after the function completes. It is located at a specific location in RAM relative to the LCL pointer. If you call ten nested functions, each function has a stack frame and each stack frame has a return address stored in it.

Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

niilz
Oh my! That took me faaaaar too long.
Thank you so very much. My ASM is working now!!!!

I was really close though

It also did not work immediately. Because I had to realize, that within the return-call I had to:
- first safe return-address (derived from LCL [LCL-5]) in a register (e.g. R15)
- restore THAT, THIS, ARG, LCL
- And theeeeen jump to the value in R15

Because if you derive the return address from LCL-5, just before the jump, but after you have already reinitiated LCL, the return-address is of course wrong.
Reply | Threaded
Open this post in threaded view
|

Re: Confused by return address

WBahn
Administrator
niilz wrote
Oh my! That took me faaaaar too long.
Thank you so very much. My ASM is working now!!!!

I was really close though

It also did not work immediately. Because I had to realize, that within the return-call I had to:
- first safe return-address (derived from LCL [LCL-5]) in a register (e.g. R15)
- restore THAT, THIS, ARG, LCL
- And theeeeen jump to the value in R15

Because if you derive the return address from LCL-5, just before the jump, but after you have already reinitiated LCL, the return-address is of course wrong.
If you use R15 for this purpose, then you are okay because there is no chance of it being changed by anything else during the time you need it.

There are other ways to do this as well, but at the end of the day any way that works is valid.