Compiling Code and Implementing Procedures
1. Compiling Code
1.1 Compiling Simple Expressions
- Assign variables to registers
- Use the li pseudoinstructions for large contants
- Translate operators into computational instructions(Use register-immediate instructions to handle operations with small contants)
//Example C Code
int x,y,z;
...
y = (x + 3) | (y + 123456);
z = (x * 4) ^ y;//^ denotes XOR
//RISC-V Assembly
//x:x10, y:x11, z:x12
//x13,x14 use for temporaries
addi x13, x10, 3
li x14, 123456
add x14, x11, x14
or x11, x13, x14
slli x13, x10, 2
xor x12, x13, x11
1.2 Compiling Conditionals
Use control instructions-SB type:Conditional Branches
BEQ – branch if equal (==)
BNE – branch if not equal (!=)
BLT – branch if less than (<)
BGE – branch if greater than or equal (>=)
BLTU – branch if less than using unsigned numbers (< unsigned)
BGEU – branch if greater than or equal using unsigned numbers (>= unsigned)
Example:
1.2.1 If Statements
int x,y;
...//赋值语句
if(x < y){
y = y - x;
}
//implementation by beqz
//x: x10, y: x11
slt x12, x10, x11
beqz x12, endif
sub x11, x11, x10
endif:
//implementation by bge
bge x10, x11, endif
sub x11, x11, x10
endif:
1.2.2 If-else Statements
int x,y;
...//赋值语句
if(x < y){
y = y - x;
}else{
y = x - y;
}
//x: x10, y:x11
slt x12, x10, x11
beqz x12,else
sub x11, x11, x10
j endif
else:
sub x11, x10, x11
endif:
1.3 Compiling Loops
Loops cna be compiled using backward branched:
//C code
while(expr){
while-body
}
//RISC-V Assemlby
while:
(compile expr into xN)
beqz xN, endwhile //condation jumping out if loops
(compile while body)
j while //key of implemention of loops
endwhile:
//concise version,we don't need to jump to endwhile in precess but sequentially execute loop and compare
//what we need to append is to change our begining location to label 'compare' by psuedoinstruction j
j compare
loop:
(compile while-body)
compare:
(compare expr into xN)
bnez xN, loop
1.4 Put It All Together
while(x != y){
if(x > y){
x = x - y;
}else{
y = y - x;
}
}
//RISC-V Assembly
//x: x10, y: x11
j compare
loop:
bge x11, x10, else
sub x10, x10, x11
j endif;
else:
sub x11, x11, x10
endif:
compare:
bne x10, x11, loop
2. Implementing Procedures
2.1 Procedures
- Procedure(a.k.a. function or subroutine):Reusable code fragement that performs a specific task
- Single named entry point
- Zero or more foraml arguments
- Local storage
- Returns to the caller when finished
- Using procedures enables abstraction and reuse
- Copose large programs from collections of simple precedures
//greatest commom divisor
int gcd(int a,int b){
int x = a;
int y = b;
while(x != y){
if(x > y)x -= y;
else y -= x;
}
return x;
}
bool coprimes(int a,int b){
return gcd(a,b) == 1;
}
coprimes(5, 10); //false
coprimes(9,10); //ture
//compiling loops
// x: x10, y: x11
j compare
loop:
ble x10, x11 else
sub x10, x10, x11
j endif
else:
sub x11, x11, x10
endif:
compare: bne x10, x11, loop
2.2 Arguments and Return Values
- A caller need 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 |
2.3 Calling Procedures
A procedure can be called from many different places
- The caller can get to the called procedure code simply by executing an unconditional jump instruction
- However, to return to the correct place in the calling procedure,the called procedure has to know which of the possible return address it should use
- Return address must be saved and passed to the called procedure!
2.4 Procedure Linking
How to transfer control to callee and to caller?
proc_call: jal ra,label
- Stores adress of proc_call + 4 in register ra(return adress register)
- Jumps to instruction at adress label where label is the name of the procedure
- After executing procedure ,jr ra to return the caller and continue execution
2.5 Managing a Procedure’s Register Space
- A caller uses the same register set as the called procedure
- Either the caller or the callee save the caller’s registers in memory and restores them when the procedure call has completed execution
2.6 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 x1 | Function arguments | Caller |
a0 and a1 | x10 and x11 | Function return values | Caller |
ra | x1 | Reruen 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 | s3 | Global pointer | - - - |
tp | x4 | Thread Pointer | - - - |
zero | x0 | Hardwired zero | - - - |
2.7 Procedure Storage Needs
-
Basic requirements for procedure calls
- input arguments
- Retuen address
- Results
- Use registers for 1\2\3
-
Local Storage:
- Variables that compiler can’t fit in registers
- Space to save register values according to the calling convention