| 1. | ARM 64-bit Assembly |
| 2. | Simplest programs |
| 3. | Integers |
| 4. | Main memory |
| 5. | Arrays |
| 6. | Branching and loops |
| 7. | Functions |
| 8. | Floating point |
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.
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 placeInCodeThatWeWantToGoThis 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 functionFThe 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
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.
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 inX0,X1, andX2. 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-X30before the commandRET.
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;\).
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.