github.com/aykevl/tinygo@v0.5.0/compiler/calls.go (about) 1 package compiler 2 3 import ( 4 "golang.org/x/tools/go/ssa" 5 "tinygo.org/x/go-llvm" 6 ) 7 8 // For a description of the calling convention in prose, see: 9 // https://tinygo.org/compiler-internals/calling-convention/ 10 11 // The maximum number of arguments that can be expanded from a single struct. If 12 // a struct contains more fields, it is passed as a struct without expanding. 13 const MaxFieldsPerParam = 3 14 15 // Shortcut: create a call to runtime.<fnName> with the given arguments. 16 func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value { 17 runtimePkg := c.ir.Program.ImportedPackage("runtime") 18 member := runtimePkg.Members[fnName] 19 if member == nil { 20 panic("trying to call runtime." + fnName) 21 } 22 fn := c.ir.GetFunction(member.(*ssa.Function)) 23 if !fn.IsExported() { 24 args = append(args, llvm.Undef(c.i8ptrType)) // unused context parameter 25 args = append(args, llvm.ConstPointerNull(c.i8ptrType)) // coroutine handle 26 } 27 return c.createCall(fn.LLVMFn, args, name) 28 } 29 30 // Create a call to the given function with the arguments possibly expanded. 31 func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value { 32 expanded := make([]llvm.Value, 0, len(args)) 33 for _, arg := range args { 34 fragments := c.expandFormalParam(arg) 35 expanded = append(expanded, fragments...) 36 } 37 return c.builder.CreateCall(fn, expanded, name) 38 } 39 40 // Expand an argument type to a list that can be used in a function call 41 // paramter list. 42 func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type { 43 switch t.TypeKind() { 44 case llvm.StructTypeKind: 45 fields := c.flattenAggregateType(t) 46 if len(fields) <= MaxFieldsPerParam { 47 return fields 48 } else { 49 // failed to lower 50 return []llvm.Type{t} 51 } 52 default: 53 // TODO: split small arrays 54 return []llvm.Type{t} 55 } 56 } 57 58 // Equivalent of expandFormalParamType for parameter values. 59 func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value { 60 switch v.Type().TypeKind() { 61 case llvm.StructTypeKind: 62 fieldTypes := c.flattenAggregateType(v.Type()) 63 if len(fieldTypes) <= MaxFieldsPerParam { 64 fields := c.flattenAggregate(v) 65 if len(fields) != len(fieldTypes) { 66 panic("type and value param lowering don't match") 67 } 68 return fields 69 } else { 70 // failed to lower 71 return []llvm.Value{v} 72 } 73 default: 74 // TODO: split small arrays 75 return []llvm.Value{v} 76 } 77 } 78 79 // Try to flatten a struct type to a list of types. Returns a 1-element slice 80 // with the passed in type if this is not possible. 81 func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type { 82 switch t.TypeKind() { 83 case llvm.StructTypeKind: 84 fields := make([]llvm.Type, 0, t.StructElementTypesCount()) 85 for _, subfield := range t.StructElementTypes() { 86 subfields := c.flattenAggregateType(subfield) 87 fields = append(fields, subfields...) 88 } 89 return fields 90 default: 91 return []llvm.Type{t} 92 } 93 } 94 95 // Break down a struct into its elementary types for argument passing. The value 96 // equivalent of flattenAggregateType 97 func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value { 98 switch v.Type().TypeKind() { 99 case llvm.StructTypeKind: 100 fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount()) 101 for i := range v.Type().StructElementTypes() { 102 subfield := c.builder.CreateExtractValue(v, i, "") 103 subfields := c.flattenAggregate(subfield) 104 fields = append(fields, subfields...) 105 } 106 return fields 107 default: 108 return []llvm.Value{v} 109 } 110 } 111 112 // Collapse a list of fields into its original value. 113 func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value { 114 param, remaining := c.collapseFormalParamInternal(t, fields) 115 if len(remaining) != 0 { 116 panic("failed to expand back all fields") 117 } 118 return param 119 } 120 121 // Returns (value, remainingFields). Used by collapseFormalParam. 122 func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) { 123 switch t.TypeKind() { 124 case llvm.StructTypeKind: 125 if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam { 126 value, err := c.getZeroValue(t) 127 if err != nil { 128 panic("could not get zero value of struct: " + err.Error()) 129 } 130 for i, subtyp := range t.StructElementTypes() { 131 structField, remaining := c.collapseFormalParamInternal(subtyp, fields) 132 fields = remaining 133 value = c.builder.CreateInsertValue(value, structField, i, "") 134 } 135 return value, fields 136 } else { 137 // this struct was not flattened 138 return fields[0], fields[1:] 139 } 140 default: 141 return fields[0], fields[1:] 142 } 143 }