Skip to content

Commit f7b2a2c

Browse files
aykevldeadprogram
authored andcommitted
compiler: implement syscall.Syscall* as builtins
Treating them as builtins is easier to implement and likely reduces code size.
1 parent 6ae4b43 commit f7b2a2c

File tree

3 files changed

+72
-13
lines changed

3 files changed

+72
-13
lines changed

compiler/compiler.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
16861686
return c.builder.CreateCall(target, args, ""), nil
16871687
}
16881688

1689+
switch fn.RelString(nil) {
1690+
case "syscall.Syscall", "syscall.Syscall6":
1691+
return c.emitSyscall(frame, instr)
1692+
}
1693+
16891694
targetFunc := c.ir.GetFunction(fn)
16901695
if targetFunc.LLVMFn.IsNil() {
16911696
return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())

compiler/syscall.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package compiler
2+
3+
// This file implements the syscall.Syscall and syscall.Syscall6 instructions as
4+
// compiler builtins.
5+
6+
import (
7+
"go/constant"
8+
9+
"golang.org/x/tools/go/ssa"
10+
"tinygo.org/x/go-llvm"
11+
)
12+
13+
// emitSyscall emits an inline system call instruction, depending on the target
14+
// OS/arch.
15+
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
16+
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
17+
switch {
18+
case c.GOARCH == "amd64" && c.GOOS == "linux":
19+
// Sources:
20+
// https://stackoverflow.com/a/2538212
21+
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
22+
args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)}
23+
argTypes := []llvm.Type{c.uintptrType}
24+
// Constraints will look something like:
25+
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
26+
constraints := "={rax},0"
27+
for i, arg := range call.Args[1:] {
28+
constraints += "," + [...]string{
29+
"{rdi}",
30+
"{rsi}",
31+
"{rdx}",
32+
"{r10}",
33+
"{r8}",
34+
"{r9}",
35+
}[i]
36+
llvmValue, err := c.parseExpr(frame, arg)
37+
if err != nil {
38+
return llvm.Value{}, err
39+
}
40+
args = append(args, llvmValue)
41+
argTypes = append(argTypes, llvmValue.Type())
42+
}
43+
constraints += ",~{rcx},~{r11}"
44+
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
45+
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
46+
syscallResult := c.builder.CreateCall(target, args, "")
47+
// Return values: r1, r1, err uintptr
48+
// Pseudocode:
49+
// var err uintptr
50+
// if syscallResult < 0 && syscallResult > -4096 {
51+
// err = -syscallResult
52+
// }
53+
// return syscallResult, 0, err
54+
zero := llvm.ConstInt(c.uintptrType, 0, false)
55+
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
56+
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
57+
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
58+
errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError")
59+
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
60+
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
61+
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
62+
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
63+
return retval, nil
64+
default:
65+
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
66+
}
67+
}

src/runtime/syscall.go

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)