0% found this document useful (0 votes)
13 views12 pages

Labtask 5

This lab focuses on the compilation of recursive function calls from C language into RISC-V assembly and understanding assembler directives. It explains the behavior of recursive functions, using the factorial function as an example, and illustrates the stack operations during recursive calls. Additionally, it covers assembler directives for global variable allocation and initialization, and outlines laboratory tasks involving recursive matrix multiplication.

Uploaded by

omarashraf13456
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views12 pages

Labtask 5

This lab focuses on the compilation of recursive function calls from C language into RISC-V assembly and understanding assembler directives. It explains the behavior of recursive functions, using the factorial function as an example, and illustrates the stack operations during recursive calls. Additionally, it covers assembler directives for global variable allocation and initialization, and outlines laboratory tasks involving recursive matrix multiplication.

Uploaded by

omarashraf13456
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 12

Department of Computer Science

Institute of Business Administration, Karachi

Lab #5: Recursive Function Calls &


Assembler
Directives in RISC-V Assembly Codes

Computer Architecture & Assembly Language


February 19, 2024

Course Instructor ..........................................................Salman Zaffar Lab


Instructor ............................................................Mehwish Zafar
Week Performed ................................................................: Week 1
Room .............................................................................MTL4

1 Introduction
There are two purposes of this lab. These are

1. to introduce the compilation of recursive function calling (of C language) into RISC-V
assembly.

2. to understand the assembler directives in RISC-V assembly language

2 Recursive Functions without Assembler Directives


A recursive function is a nonleaf function that calls itself. Recursive functions behave as both
caller and callee and must save both preserved and nonpreserved registers. For example, the
factorial function can be written as a recursive function. Recall that factorial(n) = n ×
(n−1)×(n−2)×...×2×1. The factorial function can be written recursively as factorial(n) = n ×
factorial(n – 1), as shown in Code Example 6.28.

1
Figure 1: Recursive Function Call

The factorial of 1 is simply 1. To conveniently refer to program addresses, we show the


program starting at address 0x8500. According to the callee save rule, factorial is a nonleaf
function and must save ra. According to the caller save rule, factorial will need n after calling
itself, so it must save a0. Hence, it pushes both registers onto the stack at the start. It then
checks whether n ≤ 1. If so, it puts the return value of 1 in a0, restores the stack pointer, and
returns to the caller. It does not have to restore ra in this case, because it was never modified.
If n > 1, the function recursively calls factorial(n-1). It then restores the value of n and the
return address register (ra) from the stack, performs the multiplication, and returns this
result. Notice that the function cleverly restores n into t1 so as not to overwrite the returned
value. The multiply instruction (mul a0, t1, a0) multiplies n (t1) and the returned value (a0)
and puts the result in a0, the return register. For clarity, we save registers at the start of a
function call. An optimizing compiler might observe that there is no need to save a0 and ra
when n ≤ 1 and, thus, save registers on the stack only in the else portion of the function.
Figure 2 shows the stack when executing factorial(3). For illustration, we show sp initially
pointing to 0xFF0 (the upper address bits are 0), as shown in Figure 2(a). The function
creates a two-word stack frame to hold n (a0) and ra. On the first invocation, factorial saves
a0 (holding n = 3) at 0xFEC and ra at 0xFE8, as shown in Figure 2(b). The function then
changes n to 2 and recursively calls factorial(2), making ra hold 0x8528. On the second
invocation, it saves a0 (holding n = 2) at 0xFE4 and ra at 0xFE0. This time, we know that ra
contains 0x8528. The function then changes n to 1 and recursively calls factorial(1). On the
third invocation, it saves a0 (holding n = 1) at 0xFDC and ra at 0xFD8. This time, ra again
contains 0x8528. The third invocation of factorial returns the value 1 in a0 and deallocates
the stack frame before returning to the second invocation. The second invocation restores n
(into t1) to 2, restores ra to 0x8528 (it happened to already have this value), deallocates the
stack frame, and returns a0 = 2 × 1 = 2 to the first invocation. The first invocation restores n
(into t1) to 3, restores ra, the return address of the caller, deallocates the stack frame, and
returns a0 = 3 × 2 = 6.

2
Figure 2: Stack: (a) before, (b) during, and (c) after factorial function call with n = 3

Figure 2(c) shows the stack as the recursively called functions return. When factorial
returns to the caller, the stack pointer is in its original position (0xFF0), none of the contents
of the stack above the pointer have changed, and all of the preserved registers hold their
original values. a0 holds the return value, 6.

3
Figure 3: Complete run-through factorial code in Code Example 6.28 for n=4

4
3 Recursive Functions with Assembler Directives
Assembler directives guide the assembler in allocating and initializing global variables,
defining constants, and differentiating between code and data. Table 6.5 in the book lists
common
RISC-V assembler directives. Let us discuss the assembler directives given in Figure 3 now

1. The program begins by making the main label global (.globl main) so that the main
function can be called from outside this file, typically by the OS or bootloader.

2. Next, the program allocates the following global variables A (a 7−element array of
32−byte values), B (an element), and str1 (a null−terminated string). A, B, and str1 are
initialized, respectively, to 5, 42, −88, 2, −5033, 720, 314, 0x9, and “RISC−V” (i.e., 52, 49,
53, 43, 2D, 56, 00. These global variables are stored in data segment of the memory
which starts from 0x00000000.

3. .text specifies the beginning of user code which is stored in the text segment of main
memory which starts from 0x10000000. This is actually the instruction memory.

4. There is a assembler-specific pseudo-instruction la which loads the starting address of


global variable A. The second instruction loads the actual content of memory location A
into the register a0.

5
Figure 4: Code with Assembler Directives

6
Figure 5: Asembler directives to store global variables in data segment of the main memory

4 Laboratory Tasks
Implement the 2x2 multiplication with all the elements of Matrices A, B and resultant in the
data segment of the main memory. Use Recursive Function Call to implement Matrix
multiplication.

Figure 6: 2x2 Matrix Multiplication


1. Fill the parts of code below for Recursive Matrix Multiplication.

7
8
9
2. Provide a screenshot of Memory Address section that depicts the values of input and
resultant matrices. Highlight it in the screenshot.

10
3. Write the values of the mentioned registers in HEX form:
Register Value
ra 0x0000002C

sp 0x7FFFFFDC

gp 0x10000000

s0 0x10000000

s1 0x10000010

s2 0x10000020

Figure 7: RISC-V Base ISA

11
12
Figure 8: Complete Single-Cycle RISC-V Microarchitecture

You might also like