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  }