github.com/aykevl/tinygo@v0.5.0/compiler/syscall.go (about) 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 "strconv" 9 10 "golang.org/x/tools/go/ssa" 11 "tinygo.org/x/go-llvm" 12 ) 13 14 // emitSyscall emits an inline system call instruction, depending on the target 15 // OS/arch. 16 func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { 17 num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value) 18 var syscallResult llvm.Value 19 switch { 20 case c.GOARCH == "amd64": 21 if c.GOOS == "darwin" { 22 // Darwin adds this magic number to system call numbers: 23 // 24 // > Syscall classes for 64-bit system call entry. 25 // > For 64-bit users, the 32-bit syscall number is partitioned 26 // > with the high-order bits representing the class and low-order 27 // > bits being the syscall number within that class. 28 // > The high-order 32-bits of the 64-bit syscall number are unused. 29 // > All system classes enter the kernel via the syscall instruction. 30 // 31 // Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h 32 num += 0x2000000 33 } 34 // Sources: 35 // https://stackoverflow.com/a/2538212 36 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall 37 args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)} 38 argTypes := []llvm.Type{c.uintptrType} 39 // Constraints will look something like: 40 // "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}" 41 constraints := "={rax},0" 42 for i, arg := range call.Args[1:] { 43 constraints += "," + [...]string{ 44 "{rdi}", 45 "{rsi}", 46 "{rdx}", 47 "{r10}", 48 "{r8}", 49 "{r9}", 50 "{r11}", 51 "{r12}", 52 "{r13}", 53 }[i] 54 llvmValue, err := c.parseExpr(frame, arg) 55 if err != nil { 56 return llvm.Value{}, err 57 } 58 args = append(args, llvmValue) 59 argTypes = append(argTypes, llvmValue.Type()) 60 } 61 constraints += ",~{rcx},~{r11}" 62 fnType := llvm.FunctionType(c.uintptrType, argTypes, false) 63 target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) 64 syscallResult = c.builder.CreateCall(target, args, "") 65 case c.GOARCH == "arm" && c.GOOS == "linux": 66 // Implement the EABI system call convention for Linux. 67 // Source: syscall(2) man page. 68 args := []llvm.Value{} 69 argTypes := []llvm.Type{} 70 // Constraints will look something like: 71 // ={r0},0,{r1},{r2},{r7},~{r3} 72 constraints := "={r0}" 73 for i, arg := range call.Args[1:] { 74 constraints += "," + [...]string{ 75 "0", // tie to output 76 "{r1}", 77 "{r2}", 78 "{r3}", 79 "{r4}", 80 "{r5}", 81 "{r6}", 82 }[i] 83 llvmValue, err := c.parseExpr(frame, arg) 84 if err != nil { 85 return llvm.Value{}, err 86 } 87 args = append(args, llvmValue) 88 argTypes = append(argTypes, llvmValue.Type()) 89 } 90 args = append(args, llvm.ConstInt(c.uintptrType, num, false)) 91 argTypes = append(argTypes, c.uintptrType) 92 constraints += ",{r7}" // syscall number 93 for i := len(call.Args) - 1; i < 4; i++ { 94 // r0-r3 get clobbered after the syscall returns 95 constraints += ",~{r" + strconv.Itoa(i) + "}" 96 } 97 fnType := llvm.FunctionType(c.uintptrType, argTypes, false) 98 target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) 99 syscallResult = c.builder.CreateCall(target, args, "") 100 case c.GOARCH == "arm64" && c.GOOS == "linux": 101 // Source: syscall(2) man page. 102 args := []llvm.Value{} 103 argTypes := []llvm.Type{} 104 // Constraints will look something like: 105 // ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17} 106 constraints := "={x0}" 107 for i, arg := range call.Args[1:] { 108 constraints += "," + [...]string{ 109 "0", // tie to output 110 "{x1}", 111 "{x2}", 112 "{x3}", 113 "{x4}", 114 "{x5}", 115 }[i] 116 llvmValue, err := c.parseExpr(frame, arg) 117 if err != nil { 118 return llvm.Value{}, err 119 } 120 args = append(args, llvmValue) 121 argTypes = append(argTypes, llvmValue.Type()) 122 } 123 args = append(args, llvm.ConstInt(c.uintptrType, num, false)) 124 argTypes = append(argTypes, c.uintptrType) 125 constraints += ",{x8}" // syscall number 126 for i := len(call.Args) - 1; i < 8; i++ { 127 // x0-x7 may get clobbered during the syscall following the aarch64 128 // calling convention. 129 constraints += ",~{x" + strconv.Itoa(i) + "}" 130 } 131 constraints += ",~{x16},~{x17}" // scratch registers 132 fnType := llvm.FunctionType(c.uintptrType, argTypes, false) 133 target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) 134 syscallResult = c.builder.CreateCall(target, args, "") 135 default: 136 return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) 137 } 138 switch c.GOOS { 139 case "linux": 140 // Return values: r0, r1 uintptr, err Errno 141 // Pseudocode: 142 // var err uintptr 143 // if syscallResult < 0 && syscallResult > -4096 { 144 // err = -syscallResult 145 // } 146 // return syscallResult, 0, err 147 zero := llvm.ConstInt(c.uintptrType, 0, false) 148 inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") 149 inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 150 hasError := c.builder.CreateAnd(inrange1, inrange2, "") 151 errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError") 152 retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) 153 retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") 154 retval = c.builder.CreateInsertValue(retval, zero, 1, "") 155 retval = c.builder.CreateInsertValue(retval, errResult, 2, "") 156 return retval, nil 157 case "darwin": 158 // Return values: r0, r1 uintptr, err Errno 159 // Pseudocode: 160 // var err uintptr 161 // if syscallResult != 0 { 162 // err = syscallResult 163 // } 164 // return syscallResult, 0, err 165 zero := llvm.ConstInt(c.uintptrType, 0, false) 166 hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") 167 errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError") 168 retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) 169 retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") 170 retval = c.builder.CreateInsertValue(retval, zero, 1, "") 171 retval = c.builder.CreateInsertValue(retval, errResult, 2, "") 172 return retval, nil 173 default: 174 return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) 175 } 176 }