Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for riscv syscalls #4398

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading