From f7b2a2c977bcca51dfd359894f7116c447a88572 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 5 Feb 2019 00:25:33 +0100 Subject: [PATCH] compiler: implement syscall.Syscall* as builtins Treating them as builtins is easier to implement and likely reduces code size. --- compiler/compiler.go | 5 ++++ compiler/syscall.go | 67 ++++++++++++++++++++++++++++++++++++++++++ src/runtime/syscall.go | 13 -------- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 compiler/syscall.go delete mode 100644 src/runtime/syscall.go diff --git a/compiler/compiler.go b/compiler/compiler.go index 522929af..0d9a3273 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1686,6 +1686,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e return c.builder.CreateCall(target, args, ""), nil } + switch fn.RelString(nil) { + case "syscall.Syscall", "syscall.Syscall6": + return c.emitSyscall(frame, instr) + } + targetFunc := c.ir.GetFunction(fn) if targetFunc.LLVMFn.IsNil() { return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) diff --git a/compiler/syscall.go b/compiler/syscall.go new file mode 100644 index 00000000..bfc99757 --- /dev/null +++ b/compiler/syscall.go @@ -0,0 +1,67 @@ +package compiler + +// This file implements the syscall.Syscall and syscall.Syscall6 instructions as +// compiler builtins. + +import ( + "go/constant" + + "golang.org/x/tools/go/ssa" + "tinygo.org/x/go-llvm" +) + +// emitSyscall emits an inline system call instruction, depending on the target +// OS/arch. +func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { + num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value) + switch { + case c.GOARCH == "amd64" && c.GOOS == "linux": + // Sources: + // https://stackoverflow.com/a/2538212 + // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall + args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)} + argTypes := []llvm.Type{c.uintptrType} + // Constraints will look something like: + // "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}" + constraints := "={rax},0" + for i, arg := range call.Args[1:] { + constraints += "," + [...]string{ + "{rdi}", + "{rsi}", + "{rdx}", + "{r10}", + "{r8}", + "{r9}", + }[i] + llvmValue, err := c.parseExpr(frame, arg) + if err != nil { + return llvm.Value{}, err + } + args = append(args, llvmValue) + argTypes = append(argTypes, llvmValue.Type()) + } + constraints += ",~{rcx},~{r11}" + fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) + syscallResult := c.builder.CreateCall(target, args, "") + // Return values: r1, r1, err uintptr + // Pseudocode: + // var err uintptr + // if syscallResult < 0 && syscallResult > -4096 { + // err = -syscallResult + // } + // return syscallResult, 0, err + zero := llvm.ConstInt(c.uintptrType, 0, false) + inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") + inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 + hasError := c.builder.CreateAnd(inrange1, inrange2, "") + errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError") + retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) + retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") + retval = c.builder.CreateInsertValue(retval, zero, 1, "") + retval = c.builder.CreateInsertValue(retval, errResult, 2, "") + return retval, nil + default: + return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) + } +} diff --git a/src/runtime/syscall.go b/src/runtime/syscall.go deleted file mode 100644 index 6bc703b8..00000000 --- a/src/runtime/syscall.go +++ /dev/null @@ -1,13 +0,0 @@ -package runtime - -// This file implements syscall.Syscall and the like. - -//go:linkname syscall_Syscall syscall.Syscall -func syscall_Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err uintptr) { - panic("syscall") -} - -//go:linkname syscall_Syscall6 syscall.Syscall6 -func syscall_Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err uintptr) { - panic("syscall6") -}