Procedures Continued
The explanation assumes that the current point of execution is inside the calc function. In this case calc is known as the caller function and add is known as the callee function. The following presents the assembly code inside the calc function

The add function is invoked using the call operand in assembly, in this case callq sym.add. The call operand can either take a label as an argument(e.g. A function name), or it can take a memory address as an offset to the location of the start of the function in the form of call *value. Once the add function is invoked(and after it is completed), the program would need to know what point to continue in the program. To do this, the computer pushes the address of the next instruction onto the stack, in this case the address of the instruction on the line that contains movl %eax, local_4h. After this, the program would allocate a stack frame for the new function, change the current instruction pointer to the first instruction in the function, change the stack pointer(rsp) to the top of the stack, and change the frame pointer(rbp) to point to the start of the new frame.

Once the function is finished executing, it will call the return instruction(retq). This instruction will pop the value of the return address of the stack, deallocate the stack frame for the add function, change the instruction pointer to the value of the return address, change the stack pointer(rsp) to the top of the stack and change the frame pointer(rbp) to the stack frame of calc.

Now that we’ve understood how control is transferred through functions, let’s look at how data is transferred.
In the above example, we save that functions take arguments. The calc function takes 2 arguments(a and b). Upto 6 arguments for functions can be stored in the following registers:
-
rdi
-
rsi
-
rdx
-
rcx
-
r8
-
r9
Note: rax is a special register that stores the return values of the functions(if any).
If a function has anymore arguments, these arguments would be stored on the functions stack frame.
We can now see that a caller function may save values in their registers, but what happens if a callee function also wants to save values in the registers? To ensure the values are not overwritten, the callee values first save the values of the registers on their stack frame, use the registers and then load the values back into the registers. The caller function can also save values on the caller function frame to prevent the values from being overwritten. Here are some rules around which registers are caller and callee saved:
-
rax is caller saved
-
rdi, rsi, rdx, rcx r8 and r9 are called saved(and they are usually arguments for functions)
-
r10, r11 are caller saved
-
rbx, r12, r13, r14 are callee saved
-
rbp is also callee saved(and can be optionally used as a frame pointer)
-
rsp is callee saved
So far, this is a more thorough example of the run time stack:
|
|

No comments to display
No comments to display