createSyscall emits instructions for the syscall.Syscall* family of functions, depending on the target OS/arch.
(call *ssa.CallCommon)
| 246 | // createSyscall emits instructions for the syscall.Syscall* family of |
| 247 | // functions, depending on the target OS/arch. |
| 248 | func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { |
| 249 | switch b.GOOS { |
| 250 | case "linux": |
| 251 | syscallResult, err := b.createRawSyscall(call) |
| 252 | if err != nil { |
| 253 | return syscallResult, err |
| 254 | } |
| 255 | // Return values: r0, r1 uintptr, err Errno |
| 256 | // Pseudocode: |
| 257 | // var err uintptr |
| 258 | // if syscallResult < 0 && syscallResult > -4096 { |
| 259 | // err = -syscallResult |
| 260 | // } |
| 261 | // return syscallResult, 0, err |
| 262 | zero := llvm.ConstInt(b.uintptrType, 0, false) |
| 263 | inrange1 := b.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "") |
| 264 | inrange2 := b.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(b.uintptrType, 0xfffffffffffff000, true), "") // -4096 |
| 265 | hasError := b.CreateAnd(inrange1, inrange2, "") |
| 266 | errResult := b.CreateSelect(hasError, b.CreateSub(zero, syscallResult, ""), zero, "syscallError") |
| 267 | retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)) |
| 268 | retval = b.CreateInsertValue(retval, syscallResult, 0, "") |
| 269 | retval = b.CreateInsertValue(retval, zero, 1, "") |
| 270 | retval = b.CreateInsertValue(retval, errResult, 2, "") |
| 271 | return retval, nil |
| 272 | case "windows": |
| 273 | // On Windows, syscall.Syscall* is basically just a function pointer |
| 274 | // call. This is complicated in gc because of stack switching and the |
| 275 | // different ABI, but easy in TinyGo: just call the function pointer. |
| 276 | // The signature looks like this: |
| 277 | // func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) |
| 278 | |
| 279 | isI386 := strings.HasPrefix(b.Triple, "i386-") |
| 280 | |
| 281 | // Prepare input values. |
| 282 | var paramTypes []llvm.Type |
| 283 | var params []llvm.Value |
| 284 | for _, val := range call.Args[2:] { |
| 285 | param := b.getValue(val, getPos(call)) |
| 286 | params = append(params, param) |
| 287 | paramTypes = append(paramTypes, param.Type()) |
| 288 | } |
| 289 | llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false) |
| 290 | fn := b.getValue(call.Args[0], getPos(call)) |
| 291 | fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "") |
| 292 | |
| 293 | // Prepare some functions that will be called later. |
| 294 | setLastError := b.mod.NamedFunction("SetLastError") |
| 295 | if setLastError.IsNil() { |
| 296 | llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false) |
| 297 | setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType) |
| 298 | if isI386 { |
| 299 | setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv) |
| 300 | } |
| 301 | } |
| 302 | getLastError := b.mod.NamedFunction("GetLastError") |
| 303 | if getLastError.IsNil() { |
| 304 | llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false) |
| 305 | getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType) |
no test coverage detected