github.com/aykevl/tinygo@v0.5.0/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 type funcValueImplementation int 14 15 const ( 16 funcValueNone funcValueImplementation = iota 17 18 // A func value is implemented as a pair of pointers: 19 // {context, function pointer} 20 // where the context may be a pointer to a heap-allocated struct containing 21 // the free variables, or it may be undef if the function being pointed to 22 // doesn't need a context. The function pointer is a regular function 23 // pointer. 24 funcValueDoubleword 25 26 // As funcValueDoubleword, but with the function pointer replaced by a 27 // unique ID per function signature. Function values are called by using a 28 // switch statement and choosing which function to call. 29 funcValueSwitch 30 ) 31 32 // funcImplementation picks an appropriate func value implementation for the 33 // target. 34 func (c *Compiler) funcImplementation() funcValueImplementation { 35 if c.GOARCH == "wasm" { 36 return funcValueSwitch 37 } else { 38 return funcValueDoubleword 39 } 40 } 41 42 // createFuncValue creates a function value from a raw function pointer with no 43 // context. 44 func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) (llvm.Value, error) { 45 var funcValueScalar llvm.Value 46 switch c.funcImplementation() { 47 case funcValueDoubleword: 48 // Closure is: {context, function pointer} 49 funcValueScalar = funcPtr 50 case funcValueSwitch: 51 sigGlobal := c.getFuncSignature(sig) 52 funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" 53 funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) 54 if funcValueWithSignatureGlobal.IsNil() { 55 funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature") 56 funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{ 57 llvm.ConstPtrToInt(funcPtr, c.uintptrType), 58 sigGlobal, 59 }) 60 funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName) 61 funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature) 62 funcValueWithSignatureGlobal.SetGlobalConstant(true) 63 funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage) 64 } 65 funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType) 66 default: 67 panic("unimplemented func value variant") 68 } 69 funcValueType, err := c.getFuncType(sig) 70 if err != nil { 71 return llvm.Value{}, err 72 } 73 funcValue := llvm.Undef(funcValueType) 74 funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "") 75 funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "") 76 return funcValue, nil 77 } 78 79 // getFuncSignature returns a global for identification of a particular function 80 // signature. It is used in runtime.funcValueWithSignature and in calls to 81 // getFuncPtr. 82 func (c *Compiler) getFuncSignature(sig *types.Signature) llvm.Value { 83 typeCodeName := getTypeCodeName(sig) 84 sigGlobalName := "reflect/types.type:" + typeCodeName 85 sigGlobal := c.mod.NamedGlobal(sigGlobalName) 86 if sigGlobal.IsNil() { 87 sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName) 88 sigGlobal.SetInitializer(llvm.Undef(c.ctx.Int8Type())) 89 sigGlobal.SetGlobalConstant(true) 90 sigGlobal.SetLinkage(llvm.InternalLinkage) 91 } 92 return sigGlobal 93 } 94 95 // extractFuncScalar returns some scalar that can be used in comparisons. It is 96 // a cheap operation. 97 func (c *Compiler) extractFuncScalar(funcValue llvm.Value) llvm.Value { 98 return c.builder.CreateExtractValue(funcValue, 1, "") 99 } 100 101 // extractFuncContext extracts the context pointer from this function value. It 102 // is a cheap operation. 103 func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value { 104 return c.builder.CreateExtractValue(funcValue, 0, "") 105 } 106 107 // decodeFuncValue extracts the context and the function pointer from this func 108 // value. This may be an expensive operation. 109 func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value, err error) { 110 context = c.builder.CreateExtractValue(funcValue, 0, "") 111 switch c.funcImplementation() { 112 case funcValueDoubleword: 113 funcPtr = c.builder.CreateExtractValue(funcValue, 1, "") 114 case funcValueSwitch: 115 llvmSig, err := c.getRawFuncType(sig) 116 if err != nil { 117 return llvm.Value{}, llvm.Value{}, err 118 } 119 sigGlobal := c.getFuncSignature(sig) 120 funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") 121 funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "") 122 default: 123 panic("unimplemented func value variant") 124 } 125 return 126 } 127 128 // getFuncType returns the type of a func value given a signature. 129 func (c *Compiler) getFuncType(typ *types.Signature) (llvm.Type, error) { 130 switch c.funcImplementation() { 131 case funcValueDoubleword: 132 rawPtr, err := c.getRawFuncType(typ) 133 if err != nil { 134 return llvm.Type{}, err 135 } 136 return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false), nil 137 case funcValueSwitch: 138 return c.mod.GetTypeByName("runtime.funcValue"), nil 139 default: 140 panic("unimplemented func value variant") 141 } 142 } 143 144 // getRawFuncType returns a LLVM function pointer type for a given signature. 145 func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) { 146 // Get the return type. 147 var err error 148 var returnType llvm.Type 149 switch typ.Results().Len() { 150 case 0: 151 // No return values. 152 returnType = c.ctx.VoidType() 153 case 1: 154 // Just one return value. 155 returnType, err = c.getLLVMType(typ.Results().At(0).Type()) 156 if err != nil { 157 return llvm.Type{}, err 158 } 159 default: 160 // Multiple return values. Put them together in a struct. 161 // This appears to be the common way to handle multiple return values in 162 // LLVM. 163 members := make([]llvm.Type, typ.Results().Len()) 164 for i := 0; i < typ.Results().Len(); i++ { 165 returnType, err := c.getLLVMType(typ.Results().At(i).Type()) 166 if err != nil { 167 return llvm.Type{}, err 168 } 169 members[i] = returnType 170 } 171 returnType = c.ctx.StructType(members, false) 172 } 173 174 // Get the parameter types. 175 var paramTypes []llvm.Type 176 if typ.Recv() != nil { 177 recv, err := c.getLLVMType(typ.Recv().Type()) 178 if err != nil { 179 return llvm.Type{}, err 180 } 181 if recv.StructName() == "runtime._interface" { 182 // This is a call on an interface, not a concrete type. 183 // The receiver is not an interface, but a i8* type. 184 recv = c.i8ptrType 185 } 186 paramTypes = append(paramTypes, c.expandFormalParamType(recv)...) 187 } 188 for i := 0; i < typ.Params().Len(); i++ { 189 subType, err := c.getLLVMType(typ.Params().At(i).Type()) 190 if err != nil { 191 return llvm.Type{}, err 192 } 193 paramTypes = append(paramTypes, c.expandFormalParamType(subType)...) 194 } 195 // All functions take these parameters at the end. 196 paramTypes = append(paramTypes, c.i8ptrType) // context 197 paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine 198 199 // Make a func type out of the signature. 200 return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace), nil 201 } 202 203 // parseMakeClosure makes a function value (with context) from the given 204 // closure expression. 205 func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.Value, error) { 206 if len(expr.Bindings) == 0 { 207 panic("unexpected: MakeClosure without bound variables") 208 } 209 f := c.ir.GetFunction(expr.Fn.(*ssa.Function)) 210 211 // Collect all bound variables. 212 boundVars := make([]llvm.Value, 0, len(expr.Bindings)) 213 boundVarTypes := make([]llvm.Type, 0, len(expr.Bindings)) 214 for _, binding := range expr.Bindings { 215 // The context stores the bound variables. 216 llvmBoundVar, err := c.parseExpr(frame, binding) 217 if err != nil { 218 return llvm.Value{}, err 219 } 220 boundVars = append(boundVars, llvmBoundVar) 221 boundVarTypes = append(boundVarTypes, llvmBoundVar.Type()) 222 } 223 contextType := c.ctx.StructType(boundVarTypes, false) 224 225 // Allocate memory for the context. 226 contextAlloc := llvm.Value{} 227 contextHeapAlloc := llvm.Value{} 228 if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) { 229 // Context fits in a pointer - e.g. when it is a pointer. Store it 230 // directly in the stack after a convert. 231 // Because contextType is a struct and we have to cast it to a *i8, 232 // store it in an alloca first for bitcasting (store+bitcast+load). 233 contextAlloc = c.builder.CreateAlloca(contextType, "") 234 } else { 235 // Context is bigger than a pointer, so allocate it on the heap. 236 size := c.targetData.TypeAllocSize(contextType) 237 sizeValue := llvm.ConstInt(c.uintptrType, size, false) 238 contextHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "") 239 contextAlloc = c.builder.CreateBitCast(contextHeapAlloc, llvm.PointerType(contextType, 0), "") 240 } 241 242 // Store all bound variables in the alloca or heap pointer. 243 for i, boundVar := range boundVars { 244 indices := []llvm.Value{ 245 llvm.ConstInt(c.ctx.Int32Type(), 0, false), 246 llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false), 247 } 248 gep := c.builder.CreateInBoundsGEP(contextAlloc, indices, "") 249 c.builder.CreateStore(boundVar, gep) 250 } 251 252 context := llvm.Value{} 253 if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) { 254 // Load value (as *i8) from the alloca. 255 contextAlloc = c.builder.CreateBitCast(contextAlloc, llvm.PointerType(c.i8ptrType, 0), "") 256 context = c.builder.CreateLoad(contextAlloc, "") 257 } else { 258 // Get the original heap allocation pointer, which already is an 259 // *i8. 260 context = contextHeapAlloc 261 } 262 263 // Create the closure. 264 return c.createFuncValue(f.LLVMFn, context, f.Signature) 265 }