• No results found

11 Procedures and the Stack

In document Guide to RISC Processors (Page 188-191)

We have given an overview of the MIPS assembly language in the last chapter. This chapter discusses how procedures are written in the MIPS assembly language. Procedure is an important programming construct that facilitates modular programming. Procedures can be divided into leaf and nonleaf procedures. A leaf procedure does not call other procedures. On the other hand, a nonleaf procedure calls other procedures. In the MIPS architecture, we can implement simple leaf procedures without using the stack. Nonleaf procedures always need to use the stack, at least to store the return address of the calling procedure.

In this chapter, we discuss how both leaf and nonleaf procedures are implemented in the assembly language. Our initial discussion deals with simple leaf procedures that can be implemented using the registers. Then we look at how the stack is implemented in the MIPS architecture. The details about the stack implementation are useful in understanding how we can handle nonleaf procedures in the assembly language.

Parameter passing is an important aspect of procedure invocation. As you know, we can use either the call-by-value or call-by-reference mechanism for parameter passing. Here we give details on how we can implement these two types of parameter-passing mechanisms in the assembly language programs.

Introduction

A procedure is a logically self-contained unit of code designed to perform a specific task. Procedures are sometimes calledsubprogramsand play an important role in modular pro- gram development. In high-level languages, there are two types of subprograms: proce- duresandfunctions. Each function receives a list of arguments and performs a computa- tion based on the arguments passed onto it and returns a single value.

Procedures also receive a list of arguments just as the functions do. However, pro- cedures, after performing their computation, may return zero or more results back to the calling procedure. In C language, both these subprogram types are combined into a single

184 Guide to RISC Processors

function construct. For example, in the following C function

int sum (int x, int y, int z) {

return (x+y+z); }

the parametersx,y, andzare called formal parameters and the function body is defined based on these parameters. When this function is called (or invoked) by a statement like

total = sum(number1,number2,number3);

the actual parameters—number1,number2, andnumber3—are used in the computa- tion of the functionsum.

There are two types of parameter-passing mechanisms: call-by-value and call-by- reference. In the call-by-value mechanism, the called function (sumin our example) is provided only the values of the parameters for its use. Thus, in this mechanism, the values of these actual parameters are not changed in the called function; these values can only be used as in a mathematical function. In our example, thesumfunction is invoked by using the call-by-value mechanism, as we simply pass the values ofnumber1,number2, and number3to the called function sum. Thus, ifsummodifies x,y, orz, these changes are not reflected in the calling function.

To illustrate this point, let’s assume that we want to exchange two values aand b passed on to the functionswap. Suppose we define ourswapfunction as follows.

/* Incorrect swap procedure */ int swap (int a, int b)

{ int temp; temp = a; a = b; b = temp; return; } If we callswapas swap(value1, value2);

it will not exchange the two valuesvalue1andvalue2because we are using the call- by-value mechanism. We can remedy this problem by using the call-by-reference mecha- nism.

In the call-by-reference mechanism, the called function actually receives the addresses (i.e., pointers) of the parameters from the calling function. The function can change the contents of these parameters—and these changes are seen by the calling function—by di- rectly manipulating the actual parameter storage space. We can rewrite theswapfunction as

/* Correct swap procedure */ void swap (int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; }

This procedure works fine as it passes the addresses of the two parameters from the calling function. Such a function can be invoked as

swap (&data1, &data2);

Often both types of parameter-passing mechanisms are used in the same function. As an example, consider finding the roots of the quadratic equation

ax2+bx+c= 0. The two roots are defined as

root1 = −b+ b24ac 2a , root2 = −b− b24ac 2a .

The roots are real ifb24ac, and imaginary otherwise.

Suppose that we want to write a function that receives a, b, and c and returns the values of the two roots (if real) and indicates whether the roots are real or imaginary.

int roots (double a, double b, double c, double *root1, double *root2) {

int root_type = 1;

if (4*a*c <= b*b){ /* roots are real */ *root1 = (b + sqrt(b*b 4*a*c))/(2*a); *root2 = (b sqrt(b*b 4*a*c))/(2*a); }

else /* roots are imaginary */

root_type = 0; return (root_type); }

The function receives parametersa,b, andcvia the call-by-value mechanism, androot1 and root2parameters are passed using the call-by-reference mechanism. A typical in- vocation ofrootsis

186 Guide to RISC Processors

root_type = roots (a, b, c, &root1, &root2);

In summary, procedures receive a list of parameters, which may be passed either by the call-by-value or by the call-by-reference mechanism. If more than one result is to be returned by a called procedure, the call-by-reference parameter-passing mechanism should be used.

In document Guide to RISC Processors (Page 188-191)