• No se han encontrado resultados

VI. Proyecto de inversión para la construcción de un centro de acopio

6.3 Estrategia de abasto

In architectures with large general-purpose register files, parameters are usually passed in registers. A sequence of integer registers and a sequence of floating-point registers are designated to contain the first ia integer arguments and the first fa floating-point arguments, for some small values of ia and fa,6 with the arguments divided in order between the two register sets according to their types, and the remaining arguments, if any, passed in storage at an agreed point in the stack. Suppose we have a call f ( i , x , j ) with parameters passed by value, where the first and third parameters are integers and the second is a single-precision floating-point value. Thus, for our example, the arguments i and j would be passed in the first two integer parameter registers, and x would be passed in the first floating-point parameter register. The procedure-invocation handshake includes making the code generated for f ( ) receive its parameters this way (this example is used in Exercises 5 .4 -5.6 in Section 5.11).

This mechanism is adequate for value parameters that fit into single registers or into pairs of registers and for all reference parameters. Beyond that size, another con­ vention is typically used for value parameters, namely, the address of the argument is passed to the called procedure and it is responsible for copying the param eter’s value into its own stack frame or into other storage. The size of the argument may also be passed, if needed.

If more than ia integer arguments or more than fa floating-point arguments are passed, the additional ones are typically passed on the stack just beyond the current stack pointer and, hence, can be accessed by the called routine with non-negative offsets from the new frame pointer.

Returning a value from a procedure is typically done in the same way that arguments are passed, except that, in most languages, there may not be more than one return value and some care is required to make procedures reentrant, i.e., executable by more than one thread of control at the same time. Reentrancy is achieved by returning values that are too large to fit into a register (or two registers) in storage provided by the caller, rather than storage provided by the callee. To make this work, the caller must provide, usually as an extra hidden argument, a pointer to an area of storage where it will receive such a value.7 The callee can then put it there, rather than having to provide storage for the value itself.

6. Weicker found that the average number of arguments passed to a procedure is about 2, and other studies have substantially agreed with that result, so the value of n is typically in the range 5 to 8. However, some system specifications allow many more; in particular, the u n i xSystem V ABI for

the Intel i860 makes available 12 integer and 8 floating-point registers for parameter passing. 7. If the caller also provides the size of the area as a hidden argument, the callee can check that the value it returns fits into the provided area.

122

Run-Tim e

Support

fp

f p - 4 f p - 8 fp - 1 2 f p - 5 6 fp - 6 0 fp - 1 1 2 fp - 1 1 6 fp - 1 2 8 sp+100 sp+60 sp +56 sp +4 sp Old f p Static link Return address Callee-saved g rs (12 words) Callee-saved f r s (14 words) Local variables (4 words) Caller-saved g rs (11 words) Caller-saved f r s (14 words)

FIG. 5 .9 Structure of the stack frame for the procedure-calling example with parameters passed in registers.

A typical register usage might be as follows: Registers U sage rO 0 r l — r5 Parameter passing r6 Frame pointer r7 Stack pointer r8— r l 9 Caller-saved r2 0 — r30 Callee-saved r31 Return address fO— f 4 Param eter passing f 5— f 18 Caller-saved f 19— f 31 Callee-saved

and might return a result in r l or fO, according to its type. We choose the stack structure shown in Figure 5.9, where we assum e that the local variables occupy four words and that g r and f r abbreviate “ general register” and “ floating-point register,”

Section 5.6 Procedure Prologues, Epilogues, Calls, and Returns 123

respectively. Were it necessary to pass some parameters on the stack because there are too many of them to all be passed in registers or if the called routine were to traverse the parameter list, space would be allocated between fp-128 and sp+104 to accommodate them. Exercise 5.4 requests that you produce code for a procedure call, prologue, parameter use, epilogue, and return for this model.

5.6.2

Parameters Passed on the Run-Time Stack

In the stack-based model, the arguments are pushed onto the run-time stack and used from there. In a machine with few registers and stack-manipulation instructions, such as the VAX and the Intel 386 architecture, we use those instructions to store the arguments in the stack. This would, for example, replace

r l <- 5 II put th ir d argument on sta c k sp <r- sp - 4

by

pushl 5 ; push th ir d argument onto sta c k

for the Intel 386 architecture family. Also, we would not need to adjust the stack pointer after putting the arguments on the stack—the pushes would do it for us. The return value could either be passed in a register or on the stack; we use the top of the floating-point register stack in our example.

For the Intel 386 and its successors, the architecture provides eight 32-bit integer registers. Six of the registers are named eax, ebx, ecx, edx, e s i, and ed i and are, for most instructions, general-purpose. The other two, ebp and esp, are the base (i.e., frame) pointer and stack pointer, respectively. The architecture also provides eight 80-bit floating-point registers known as s t (0) (or just s t) through s t ( 7 ) that function as a stack, with s t (0) as the current top of stack. In particular, a floating-point return value is placed in s t (0). The run-time stack layout is shown in Figure 5.10.

Exercise 5.5 requests that you produce code for a procedure call, prologue, parameter use, epilogue, and return for this model.

5.6.3

Parameter Passing in Registers with Register Windows

