Skip to content

Commit

Permalink
add initial support for riscv syscalls, add build targets (wip)
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <leon.gross@9elements.com>
  • Loading branch information
leongross committed Aug 8, 2024
1 parent fb3d98c commit 913a33f
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 1 deletion.
2 changes: 2 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@ endif
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/riscv32 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/riscv64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
Expand Down
2 changes: 2 additions & 0 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "linux", GOARCH: "arm64"},
{GOOS: "linux", GOARCH: "mips"},
{GOOS: "linux", GOARCH: "mipsle"},
{GOOS: "linux", GOARCH: "riscv"},
{GOOS: "linux", GOARCH: "riscv64"},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
Expand Down
5 changes: 5 additions & 0 deletions compileopts/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ func CanonicalArchName(triple string) string {
if arch == "mipsel" {
return "mips"
}

if arch == "riscv32" {
return "riscv"
}

return arch
}

Expand Down
7 changes: 7 additions & 0 deletions compileopts/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
llvmarch = "mipsel"
case "wasm":
llvmarch = "wasm32"
case "riscv":
llvmarch = "riscv64"
default:
llvmarch = options.GOARCH
}
Expand Down Expand Up @@ -344,6 +346,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"-mnontrapping-fptoint",
"-msign-ext",
)
case "riscv64":
// TODO: are these the features we really need? What does SiFive use? what does qemu support?
spec.CPU = "generic-rv64"
spec.Features = "+a,+c,+d,+f,+m,+q,+rvc,+zbb,+zbp"
spec.CFlags = append(spec.CFlags, "-march=rv64gc")
}
if goos == "darwin" {
spec.Linker = "ld.lld"
Expand Down
53 changes: 52 additions & 1 deletion compiler/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// Also useful:
// https://web.archive.org/web/20220529105937/https://www.linux-mips.org/wiki/Syscall
// The syscall number goes in r2, the result also in r2.
// Register r7 is both an input paramter and an output parameter: if it
// Register r7 is both an input parameter and an output parameter: if it
// is non-zero, the system call failed and r2 is the error code.
// The code below implements the O32 syscall ABI, not the N32 ABI. It
// could implement both at the same time if needed (like what appears to
Expand All @@ -156,6 +156,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
argTypes := []llvm.Type{b.uintptrType}
constraints := "={$2},={$7},0"
syscallParams := call.Args[1:]

if len(syscallParams) > 7 {
// There is one syscall that uses 7 parameters: sync_file_range.
// But only 7, not more. Go however only has Syscall6 and Syscall9.
Expand Down Expand Up @@ -229,6 +230,56 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
result := b.CreateSelect(isError, negativeResult, resultCode, "")
return result, nil

case (b.GOARCH == "riscv" || b.GOARCH == "riscv64") && b.GOOS == "linux":
// https://stackoverflow.com/questions/59800430/risc-v-ecall-syscall-calling-convention-on-pk-linux
// https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/syscall.2?h=man-pages-5.04#n200
// https://pdos.csail.mit.edu/6.S081/2021/slides/6s081-lec-syscall.pdf
// https://git.musl-libc.org/cgit/musl/tree/arch/riscv64/syscall_arch.h
// https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
args := []llvm.Value{num}
argTypes := []llvm.Type{}
constraints := "={a7}" // syscall number
for i, arg := range call.Args[1:] {
constraints += "," + [...]string{
"{a0}",
"{a1}",
"{a2}",
"{a3}",
"{a4}",
"{a5}",
}[i]
llvmValue := b.getValue(arg, getPos(call))
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
args = append(args, num)
argTypes = append(argTypes, b.uintptrType)

// constrain registers used for syscall/ecall
for i := len(call.Args) - 1; i < 4; i++ {
constraints += ",~{a" + strconv.Itoa(i) + "}"
}

// constrain caller responsible registers
// See Table 18.2: RISC-V calling convention register usage
// TODO: does llvm take their stack usage into account?
// TODO: are only the registers needed saved here or all? can llvm determine this itself?

// temporary integer registers
for i := 0; i < 8; i++ {
constraints += ",~{t" + strconv.Itoa(i) + "}"
}

// temporary floating-point registers
for i := 0; i < 12; i++ {
constraints += ",~{ft" + strconv.Itoa(i) + "}"
}

// generate function
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "ecall", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil

default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
Expand Down

0 comments on commit 913a33f

Please sign in to comment.