0% found this document useful (0 votes)
5 views29 pages

Computer Architecture: Vnu - University Engineering Technology

The document provides an overview of the RV32I instruction set architecture, detailing its basic instructions and how high-level language constructs are translated into RV32I code. It covers key programming concepts such as if statements, loops, arrays, pointers, and procedure calls, along with their assembly implementations. Additionally, it discusses memory layout, stack management, and the compilation process, highlighting the differences between compiling and interpreting code.

Uploaded by

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

Computer Architecture: Vnu - University Engineering Technology

The document provides an overview of the RV32I instruction set architecture, detailing its basic instructions and how high-level language constructs are translated into RV32I code. It covers key programming concepts such as if statements, loops, arrays, pointers, and procedure calls, along with their assembly implementations. Additionally, it discusses memory layout, stack management, and the compilation process, highlighting the differences between compiling and interpreting code.

Uploaded by

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

COMPUTER

ARCHITECTURE

VNU - UNIVERSITY of ENGINEERING & TECHNOLOGY


LECTURE 3: RV32I ISA

CONTENTS

> Introduction
> If statements
> Loops
> Arrays and pointers
> Procedure calling
> Translation and startup
Introduction

> RV32I instruction set:


▪ has around 40 basic instructions, with no complex/specialized operations;
▪ only supports integer data types.

> How compilers convert complex HLL constructs to RV32I codes?


▪ If statements
▪ Loops
▪ Arrays and pointers
▪ Procedure calling
If Statement
C Statement Variables Mapping
if (i == j) f ➔ x12
g ➔ x13
f = g + h;
h ➔ x14
i ➔ x15
j ➔ x16

beq x15, x16, L1


j Exit bne x15, x16, Exit
L1: add x12, x13, x14 add x12, x13, x14
Exit: Exit:

> Two equivalent translations


▪ The one on the right is more efficient
▪ Common practice: invert the condition for shorter code.
Loops

▪ C while-loop: ▪ Rewritten with goto


Loop: if (j != k)
goto Exit;
while (j == k)
i = i+1;
i = i + 1;
goto Loop;
Exit:

Key concept
Any form of loop can be written in assembly with the help of
conditional branches and jumps.
Loops (cont.)

C Statement Variables Mapping


Loop: if (j != k) i ➔ x13
goto Exit; j ➔ x14
i = i+1; k ➔ x15
goto Loop;
Exit:

Loop: bne x14, x15, Exit # if (j!= k) Exit


addi x13, x13, 1
j Loop # repeat loop
Exit:
Array

> Large amounts of similar data (in memory)


▪ E.g. count the number of zeros in a 40-element array A, each element is a 32-bit
integer.
Zero count C code
Element size
result = 0;
0x12340010 array[4] i = 0;
0x1234800C array[3]
0x12348008 array[2] while ( i < 40 ) {
0x12348004 array[1] if ( A[i] == 0 )
0x12348000 array[0] result++;
i++;
}

> Questions for RV32I compilers:


▪ How to represent A[i] correctly,
▪ How to perform the right comparison.
Accessing an Array Element
> Two common ways: array indexing & pointer.

Array indexing Pointer


▪ Loop:
Multiply index by element size Initialization for result
variables, loop counter, and
Adding to array base address array pointers.
Load data
Label:
Compare and branch
Workflow:
Element size 1. Calculating address
2. Load data
3. Perform task
0x12340010 array[4]
0x1234800C array[3] Update loop counter and
𝑖 ∗ size 0x12348008 array[2] array pointers.
+ 0x12348004 array[1]
Base 0x12348000 array[0]
Compare and branch.
Assembly Implementation for Array Indexing

Address of A[] ➔ t0
Result ➔ t6 Comments
i ➔ t1
addi t6, zero, 0
addi t1, zero, 0
addi t2, zero, 40 # end point
loop: bge t1, t2, end
slli t3, t1, 2 # i * 4
add t4, t0, t3 # &A[i]
lw t5, 0(t4) # t5  A[i]
bne t5, zero, skip
addi t6, t6, 1 # result++
skip: addi t1, t1, 1 # i++
j loop
end:
Assembly Implementation for Pointer
Address of A[] ➔ t0
Result ➔ t6 Comments
&A[i] ➔ t1
addi t6, zero, 0
addi t1, t0, 0 # pointer to &A[current]
addi t2, t0, 160 # end point: &A[40]
loop: bge t1, t2, end # comparing address!
lw t3, 0(t1) # t3  A[i]
bne t3, zero, skip
addi t6, t6, 1 # result++
skip: addi t1, t1, 4 # move to next item
j loop
end:

> Use of pointer produces more efficient code!


Procedure Call
main() {
int a=1,b=2,c;
c=sum(a,b);
}
int sum(int x, int y) {
return x+y;}

> A procedure is a defined segment of code within a program,


