diff options
author | Ayke van Laethem <[email protected]> | 2019-11-23 21:36:26 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2020-03-25 20:17:46 +0100 |
commit | d752e66be552f074c3e23ca2c22507f95db6eef0 (patch) | |
tree | 066fec54d0ccca6ee0e8bc6ae1e08a0d7363ce0f | |
parent | d46934d1f1a0dc4f534a1f68012659acf50a5774 (diff) | |
download | tinygo-d752e66be552f074c3e23ca2c22507f95db6eef0.tar.gz tinygo-d752e66be552f074c3e23ca2c22507f95db6eef0.zip |
compiler: refactor function calling
-rw-r--r-- | compiler/compiler.go | 108 | ||||
-rw-r--r-- | compiler/func.go | 20 | ||||
-rw-r--r-- | compiler/inlineasm.go | 58 | ||||
-rw-r--r-- | compiler/interrupt.go | 50 | ||||
-rw-r--r-- | compiler/syscall.go | 90 | ||||
-rw-r--r-- | compiler/volatile.go | 22 |
6 files changed, 177 insertions, 171 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 7222041e7..1807b8af5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1066,7 +1066,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { // A goroutine call on a func value, but the callee is trivial to find. For // example: immediately applied functions. funcValue := frame.getValue(value) - context = c.extractFuncContext(funcValue) + context = frame.extractFuncContext(funcValue) default: panic("StaticCallee returned an unexpected value") } @@ -1078,7 +1078,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { // goroutine: // * The function context, for closures. // * The function pointer (for tasks). - funcPtr, context := c.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature)) + funcPtr, context := frame.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature)) params = append(params, context) // context parameter switch c.Scheduler() { case "none", "coroutines": @@ -1315,86 +1315,90 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos } } -func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, exported bool) llvm.Value { - var params []llvm.Value - for _, param := range args { - params = append(params, frame.getValue(param)) - } - - if !exported { - // This function takes a context parameter. - // Add it to the end of the parameter list. - params = append(params, context) - - // Parent coroutine handle. - params = append(params, llvm.Undef(c.i8ptrType)) - } - - return c.createCall(llvmFn, params, "") -} - -func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +// createFunctionCall lowers a Go SSA call instruction (to a simple function, +// closure, function pointer, builtin, method, etc.) to LLVM IR, usually a call +// instruction. +// +// This is also where compiler intrinsics are implemented. +func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) { if instr.IsInvoke() { - fnCast, args := frame.getInvokeCall(instr) - return c.createCall(fnCast, args, ""), nil + fnCast, args := b.getInvokeCall(instr) + return b.createCall(fnCast, args, ""), nil } // Try to call the function directly for trivially static calls. + var callee, context llvm.Value + exported := false if fn := instr.StaticCallee(); fn != nil { + // Direct function call, either to a named or anonymous (directly + // applied) function call. If it is anonymous, it may be a closure. name := fn.RelString(nil) switch { case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister": - return c.emitReadRegister(name, instr.Args) + return b.createReadRegister(name, instr.Args) case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": - return c.emitAsm(instr.Args) + return b.createInlineAsm(instr.Args) case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": - return c.emitAsmFull(frame, instr) + return b.createInlineAsmFull(instr) case strings.HasPrefix(name, "device/arm.SVCall"): - return c.emitSVCall(frame, instr.Args) + return b.emitSVCall(instr.Args) case strings.HasPrefix(name, "(device/riscv.CSR)."): - return c.emitCSROperation(frame, instr) + return b.emitCSROperation(instr) case strings.HasPrefix(name, "syscall.Syscall"): - return c.emitSyscall(frame, instr) + return b.createSyscall(instr) case strings.HasPrefix(name, "runtime/volatile.Load"): - return c.emitVolatileLoad(frame, instr) + return b.createVolatileLoad(instr) case strings.HasPrefix(name, "runtime/volatile.Store"): - return c.emitVolatileStore(frame, instr) + return b.createVolatileStore(instr) case name == "runtime/interrupt.New": - return c.emitInterruptGlobal(frame, instr) + return b.createInterruptGlobal(instr) } - targetFunc := c.ir.GetFunction(fn) + targetFunc := b.ir.GetFunction(fn) if targetFunc.LLVMFn.IsNil() { - return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) + return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) } - var context llvm.Value switch value := instr.Value.(type) { case *ssa.Function: // Regular function call. No context is necessary. - context = llvm.Undef(c.i8ptrType) + context = llvm.Undef(b.i8ptrType) case *ssa.MakeClosure: // A call on a func value, but the callee is trivial to find. For // example: immediately applied functions. - funcValue := frame.getValue(value) - context = c.extractFuncContext(funcValue) + funcValue := b.getValue(value) + context = b.extractFuncContext(funcValue) default: panic("StaticCallee returned an unexpected value") } - return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, targetFunc.IsExported()), nil - } - - // Builtin or function pointer. - switch call := instr.Value.(type) { - case *ssa.Builtin: - return frame.createBuiltin(instr.Args, call.Name(), instr.Pos()) - default: // function pointer - value := frame.getValue(instr.Value) + callee = targetFunc.LLVMFn + exported = targetFunc.IsExported() + } else if call, ok := instr.Value.(*ssa.Builtin); ok { + // Builtin function (append, close, delete, etc.).) + return b.createBuiltin(instr.Args, call.Name(), instr.Pos()) + } else { + // Function pointer. + value := b.getValue(instr.Value) // This is a func value, which cannot be called directly. We have to // extract the function pointer and context first from the func value. - funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) - frame.createNilCheck(funcPtr, "fpcall") - return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil + callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) + b.createNilCheck(callee, "fpcall") + } + + var params []llvm.Value + for _, param := range instr.Args { + params = append(params, b.getValue(param)) + } + + if !exported { + // This function takes a context parameter. + // Add it to the end of the parameter list. + params = append(params, context) + + // Parent coroutine handle. + params = append(params, llvm.Undef(b.i8ptrType)) } + + return b.createCall(callee, params, ""), nil } // getValue returns the LLVM value of a constant, function value, global, or @@ -1462,9 +1466,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { y := frame.getValue(expr.Y) return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos()) case *ssa.Call: - // Passing the current task here to the subroutine. It is only used when - // the subroutine is blocking. - return c.parseCall(frame, expr.Common()) + return frame.createFunctionCall(expr.Common()) case *ssa.ChangeInterface: // Do not change between interface types: always use the underlying // (concrete) type in the type number of the interface. Every method diff --git a/compiler/func.go b/compiler/func.go index 69fb78bce..9632e7125 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -71,22 +71,22 @@ func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { // extractFuncContext extracts the context pointer from this function value. It // is a cheap operation. -func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value { - return c.builder.CreateExtractValue(funcValue, 0, "") +func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value { + return b.CreateExtractValue(funcValue, 0, "") } // decodeFuncValue extracts the context and the function pointer from this func // value. This may be an expensive operation. -func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { - context = c.builder.CreateExtractValue(funcValue, 0, "") - switch c.FuncImplementation() { +func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { + context = b.CreateExtractValue(funcValue, 0, "") + switch b.FuncImplementation() { case compileopts.FuncValueDoubleword: - funcPtr = c.builder.CreateExtractValue(funcValue, 1, "") + funcPtr = b.CreateExtractValue(funcValue, 1, "") case compileopts.FuncValueSwitch: - llvmSig := c.getRawFuncType(sig) - sigGlobal := c.getTypeCode(sig) - funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") - funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "") + llvmSig := b.getRawFuncType(sig) + sigGlobal := b.getTypeCode(sig) + funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") + funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") default: panic("unimplemented func value variant") } diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go index 77e1af96a..2727228a1 100644 --- a/compiler/inlineasm.go +++ b/compiler/inlineasm.go @@ -18,8 +18,8 @@ import ( // func ReadRegister(name string) uintptr // // The register name must be a constant, for example "sp". -func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) { - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false) +func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) { + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false) regname := constant.StringVal(args[0].(*ssa.Const).Value) var asm string switch name { @@ -31,7 +31,7 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, panic("unknown architecture") } target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil } // This is a compiler builtin, which emits a piece of inline assembly with no @@ -41,12 +41,12 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, // func Asm(asm string) // // The provided assembly must be a constant. -func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) { +func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) { // Magic function: insert inline assembly instead of calling it. - fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false) asm := constant.StringVal(args[0].(*ssa.Const).Value) target := llvm.InlineAsm(fnType, asm, "", true, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil } // This is a compiler builtin, which allows assembly to be called in a flexible @@ -63,7 +63,7 @@ func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) { // "value": 1 // "result": &dest, // }) -func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) { asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value) registers := map[string]llvm.Value{} registerMap := instr.Args[1].(*ssa.MakeMap) @@ -73,17 +73,17 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, // ignore case *ssa.MapUpdate: if r.Block() != registerMap.Block() { - return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block") + return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block") } key := constant.StringVal(r.Key.(*ssa.Const).Value) //println("value:", r.Value.(*ssa.MakeInterface).X.String()) - registers[key] = frame.getValue(r.Value.(*ssa.MakeInterface).X) + registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X) case *ssa.Call: if r.Common() == instr { break } default: - return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String()) + return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String()) } } // TODO: handle dollar signs in asm string @@ -98,7 +98,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, name := s[1 : len(s)-1] if _, ok := registers[name]; !ok { if err == nil { - err = c.makeError(instr.Pos(), "unknown register name: "+name) + err = b.makeError(instr.Pos(), "unknown register name: "+name) } return s } @@ -112,7 +112,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, case llvm.PointerTypeKind: constraints = append(constraints, "*m") default: - err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) + err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) return s } } @@ -121,9 +121,9 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, if err != nil { return llvm.Value{}, err } - fnType := llvm.FunctionType(c.ctx.VoidType(), argTypes, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false) target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0) - return c.builder.CreateCall(target, args, ""), nil + return b.CreateCall(target, args, ""), nil } // This is a compiler builtin which emits an inline SVCall instruction. It can @@ -137,7 +137,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, // // The num parameter must be a constant. All other parameters may be any scalar // value supported by LLVM inline assembly. -func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error) { +func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) { num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value) llvmArgs := []llvm.Value{} argTypes := []llvm.Type{} @@ -150,7 +150,7 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error } else { constraints += ",{r" + strconv.Itoa(i) + "}" } - llvmValue := frame.getValue(arg) + llvmValue := b.getValue(arg) llvmArgs = append(llvmArgs, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } @@ -158,9 +158,9 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error // clobbered. r0 is used as an output register so doesn't have to be // marked as clobbered. constraints += ",~{r1},~{r2},~{r3}" - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0) - return c.builder.CreateCall(target, llvmArgs, ""), nil + return b.CreateCall(target, llvmArgs, ""), nil } // This is a compiler builtin which emits CSR instructions. It can be one of: @@ -172,38 +172,38 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error // // The csr parameter (method receiver) must be a constant. Other parameter can // be any value. -func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) { csrConst, ok := call.Args[0].(*ssa.Const) if !ok { - return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant") + return llvm.Value{}, b.makeError(call.Pos(), "CSR must be constant") } csr := csrConst.Uint64() switch name := call.StaticCallee().Name(); name { case "Get": // Note that this instruction may have side effects, and thus must be // marked as such. - fnType := llvm.FunctionType(c.uintptrType, nil, false) + fnType := llvm.FunctionType(b.uintptrType, nil, false) asm := fmt.Sprintf("csrr $0, %d", csr) target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil case "Set": - fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrw %d, $0", csr) target := llvm.InlineAsm(fnType, asm, "r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil case "SetBits": // Note: it may be possible to optimize this to csrrsi in many cases. - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrrs $0, %d, $1", csr) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil case "ClearBits": // Note: it may be possible to optimize this to csrrci in many cases. - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrrc $0, %d, $1", csr) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name) + return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name) } } diff --git a/compiler/interrupt.go b/compiler/interrupt.go index 7f4fd77ac..e4e31c080 100644 --- a/compiler/interrupt.go +++ b/compiler/interrupt.go @@ -8,62 +8,62 @@ import ( "tinygo.org/x/go-llvm" ) -// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that +// createInterruptGlobal creates a new runtime/interrupt.Interrupt struct that // will be lowered to a real interrupt during interrupt lowering. // // This two-stage approach allows unused interrupts to be optimized away if // necessary. -func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, error) { // Get the interrupt number, which must be a compile-time constant. id, ok := instr.Args[0].(*ssa.Const) if !ok { - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant") + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt ID is not a constant") } // Get the func value, which also must be a compile time constant. // Note that bound functions are allowed if the function has a pointer // receiver and is a global. This is rather strict but still allows for // idiomatic Go code. - funcValue := frame.getValue(instr.Args[1]) + funcValue := b.getValue(instr.Args[1]) if funcValue.IsAConstant().IsNil() { // Try to determine the cause of the non-constantness for a nice error // message. switch instr.Args[1].(type) { case *ssa.MakeClosure: // This may also be a bound method. - return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New") + return llvm.Value{}, b.makeError(instr.Pos(), "closures are not supported in interrupt.New") } // Fall back to a generic error. - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant") + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") } // Create a new global of type runtime/interrupt.handle. Globals of this // type are lowered in the interrupt lowering pass. - globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type() - globalLLVMType := c.getLLVMType(globalType) + globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type() + globalLLVMType := b.getLLVMType(globalType) globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10) - if global := c.mod.NamedGlobal(globalName); !global.IsNil() { - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program") + if global := b.mod.NamedGlobal(globalName); !global.IsNil() { + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program") } - global := llvm.AddGlobal(c.mod, globalLLVMType, globalName) + global := llvm.AddGlobal(b.mod, globalLLVMType, globalName) global.SetLinkage(llvm.PrivateLinkage) global.SetGlobalConstant(true) global.SetUnnamedAddr(true) initializer := llvm.ConstNull(globalLLVMType) initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0}) - initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0}) + initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 0}) global.SetInitializer(initializer) // Add debug info to the interrupt global. - if c.Debug() { - pos := c.ir.Program.Fset.Position(instr.Pos()) - diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{ + if b.Debug() { + pos := b.ir.Program.Fset.Position(instr.Pos()) + diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{ Name: "interrupt" + strconv.FormatInt(id.Int64(), 10), LinkageName: globalName, - File: c.getDIFile(pos.Filename), + File: b.getDIFile(pos.Filename), Line: pos.Line, - Type: c.getDIType(globalType), - Expr: c.dibuilder.CreateExpression(nil), + Type: b.getDIType(globalType), + Expr: b.dibuilder.CreateExpression(nil), LocalToUnit: false, }) global.AddMetadata(0, diglobal) @@ -71,21 +71,21 @@ func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llv // Create the runtime/interrupt.Interrupt type. It is a struct with a single // member of type int. - num := llvm.ConstPtrToInt(global, c.intType) - interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num}) + num := llvm.ConstPtrToInt(global, b.intType) + interrupt := llvm.ConstNamedStruct(b.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num}) // Add dummy "use" call for AVR, because interrupts may be used even though // they are never referenced again. This is unlike Cortex-M or the RISC-V // PLIC where each interrupt must be enabled using the interrupt number, and // thus keeps the Interrupt object alive. // This call is removed during interrupt lowering. - if strings.HasPrefix(c.Triple(), "avr") { - useFn := c.mod.NamedFunction("runtime/interrupt.use") + if strings.HasPrefix(b.Triple(), "avr") { + useFn := b.mod.NamedFunction("runtime/interrupt.use") if useFn.IsNil() { - useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) - useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType) + useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) + useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType) } - c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "") + b.CreateCall(useFn, []llvm.Value{interrupt}, "") } return interrupt, nil diff --git a/compiler/syscall.go b/compiler/syscall.go index 074a88caf..8bb873fac 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -10,14 +10,14 @@ import ( "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 := frame.getValue(call.Args[0]) +// createSyscall emits an inline system call instruction, depending on the +// target OS/arch. +func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { + num := b.getValue(call.Args[0]) var syscallResult llvm.Value switch { - case c.GOARCH() == "amd64": - if c.GOOS() == "darwin" { + case b.GOARCH() == "amd64": + if b.GOOS() == "darwin" { // Darwin adds this magic number to system call numbers: // // > Syscall classes for 64-bit system call entry. @@ -28,13 +28,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // > All system classes enter the kernel via the syscall instruction. // // Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h - num = c.builder.CreateOr(num, llvm.ConstInt(c.uintptrType, 0x2000000, false), "") + num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "") } // Sources: // https://stackoverflow.com/a/2538212 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall args := []llvm.Value{num} - argTypes := []llvm.Type{c.uintptrType} + argTypes := []llvm.Type{b.uintptrType} // Constraints will look something like: // "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}" constraints := "={rax},0" @@ -50,21 +50,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{r12}", "{r13}", }[i] - llvmValue := frame.getValue(arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } constraints += ",~{rcx},~{r11}" - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "386" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "386" && b.GOOS() == "linux": // Sources: // syscall(2) man page // https://stackoverflow.com/a/2538212 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#int_0x80 args := []llvm.Value{num} - argTypes := []llvm.Type{c.uintptrType} + argTypes := []llvm.Type{b.uintptrType} // Constraints will look something like: // "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}" constraints := "={eax},0" @@ -77,14 +77,14 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{edi}", "{ebp}", }[i] - llvmValue := frame.getValue(arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "arm" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "arm" && b.GOOS() == "linux": // Implement the EABI system call convention for Linux. // Source: syscall(2) man page. args := []llvm.Value{} @@ -102,21 +102,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{r5}", "{r6}", }[i] - llvmValue := frame.getValue(arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } args = append(args, num) - argTypes = append(argTypes, c.uintptrType) + argTypes = append(argTypes, b.uintptrType) constraints += ",{r7}" // syscall number for i := len(call.Args) - 1; i < 4; i++ { // r0-r3 get clobbered after the syscall returns constraints += ",~{r" + strconv.Itoa(i) + "}" } - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "arm64" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "arm64" && b.GOOS() == "linux": // Source: syscall(2) man page. args := []llvm.Value{} argTypes := []llvm.Type{} @@ -132,12 +132,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{x4}", "{x5}", }[i] - llvmValue := frame.getValue(arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } args = append(args, num) - argTypes = append(argTypes, c.uintptrType) + argTypes = append(argTypes, b.uintptrType) constraints += ",{x8}" // syscall number for i := len(call.Args) - 1; i < 8; i++ { // x0-x7 may get clobbered during the syscall following the aarch64 @@ -145,13 +145,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, constraints += ",~{x" + strconv.Itoa(i) + "}" } constraints += ",~{x16},~{x17}" // scratch registers - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) - syscallResult = c.builder.CreateCall(target, args, "") + syscallResult = b.CreateCall(target, args, "") default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) + return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH()) } - switch c.GOOS() { + switch b.GOOS() { case "linux", "freebsd": // Return values: r0, r1 uintptr, err Errno // Pseudocode: @@ -160,15 +160,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // 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.CreateSub(zero, syscallResult, ""), zero, "syscallError") - retval := llvm.Undef(c.ctx.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, "") + zero := llvm.ConstInt(b.uintptrType, 0, false) + inrange1 := b.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "") + inrange2 := b.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(b.uintptrType, 0xfffffffffffff000, true), "") // -4096 + hasError := b.CreateAnd(inrange1, inrange2, "") + errResult := b.CreateSelect(hasError, b.CreateSub(zero, syscallResult, ""), zero, "syscallError") + retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)) + retval = b.CreateInsertValue(retval, syscallResult, 0, "") + retval = b.CreateInsertValue(retval, zero, 1, "") + retval = b.CreateInsertValue(retval, errResult, 2, "") return retval, nil case "darwin": // Return values: r0, r1 uintptr, err Errno @@ -178,15 +178,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // err = syscallResult // } // return syscallResult, 0, err - zero := llvm.ConstInt(c.uintptrType, 0, false) - hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") - errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError") - retval := llvm.Undef(c.ctx.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, "") + zero := llvm.ConstInt(b.uintptrType, 0, false) + hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "") + errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError") + retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)) + retval = b.CreateInsertValue(retval, syscallResult, 0, "") + retval = b.CreateInsertValue(retval, zero, 1, "") + retval = b.CreateInsertValue(retval, errResult, 2, "") return retval, nil default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) + return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH()) } } diff --git a/compiler/volatile.go b/compiler/volatile.go index 237d55461..d2e51c84d 100644 --- a/compiler/volatile.go +++ b/compiler/volatile.go @@ -8,19 +8,23 @@ import ( "tinygo.org/x/go-llvm" ) -func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { - addr := frame.getValue(instr.Args[0]) - frame.createNilCheck(addr, "deref") - val := c.builder.CreateLoad(addr, "") +// createVolatileLoad is the implementation of the intrinsic function +// runtime/volatile.LoadT(). +func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) { + addr := b.getValue(instr.Args[0]) + b.createNilCheck(addr, "deref") + val := b.CreateLoad(addr, "") val.SetVolatile(true) return val, nil } -func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { - addr := frame.getValue(instr.Args[0]) - val := frame.getValue(instr.Args[1]) - frame.createNilCheck(addr, "deref") - store := c.builder.CreateStore(val, addr) +// createVolatileStore is the implementation of the intrinsic function +// runtime/volatile.StoreT(). +func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) { + addr := b.getValue(instr.Args[0]) + val := b.getValue(instr.Args[1]) + b.createNilCheck(addr, "deref") + store := b.CreateStore(val, addr) store.SetVolatile(true) return llvm.Value{}, nil } |