7. Functions

1. Introduction

The assembly language does not have functions that are equivalent to the functions from high level languages. The functions in the assembly language are implemented using the branching commands. Their implementation is very similar to the implementation of loops. However, there are some differences. The functions require a new branching instruction called branch and link. The functions also require a more advanced usage of the stack and of the link register.

We will first start with one example that will illustrate why our current branching instructions are not sufficient to implement the functions effectively.

Problem 1. The number \(a\), \(b\), and \(c\) are placed in registers X3, X4, and X5. Let \(f(x)\) be the function defined as \(f(x)=x^3-7x^2-25x-12\). Create a code that puts the number \(f(a)\) in the register X6, the number \(f(b)\) in the register X7, and the number \(f(c)\) in the register X8.

1.1. A code that implements the function

Since we are going to execute the function three times, it would be a good idea to write the code only once. We will use the following trick: \[f(x)=x^3-7x^2-25x-12=((x-7)x-25)x-12.\] We will evaluate \(f(x)\) for the number \(x\) stored in the register X0 and place the result in the register X1.

functionF:
     SUB     X1,     X0,     7      /* X1 = x-7           */
     MUL     X1,     X1,     X0     /* X1=(x-7)x          */
     SUB     X1,     X1,     25     /* X1=(x-7)x-25       */
     MUL     X1,     X1,     X0     /* X1=((x-7)x-25)x    */
     SUB     X1,     X1,     12     /* X1=((x-7)x-25)x-12 */

1.2. Where to go after the function?

Now we are facing a serious problem. Once the function finishes its execution, we need to use a jump instruction to return to the place from where the call was made. The jump instructions that we used before looked like this:

B     placeInCodeThatWeWantToGo
This time, placeInCodeThatWeWantToGo is not the same in each of the function calls.

The solution is the link register.

2. Link register and the return instructions

One of the 32 fundamental registers is called the link register. Its name is LR, or X30. It is ignored most of the time. The only instructions that are using the link register are the instruction RET and the instruction BL.

3. Return instruction

The return instruction RET is placed at the end of the functions. The instruction causes the code to jump to the place whose address in inside LR.

Therefore, the code that implements the function should be this:

functionF:
     SUB     X1,     X0,     7      /* X1 = x-7           */
     MUL     X1,     X1,     X0     /* X1=(x-7)x          */
     SUB     X1,     X1,     25     /* X1=(x-7)x-25       */
     MUL     X1,     X1,     X0     /* X1=((x-7)x-25)x    */
     SUB     X1,     X1,     12     /* X1=((x-7)x-25)x-12 */
     RET

4. Function call. Branch and link instruction

Before the function call, we need to update the register LR. The update is achieved with the instruction branch and link. The function call consists of only one instruction

BL     functionF
The instruction first updates the register LR to contain the address of the place in the code that is exactly after the BL instruction.

We are almost ready to implement the solution to problem 1. Our current draft of the code is given below. It contains a bug that we will describe soon.

/* FIRST DRAFT:   THERE IS A BUG */
     B     codeAfterTheFunction
functionF:
     SUB     X1,     X0,     7      /* X1 = x-7           */
     MUL     X1,     X1,     X0     /* X1=(x-7)x          */
     SUB     X1,     X1,     25     /* X1=(x-7)x-25       */
     MUL     X1,     X1,     X0     /* X1=((x-7)x-25)x    */
     SUB     X1,     X1,     12     /* X1=((x-7)x-25)x-12 */
     RET
codeAfterTheFunction:
     MOV     X0,     X3
     BL     functionF
     MOV     X6,     X1
     MOV     X0,     X4
     BL     functionF
     MOV     X7,     X1
     MOV     X0,     X5
     BL     functionF
     MOV     X8,     X1

Bug. If we try to execute the program from above in the autograder, we are going to see a crash of the terminal. The crash is due to the modifications that we did to the link register LR. Our program is a function itself. This function was called by the terminal. The call was made using a BL instruction. This BL instruction placed a very important content in the register LR. The content of LR was the address of the next instruction in the terminal. We modified the content of LR (we did it three times: each of the BL instructions modified the content of LR). The disaster happened once our program reached the end. The end of the program consists of the instruction RET that is added by the autograder. Our program must exit successfully and the terminal program must continue its life. This peaceful transition could not occur, because we modified LR.

