# More RISC, RISC-V functions
> Reference [Computer Architecture (Fall 2021) Week4](/s-RaYQGiRuCeUEA0cZtTAw)
## Pseudo instructions
- What do we need pseudo instructions?
1. consideration of different architecture supports
- good for *retargetting* to several hardwares
2. for human readablility
- Examples
- `mv` dst,reg1; # translates into addi dst,reg1,0
- Load Immediate (`li`)
- Loads 32-bit immediate into `dst`
```
li dst, imm # utilized addi, lui
```
- Load Address (`la`)
```
la dst, label # auipc dst, <offset to la
```
- No Operation (nop)
- For pipeline stall
- Full list of RISC-V supported pseudo instructions is on the [greensheet](https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf)
## C to RISC-V Practice
- Example: "Fast String Copy", which illustrates how to translate:
- dereference of pointers
- single `while` loop
- Exit condition is an equality test
C code:
```c=
/* Copy string from p to q */
char *p, *q;
while((*q++ = *p++) != ‘\0’) ;
```
Start with code skeleton:
```
# copy String p to q
# p→s0, q→s1 (char* pointers)
Loop: # t0 = *p
# *q = t0
# p = p + 1
# q = q + 1
# if *p==0, go to Exit
j Loop # go to Loop
Exit:
```
Finished code:
```
# copy String p to q
# p→s0, q→s1 (char* pointers)
Loop: lb t0,0(s0) # t0 = *p
sb t0,0(s1) # *q = $t0
addi s0,s0,1 # p = p + 1
addi s1,s1,1 # q = q + 1
beq t0,x0,Exit # if *p==0, go to Exit
j Loop # go to Loop
Exit: # N chars in p => N*6 instructions
```
:information_source: What if`lb` sign extends?
--> Not a problem
Because the data type of the pointers are pointed here which is `char ` , only one byte. The `sb` instruction only writes a single byte, so the sign extension is ignored.
We can shorten the code by using `bne` on loop condition
```
# copy String p to q
# p→s0, q→s1 (char* pointers)
Loop: lb t0,0(s0) # t0 = *p
sb t0,0(s1) # *q = $t0
addi s0,s0,1 # p = p + 1
addi s1,s1,1 # q = q + 1
bnq t0,x0,Loop # change to if *p!=0, go to Loop
Exit: # N chars in p => N*6 instructions
## RISC-V functions
### 1. Put parameters in a place where function can access them
- a0-a7 for function arguments, a0-a1 for return values
- sp: "stack pointer"
- Holds the current memory address of the "bottom" of the stack
### 2. Transfer control to function
- by `jump` instructions
### 3. Acquire (local) storage resources needed for function
:notes: ~~unlike Stack pointer(sp) in *Intel* architecture is hold the address of the **top** of the stack, sp in RISC-V arch~~, holds the address of the ==bottom== of the stack.
#### difference with Intel arch.?
- on Intel side
> Reference other course in UCB[^first], which is related to 61C but introducing x86
- on RISC-V side
> Further reference Cornel Univ. CS 3240[^second]
the calling convention is nearly the same; the differences are name of registers and instructions, ==to be worth mentioning, RISC systems often omit the Frame pointer==:
> **RISC systems often omit this register** because it is not necessary with the RISC stack design. For example, in RISC-V, `FP` is sometimes renamed `s0` and used as a general-purpose register[^first].
#### The usage of frame pointer:
- On **7. Execute the function.** Since frame pointer is always pointing at the top of the stack frame, it can be used as a point of reference to find other variables on the stack (e.g. in x86, the arguments will be located starting at the address stored in ebp, plus 8)

- On **8. Move esp up.** Once the function is ready to return, we increment esp to point to the top of the stack frame (ebp).
```
foo:
...
# Step 8. Move esp up to ebp
mov %ebp, %esp # AT&T: src, dst
...
```
- Why FP *not* necessary with the RISC stack design?
- Recall: Registers way faster than memory, so use them whenever possible
- a0–a7: eight argument registers to pass parameters
- SP could be restored by **adding framesize directly**

### 4. Perform desired task of the function
#### Which registers can we use?
- Problem: how does the function know which registers are safe to use?
- it's defined in ABI, is a matter of caller and callee relationship
- the high-level program languages can be ran on the the same hardwares if they follow the same ABI
- Use `Stack Frames` to isolate register use of function calls
- Calling Convention on Greencard

#### Stack Before, During, After Call

#### Examples
- Using Saved Registers on **CalleE**

- Using Volatile Registers on **CalleR**

#### Choosing Your Registers
- Minimize register footprint
- Optimize to reduce number of registers you need to save by choosing which registers to use in a function
- Only save when you absolutely have to
- Function does NOT call another function
- Use only **t0-t6** and there is nothing to save!
- Function calls other function(s)
- **Values you need throughout go in s0-s11**, others go in t0-t6
- At each function call, check number **arguments and return values** for *whether you or not you need to save*
### 5. Put result value in a place where calling code can access it and restore any registers you used; release local storage
### 6. Return control to point of origin, since a function can be called from several points in a program
- like step2, by `jump` instructions
[^first]: [x86 function calls in UCB CS161 Computer Security](https://textbook.cs161.org/memory-safety/x86.html#28-x86-function-calls)
[^second]: [RISC-V Calling Convention Cornel Univ. CS 3410: Computer System Organization and Programming, Spring 2019](https://www.cs.cornell.edu/courses/cs3410/2019sp/schedule/slides/10-calling-notes.pdf)