You are on page 1of 21

Compiling Code, and

Implementing Procedures

February 23, 2021 MIT 6.004 Spring 2021 L03-1


RISC-V Instruction Types
§ Computational Instructions executed by ALU
§ Register-Register: op dest, src1, src2
§ Register-Immediate: op dest, src1, const
§ Control flow instructions
§ Conditional: br_comp src1, src2, label
§ Unconditional: jal label and jalr register
§ Loads and Stores
§ lw dest, offset(base)
§ sw src, offset(base)
§ Base is a register, offset is a small constant
§ Pseudoinstructions
§ Shorthand for other instructions

February 23, 2021 MIT 6.004 Spring 2021 L03-2


Program to sum array elements
sum = a[0] + a[1] + a[2] + ... + a[n-1]
(Assume 100 (address of base) already loaded into x10)

Main Memory
lw ß
x1 x1,load(base)
0x0(x10)
lw ß
x2 x2,load(n)
0x4(x10) Register File 31 0

addßx3,
x3 0 x0, x0 0
x1 Addr of a[i] 4 a[0]
loop: n
x2 8 a[1]
lw ß
x4 x4,load(Mem[x1])
0x0(x1) x3 sum
add x3, x3, x4 a[n-1]
addi
addi x1,
x1, x1,
x1, 44 x10 100
100 base
addi
addi x2,
x2, x2,
x2, -1
-1 n
104
bnez
bnez x2,
x2, loop
loop 108 sum

store(sum) ß x3
sw x3, 0x8(x10)
February 23, 2021 MIT 6.004 Spring 2021 L03-3
Registers vs Memory
add x1, x2, x3
x1 = 0x1C
mv x4, x3 Main Memory
x4 = 0x14 Address
Register File 31 0
lw x5, 0(x3)
0x0 0x35
x5 = 0x23 x1 0x1C 0x4 0x3
x2 0x8 0x8 0x9
lw x6, 8(x3)
x3 0x14 0xC 0x1
x6 = 0x16 0x14 0x10 0x22
x4
sw x6, 0xC(x3) 0x23 0x14 0x23
x5
0x16 0x18 0x21
value of x6 (0x16) x6
0x1C 0x16
is written to M[0x14+0xC] 0x20 0x18
0x16

February 23, 2021 MIT 6.004 Spring 2021 L03-4


Compiling Simple Expressions
§ Assign variables to registers
§ Translate operators into computational instructions
§ Use register-immediate instructions to handle
operations with small constants
§ Use the li pseudoinstruction for large constants
Example C code RISC-V Assembly
int x, y, z; // x: x10, y: x11, z: x12
… // x13, x14 used for temporaries
y = (x + 3) | (y + 123456); addi x13, x10, 3
z = (x * 4) ^ y; li x14, 123456
add x14, x11, x14
or x11, x13, x14
slli x13, x10, 2
xor x12, x13, x11

February 23, 2021 MIT 6.004 Spring 2021 L03-5


Compiling Conditionals
§ if statements can be compiled using branches:
C code RISC-V Assembly
if (expr) { (compile expr into xN)
if-body beqz xN, endif
} (compile if-body)
endif:

§ Example: Compile the following C code


We can sometimes
int x, y; // x: x10, y: x11 combine expr
… slt x12, x10, x11 and the branch
if (x < y) { beqz x12, endif bge x10, x11, endif
y = y – x; sub x11, x11, x10 sub x11, x11, x10
} endif: endif:

February 23, 2021 MIT 6.004 Spring 2021 L03-6


Compiling Conditionals
§ if-else statements are similar:
C code RISC-V Assembly
if (expr) { (compile expr into xN)
if-body beqz xN, else
} else { (compile if-body)
else-body j endif
} else:
(compile else-body)
endif:

February 23, 2021 MIT 6.004 Spring 2021 L03-7


Compiling Loops
§ Loops can be compiled using backward branches:
C code RISC-V Assembly
while (expr) { while:
while-body (compile expr into xN)
} beqz xN, endwhile
(compile while-body)
j while
endwhile: // Version with one branch
// or jump per iteration
j compare
§ Can you write a version loop:
that executes fewer (compile while-body)
instructions? compare:
(compile expr into xN)
bnez xN, loop
February 23, 2021 MIT 6.004 Spring 2021 L03-8
Putting it all together
C code RISC-V Assembly
while (x != y) { // x: x10, y: x11
if (x > y) { j compare
x = x – y; loop:
} else { (compile while-body)
y = y – x; compare:
}
bne x10, x11, loop
}

February 23, 2021 MIT 6.004 Spring 2021 L03-9


Putting it all together
C code RISC-V Assembly
while (x != y) { // x: x10, y: x11
if (x > y) { j compare
x = x – y; loop:
} else { ble x10, x11, else
y = y – x; sub x10, x10, x11
}
j endif
}
else:
sub x11, x11, x10
endif:
compare:
bne x10, x11, loop

February 23, 2021 MIT 6.004 Spring 2021 L03-10


