github.com/aykevl/tinygo@v0.5.0/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  type funcValueImplementation int
    14  
    15  const (
    16  	funcValueNone funcValueImplementation = iota
    17  
    18  	// A func value is implemented as a pair of pointers:
    19  	//     {context, function pointer}
    20  	// where the context may be a pointer to a heap-allocated struct containing
    21  	// the free variables, or it may be undef if the function being pointed to
    22  	// doesn't need a context. The function pointer is a regular function
    23  	// pointer.
    24  	funcValueDoubleword
    25  
    26  	// As funcValueDoubleword, but with the function pointer replaced by a
    27  	// unique ID per function signature. Function values are called by using a
    28  	// switch statement and choosing which function to call.
    29  	funcValueSwitch
    30  )
    31  
    32  // funcImplementation picks an appropriate func value implementation for the
    33  // target.
    34  func (c *Compiler) funcImplementation() funcValueImplementation {
    35  	if c.GOARCH == "wasm" {
    36  		return funcValueSwitch
    37  	} else {
    38  		return funcValueDoubleword
    39  	}
    40  }
    41  
    42  // createFuncValue creates a function value from a raw function pointer with no
    43  // context.
    44  func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) (llvm.Value, error) {
    45  	var funcValueScalar llvm.Value
    46  	switch c.funcImplementation() {
    47  	case funcValueDoubleword:
    48  		// Closure is: {context, function pointer}
    49  		funcValueScalar = funcPtr
    50  	case funcValueSwitch:
    51  		sigGlobal := c.getFuncSignature(sig)
    52  		funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
    53  		funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
    54  		if funcValueWithSignatureGlobal.IsNil() {
    55  			funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature")
    56  			funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
    57  				llvm.ConstPtrToInt(funcPtr, c.uintptrType),
    58  				sigGlobal,
    59  			})
    60  			funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
    61  			funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
    62  			funcValueWithSignatureGlobal.SetGlobalConstant(true)
    63  			funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
    64  		}
    65  		funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
    66  	default:
    67  		panic("unimplemented func value variant")
    68  	}
    69  	funcValueType, err := c.getFuncType(sig)
    70  	if err != nil {
    71  		return llvm.Value{}, err
    72  	}
    73  	funcValue := llvm.Undef(funcValueType)
    74  	funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "")
    75  	funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
    76  	return funcValue, nil
    77  }
    78  
    79  // getFuncSignature returns a global for identification of a particular function
    80  // signature. It is used in runtime.funcValueWithSignature and in calls to
    81  // getFuncPtr.
    82  func (c *Compiler) getFuncSignature(sig *types.Signature) llvm.Value {
    83  	typeCodeName := getTypeCodeName(sig)
    84  	sigGlobalName := "reflect/types.type:" + typeCodeName
    85  	sigGlobal := c.mod.NamedGlobal(sigGlobalName)
    86  	if sigGlobal.IsNil() {
    87  		sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
    88  		sigGlobal.SetInitializer(llvm.Undef(c.ctx.Int8Type()))
    89  		sigGlobal.SetGlobalConstant(true)
    90  		sigGlobal.SetLinkage(llvm.InternalLinkage)
    91  	}
    92  	return sigGlobal
    93  }
    94  
    95  // extractFuncScalar returns some scalar that can be used in comparisons. It is
    96  // a cheap operation.
    97  func (c *Compiler) extractFuncScalar(funcValue llvm.Value) llvm.Value {
    98  	return c.builder.CreateExtractValue(funcValue, 1, "")
    99  }
   100  
   101  // extractFuncContext extracts the context pointer from this function value. It
   102  // is a cheap operation.
   103  func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
   104  	return c.builder.CreateExtractValue(funcValue, 0, "")
   105  }
   106  
   107  // decodeFuncValue extracts the context and the function pointer from this func
   108  // value. This may be an expensive operation.
   109  func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value, err error) {
   110  	context = c.builder.CreateExtractValue(funcValue, 0, "")
   111  	switch c.funcImplementation() {
   112  	case funcValueDoubleword:
   113  		funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
   114  	case funcValueSwitch:
   115  		llvmSig, err := c.getRawFuncType(sig)
   116  		if err != nil {
   117  			return llvm.Value{}, llvm.Value{}, err
   118  		}
   119  		sigGlobal := c.getFuncSignature(sig)
   120  		funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
   121  		funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
   122  	default:
   123  		panic("unimplemented func value variant")
   124  	}
   125  	return
   126  }
   127  
   128  // getFuncType returns the type of a func value given a signature.
   129  func (c *Compiler) getFuncType(typ *types.Signature) (llvm.Type, error) {
   130  	switch c.funcImplementation() {
   131  	case funcValueDoubleword:
   132  		rawPtr, err := c.getRawFuncType(typ)
   133  		if err != nil {
   134  			return llvm.Type{}, err
   135  		}
   136  		return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false), nil
   137  	case funcValueSwitch:
   138  		return c.mod.GetTypeByName("runtime.funcValue"), nil
   139  	default:
   140  		panic("unimplemented func value variant")
   141  	}
   142  }
   143  
   144  // getRawFuncType returns a LLVM function pointer type for a given signature.
   145  func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
   146  	// Get the return type.
   147  	var err error
   148  	var returnType llvm.Type
   149  	switch typ.Results().Len() {
   150  	case 0:
   151  		// No return values.
   152  		returnType = c.ctx.VoidType()
   153  	case 1:
   154  		// Just one return value.
   155  		returnType, err = c.getLLVMType(typ.Results().At(0).Type())
   156  		if err != nil {
   157  			return llvm.Type{}, err
   158  		}
   159  	default:
   160  		// Multiple return values. Put them together in a struct.
   161  		// This appears to be the common way to handle multiple return values in
   162  		// LLVM.
   163  		members := make([]llvm.Type, typ.Results().Len())
   164  		for i := 0; i < typ.Results().Len(); i++ {
   165  			returnType, err := c.getLLVMType(typ.Results().At(i).Type())
   166  			if err != nil {
   167  				return llvm.Type{}, err
   168  			}
   169  			members[i] = returnType
   170  		}
   171  		returnType = c.ctx.StructType(members, false)
   172  	}
   173  
   174  	// Get the parameter types.
   175  	var paramTypes []llvm.Type
   176  	if typ.Recv() != nil {
   177  		recv, err := c.getLLVMType(typ.Recv().Type())
   178  		if err != nil {
   179  			return llvm.Type{}, err
   180  		}
   181  		if recv.StructName() == "runtime._interface" {
   182  			// This is a call on an interface, not a concrete type.
   183  			// The receiver is not an interface, but a i8* type.
   184  			recv = c.i8ptrType
   185  		}
   186  		paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
   187  	}
   188  	for i := 0; i < typ.Params().Len(); i++ {
   189  		subType, err := c.getLLVMType(typ.Params().At(i).Type())
   190  		if err != nil {
   191  			return llvm.Type{}, err
   192  		}
   193  		paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
   194  	}
   195  	// All functions take these parameters at the end.
   196  	paramTypes = append(paramTypes, c.i8ptrType) // context
   197  	paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
   198  
   199  	// Make a func type out of the signature.
   200  	return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace), nil
   201  }
   202  
   203  // parseMakeClosure makes a function value (with context) from the given
   204  // closure expression.
   205  func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.Value, error) {
   206  	if len(expr.Bindings) == 0 {
   207  		panic("unexpected: MakeClosure without bound variables")
   208  	}
   209  	f := c.ir.GetFunction(expr.Fn.(*ssa.Function))
   210  
   211  	// Collect all bound variables.
   212  	boundVars := make([]llvm.Value, 0, len(expr.Bindings))
   213  	boundVarTypes := make([]llvm.Type, 0, len(expr.Bindings))
   214  	for _, binding := range expr.Bindings {
   215  		// The context stores the bound variables.
   216  		llvmBoundVar, err := c.parseExpr(frame, binding)
   217  		if err != nil {
   218  			return llvm.Value{}, err
   219  		}
   220  		boundVars = append(boundVars, llvmBoundVar)
   221  		boundVarTypes = append(boundVarTypes, llvmBoundVar.Type())
   222  	}
   223  	contextType := c.ctx.StructType(boundVarTypes, false)
   224  
   225  	// Allocate memory for the context.
   226  	contextAlloc := llvm.Value{}
   227  	contextHeapAlloc := llvm.Value{}
   228  	if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
   229  		// Context fits in a pointer - e.g. when it is a pointer. Store it
   230  		// directly in the stack after a convert.
   231  		// Because contextType is a struct and we have to cast it to a *i8,
   232  		// store it in an alloca first for bitcasting (store+bitcast+load).
   233  		contextAlloc = c.builder.CreateAlloca(contextType, "")
   234  	} else {
   235  		// Context is bigger than a pointer, so allocate it on the heap.
   236  		size := c.targetData.TypeAllocSize(contextType)
   237  		sizeValue := llvm.ConstInt(c.uintptrType, size, false)
   238  		contextHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
   239  		contextAlloc = c.builder.CreateBitCast(contextHeapAlloc, llvm.PointerType(contextType, 0), "")
   240  	}
   241  
   242  	// Store all bound variables in the alloca or heap pointer.
   243  	for i, boundVar := range boundVars {
   244  		indices := []llvm.Value{
   245  			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   246  			llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
   247  		}
   248  		gep := c.builder.CreateInBoundsGEP(contextAlloc, indices, "")
   249  		c.builder.CreateStore(boundVar, gep)
   250  	}
   251  
   252  	context := llvm.Value{}
   253  	if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
   254  		// Load value (as *i8) from the alloca.
   255  		contextAlloc = c.builder.CreateBitCast(contextAlloc, llvm.PointerType(c.i8ptrType, 0), "")
   256  		context = c.builder.CreateLoad(contextAlloc, "")
   257  	} else {
   258  		// Get the original heap allocation pointer, which already is an
   259  		// *i8.
   260  		context = contextHeapAlloc
   261  	}
   262  
   263  	// Create the closure.
   264  	return c.createFuncValue(f.LLVMFn, context, f.Signature)
   265  }