The correct behavior is to save the content of LR at the very beginning of our code. Our first instruction will be MOV X17, LR. Our last instruction (the instruction before the auto-grader adds RET) should be MOV LR, X17. Therefore, the improved code is

     MOV     X17,    LR
     B     codeAfterTheFunction
functionF:
     SUB     X1,     X0,     7      /* X1 = x-7           */
     MUL     X1,     X1,     X0     /* X1=(x-7)x          */
     SUB     X1,     X1,     25     /* X1=(x-7)x-25       */
     MUL     X1,     X1,     X0     /* X1=((x-7)x-25)x    */
     SUB     X1,     X1,     12     /* X1=((x-7)x-25)x-12 */
     RET
codeAfterTheFunction:
     MOV     X0,     X3
     BL     functionF
     MOV     X6,     X1
     MOV     X0,     X4
     BL     functionF
     MOV     X7,     X1
     MOV     X0,     X5
     BL     functionF
     MOV     X8,     X1
     MOV     LR,     X17
Problem 2.

The numbers \(a\) and \(b\) are placed in registers X3 and X4. Let \(g(x)\) be the function defined as \[g(x)=\left\{\begin{array}{ll}0, & \mbox{if } x< 20,\\ 1, &\mbox{if }x \in[20,50),\\ 2, &\mbox{if } x\geq 50\end{array}\right.\] Create a code that puts the number \(g(a)\) in the register X5 and the number \(g(b)\) in the register X6.

Code editor

5. Stack pointer

5.1. Introduction

We used the register X17 to save the content of LR. This is not the safest practice. If our function called another function, then the content of X17 could be overwritten. Therefore, X17 is not a safe place for the link register.

The link register is not the only data that may need to be saved. The functions will be calling other functions. All of these functions must use the same registers. Therefore, if we call a function, then this called function may overwrite some of our registers.

The stack is the place where we can save the contents of registers before calling the functions. After the call is finished, then we can load the contents from the stack into our registers.

The register SP is called stack pointer. It contains the address in the main memory that is reserved for our program. If we want to push the content of the register Xi to the stack, we need to give the following two instructions

ADD     SP,     SP,     -8
STR     Xi,     [SP]
Then the content of Xi is saved. We can use the functions or instructions that overwrite Xi. Once we wish to take the content back, we give the instructions
LDR     Xi,     [SP]
ADD     SP,     SP,     8

5.2 Conventions

The following conventions are introduced to prevent surprises with functions overwriting the registers.

  • Registers X0-X7 are function parameters. These registers serve as communication between the caller (the code that calls the function) and callee (the function that is being called). The function parameters should be stored in the registers X0, X1, ..., X7. If the function has only three parameters, they should be stored in X0, X1, and X2. If the function has more than 8 parameters, then the extra parameters should be put on stack.
  • Registers X0-X18 are caller saved. The function can use and overwrite these registers. If the caller needs the content in these registers, then the caller should save them before making the function call.
  • Registers X19-X30 are callee saved. If the function wants to modify these registers, then the function should put the old content on the stack, and restore the content in X19-X30 before the command RET.
Problem 3.

Create three functions Fun1, Fun2, and Fun3. Each function receives its arguments in registers X1 and X2. Each function should store its return value in X0. The function Fun1 should evaluate X1+5*X2. The second function should evaluate 3*X1-2*X2. The third function should evaluate the product X1 * X2.

Assume that the user input consists of four integers \(a\), \(b\), \(c\), and \(d\) and that these integers are stored in the registers X19, X20, X21, and X22, respectively. Write the code that uses the functions Fun1, Fun2, and Fun3 to update the registers X23, X24, and X25 in the following way:

X23 \(=a+5b;\) X24 \(=3c-2d;\) and X25\(=a\cdot c;\).

Code editor

Problem 4.

The numbers \(a\) and \(b\) are placed in registers X22 and X23. Let \(g(x)\) be the function defined as \[g(x)=x^3-10x^2-100x-250.\] Let \(h\) be the function defined as \[h(x)=\left\{\begin{array}{ll}x, & \mbox{if } x < 1000000,\\ x-1000000, &\mbox{if } x\geq 1000000\end{array}\right.\] Create a code that puts the number \(h(g(a))\) in the register X24 and the number \(h(g(b))\) in the register X25.

Code editor