Procedure
- Suppose procedure P calls procedure Q, and Q then executes and returns back to P.
Run-time Stack
- C programs use the (run-time) stack for local variables, function parameters, and return addresses.
- When procedure P calls procedure Q, it’ll push the return address onto the stack, indicating where within P the program should resume execution once Q returns
Control transfer
Instruction | Description |
---|---|
call Label | Procedure call |
call *Operand | Procedure call |
ret | Return from call |
-
The instruction
call
Q pushes an address A onto the stack, sets the PC to the beginning of Q. -
The pushed address A is referred as the
return address
and is computed as the address of the instruction immediately following the call instruction. -
The counterpart instruction
ret
pops an address A off the stack and sets the PC to A. -
Example: consider the following C code
-
In this code, we can see that:
- The
callq
instruction with address0x400563
inmain
calls functionmultstore
. - The effect of
call
is to pushes the next instruction address (0x400568
) to top of the stack - Then jumps to the instruction in function
multstore
at address0x400540
(by set this value to %rip register). - The execution of
multstore
continues until it hits theretq
instruction at0x40054d
. - The instruction
retq
pops0x400568
from the stack and jumps to this address (by set this value to %rip register), resuming the execution ofmain
- The
-
Example:
Data transfer
-
In addition to passing control to a procedure when called, and then back again when the procedure returns, procedure calls may involve passing data as arguments, and returning from a procedure may also involve returning a value.
-
With x86_64, up to 6 integral (integer and pointer) arguments can be passed via registers.
-
When a function has more than 6 integral arguments, the other ones are passed on the stack.
Local Storage on the Stack (stores local variable)
-
Not enough registers to hold all of the local data.
-
The address operator
&
is applied to local variable, and hence we must be able to generate an address for it. -
Some of the local variables are arrays or structures and hence must be accessed by array or structure references.
-
Portion of the stack frame labeled “Local variables”
-
Consider this example:
-
Example 2:
Local storage in registers (stores local variable)
-
Callee-saved registers: %rbx, %rbp, %r12-%r15
-
When procedure P calls procedure Q, Q must preserve the values of these registers, ensuring that they have the same values when Q returns to P as they did when Q was called. Procedure Q can preserve a register value by either not changing it at all or by pushing the original value on the stack, altering it, and then popping the old value from the stack before returning. The pushing of register values has the effect of creating the portion of the stack frame labeled “Saved registers” in Figure 3.25. With this convention, the code for P can safely store a value in a callee-saved register (after saving the previous value on the stack, of course), call Q, and then use the value in the register without risk of it having been corrupted.
-
Consider the following example:
- Initially,
x
is in %rdi,y
is in %rsi, if we want to callQ(y)
, somehow we must pass the value ofy
into the%rdi
register (1st arg), but before that we need to save the value x somewhere (%rbp in this case). Same goes forx
. - Function
P
will use%rbp
and%rbx
registers to save x, y. Since they’recallee-saved
, functionP
will have to pushing them to the stack (Saved registers
portion). - At the end of the function, it restores the values from the stack into these 2 registers.
- Initially,
Recursive procedures
Variable-size stack frames
-
Consider an example of a function containing a variable-size array.
-
The function declares local array
p
ofn
pointers, wheren
is given by the first argument. This requires allocation8n
bytes on the stack, where the value ofn
may vary from one call of the function to another. -
The compiler therefore can’t determine how much space it must allocate for the function’s stack frame.
-
In addition, the program generates a reference to the address of the local variable
i
(so this variable must be stored on the stack). -
During execution, the program must be able to access both local variable i and the elements of array p. On returning, the function must deallocate the stack frame and set the stack pointer to the position of the stored return address.
-
To manage a variable-size frame, x86_64 code usees register
%rbp
to serve as a frame pointer (or base pointer). When using a frame pointer, the stack frame is organized as shown below:- The code starts by pushing the current value of %rbp onto the stack and setting %rbp to point to this stack position (lines 2–3).
- Next, it allocates 16 bytes on the stack, the first 8 of which are used to store local variable i, and the second 8 of which are unused.
- Then it allocates space for array p (lines 5–11).