Chapter 8Advanced Procedures |
Modified: |
Call-by-value and call-by-reference parameters both have merits in given applications and are commonly supported by high level languages. The meaning here is that normally used in C++.Call-by-value passes a copy of the value for the actual parameter via the stack from the caller to the callee.
Call-by-reference differs in that the offset of the actual parameter is passed via the stack from the caller to the callee.
The implications for the programmer is relatively straightforward and is summarized by the following examples.
Indirection is necessary for implementing parameter passing via the stack. It is useful to first relate our usual notion of indirectionto assembly language and the machine's architecture. C++ supports indirection using pointer variables that 1) hold an address that references 2) a variable, hence the indirection. The Intel processor uses registers (Bx, Si, Di, and Bp for 16-bit or any 32-bit) as pointers, these registers can hold an address used for the indirection. The following three examples are identical with variables i, an integer, and bx, a pointer to an integer. In the diagram at right, BX holds the address of variable i.
In the following Assembler program, we can examine the offsets of the variables and determine precisely what is the contents of eBx register. The instruction
Mov eBx, Offset icopies the offset of i, which is 32-bit 00000000416 to eBx.
C++ version
#include <iostream.h>
void main ( void )
{
int i;
int *ebx;
i = 5;
ebx = &i; // Point to i
cout << *ebx; // Prints 5
}
|
Assembler version
.data
00000000 h dd 3
00000004 i dd 5
00000008 j dd 9
.code
Main Proc Near
Mov eBx, Offset i ;; Point to i, eBx=0000004
Mov eAx, [eBx] ;; Get i value indirectly
Call WriteDec ;; Prints 5
Mov eAx, [eBx+4] ;; Get j value indirectly
Call WriteDec ;; Prints 9
Exit
Main Endp
End Main
|
Indirection to Access Parameters
By enclosing in square brackets, [], registers such as eBx, eSi, eDi and other 32-bit registers act as pointers to reference memory.
Using 16-bit variables, Bx and Si can point to the data segement, Di a pointer to the extra segment, while Bp can be a pointer to the stack segment. In the above example Bx or Si could be used but not Bp since the variable i is in the data segment and Bp references the stack segment.
In the following assembly program examples, notice the use of eBp as a pointer to access the parameters on the stack. Suppose program fragments of:
Notice that after Mov e Bp, eSP the eBp register can be used to access parameters by adding an appropriate displacement to eBp. For example, the instruction:
Mov eAx, [eBp+8]
adds 8 to the offset in eBp to indirectly access the stack location holding the A parameter. Because any calls made to this function would place the actual parameters at the same relative stack location, [eBp+8] will always correspond to the formal A parameter. This follows the protocol used by C++ in calling functions and passing value parameters.General Rules for C++ Functions
Using functions requires following specific rules or protocol by both the caller and the callee. In general, the rules can be summarized by the following:
| Caller | Callee |
|
|
Assembly version
Stack after Mov eBp, eSp _________ | eDx | |_________| | Flags | |_________| B | 7 | <- eBp+12 |_________| A | 9 | <- eBp+8 |_________| | Ret Addr| <- eBp+4 |_________| | Old eBp | <- eBp+0 = eSp |_________| |
Call-by-Value versus Call-by-Reference
The following figure illustrates the difference between value (left figure) and reference (right figure) passing in the program examples that follow later. In value passing the function does not have access to the original parameter, in reference passing the reference allows indirect access to the caller's parameters.
Call by Value
Call by Reference
![]() |
||
|
Requires the value of the actual parameter to be pushed onto the stack by the calling routine. The called function uses the value directly from the stack. The called function cannot alter parameters because it lacks a reference (i.e. the data segment offset of the actual parameter is unknown).
Caller - The caller's responsibility is to:
|
Callee - The callee's responsibility is to:
|
A more complete example of call-by-value implementation is given below using C++ calling methods.
The protocol is similar to call-by-value but rather than pushing values onto the stack, references to the parameters (offsets) are pushed.The protocol requires the data segment offset of the actual parameter to be pushed onto the stack by the calling routine. The called function then accesses the location of the actual parameter using the data segment offset. Since the parameter's location in memory is now accessible by the called function, it can be directly referenced and altered. The responsibility of the caller and callee is nearly identical for value and reference parameter passing, except that offsets are used rather than values to access parameters.
Caller - The caller's responsibility is to:
|
Callee - The callee's responsibility is to:
|
A more complete example of call-by-reference implementation is given below using C++ calling methods.
Dynamic (Local) Variables Discussion
In C++ procedures, dynamic variables are generally termed local variables, which are allocated each time the procedure is entered and deallocated on each exit. To allocate storage for a local or dynamic variable, the stack pointer is decremented, once for a byte variable, twice for a word variable, 200 times for an array of 100 words, etc. Deallocation is by restoring the stack pointer, by Mov e Sp, eBp. References to the dynamic variable are still relative to eBp though the displacement is normally negative rather than positive. Generally, dynamic variables use requires all the same steps used when entering or exiting a procedure, summarized below:
Entering 1) Push eBp 2) Mov eBp, eSp 3) Sub eSp, # # is the number of bytes to allocate for dynamic variables 4) Push Registers Save any registers Exiting 4) Pop Registers Restore any registers 3) Mov eSp, eBp Restore eSp which deallocates dynamic variables 2) Pop eBp 1) Ret
Dynamic (Local) Variables C++ and Assembler
int a = 5, b = 6;
void main(void) {
cout << addup(a, b);
}
|
.data a dd 5 b dd 6 .code main proc near push offset b push a call addup add eSp, 8 call PutDec push 0 call ExitProcess main endp |
int addup(int x, int &y)
{ int sum;
sum = y;
sum = sum + x;
return sum;
}
|
addup PROC NEAR push eBp mov eBp, eSp sub eSp, 4 ; int sum mov eBx, [eBp+12] ; sum = y; mov eAx, [eBx] add eAx, [eBp+8] ; sum = sum + x mov [eBp-4], eAx mov eAx, [eBp-4] ; return sum mov eSp, eBp pop eBp ret addup ENDP |
Double Indirection
The swap function illustrates an appropriate use of reference parameters, that is when the parameter requires a side-effect. It also illustrates that call-by-reference parameters have an access cost of two indirections. To access the right-most variable B in the following example requires: