github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/func.go (about) 1 package compiler 2 3 // This file implements function values and closures. It may need some lowering 4 // in a later step, see func-lowering.go. 5 6 import ( 7 "go/types" 8 9 "golang.org/x/tools/go/ssa" 10 "tinygo.org/x/go-llvm" 11 ) 12 13 // createFuncValue creates a function value from a raw function pointer with no 14 // context. 15 func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { 16 // Closure is: {context, function pointer} 17 funcValueType := b.getFuncType(sig) 18 funcValue := llvm.Undef(funcValueType) 19 funcValue = b.CreateInsertValue(funcValue, context, 0, "") 20 funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "") 21 return funcValue 22 } 23 24 // extractFuncScalar returns some scalar that can be used in comparisons. It is 25 // a cheap operation. 26 func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { 27 return b.CreateExtractValue(funcValue, 1, "") 28 } 29 30 // extractFuncContext extracts the context pointer from this function value. It 31 // is a cheap operation. 32 func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value { 33 return b.CreateExtractValue(funcValue, 0, "") 34 } 35 36 // decodeFuncValue extracts the context and the function pointer from this func 37 // value. 38 func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) { 39 context = b.CreateExtractValue(funcValue, 0, "") 40 funcPtr = b.CreateExtractValue(funcValue, 1, "") 41 return 42 } 43 44 // getFuncType returns the type of a func value given a signature. 45 func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { 46 return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false) 47 } 48 49 // getLLVMFunctionType returns a LLVM function type for a given signature. 50 func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type { 51 // Get the return type. 52 var returnType llvm.Type 53 switch typ.Results().Len() { 54 case 0: 55 // No return values. 56 returnType = c.ctx.VoidType() 57 case 1: 58 // Just one return value. 59 returnType = c.getLLVMType(typ.Results().At(0).Type()) 60 default: 61 // Multiple return values. Put them together in a struct. 62 // This appears to be the common way to handle multiple return values in 63 // LLVM. 64 members := make([]llvm.Type, typ.Results().Len()) 65 for i := 0; i < typ.Results().Len(); i++ { 66 members[i] = c.getLLVMType(typ.Results().At(i).Type()) 67 } 68 returnType = c.ctx.StructType(members, false) 69 } 70 71 // Get the parameter types. 72 var paramTypes []llvm.Type 73 if typ.Recv() != nil { 74 recv := c.getLLVMType(typ.Recv().Type()) 75 if recv.StructName() == "runtime._interface" { 76 // This is a call on an interface, not a concrete type. 77 // The receiver is not an interface, but a i8* type. 78 recv = c.dataPtrType 79 } 80 for _, info := range c.expandFormalParamType(recv, "", nil) { 81 paramTypes = append(paramTypes, info.llvmType) 82 } 83 } 84 for i := 0; i < typ.Params().Len(); i++ { 85 subType := c.getLLVMType(typ.Params().At(i).Type()) 86 for _, info := range c.expandFormalParamType(subType, "", nil) { 87 paramTypes = append(paramTypes, info.llvmType) 88 } 89 } 90 // All functions take these parameters at the end. 91 paramTypes = append(paramTypes, c.dataPtrType) // context 92 93 // Make a func type out of the signature. 94 return llvm.FunctionType(returnType, paramTypes, false) 95 } 96 97 // parseMakeClosure makes a function value (with context) from the given 98 // closure expression. 99 func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) { 100 if len(expr.Bindings) == 0 { 101 panic("unexpected: MakeClosure without bound variables") 102 } 103 f := expr.Fn.(*ssa.Function) 104 105 // Collect all bound variables. 106 boundVars := make([]llvm.Value, len(expr.Bindings)) 107 for i, binding := range expr.Bindings { 108 // The context stores the bound variables. 109 llvmBoundVar := b.getValue(binding, getPos(expr)) 110 boundVars[i] = llvmBoundVar 111 } 112 113 // Store the bound variables in a single object, allocating it on the heap 114 // if necessary. 115 context := b.emitPointerPack(boundVars) 116 117 // Create the closure. 118 _, fn := b.getFunction(f) 119 return b.createFuncValue(fn, context, f.Signature), nil 120 }