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