executed via a procedure call to achieve a designated task.
▪ Promotes code reuse by allowing multiple calls from various program locations.
▪ Promotes abstraction by encapsulating implementation details and exposing only
a clean interface to programmers.

> How does RV32I provide ISA-level support for HLL calls
▪ to leaf procedures?
▪ to nested procedures?
Implementing Procedure Call
Main program

Address
1000 Instruction 1
1004 Instruction 2 2. Control passes Procedure
1. Procedure 1008 Instruction 3 to the procedure
call 1012 Procedure call Address
3. procedure
1016 Instruction 𝐼𝑛𝑒𝑥𝑡
execution 2000 Instruction 1
1020 … 4. Control returns to 2004 Instruction 2
1024 … main program

> Six fundamental steps in calling a procedure:


1. Put arguments in a place where procedure can access them.
2. Transfer control to procedure
3. Acquire (local) storage resources needed for the procedure
4. Perform desired task of the procedure
5. Put return value in a place where calling code can access & release local storage
6. Return control to the caller
RV32I Procedure Call Convention

> Calling convention: provides a uniform way to use the machine


resources to transfer control and data between procedures.

Step Description RISC-V conventions


1 Put arguments in registers a2–a7 (x12-x17)
2 Use jump instructions to transfer control to procedure j, jal, jalr
3 Use stack registers to acquire local storage for procedure $gp, $sp, $fp
Allocate registers while performing the desired task of
4 t0–t6, s1-s11
the procedure
5 Place return value in register a0, a1
Use register to save the return address and jump jr, jal, jalr,
6
instructions to transfer control to the caller. ra
Example: Implementing Procedure Call in RV32I
main() {
int a=1,b=2,c;
c=sum(a,b);
}
int sum(int x, int y) {
return x+y;}

address (in decimal)



1000 mv a0,s0 # x = a
1004 mv a1,s1 # y = b
1008 addi ra,zero,1016 # ra=1016
1012 j sum # jump to sum
1016 … # next instruction

2000 sum: add s2,a0,a1 # s2 = x + y
2004 jr ra # why do we use jr instead of j?
RVI32I Support for Passing Control

> Transfer control to procedure: jal, jalr


▪ In the example:
1008 addi ra,zero,1016 # ra=1016
1012 j sum # jump to sum
▪ Make the common case fast: single instruction to jump & save return address :
1008 jal sum # ra=1012,goto sum

> Return control to the caller: jal, jalr


▪ j, jr and ret are pseudoinstructions!

> Does jal also work for recursive procedure calls?


int myfn(int n) {
if(n > 0) {
return n * myfn(n - 1);}
else {return 1;}
}
RVI32I Support for Passing Control (cont.)
1
myfn:
if (test)
main: 2
jal myfn
jal myfn
after2:
after1:
… 3
add x1,x2,x3
jr ra

> Problem with recursion: overwrites contents of ra.


▪ Procedure cannot return control back to caller!
▪ Need a way to save and restore register contents.
RVI32I Support for Passing Data
> Need stack to save old values before calling procedure, restore
them when return, and delete.
▪ Push: placing data onto stack (spilling register)
▪ Pop: restoring register

> RISC-V stack conventions:


▪ sp (x2) is the stack pointer, push decrements sp, pop increments sp.

High address

sp Saved s1 sp Saved s1
Saved s0 Saved s0
sp

Low address
Before call During call After call
Implementing Stack in RVI32I
> Key steps:
▪ Spilling: calculate the amount of space for spilling registers & decrease sp by the
amount of space we need, then fill it with data via store instructions.
▪ Restoring: restore the registers to previously spilled values via load instructions,
then increase sp by the same amount to clear the stack.

> Example:
Leaf: addi sp,sp,-8 # adjust stack for 2 items
sw s1, 4(sp) # save s1 for use afterwards
sw s0, 0(sp) # save s0 for use afterwards

add s0,a0,a1 # f = g + h
add s1,a2,a3 # s1 = i + j
sub a0,s0,s1 # return value (g + h) – (i + j)

lw s0, 0(sp) # restore register s0 for caller


lw s1, 4(sp) # restore register s1 for caller
addi sp,sp,8 # adjust stack to delete 2 items
jr ra # jump back to calling routine
Nested Procedures (1/2)
❑ Recursive function calls (a function calls a function):
int sumSquare(int x, int y) {
return mult(x,x)+ y; }
➢ Something called sumSquare, now sumSquare is calling mult → the return
address in ra that sumSquare wants to jump back to will be overwritten by
the call to mult → need to save ra before the call to mult.
➢ In general, we may need to save some other info in addition to ra → need
to reduce expensive loads and stores from spilling and restoring registers.

❑ RISC-V function-calling convention divides registers into:


1. Preserved across function call
▪ Caller can rely on values being unchanged
▪ sp, gp, tp, “saved registers” s0-s11 (s0 is also fp)
2. Non preserved across function call
▪ Caller cannot rely on values being unchanged
▪ Argument/return registers a0-a7,ra, “temporary registers” t0-t6
Nested Procedures (2/2)
❑ sumSquare compilation:
int sumSquare(int x, int y) {
return mult(x,x)+ y; }

