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