Procedures
C code RISC-V Assembly
int gcd(int a, int b) // x: x10, y: x11
{ j compare
int x = a; loop:
int y = b; ble x10, x11 else
while (x != y) { sub x10, x10, x11
if (x > y) {
j endif
x = x – y;
} else { else:
y = y – x; sub x11, x11, x10
} endif:
} compare:
return x; bne x10, x11, loop
}
February 23, 2021 MIT 6.004 Spring 2021 L03-11
Procedures
§ Procedure (a.k.a. function or int gcd(int a, int b) {
int x = a;
subroutine): Reusable code int y = b;
fragment that performs a while (x != y) {
specific task if (x > y) {
§ Single named entry point x = x – y;
§ Zero or more formal arguments } else {
§ Local storage y = y – x;
§ Returns to the caller when }
finished }
return x;
}
§ Using procedures enables bool coprimes(int a, int b) {
abstraction and reuse return gcd(a, b) == 1;
§ Compose large programs from }
collections of simple procedures
coprimes(5, 10); // false
coprimes(9, 10); // true

February 23, 2021 MIT 6.004 Spring 2021 L03-12


Arguments and return values
§ A caller needs to pass arguments to the called
procedure, as well as get results back from the
called procedure
§ Both are done through registers

§ A calling convention specifies rules for register usage


across procedures
§ RISC-V calling convention gives symbolic names to registers
x0-x31 to denote their role:

Symbolic name Registers Description


a0 to a7 x10 to x17 Function arguments
a0 and a1 x10 and x11 Function return values

February 23, 2021 MIT 6.004 Spring 2021 L03-13


Calling procedures

§ A procedure can be called from


many different places …
§ The caller can get to the called [0x100] j sum
procedure code simply by …
executing an unconditional jump [0x678] j sum
instruction …
§ However, to return to the correct
place in the calling procedure, the
called procedure has to know which sum:
of the possible return addresses it …
should use j ?
0x104?
0x67C?

Return address must be saved and passed to the called procedure!

February 23, 2021 MIT 6.004 Spring 2021 L03-14


Procedure Linking
§ How to transfer control to callee and back to caller?
proc_call: jal ra, label
1. Stores address of proc_call + 4 in register ra (return
address register)
2. Jumps to instruction at address label where label is the
name of the procedure
3. After executing procedure, jr ra to return to caller
and continue execution
… 4
ra = 0x10 sum:
[0x100] jal ra, sum

… C
0x67 jr ra
[0x678] jal ra, sum =
ra

1st time: jump to 0x104
2nd time: jump to 0x67C

February 23, 2021 MIT 6.004 Spring 2021 L03-15


Managing a procedure’s register
space
§ A caller uses the same register set as the called
procedure
§ A caller should not rely on how the called procedure
manages its register space
§ Ideally, procedure implementation should be able to use
all registers
§ Either the caller or the callee saves the caller’s
registers in memory and restores them when the
procedure call has completed execution

February 23, 2021 MIT 6.004 Spring 2021 L03-16


Calling Convention
§ RISC-V calling convention gives symbolic names to registers
x0-x31 to denote their role:

Symbolic name Registers Description Saver


a0 to a7 x10 to x17 Function arguments Caller
a0 and a1 x10 and x11 Function return values Caller
ra x1 Return address Caller
t0 to t6 x5-7, x28-31 Temporaries Caller
s0 to s11 x8-9, x18-27 Saved registers Callee
sp x2 Stack pointer Callee
gp x3 Global pointer ---
tp x4 Thread pointer ---
zero x0 Hardwired zero ---

February 23, 2021 MIT 6.004 Spring 2021 L03-17


Procedure Storage Needs
§ Basic requirements for procedure calls:
§ Input arguments
§ Return address
§ Results
Use registers for procedures arguments, return
address, and results.
§ Local storage:
§ Variables that compiler can’t fit in registers
§ Space to save register values according to the calling
convention (e.g., s registers that procedure will overwrite)
Each procedure call has its own instance of local
storage known as the procedure’s activation record.

February 23, 2021 MIT 6.004 Spring 2021 L03-18


Activation record and procedure
calls
§ An Activation record holds all storage needs of
procedure that do not fit in registers
§ A new activation record is allocated in memory when a
procedure is called
§ An activation record is deallocated at the time of the
procedure exit
§ Activation records are allocated in a stack manner
(Last-In-First-Out)
proc C
proc B proc B proc B
proc A proc A proc A proc A proc A

§ The current procedure’s activation record (a.k.a.


stack frame) is always at the top of the stack
February 23, 2021 MIT 6.004 Spring 2021 L03-19
Caller-Saved vs Callee-Saved
Registers
§ A caller-saved register is not preserved across function
calls (callee can overwrite it)
§ If caller wants to preserve its value, it must save it on the
stack before transferring control to the callee
§ argument registers (aN), return address (ra), and
temporary registers (tN)

§ A callee-saved register is preserved across function calls


§ If callee wants to use it, it must save its value on stack
and restore it before returning control to the caller
§ Saved registers (sN), stack pointer (sp)

February 23, 2021 MIT 6.004 Spring 2021 L03-20


Thank you!

Next lecture:
Procedures, Stacks, and MMIO

February 23, 2021 MIT 6.004 Spring 2021 L03-21

You might also like