sumSquare:
addi sp,sp,-8 # space on stack “push” non-
sw ra, 4(sp) # save ret addr preserved
sw a1, 0(sp) # save y
registers
mv a1,a0 # mult(x,x)
jal mult # call mult
lw a1, 0(sp) # restore y
add a0,a0,a1 # mult()+y “pop” non-
lw ra, 4(sp) # get ret addr
preserved
addi sp,sp,8 # restore stack
jr ra registers
mult: ...
Memory Layout
❑ Text: program code (binary instruction codes)
❑ Data:
➢ Static data: variables declared once per program, e.g. global variables
➢ Dynamic data: variables declared dynamically, e.g. heap (malloc in C)
➢ Stack: stores saved registers & variables are local to function & discarded
when function exits (called automatic variables) that don’t fit in registers.
▪ E.g. local arrays

❑ RV32 memory convention:


➢ Stack starts in high memory & grows down.
➢ Text segment starts in the low end right above
the reserved segment.
➢ Static data segment is above the text
segment. The RISC-V’s global pointer (gp)
points to an address in this segment.
➢ Heap segment is located above static &
grows up to high addresses.
Allocating space on the stack
❑ Local data is allocated by callee
➢ The saved registers and local variables for each callee are bundled into a
segment on the stack called procedure frame or activation record.
➢ In RV32, the frame pointer (fp) points to the first word of the frame → easier
for programmers to reference variables via the stable fp
➢ fp is initialized using the address in sp on a call, and sp is restored using fp

Before call During call After call


From writing to running a program

sum.c
Many compilers produce
object modules directly

sum.s

sum.obj

Static linking
sum.exe

When most people say


“compile” they mean
the entire process:
compile + assemble + link
Translating vs interpreting
❑ Translating (compiling) versus interpretating
HLL
HLL
Interpreting
Compiling
Machine Virtual Machine
10010100
code 11001101 Machine
Running code
Program Outputs
Hardware Hardware
outputs

Compiler Interpreter

How it converts the input? Entire program at once Read one instruction at a time

When is it needed? Once, before the 1st run Every time the program is run

Decision made at Compile time Run time

What ➢
it slows
Somedown
languagesProgram
mix bothdevelopment
concepts, e.g. Java. Program execution
Compiling
❑ Assembler (or compiler) translates HLL into machine language
using a (modified) Harvard architecture
➢ Has distinct code and data caches, backed by a common address space.
➢ Most assembler instructions represent machine instructions one-to-one
➢ Pseudo-instructions: figments of the assembler’s imagination, e.g.
mv t0, t1 → add t0, zero, t1

❑ Produces object files that contain relevant information to build a


complete program
➢ Header: described contents of object module
➢ Text segment: translated instructions (machine code)
➢ Static data segment: the static data (in binary) of the source file
➢ Relocation information: identifies lines of code that need to be fixed up later
(depend on absolute location of loaded program)
➢ Symbol table: matches names of labels and static data to the addresses of
the memory words they occupy.
➢ Debug info
Linking object modules
❑ Combines several object (.o) files into a single executable
1. Merges segments (i.e. “stitches” standard library routines together)
2. Resolve labels (determine their addresses) through relocation information
and symbol table in each object module
3. Patch location-dependent and external references

❑ Executable file has the same format as an object file, except


that it contains no unresolved references.
➢ Some location dependencies might be fixed by relocating loader, but with
virtual memory, no need to do this
➢ Program can be loaded into absolute location in virtual memory space

❑ Linking libraries
➢ Static linking: all/most needed routines in the library are loaded as part of
the executable code → image bloat & recompilation of the whole library
when new versions arise.
➢ Dynamically linked libraries (DLLs): only link/load library procedure when it
is called → adds complexity to the compiler, but avoids image bloat &
automatically pick up new library versions.
Example: Linking of two RV64 obj files
A.obj

Linking

Assembling B.obj
Loading
❑ Load from image file on disk into memory for execution
1. Reads executable file’s header to determine size of text and data segments
2. Creates new address space for program large enough to hold text and data
segments, along with a stack segment
3. Copies instructions + data from executable file into the new address space
4. Copies arguments passed to the program onto the stack
5. Initialize registers (most registers cleared, but sp assigned address of 1st
free stack location)
6. Jump to startup routine
▪ Copies program’s arguments from stack to registers & sets the PC
▪ When main returns, start-up routine terminates program with the exit
system call

❑ In reality, loader is the operating system (OS).


➢ loading a program into memory is one of the OS kernel tasks
NEXT LECTURE

[Flipped class] Datapath design


> Pre-class
▪ Study pre-class materials on Canvas

> In class
▪ Reinforcement/enrichment discussion
▪ Quizzes

> Post class


▪ Homework
▪ Consultation (if needed)

You might also like