
This is the first commit in a series to refactor the compiler. The intention is to make sure every function to be compiled eventually has its own IR builder. This will make it much easier to do other refactorings in the future: * Most code won't depend (directly) on the central Compiler object, perhaps making it possible to eliminate it in the future. Right now it's embedded in the `builder` struct but individual fields from the `Compiler` can easily be moved into the `builder` object. * Some functions are not directly exposed in Go SSA, they are wrapper functions for something. At the moment they are included in the list of functions to be compiled with the reachability analysis (SimpleDCE) in the ir package, but eventually this reachability analys will be removed. At that point, it would be very convenient to be able to simply build a function with a new IR builder. The `compilerContext` struct makes sure that it is not possible for `builder` methods to accidentally use global state such as the global IR builder. It is a transitional mechanism and may be removed when finished.
158 строки
5,8 КиБ
Go
158 строки
5,8 КиБ
Go
package compiler
|
|
|
|
// This file implements function values and closures. It may need some lowering
|
|
// in a later step, see func-lowering.go.
|
|
|
|
import (
|
|
"go/types"
|
|
|
|
"github.com/tinygo-org/tinygo/compileopts"
|
|
"golang.org/x/tools/go/ssa"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// createFuncValue creates a function value from a raw function pointer with no
|
|
// context.
|
|
func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
|
var funcValueScalar llvm.Value
|
|
switch c.FuncImplementation() {
|
|
case compileopts.FuncValueDoubleword:
|
|
// Closure is: {context, function pointer}
|
|
funcValueScalar = funcPtr
|
|
case compileopts.FuncValueSwitch:
|
|
sigGlobal := c.getTypeCode(sig)
|
|
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
|
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
|
if funcValueWithSignatureGlobal.IsNil() {
|
|
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
|
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
|
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
|
sigGlobal,
|
|
})
|
|
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
|
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
|
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
|
funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
|
|
}
|
|
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
|
default:
|
|
panic("unimplemented func value variant")
|
|
}
|
|
funcValueType := c.getFuncType(sig)
|
|
funcValue := llvm.Undef(funcValueType)
|
|
funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "")
|
|
funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
|
|
return funcValue
|
|
}
|
|
|
|
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
|
// a cheap operation.
|
|
func (c *Compiler) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
|
return c.builder.CreateExtractValue(funcValue, 1, "")
|
|
}
|
|
|
|
// extractFuncContext extracts the context pointer from this function value. It
|
|
// is a cheap operation.
|
|
func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
|
return c.builder.CreateExtractValue(funcValue, 0, "")
|
|
}
|
|
|
|
// decodeFuncValue extracts the context and the function pointer from this func
|
|
// value. This may be an expensive operation.
|
|
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
|
context = c.builder.CreateExtractValue(funcValue, 0, "")
|
|
switch c.FuncImplementation() {
|
|
case compileopts.FuncValueDoubleword:
|
|
funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
|
|
case compileopts.FuncValueSwitch:
|
|
llvmSig := c.getRawFuncType(sig)
|
|
sigGlobal := c.getTypeCode(sig)
|
|
funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
|
funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
|
|
default:
|
|
panic("unimplemented func value variant")
|
|
}
|
|
return
|
|
}
|
|
|
|
// getFuncType returns the type of a func value given a signature.
|
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
|
switch c.FuncImplementation() {
|
|
case compileopts.FuncValueDoubleword:
|
|
rawPtr := c.getRawFuncType(typ)
|
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
|
case compileopts.FuncValueSwitch:
|
|
return c.getLLVMRuntimeType("funcValue")
|
|
default:
|
|
panic("unimplemented func value variant")
|
|
}
|
|
}
|
|
|
|
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
|
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|
// Get the return type.
|
|
var returnType llvm.Type
|
|
switch typ.Results().Len() {
|
|
case 0:
|
|
// No return values.
|
|
returnType = c.ctx.VoidType()
|
|
case 1:
|
|
// Just one return value.
|
|
returnType = c.getLLVMType(typ.Results().At(0).Type())
|
|
default:
|
|
// Multiple return values. Put them together in a struct.
|
|
// This appears to be the common way to handle multiple return values in
|
|
// LLVM.
|
|
members := make([]llvm.Type, typ.Results().Len())
|
|
for i := 0; i < typ.Results().Len(); i++ {
|
|
members[i] = c.getLLVMType(typ.Results().At(i).Type())
|
|
}
|
|
returnType = c.ctx.StructType(members, false)
|
|
}
|
|
|
|
// Get the parameter types.
|
|
var paramTypes []llvm.Type
|
|
if typ.Recv() != nil {
|
|
recv := c.getLLVMType(typ.Recv().Type())
|
|
if recv.StructName() == "runtime._interface" {
|
|
// This is a call on an interface, not a concrete type.
|
|
// The receiver is not an interface, but a i8* type.
|
|
recv = c.i8ptrType
|
|
}
|
|
paramTypes = append(paramTypes, expandFormalParamType(recv)...)
|
|
}
|
|
for i := 0; i < typ.Params().Len(); i++ {
|
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
|
paramTypes = append(paramTypes, expandFormalParamType(subType)...)
|
|
}
|
|
// All functions take these parameters at the end.
|
|
paramTypes = append(paramTypes, c.i8ptrType) // context
|
|
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
|
|
|
// Make a func type out of the signature.
|
|
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
|
|
}
|
|
|
|
// parseMakeClosure makes a function value (with context) from the given
|
|
// closure expression.
|
|
func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.Value, error) {
|
|
if len(expr.Bindings) == 0 {
|
|
panic("unexpected: MakeClosure without bound variables")
|
|
}
|
|
f := c.ir.GetFunction(expr.Fn.(*ssa.Function))
|
|
|
|
// Collect all bound variables.
|
|
boundVars := make([]llvm.Value, len(expr.Bindings))
|
|
for i, binding := range expr.Bindings {
|
|
// The context stores the bound variables.
|
|
llvmBoundVar := c.getValue(frame, binding)
|
|
boundVars[i] = llvmBoundVar
|
|
}
|
|
|
|
// Store the bound variables in a single object, allocating it on the heap
|
|
// if necessary.
|
|
context := c.emitPointerPack(boundVars)
|
|
|
|
// Create the closure.
|
|
return c.createFuncValue(f.LLVMFn, context, f.Signature), nil
|
|
}
|