11package compiler
22
33import (
4+ "go/types"
5+
46 "tinygo.org/x/go-llvm"
57)
68
@@ -11,6 +13,16 @@ import (
1113// a struct contains more fields, it is passed as a struct without expanding.
1214const MaxFieldsPerParam = 3
1315
16+ // paramFlags identifies parameter attributes for flags. Most importantly, it
17+ // determines which parameters are dereferenceable_or_null and which aren't.
18+ type paramFlags uint8
19+
20+ const (
21+ // Parameter may have the deferenceable_or_null attribute. This attribute
22+ // cannot be applied to unsafe.Pointer and to the data pointer of slices.
23+ paramIsDeferenceableOrNull = 1 << iota
24+ )
25+
1426// createCall creates a new call to runtime.<fnName> with the given arguments.
1527func (b * builder ) createRuntimeCall (fnName string , args []llvm.Value , name string ) llvm.Value {
1628 fullName := "runtime." + fnName
@@ -36,19 +48,19 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
3648
3749// Expand an argument type to a list that can be used in a function call
3850// parameter list.
39- func expandFormalParamType (t llvm.Type ) []llvm.Type {
51+ func expandFormalParamType (t llvm.Type , goType types. Type ) ( []llvm.Type , [] paramFlags ) {
4052 switch t .TypeKind () {
4153 case llvm .StructTypeKind :
42- fields := flattenAggregateType (t )
54+ fields , fieldFlags := flattenAggregateType (t , goType )
4355 if len (fields ) <= MaxFieldsPerParam {
44- return fields
56+ return fields , fieldFlags
4557 } else {
4658 // failed to lower
47- return []llvm.Type {t }
59+ return []llvm.Type {t }, [] paramFlags { getTypeFlags ( goType )}
4860 }
4961 default :
5062 // TODO: split small arrays
51- return []llvm.Type {t }
63+ return []llvm.Type {t }, [] paramFlags { getTypeFlags ( goType )}
5264 }
5365}
5466
@@ -79,7 +91,7 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
7991func (b * builder ) expandFormalParam (v llvm.Value ) []llvm.Value {
8092 switch v .Type ().TypeKind () {
8193 case llvm .StructTypeKind :
82- fieldTypes := flattenAggregateType (v .Type ())
94+ fieldTypes , _ := flattenAggregateType (v .Type (), nil )
8395 if len (fieldTypes ) <= MaxFieldsPerParam {
8496 fields := b .flattenAggregate (v )
8597 if len (fields ) != len (fieldTypes ) {
@@ -98,17 +110,62 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
98110
99111// Try to flatten a struct type to a list of types. Returns a 1-element slice
100112// with the passed in type if this is not possible.
101- func flattenAggregateType (t llvm.Type ) []llvm.Type {
113+ func flattenAggregateType (t llvm.Type , goType types.Type ) ([]llvm.Type , []paramFlags ) {
114+ typeFlags := getTypeFlags (goType )
102115 switch t .TypeKind () {
103116 case llvm .StructTypeKind :
104117 fields := make ([]llvm.Type , 0 , t .StructElementTypesCount ())
105- for _ , subfield := range t .StructElementTypes () {
106- subfields := flattenAggregateType (subfield )
118+ fieldFlags := make ([]paramFlags , 0 , cap (fields ))
119+ for i , subfield := range t .StructElementTypes () {
120+ subfields , subfieldFlags := flattenAggregateType (subfield , extractSubfield (goType , i ))
121+ for i := range subfieldFlags {
122+ subfieldFlags [i ] |= typeFlags
123+ }
107124 fields = append (fields , subfields ... )
125+ fieldFlags = append (fieldFlags , subfieldFlags ... )
108126 }
109- return fields
127+ return fields , fieldFlags
128+ default :
129+ return []llvm.Type {t }, []paramFlags {typeFlags }
130+ }
131+ }
132+
133+ // getTypeFlags returns the type flags for a given type. It will not recurse
134+ // into sub-types (such as in structs).
135+ func getTypeFlags (t types.Type ) paramFlags {
136+ if t == nil {
137+ return 0
138+ }
139+ switch t .Underlying ().(type ) {
140+ case * types.Pointer :
141+ // Pointers in Go must either point to an object or be nil.
142+ return paramIsDeferenceableOrNull
143+ case * types.Chan , * types.Map :
144+ // Channels and maps are implemented as pointers pointing to some
145+ // object, and follow the same rules as *types.Pointer.
146+ return paramIsDeferenceableOrNull
147+ default :
148+ return 0
149+ }
150+ }
151+
152+ // extractSubfield extracts a field from a struct, or returns null if this is
153+ // not a struct and thus no subfield can be obtained.
154+ func extractSubfield (t types.Type , field int ) types.Type {
155+ if t == nil {
156+ return nil
157+ }
158+ switch t := t .Underlying ().(type ) {
159+ case * types.Struct :
160+ return t .Field (field ).Type ()
161+ case * types.Interface , * types.Slice , * types.Basic , * types.Signature :
162+ // These Go types are (sometimes) implemented as LLVM structs but can't
163+ // really be split further up in Go (with the possible exception of
164+ // complex numbers).
165+ return nil
110166 default :
111- return []llvm.Type {t }
167+ // This should be unreachable.
168+ panic ("cannot split subfield: " + t .String ())
112169 }
113170}
114171
@@ -169,7 +226,8 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
169226func (b * builder ) collapseFormalParamInternal (t llvm.Type , fields []llvm.Value ) (llvm.Value , []llvm.Value ) {
170227 switch t .TypeKind () {
171228 case llvm .StructTypeKind :
172- if len (flattenAggregateType (t )) <= MaxFieldsPerParam {
229+ flattened , _ := flattenAggregateType (t , nil )
230+ if len (flattened ) <= MaxFieldsPerParam {
173231 value := llvm .ConstNull (t )
174232 for i , subtyp := range t .StructElementTypes () {
175233 structField , remaining := b .collapseFormalParamInternal (subtyp , fields )
0 commit comments