Register windows, as provided in sparc, simplify the process of passing arguments to and returning results from procedures. They also frequently result in a significant reduction in the number of loads and stores executed, since they make it possible to provide a much larger register file without increasing the number of bits required to specify register numbers (typical sparc implementations provide seven or eight windows, or a total of 128 or 144 integer registers) and take advantage of locality of procedure calls through time.

The use of register windows prescribes, in part, the division of the integer registers into caller- and callee-saved: the caller’s local registers are not accessible to the callee, and vice versa for the callee’s local registers; the caller’s out registers are the callee’s in registers and so are primarily dedicated (including the return address

124 Run-Time Support ebp+20 ebp+16 ebp+12 ebp+8 ebp+4 ebp ebp-4 e b p -16 esp+8 esp+4 e sp

FIG. 5.10 Structure of the stack frame for the procedure-calling example with parameters passed on the run-time stack for the Intel 386 architecture family.

3rd argument 2nd argument 1st argument Static link Return address Caller’ s ebp Local variables (4 words) Caller’ s e d i Caller’s e s i Caller’ s ebx

and the caller’s stack pointer, which becomes the callee’s frame pointer) or used for receiving parameters; the callee’s out registers can be used as temporaries and are used to pass arguments to routines the callee calls. Saving values in the windowed registers to memory and restoring them is done by the window spill and fill trap handlers, rather than by user code. Figure 5.11 shows the overlapping relationships among three routines’ register windows.

When a procedure call is executed, out registers oO through o5 conventionally contain integer arguments that are being passed to the current procedure (floating­ point arguments are passed in the floating-point registers). The stack pointer sp is conventionally in 0 6 and the frame pointer fp is in i6, so that a sav e executed in a called procedure causes the old sp to become the new fp . Additional arguments, if any, are passed on the run-time stack, as in the flat register model. When a procedure returns, it places its return value in one (or a pair) of its in registers if it is an integer value or in floating-point register fO if it is a floating-point value. Then it restores the caller’s stack pointer by issuing a r e s t o r e instruction to move to the previous register window.

Figure 5.12 shows a typical stack frame layout for sparc. The 16 words of storage at sp through sp+60 are for use by the register-window spill trap handler, which stores the contents of the current window’s ins and locals there. Because of this, sp must always point to a valid spill area; it is modified only by the indivisible sav e and r e s t o r e instructions. The former can be used to advance to a new window and to allocate a new stack frame at once and the latter reverses this process. The word at sp+64 is used to return structures and unions to the caller. It is set up by the caller with the address of the area to receive the value.

Section 5.6 Procedure Prologues, Epilogues, C alls, and Returns 125 Caller’s window r7 (g7) • g lo b a ls r l ( g l ) rO (gO) 0 FIG. 5.11 s p a r c r e g i s t e r w i n d o w s f o r t h r e e s u c c e s s i v e p r o c e d u r e s .

Th e first six w o rd s o f integer argu m en ts are p asse d in registers. Succeedin g a rg u ­ m ents are p asse d in the stack fram e. If it sh ou ld be necessary to traverse a variable- length argu m ent list, the entry-point code stores the first six argu m en ts beginning at sp + 6 8 . The area beginning at sp + 9 2 is used to hold ad d itio n al argu m en ts an d tem ­ p oraries and to store the glo b al and floatin g-point registers when they need to be saved. F or o u r exam p le, b in Figure 5 .1 2 is 3 2 , so the size o f the entire stack fram e is 148 bytes.

Exercise 5 .6 requ ests th at you p rodu ce co de for a procedu re call, p rologu e, p aram eter use, ep ilogue, an d return fo r this m odel.

1 2 6 Run-Tim e Su p port

fp

f p - 4 f p - 8 f p - 2 0 sp+92+b sp+92 sp +88 sp+68 sp +64 sp+60 sp Static link Local variables (four words) Temporaries, global and floating-point register save area, arguments 7 ,8 ,...

Storage for arguments 1 through 6

s/u return pointer Register window save area (16 words)

FIG. 5.12 Structure of the stack frame for the procedure-calling example with register windows (s/u means structure or union).

4 0

FIG. 5.13 A procedure descriptor containing a procedure’s address and its static link.

5 .6 .4

Procedure-Valued Variables

Calling a procedure that is the value of a variable requires special care in setting up its environment. If the target procedure is local, it must be passed a static link that is appropriate for it. This is best handled by making the value of the variable not be the address of the procedure’s code, but rather a pointer to a procedure descriptor that includes the code address and the static link. We show such a de­ scriptor in Figure 5.13. Given this descriptor design, the “ call” code, regardless of the param eter-passing model, must be modified to get the static link and address of the called routine from the descriptor. To call the procedure, we load the address of the procedure into a register, load the static link into the appropriate register, and perform a register-based call. Alternately, since this code sequence is short and in­ variant, we could make one copy of it, do all calls to procedure variables by calling it, and replace the register-based call that terminates it by a register-based branch, since the correct return address is the one used to call this code sequence.

Static link Procedure’s address

Section 5.7 Code Sharing and Position-Independent Code 127

Code Sharing and Position-Independent Code