Skip to content

Latest commit

 

History

History
105 lines (93 loc) · 4.24 KB

compiler_explorer.md

File metadata and controls

105 lines (93 loc) · 4.24 KB

For users new to the RISC-V ISA but familiar with the C or C++ programming languages, Matt Godbolt's Compiler Explorer is a great tool for interactively seeing how C/C++ constructs gets compiled to the RISC-V ISA. In Compiler Explorer, to compile to the RISC-V ISA, select "riscv32 clang (trunk)" as the compiler.

Since the assembly code generated by Compiler Explorer is stripped of debug symbols, program entry points (there is no main function) etc., it is fairly straight-forward to adapt the generated assembly code to be able to run it in Ripes.

Adapting a simple C program

In the following segment, a C-style factorial function will be adapted to be run in Ripes.

Initially, we define our factorial function as being:

int arg = 7;

int fact(int n) {
   int c;
   int result = 1;
 
   for(c = 1 ; c <= n ; c++)
         result = result*c;
 
   return result;
}

Where we define the argument for the function as a global variable. This will yield a symbol that is stored in the static data segment of the output assembly file.
This initially produces the following RISC-V assembly file (With compiler flag -O3):

fact(int):                               # @fact(int)
        addi    sp, sp, -16
        sw      ra, 12(sp)
        sw      s1, 8(sp)
        sw      s2, 4(sp)
        mv      s2, a0
        addi    a0, zero, 1
        blt     s2, a0, .LBB0_3
        addi    a0, zero, 1
        mv      s1, zero
.LBB0_2:                                # =>This Inner Loop Header: Depth=1
        addi    s1, s1, 1
        mv      a1, s1
        call    __mulsi3
        bne     s2, s1, .LBB0_2
.LBB0_3:
        lw      s2, 4(sp)
        lw      s1, 8(sp)
        lw      ra, 12(sp)
        addi    sp, sp, 16
        ret
arg:
        .word   7                       # 0x7

This is already pretty good! Compiler Explorer helpfully colors the different segments of both the C source code and the RISC-V assembly code to indicate which code-segments are generated from what.
The biggest issue is call __mulsi3. This is an attempt to call a built-in runtime function for calculating the product of two registers (a0 and a1) - For more info on runtime function calls, refer to compiler-rt. Since Ripes supports RV32M - the ISA extension for integer multiplication and division, such a call may be replaced with theinstruction mul a0 a0 a1. The input registers are a0, a1 given that these are the function argument/return registers, which was already prepared for a call to __mulsi3 as per the RISC-V calling convention, and the output register is a0, a function return value register.

For specifying variables to be loaded into simulator memory, we must specify that arg resides in the .data segment. Thus, we need to move the arg specification to the top of the file, and discern between the .data and .text segments of the file. The top segment of the file should therefore look like this:

.data
arg: .word 7

.text
...

Finally, we need to run our factorial function with arg as an argument. To do this, we write a small main function as the entry code:

main:
        lw       a0, arg
        jal      ra, fact
        jal      zero, end

as well as write a label at the end of our file - end:, which we will jump to, to exit the program.

And thus the following code will be runnable in Ripes:

.data
arg:
	.word 7

.text
main:
        lw       a0, arg
        jal      ra, fact
        jal      zero, end
fact:                                  # @fact(int)
        addi    sp, sp, -16
        sw      ra, 12(sp)
        sw      s1, 8(sp)
        sw      s2, 4(sp)
        mv      s2, a0
        addi    a0, zero, 1
        blt     s2, a0, .LBB0_3
        addi    a0, zero, 1
        mv      s1, zero
.LBB0_2:                                # =>This Inner Loop Header: Depth=1
        addi    s1, s1, 1
        mv      a1, s1
        mul     a0, a0, a1
        bne     s2, s1, .LBB0_2
.LBB0_3:
        lw      s2, 4(sp)
        lw      s1, 8(sp)
        lw      ra, 12(sp)
        addi    sp, sp, 16
        ret
end:

When this is run, register a0 will contain the value 5040 after execution.