# Assignment2: RISC-V Toolchain
## Description
I choose the **Pascal’s triangle** from [陳品崴](https://hackmd.io/@pTOtphjnSgqKNp2mfa6tCw/SkwIDjUZs) based on [LeetCode 119](https://leetcode.com/problems/pascals-triangle-ii/).
- The description is as follow:
> Given an integer rowIndex, return the rowIndexth (0-indexed) row of the Pascal’s triangle.
> In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown:
>
> 
- Motivation
I think pascal's triangle is pretty interesting and learned it in high school. Now there is a chance for me to implement it in assembly language and compare it with others. So this is why I pick up this question for my assignment 2.
## C code
The original implementation of the question in C is as follow:
``` c=
#include<stdio.h>
#include<stdlib.h>
int *getRow(int ,int*);
int main(){
int *ans = NULL;
int ans_size ;
int i;
int rowIndex = 0;
ans = getRow(rowIndex, &ans_size);
for(i=0;i<ans_size;i++){
printf("%d ",ans[i]);
}
printf("\n");
rowIndex = 10;
ans = getRow(rowIndex, &ans_size);
for(i=0;i<ans_size;i++){
printf("%d ",ans[i]);
}
printf("\n");
rowIndex = 33;
ans = getRow(rowIndex, &ans_size);
for(i=0;i<ans_size;i++){
printf("%d ",ans[i]);
}
printf("\n");
return 0;
}
int* getRow(int rowIndex, int* returnSize){
int *ret_arr = malloc(sizeof(int)* (rowIndex + 1));
*returnSize = (rowIndex + 1);
int layer = 0;
int middle = layer >> 1;
while(rowIndex>=layer){
int i=0;
for(i=layer/2;i>=0;i--){
if(i==0)
ret_arr[i] = 1;
else
ret_arr[i] = ret_arr[i] + ret_arr[i-1];
}
for(i=0;i<=layer/2;i++){
ret_arr[layer-i] = ret_arr[i];
}
layer++;
}
return ret_arr;
}
```
## Assembly code
The original implementation of the question in assembly is as follow:
```s=
#s0-11 :save registers
#a0-7 :argument registers
#t0-6 :temp registers
.data
#ret_arr: .word 0,0,0,0,0,0,0,0,0,0
str1: .string " "
newline: .string "\n"
.bss
ret_arr: .word 0
.text
main:
addi a3,x0,0 #a3 = rowIndex
la s0,ret_arr #
la s1,str1
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
addi a3,x0,10 #a3 = rowIndex
la s0,ret_arr #
la s1,str1
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
addi a3,x0,33 #a3 = rowIndex
la s0,ret_arr #
la s1,str1
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
li a7, 10 # Halts the simulator
ecall
getRow:
addi sp,sp,-4
sw ra,0(sp)
addi a4,a3,1 #a4 = returnSize
addi a5,x0,0 #a5 = layer
while_loop: # to determine whether the target layer has been calculated
srli a6,a5,1 #a6 = middle, to ack the boundary of keft side
addi t1,a6,0 #t1 = i
la s0,ret_arr #s0 = base addr of ret_arr
jal for_loop1
#bge t1,x0, for_loop1
addi t1,x0,0 #t1 = i
jal for_loop2
addi a5,a5,1
bge a3,a5,while_loop # rowIndex>=layer? if true, while loop continue becaue while loop hasn't reached the target layer
while_end:
lw ra,0(sp)
addi sp,sp,4
jr ra
for_loop1: # to calculate left half elements of a layer
blt t1,x0,for_loop1_end # determine whether loop continue
bne t1,x0,else1 # determine i==0? determine if-else statement
addi t3,x0,1 #t3 = 1
slli t2,t1,2 #t2 = i*4, word addr to byte addr
add t2,t2,s0 #t2 =offset+base addr
sw t3,0(t2) #store 1 to array[0]
addi t1,t1,-1 # update iteration variable
j for_loop1
for_loop1_end:
jr ra # return to caller
else1:
addi t2,t1,-1 #t2 = i-1
slli t2,t2,2 #to byte address
add t2,s0,t2
lw t3,0(t2) # load ret_arr[i-1]
add t4,t3,x0 # t4 = ret_arr[i-1]
addi t2,t1,0 #t2 = i
slli t2,t2,2 #to byte address
add t2,s0,t2 #base addr + i*4, to update the address of storing
lw t3,0(t2) # load ret_arr[i]
add t4,t3,t4 #t4+=ret_arr[i]
sw t4,0(t2)
addi t1,t1,-1
j for_loop1
for_loop2:
bge a6,t1,for_loop2_itr
jr ra
for_loop2_itr: # for copy left to right
sub t2,a5,t1 # t2=layer-i
slli t2,t2,2
slli t3,t1,2
add t3,s0,t3
add t2,s0,t2
lw t4,0(t3)
sw t4,0(t2)
addi t1,t1,1
j for_loop2
print_loop: # to show the result
bge t1,a4,print_loop_end
lw a0, 0(s0) # a0 used for return print number
li a7,1 #to print int
ecall
la a0,str1
li a7,4 #to print string
ecall
addi t1,t1,1
addi s0,s0,4
j print_loop
print_loop_end:
la a0,newline
li a7,4 #to print string
ecall
jr ra
```
## Compare Assembly code
In this section, C code for the problem is compiled with -Os, -O3, -O2, -O1.
I will compare the hand written assembly code with compiler optimization code on rv32emu.
First, I need to generate different optimization level of assembly code.
### -Os Optimized Assembly Code
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -Os -o triangles.out triangle.c
```
Also I can use -Os compiler to output the triangle.s file for me to easily analyze the assembly code that generated by compiler.
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -Os -o triangles.s -S triangle.c
```
The code in triangles.s is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp,sp,-16
sw s2,0(sp)
addi s2,a0,1
sw s0,8(sp)
mv s0,a0
slli a0,s2,2
sw s1,4(sp)
sw ra,12(sp)
mv s1,a1
call malloc
li a3,0
sw s2,0(s1)
li a6,1
.L2:
ble a3,s0,.L6
lw ra,12(sp)
lw s0,8(sp)
lw s1,4(sp)
lw s2,0(sp)
addi sp,sp,16
jr ra
.L6:
srai a2,a3,1
slli a4,a2,2
add a4,a0,a4
mv a5,a2
.L5:
bne a5,zero,.L3
slli a4,a3,2
sw a6,0(a0)
add a4,a0,a4
.L4:
slli a1,a5,2
add a1,a0,a1
lw a1,0(a1)
addi a5,a5,1
addi a4,a4,-4
sw a1,4(a4)
bge a2,a5,.L4
addi a3,a3,1
j .L2
.L3:
lw a1,0(a4)
lw a7,-4(a4)
addi a5,a5,-1
addi a4,a4,-4
add a1,a1,a7
sw a1,4(a4)
j .L5
.size getRow, .-getRow
.section .rodata.str1.4,"aMS",@progbits,1
.align 2
.LC0:
.string "%d "
.section .text.startup,"ax",@progbits
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-32
addi a1,sp,12
li a0,0
sw s0,24(sp)
sw s1,20(sp)
sw s2,16(sp)
sw ra,28(sp)
call getRow
mv s1,a0
li s0,0
lui s2,%hi(.LC0)
.L10:
lw a5,12(sp)
bgt a5,s0,.L11
li a0,10
call putchar
addi a1,sp,12
li a0,10
call getRow
mv s1,a0
li s0,0
lui s2,%hi(.LC0)
.L12:
lw a5,12(sp)
bgt a5,s0,.L13
li a0,10
call putchar
addi a1,sp,12
li a0,33
call getRow
mv s1,a0
li s0,0
lui s2,%hi(.LC0)
.L14:
lw a5,12(sp)
bgt a5,s0,.L15
li a0,10
call putchar
lw ra,28(sp)
lw s0,24(sp)
lw s1,20(sp)
lw s2,16(sp)
li a0,0
addi sp,sp,32
jr ra
.L11:
slli a5,s0,2
add a5,s1,a5
lw a1,0(a5)
addi a0,s2,%lo(.LC0)
addi s0,s0,1
call printf
j .L10
.L13:
slli a5,s0,2
add a5,s1,a5
lw a1,0(a5)
addi a0,s2,%lo(.LC0)
addi s0,s0,1
call printf
j .L12
.L15:
slli a5,s0,2
add a5,s1,a5
lw a1,0(a5)
addi a0,s2,%lo(.LC0)
addi s0,s0,1
call printf
j .L14
.size main, .-main
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
The size of the code that generate by -Os:
```
text data bss dec hex filename
75488 2816 812 79116 1350c triangles.out
```
Using the following instruction to know the CSR cylce of the triangles.out
```shell
$ ./rv32emu --stats ../hw/triangles.out
```
The result of the instruction can know the CSR cycle count and the answer print from this function.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 0
CSR cycle count: 150348
```
---
### -O3 Optimized Assembly Code
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O3 -o triangle3.out triangle.c
```
Also I can use -O3 compiler to output the triangle.s file for me to easily analyze the assembly code that generated by compiler.
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O3 -o triangle3.s -S triangle.c
```
The code in triangle3.s is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp,sp,-16
sw s2,0(sp)
addi s2,a0,1
sw s0,8(sp)
sw s1,4(sp)
mv s0,a0
mv s1,a1
slli a0,s2,2
sw ra,12(sp)
call malloc
sw s2,0(s1)
blt s0,zero,.L1
mv a6,a0
li a7,0
addi t3,a0,4
li t1,1
.L6:
srai a3,a7,1
slli a2,a3,2
add a5,a0,a2
beq a3,zero,.L11
.L3:
lw a4,0(a5)
lw a1,-4(a5)
addi a5,a5,-4
addi a3,a3,-1
add a4,a4,a1
sw a4,4(a5)
bne a3,zero,.L3
.L11:
sw t1,0(a0)
add a2,t3,a2
mv a4,a6
mv a5,a0
.L4:
lw a3,0(a5)
addi a5,a5,4
addi a4,a4,-4
sw a3,4(a4)
bne a2,a5,.L4
addi a5,a7,1
addi a6,a6,4
beq s0,a7,.L1
mv a7,a5
j .L6
.L1:
lw ra,12(sp)
lw s0,8(sp)
lw s1,4(sp)
lw s2,0(sp)
addi sp,sp,16
jr ra
.size getRow, .-getRow
.section .rodata.str1.4,"aMS",@progbits,1
.align 2
.LC0:
.string "%d "
.section .text.startup,"ax",@progbits
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-48
addi a1,sp,12
li a0,0
sw s3,28(sp)
sw ra,44(sp)
sw s0,40(sp)
sw s1,36(sp)
sw s2,32(sp)
call getRow
lw s3,12(sp)
ble s3,zero,.L13
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L14:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L14
.L13:
li a0,10
call putchar
addi a1,sp,12
li a0,10
call getRow
lw s3,12(sp)
ble s3,zero,.L15
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L16:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L16
.L15:
li a0,10
call putchar
addi a1,sp,12
li a0,33
call getRow
lw s3,12(sp)
ble s3,zero,.L17
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L18:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L18
.L17:
li a0,10
call putchar
lw ra,44(sp)
lw s0,40(sp)
lw s1,36(sp)
lw s2,32(sp)
lw s3,28(sp)
li a0,0
addi sp,sp,48
jr ra
.size main, .-main
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
The size of the code that generate by -O3:
```
text data bss dec hex filename
75496 2816 812 79124 1350c triangle3.out
```
Using the following instruction to know the CSR cylce of the triangle3.out
```shell
$ ./rv32emu --stats ../hw/triangle3.out
```
The result of the instruction can know the CSR cycle count and the answer print from this function.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 0
CSR cycle count: 149667
```
---
### -O2 Optimized Assembly Code
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O2 -o triangle2.out triangle.c
```
Also I can use -O2 compiler to output the triangle.s file for me to easily analyze the assembly code that generated by compiler.
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O2 -o triangle2.s -S triangle.c
```
The code in triangle2.s is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp,sp,-16
sw s2,0(sp)
addi s2,a0,1
sw s0,8(sp)
sw s1,4(sp)
mv s0,a0
mv s1,a1
slli a0,s2,2
sw ra,12(sp)
call malloc
sw s2,0(s1)
blt s0,zero,.L1
mv a6,a0
li a7,0
addi t3,a0,4
li t1,1
.L6:
srai a3,a7,1
slli a2,a3,2
add a5,a0,a2
beq a3,zero,.L11
.L3:
lw a4,0(a5)
lw a1,-4(a5)
addi a5,a5,-4
addi a3,a3,-1
add a4,a4,a1
sw a4,4(a5)
bne a3,zero,.L3
.L11:
sw t1,0(a0)
add a2,t3,a2
mv a4,a6
mv a5,a0
.L4:
lw a3,0(a5)
addi a5,a5,4
addi a4,a4,-4
sw a3,4(a4)
bne a2,a5,.L4
addi a5,a7,1
addi a6,a6,4
beq s0,a7,.L1
mv a7,a5
j .L6
.L1:
lw ra,12(sp)
lw s0,8(sp)
lw s1,4(sp)
lw s2,0(sp)
addi sp,sp,16
jr ra
.size getRow, .-getRow
.section .rodata.str1.4,"aMS",@progbits,1
.align 2
.LC0:
.string "%d "
.section .text.startup,"ax",@progbits
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-48
addi a1,sp,12
li a0,0
sw s3,28(sp)
sw ra,44(sp)
sw s0,40(sp)
sw s1,36(sp)
sw s2,32(sp)
call getRow
lw s3,12(sp)
ble s3,zero,.L13
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L14:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L14
.L13:
li a0,10
call putchar
addi a1,sp,12
li a0,10
call getRow
lw s3,12(sp)
ble s3,zero,.L15
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L16:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L16
.L15:
li a0,10
call putchar
addi a1,sp,12
li a0,33
call getRow
lw s3,12(sp)
ble s3,zero,.L17
mv s0,a0
li s2,0
lui s1,%hi(.LC0)
.L18:
lw a1,0(s0)
addi a0,s1,%lo(.LC0)
addi s2,s2,1
call printf
addi s0,s0,4
bne s2,s3,.L18
.L17:
li a0,10
call putchar
lw ra,44(sp)
lw s0,40(sp)
lw s1,36(sp)
lw s2,32(sp)
lw s3,28(sp)
li a0,0
addi sp,sp,48
jr ra
.size main, .-main
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
I find that the above code generated by -O2 compiler is the same as -O3 compiler. But I don't know why this situation happens.
The size of the code that generate by -O2 is also same as -O3:
```
text data bss dec hex filename
75496 2816 812 79124 1350c triangle2.out
```
Using the following instruction to know the CSR cylce of the triangle2.out
```shell
$ ./rv32emu --stats ../hw/triangle2.out
```
The result of the instruction can know the CSR cycle count and the answer print from this function. It can find that the result of CSR cycle count of triangle2.out is also same as triangle3.out.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 0
CSR cycle count: 149667
```
---
### -O1 Optimized Assembly Code
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O1 -o triangle1.out triangle.c
```
Also I can use -O1 compiler to output the triangle.s file for me to easily analyze the assembly code that generated by compiler.
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O1 -o triangle1.s -S triangle.c
```
The code in triangle1.s is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp,sp,-16
sw ra,12(sp)
sw s0,8(sp)
sw s1,4(sp)
sw s2,0(sp)
mv s0,a0
mv s1,a1
addi s2,a0,1
slli a0,s2,2
call malloc
sw s2,0(s1)
blt s0,zero,.L1
mv t3,s2
mv t1,a0
li a6,0
li t4,-1
li a7,1
j .L8
.L4:
lw a3,0(a5)
lw a2,-4(a5)
add a3,a3,a2
sw a3,0(a5)
addi a4,a4,-1
addi a5,a5,-4
blt a4,zero,.L5
.L6:
bne a4,zero,.L4
sw a7,0(a0)
.L5:
mv a3,t1
mv a4,a0
li a5,0
.L7:
lw a2,0(a4)
sw a2,0(a3)
addi a5,a5,1
addi a4,a4,4
addi a3,a3,-4
bge a1,a5,.L7
.L3:
addi a6,a6,1
addi t1,t1,4
beq a6,t3,.L1
.L8:
srli a1,a6,31
add a1,a1,a6
srai a1,a1,1
blt a6,t4,.L3
slli a5,a1,2
add a5,a0,a5
mv a4,a1
j .L6
.L1:
lw ra,12(sp)
lw s0,8(sp)
lw s1,4(sp)
lw s2,0(sp)
addi sp,sp,16
jr ra
.size getRow, .-getRow
.section .rodata.str1.4,"aMS",@progbits,1
.align 2
.LC0:
.string "%d "
.text
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-48
sw ra,44(sp)
sw s0,40(sp)
sw s1,36(sp)
sw s2,32(sp)
sw s3,28(sp)
addi a1,sp,12
li a0,0
call getRow
lw s2,12(sp)
ble s2,zero,.L14
mv s0,a0
li s1,0
lui s3,%hi(.LC0)
.L15:
lw a1,0(s0)
addi a0,s3,%lo(.LC0)
call printf
addi s1,s1,1
addi s0,s0,4
bne s1,s2,.L15
.L14:
li a0,10
call putchar
addi a1,sp,12
li a0,10
call getRow
lw s2,12(sp)
ble s2,zero,.L16
mv s0,a0
li s1,0
lui s3,%hi(.LC0)
.L17:
lw a1,0(s0)
addi a0,s3,%lo(.LC0)
call printf
addi s1,s1,1
addi s0,s0,4
bne s1,s2,.L17
.L16:
li a0,10
call putchar
addi a1,sp,12
li a0,33
call getRow
lw s2,12(sp)
ble s2,zero,.L18
mv s0,a0
li s1,0
lui s3,%hi(.LC0)
.L19:
lw a1,0(s0)
addi a0,s3,%lo(.LC0)
call printf
addi s1,s1,1
addi s0,s0,4
bne s1,s2,.L19
.L18:
li a0,10
call putchar
li a0,0
lw ra,44(sp)
lw s0,40(sp)
lw s1,36(sp)
lw s2,32(sp)
lw s3,28(sp)
addi sp,sp,48
jr ra
.size main, .-main
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
The size of the code that generate by -O1:
```
text data bss dec hex filename
75520 2816 812 79148 1352c triangle1.out
```
Using the following instruction to know the CSR cylce of the triangle1.out.
```shell
$ ./rv32emu --stats ../hw/triangle1.out
```
The result of the instruction can know the CSR cycle count and the answer print from this function.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 0
CSR cycle count: 150108
```
---
### -O0 Optimized Assembly Code
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O0 -o triangle0.out triangle.c
```
Also I can use -O0 compiler to output the triangle.s file for me to easily analyze the assembly code that generated by compiler.
```shell
$ riscv-none-elf-gcc -march=rv32i -mabi=ilp32 -O0 -o triangle0.s -S triangle.c
```
The code in triangle0.s is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.section .rodata
.align 2
.LC0:
.string "%d "
.text
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-32
sw ra,28(sp)
sw s0,24(sp)
addi s0,sp,32
sw zero,-24(s0)
sw zero,-28(s0)
addi a5,s0,-32
mv a1,a5
lw a0,-28(s0)
call getRow
sw a0,-24(s0)
sw zero,-20(s0)
j .L2
.L3:
lw a5,-20(s0)
slli a5,a5,2
lw a4,-24(s0)
add a5,a4,a5
lw a5,0(a5)
mv a1,a5
lui a5,%hi(.LC0)
addi a0,a5,%lo(.LC0)
call printf
lw a5,-20(s0)
addi a5,a5,1
sw a5,-20(s0)
.L2:
lw a5,-32(s0)
lw a4,-20(s0)
blt a4,a5,.L3
li a0,10
call putchar
li a5,10
sw a5,-28(s0)
addi a5,s0,-32
mv a1,a5
lw a0,-28(s0)
call getRow
sw a0,-24(s0)
sw zero,-20(s0)
j .L4
.L5:
lw a5,-20(s0)
slli a5,a5,2
lw a4,-24(s0)
add a5,a4,a5
lw a5,0(a5)
mv a1,a5
lui a5,%hi(.LC0)
addi a0,a5,%lo(.LC0)
call printf
lw a5,-20(s0)
addi a5,a5,1
sw a5,-20(s0)
.L4:
lw a5,-32(s0)
lw a4,-20(s0)
blt a4,a5,.L5
li a0,10
call putchar
li a5,33
sw a5,-28(s0)
addi a5,s0,-32
mv a1,a5
lw a0,-28(s0)
call getRow
sw a0,-24(s0)
sw zero,-20(s0)
j .L6
.L7:
lw a5,-20(s0)
slli a5,a5,2
lw a4,-24(s0)
add a5,a4,a5
lw a5,0(a5)
mv a1,a5
lui a5,%hi(.LC0)
addi a0,a5,%lo(.LC0)
call printf
lw a5,-20(s0)
addi a5,a5,1
sw a5,-20(s0)
.L6:
lw a5,-32(s0)
lw a4,-20(s0)
blt a4,a5,.L7
li a0,10
call putchar
li a5,0
mv a0,a5
lw ra,28(sp)
lw s0,24(sp)
addi sp,sp,32
jr ra
.size main, .-main
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp,sp,-48
sw ra,44(sp)
sw s0,40(sp)
addi s0,sp,48
sw a0,-36(s0)
sw a1,-40(s0)
lw a5,-36(s0)
addi a5,a5,1
slli a5,a5,2
mv a0,a5
call malloc
mv a5,a0
sw a5,-28(s0)
lw a5,-36(s0)
addi a4,a5,1
lw a5,-40(s0)
sw a4,0(a5)
sw zero,-20(s0)
lw a5,-20(s0)
srai a5,a5,1
sw a5,-32(s0)
j .L10
.L17:
sw zero,-24(s0)
lw a5,-20(s0)
srli a4,a5,31
add a5,a4,a5
srai a5,a5,1
sw a5,-24(s0)
j .L11
.L14:
lw a5,-24(s0)
bne a5,zero,.L12
lw a5,-24(s0)
slli a5,a5,2
lw a4,-28(s0)
add a5,a4,a5
li a4,1
sw a4,0(a5)
j .L13
.L12:
lw a5,-24(s0)
slli a5,a5,2
lw a4,-28(s0)
add a5,a4,a5
lw a3,0(a5)
lw a4,-24(s0)
li a5,1073741824
addi a5,a5,-1
add a5,a4,a5
slli a5,a5,2
lw a4,-28(s0)
add a5,a4,a5
lw a4,0(a5)
lw a5,-24(s0)
slli a5,a5,2
lw a2,-28(s0)
add a5,a2,a5
add a4,a3,a4
sw a4,0(a5)
.L13:
lw a5,-24(s0)
addi a5,a5,-1
sw a5,-24(s0)
.L11:
lw a5,-24(s0)
bge a5,zero,.L14
sw zero,-24(s0)
j .L15
.L16:
lw a5,-24(s0)
slli a5,a5,2
lw a4,-28(s0)
add a4,a4,a5
lw a3,-20(s0)
lw a5,-24(s0)
sub a5,a3,a5
slli a5,a5,2
lw a3,-28(s0)
add a5,a3,a5
lw a4,0(a4)
sw a4,0(a5)
lw a5,-24(s0)
addi a5,a5,1
sw a5,-24(s0)
.L15:
lw a5,-20(s0)
srli a4,a5,31
add a5,a4,a5
srai a5,a5,1
mv a4,a5
lw a5,-24(s0)
ble a5,a4,.L16
lw a5,-20(s0)
addi a5,a5,1
sw a5,-20(s0)
.L10:
lw a4,-36(s0)
lw a5,-20(s0)
bge a4,a5,.L17
lw a5,-28(s0)
mv a0,a5
lw ra,44(sp)
lw s0,40(sp)
addi sp,sp,48
jr ra
.size getRow, .-getRow
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
The size of the code that generate by -O0:
```
text data bss dec hex filename
75804 2816 812 79432 13648 triangle0.out
```
Using the following instruction to know the CSR cylce of the triangle0.out
```shell
$ ./rv32emu --stats ../hw/triangle0.out
```
The result of the instruction can know the CSR cycle count and the answer print from this function.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 0
CSR cycle count: 162210
```
==We can easily observe that the code generated by -O3 and -O2 compiler is better than -O1 and -O0 not only in CSR cycle count but also in the size of code, and -Os compiler is in the middle of -O0 and -O1.==
## Compared with hand written assembly code
Due to rv32emu can not work together, therefore I need to revise the hand written assembly from 陳品崴, to make it run on rv32emu.
### Revised hand written assembly
I revised the hand written assembly from 陳品崴 to let it fit rv32emu. Through this, I can compare the hand wrriten assembly code and the code generate by compiler more fairly.
The revised hand written assembly code is as below:
```s=
.file "triangle.c"
.option nopic
.attribute arch, "rv32i2p1"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 2
.globl getRow
.type getRow, @function
getRow:
addi sp, sp, -4
sw ra, 0(sp)
addi a4,a3,1 #a4 = returnSize
addi a5,x0,0 #a5 = layer
while_loop:
srli a6,a5,1
addi t1,a6,0 #t1 = i
#la s0,ret_arr #s0 = base addr of ret_arr
jal for_loop1
addi t1,x0,0 #t1 = i
jal for_loop2
addi a5,a5,1
bge a3,a5,while_loop
while_end:
lw ra,0(sp)
addi sp,sp,4
jr ra
for_loop1:
blt t1,x0,for_loop1_end
bne t1,x0,else1 # determine i==0? determine if-else statement
addi t3,x0,1 #t3 = 1
slli t2,t1,2 #t2 = i*4, word addr to byte addr
add t2,t2,s0 #t2 =offset+base addr
sw t3,0(t2) #store 1 to array[0]
addi t1,t1,-1 # update iteration variable
j for_loop1
for_loop1_end:
jr ra # return to caller
else1:
addi t2,t1,-1 #t2 = i-1
slli t2,t2,2 #to byte address
add t2,s0,t2
lw t3,0(t2) # load ret_arr[i-1]
add t4,t3,x0 # t4 = ret_arr[i-1]
addi t2,t1,0 #t2 = i
slli t2,t2,2 #to byte address
add t2,s0,t2
lw t3,0(t2) # load ret_arr[i]
add t4,t3,t4 #t4+=ret_arr[i]
sw t4,0(t2)
addi t1,t1,-1
j for_loop1
for_loop2:
bge a6,t1,for_loop2_itr
jr ra
for_loop2_itr: # for copy left to right
sub t2,a5,t1 # t2=layer-i
slli t2,t2,2
slli t3,t1,2
add t3,s0,t3
add t2,s0,t2
lw t4,0(t3)
sw t4,0(t2)
addi t1,t1,1
j for_loop2
print_loop: # to show the result
addi sp, sp, -4
sw ra, 0(sp)
lui s1,%hi(.LC0)
print: bge t1,a4,print_loop_end
addi sp, sp, -8
sw a4,0(sp)
sw t1,4(sp)
lw a1,0(s0)
#add a1, x0, a4
addi a0,s1,%lo(.LC0)
#addi s2,s2,1
call printf
lw t1,4(sp)
lw a4,0(sp)
addi sp, sp, 8
addi t1,t1,1
addi s0,s0,4
j print
print_loop_end:
lw ra, 0(sp)
addi sp, sp, 4
jr ra
.size getRow, .-getRow
.section .rodata
.align 2
.LC0:
.string "%d \n"
.data
ret_arr: .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.section .text
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-80
sw ra,76(sp)
addi a3,x0,4 #a3 = rowIndex
la s0,ret_arr
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
addi a3,x0,10 #a3 = rowIndex
la s0,ret_arr
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
addi a3,x0,33 #a3 = rowIndex
la s0,ret_arr
jal getRow
addi t1,x0,0 #t1 = i
jal print_loop
lw ra,76(sp)
addi sp,sp,80
jr ra
.size main, .-main
.ident "GCC: (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0"
```
The result of the instruction can know the CSR cycle count and the answer print from this function is as below.
```
1
1 10 45 120 210 252 210 120 45 10 1
1 33 528 5456 40920 237336 1107568 4272048 13884156 38567100 92561040 193536720 354817320 573166440 818809200 1037158320 1166803110 1166803110 1037158320 818809200 573166440 354817320 193536720 92561040 38567100 13884156 4272048 1107568 237336 40920 5456 528 33 1
inferior exit code 3
CSR cycle count: 160730
```
### Comparing result
#### Code length (the .s file that compiler output)
| Compiler | -Os | -O3 | -O2 | -O1 | -O0 | revised hand written assembly code |
| ----------- | --- | --- | --- | --- | --- |:----------------------------------:|
| Code length | 140 | 142 | 142 | 150 | 222 | 151 |
We can find that the assembly code revised by me is shorter than -O0, and longer than -Os, -O3, -O2, -O1.
#### CSR cycle count
| Compiler | -Os | -O3 | -O2 | -O1 | -O0 | revised hand written assembly code |
| ----------- | ------ | ------ | ------ | ------ | ------ |:----------------------------------:|
| CSR cycle count | 150348 | 149667 | 149667 | 150108 | 162210 | 160730 |
We cand find that the assembly code revised by me of the CSR cycle count is shorter than -O0, and longer than -Os, -O3, -O2, -O1.
==So we can know that the assembly code that generated by compiler is probably better in not only code length but also in CSR cycle count.==