Implement closures and bound methods
Этот коммит содержится в:
родитель
58b853bbef
коммит
58c87329d4
5 изменённых файлов: 357 добавлений и 49 удалений
|
@ -48,13 +48,14 @@ Currently supported features:
|
||||||
* slices (partially)
|
* slices (partially)
|
||||||
* maps (very rough, unfinished)
|
* maps (very rough, unfinished)
|
||||||
* defer (only in trivial cases)
|
* defer (only in trivial cases)
|
||||||
|
* closures
|
||||||
|
* bound methods
|
||||||
|
|
||||||
Not yet supported:
|
Not yet supported:
|
||||||
|
|
||||||
* complex numbers
|
* complex numbers
|
||||||
* garbage collection
|
* garbage collection
|
||||||
* recover
|
* recover
|
||||||
* closures
|
|
||||||
* channels
|
* channels
|
||||||
* introspection (if it ever gets implemented)
|
* introspection (if it ever gets implemented)
|
||||||
* ...
|
* ...
|
||||||
|
|
256
compiler.go
256
compiler.go
|
@ -214,6 +214,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
||||||
c.ir.SimpleDCE() // remove most dead code
|
c.ir.SimpleDCE() // remove most dead code
|
||||||
c.ir.AnalyseCallgraph() // set up callgraph
|
c.ir.AnalyseCallgraph() // set up callgraph
|
||||||
c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface
|
c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface
|
||||||
|
c.ir.AnalyseFunctionPointers() // determine which function pointer signatures need context
|
||||||
c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
|
c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
|
||||||
c.ir.AnalyseGoCalls() // check whether we need a scheduler
|
c.ir.AnalyseGoCalls() // check whether we need a scheduler
|
||||||
|
|
||||||
|
@ -542,8 +543,18 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
|
||||||
}
|
}
|
||||||
paramTypes = append(paramTypes, subType)
|
paramTypes = append(paramTypes, subType)
|
||||||
}
|
}
|
||||||
// make a function pointer of it
|
var ptr llvm.Type
|
||||||
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0), nil
|
if c.ir.SignatureNeedsContext(typ) {
|
||||||
|
// make a closure type (with a function pointer type inside):
|
||||||
|
// {context, funcptr}
|
||||||
|
paramTypes = append(paramTypes, c.i8ptrType)
|
||||||
|
ptr = llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
|
||||||
|
ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false)
|
||||||
|
} else {
|
||||||
|
// make a simple function pointer
|
||||||
|
ptr = llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
|
||||||
|
}
|
||||||
|
return ptr, nil
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
elemType, err := c.getLLVMType(typ.Elem())
|
elemType, err := c.getLLVMType(typ.Elem())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -703,6 +714,12 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
||||||
frame.params[param] = i
|
frame.params[param] = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.ir.FunctionNeedsContext(f) {
|
||||||
|
// This function gets an extra parameter: the context pointer (for
|
||||||
|
// closures and bound methods). Add it as an extra paramter here.
|
||||||
|
paramTypes = append(paramTypes, c.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
fnType := llvm.FunctionType(retType, paramTypes, false)
|
fnType := llvm.FunctionType(retType, paramTypes, false)
|
||||||
|
|
||||||
name := f.LinkName()
|
name := f.LinkName()
|
||||||
|
@ -781,7 +798,13 @@ func (c *Compiler) getInterpretedValue(value Value) (llvm.Value, error) {
|
||||||
}
|
}
|
||||||
return getZeroValue(llvmType)
|
return getZeroValue(llvmType)
|
||||||
}
|
}
|
||||||
return c.ir.GetFunction(value.Elem).llvmFn, nil
|
fn := c.ir.GetFunction(value.Elem)
|
||||||
|
ptr := fn.llvmFn
|
||||||
|
if c.ir.SignatureNeedsContext(fn.fn.Signature) {
|
||||||
|
// Create closure value: {context, function pointer}
|
||||||
|
ptr = llvm.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
|
||||||
|
}
|
||||||
|
return ptr, nil
|
||||||
|
|
||||||
case *GlobalValue:
|
case *GlobalValue:
|
||||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||||
|
@ -1001,6 +1024,54 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
||||||
frame.locals[param] = llvmParam
|
frame.locals[param] = llvmParam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load free variables from the context. This is a closure (or bound
|
||||||
|
// method).
|
||||||
|
if len(frame.fn.fn.FreeVars) != 0 {
|
||||||
|
if !c.ir.FunctionNeedsContext(frame.fn) {
|
||||||
|
panic("free variables on function without context")
|
||||||
|
}
|
||||||
|
c.builder.SetInsertPointAtEnd(frame.blocks[frame.fn.fn.Blocks[0]])
|
||||||
|
context := frame.fn.llvmFn.Param(len(frame.fn.fn.Params))
|
||||||
|
|
||||||
|
// Determine the context type. It's a struct containing all variables.
|
||||||
|
freeVarTypes := make([]llvm.Type, 0, len(frame.fn.fn.FreeVars))
|
||||||
|
for _, freeVar := range frame.fn.fn.FreeVars {
|
||||||
|
typ, err := c.getLLVMType(freeVar.Type())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
freeVarTypes = append(freeVarTypes, typ)
|
||||||
|
}
|
||||||
|
contextType := llvm.StructType(freeVarTypes, false)
|
||||||
|
|
||||||
|
// Get a correctly-typed pointer to the context.
|
||||||
|
contextAlloc := llvm.Value{}
|
||||||
|
if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
|
// Context stored directly in pointer. Load it using an alloca.
|
||||||
|
contextRawAlloc := c.builder.CreateAlloca(llvm.PointerType(c.i8ptrType, 0), "")
|
||||||
|
contextRawValue := c.builder.CreateBitCast(context, llvm.PointerType(c.i8ptrType, 0), "")
|
||||||
|
c.builder.CreateStore(contextRawValue, contextRawAlloc)
|
||||||
|
contextAlloc = c.builder.CreateBitCast(contextRawAlloc, llvm.PointerType(contextType, 0), "")
|
||||||
|
} else {
|
||||||
|
// Context stored in the heap. Bitcast the passed-in pointer to the
|
||||||
|
// correct pointer type.
|
||||||
|
contextAlloc = c.builder.CreateBitCast(context, llvm.PointerType(contextType, 0), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load each free variable from the context.
|
||||||
|
// A free variable is always a pointer when this is a closure, but it
|
||||||
|
// can be another type when it is a wrapper for a bound method (these
|
||||||
|
// wrappers are generated by the ssa package).
|
||||||
|
for i, freeVar := range frame.fn.fn.FreeVars {
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := c.builder.CreateInBoundsGEP(contextAlloc, indices, "")
|
||||||
|
frame.locals[freeVar] = c.builder.CreateLoad(gep, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if frame.blocking {
|
if frame.blocking {
|
||||||
// Coroutine initialization.
|
// Coroutine initialization.
|
||||||
c.builder.SetInsertPointAtEnd(frame.blocks[frame.fn.fn.Blocks[0]])
|
c.builder.SetInsertPointAtEnd(frame.blocks[frame.fn.fn.Blocks[0]])
|
||||||
|
@ -1372,7 +1443,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn llvm.Value, blocking bool, parentHandle llvm.Value) (llvm.Value, error) {
|
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, blocking bool, parentHandle llvm.Value) (llvm.Value, error) {
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
if blocking {
|
if blocking {
|
||||||
if parentHandle.IsNil() {
|
if parentHandle.IsNil() {
|
||||||
|
@ -1391,6 +1462,12 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn llvm
|
||||||
params = append(params, val)
|
params = append(params, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !context.IsNil() {
|
||||||
|
// This function takes a context parameter.
|
||||||
|
// Add it to the end of the parameter list.
|
||||||
|
params = append(params, context)
|
||||||
|
}
|
||||||
|
|
||||||
if frame.blocking && llvmFn.Name() == "runtime.Sleep" {
|
if frame.blocking && llvmFn.Name() == "runtime.Sleep" {
|
||||||
// Set task state to TASK_STATE_SLEEP and set the duration.
|
// Set task state to TASK_STATE_SLEEP and set the duration.
|
||||||
c.builder.CreateCall(c.mod.NamedFunction("runtime.sleepTask"), []llvm.Value{frame.taskHandle, params[0]}, "")
|
c.builder.CreateCall(c.mod.NamedFunction("runtime.sleepTask"), []llvm.Value{frame.taskHandle, params[0]}, "")
|
||||||
|
@ -1443,10 +1520,22 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
llvmFnType, err := c.getLLVMType(instr.Method.Type())
|
llvmFnType, err := c.getLLVMType(instr.Method.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
|
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
||||||
|
// This is somewhat of a hack.
|
||||||
|
// getLLVMType() has created a closure type for us, but we don't
|
||||||
|
// actually want a closure type as an interface call can never be a
|
||||||
|
// closure call. So extract the function pointer type from the
|
||||||
|
// closure.
|
||||||
|
// This happens because somewhere the same function signature is
|
||||||
|
// used in a closure or bound method.
|
||||||
|
llvmFnType = llvmFnType.Subtypes()[1]
|
||||||
|
}
|
||||||
|
|
||||||
values := []llvm.Value{
|
values := []llvm.Value{
|
||||||
itf,
|
itf,
|
||||||
llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false),
|
llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false),
|
||||||
|
@ -1454,6 +1543,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
||||||
fn := c.builder.CreateCall(c.mod.NamedFunction("runtime.interfaceMethod"), values, "invoke.func")
|
fn := c.builder.CreateCall(c.mod.NamedFunction("runtime.interfaceMethod"), values, "invoke.func")
|
||||||
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
|
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
|
||||||
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
|
|
||||||
args := []llvm.Value{receiverValue}
|
args := []llvm.Value{receiverValue}
|
||||||
for _, arg := range instr.Args {
|
for _, arg := range instr.Args {
|
||||||
val, err := c.parseExpr(frame, arg)
|
val, err := c.parseExpr(frame, arg)
|
||||||
|
@ -1462,16 +1552,21 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
||||||
}
|
}
|
||||||
args = append(args, val)
|
args = append(args, val)
|
||||||
}
|
}
|
||||||
|
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) {
|
||||||
|
// This function takes an extra context parameter. An interface call
|
||||||
|
// cannot also be a closure but we have to supply the nil pointer
|
||||||
|
// anyway.
|
||||||
|
args = append(args, llvm.ConstPointerNull(c.i8ptrType))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: blocking methods (needs analysis)
|
// TODO: blocking methods (needs analysis)
|
||||||
return c.builder.CreateCall(fnCast, args, ""), nil
|
return c.builder.CreateCall(fnCast, args, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular function, builtin, or function pointer.
|
// Try to call the function directly for trivially static calls.
|
||||||
switch call := instr.Value.(type) {
|
fn := instr.StaticCallee()
|
||||||
case *ssa.Builtin:
|
if fn != nil {
|
||||||
return c.parseBuiltin(frame, instr.Args, call.Name())
|
if fn.Name() == "Asm" && len(instr.Args) == 1 {
|
||||||
case *ssa.Function:
|
|
||||||
if call.Name() == "Asm" && len(instr.Args) == 1 {
|
|
||||||
// Magic function: insert inline assembly instead of calling it.
|
// Magic function: insert inline assembly instead of calling it.
|
||||||
if named, ok := instr.Args[0].Type().(*types.Named); ok && named.Obj().Name() == "__asm" {
|
if named, ok := instr.Args[0].Type().(*types.Named); ok && named.Obj().Name() == "__asm" {
|
||||||
fnType := llvm.FunctionType(llvm.VoidType(), []llvm.Type{}, false)
|
fnType := llvm.FunctionType(llvm.VoidType(), []llvm.Type{}, false)
|
||||||
|
@ -1480,20 +1575,52 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
||||||
return c.builder.CreateCall(target, nil, ""), nil
|
return c.builder.CreateCall(target, nil, ""), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetFunc := c.ir.GetFunction(call)
|
targetFunc := c.ir.GetFunction(fn)
|
||||||
name := targetFunc.LinkName()
|
if targetFunc.llvmFn.IsNil() {
|
||||||
llvmFn := c.mod.NamedFunction(name)
|
return llvm.Value{}, errors.New("undefined function: " + targetFunc.LinkName())
|
||||||
if llvmFn.IsNil() {
|
|
||||||
return llvm.Value{}, errors.New("undefined function: " + name)
|
|
||||||
}
|
}
|
||||||
return c.parseFunctionCall(frame, instr.Args, llvmFn, targetFunc.blocking, parentHandle)
|
var context llvm.Value
|
||||||
|
if c.ir.FunctionNeedsContext(targetFunc) {
|
||||||
|
// This function call is to a (potential) closure, not a regular
|
||||||
|
// function. See whether it is a closure and if so, call it as such.
|
||||||
|
// Else, supply a dummy nil pointer as the last parameter.
|
||||||
|
var err error
|
||||||
|
if mkClosure, ok := instr.Value.(*ssa.MakeClosure); ok {
|
||||||
|
// closure is {context, function pointer}
|
||||||
|
closure, err := c.parseExpr(frame, mkClosure)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
context = c.builder.CreateExtractValue(closure, 0, "")
|
||||||
|
} else {
|
||||||
|
context, err = getZeroValue(c.i8ptrType)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.parseFunctionCall(frame, instr.Args, targetFunc.llvmFn, context, targetFunc.blocking, parentHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builtin or function pointer.
|
||||||
|
switch call := instr.Value.(type) {
|
||||||
|
case *ssa.Builtin:
|
||||||
|
return c.parseBuiltin(frame, instr.Args, call.Name())
|
||||||
default: // function pointer
|
default: // function pointer
|
||||||
value, err := c.parseExpr(frame, instr.Value)
|
value, err := c.parseExpr(frame, instr.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
// TODO: blocking function pointers (needs analysis)
|
// TODO: blocking function pointers (needs analysis)
|
||||||
return c.parseFunctionCall(frame, instr.Args, value, false, parentHandle)
|
var context llvm.Value
|
||||||
|
if c.ir.SignatureNeedsContext(instr.Signature()) {
|
||||||
|
// 'value' is a closure, not a raw function pointer.
|
||||||
|
// Extract the function pointer and the context pointer.
|
||||||
|
// closure: {context, function pointer}
|
||||||
|
context = c.builder.CreateExtractValue(value, 0, "")
|
||||||
|
value = c.builder.CreateExtractValue(value, 1, "")
|
||||||
|
}
|
||||||
|
return c.parseFunctionCall(frame, instr.Args, value, context, false, parentHandle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1583,7 +1710,17 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
}
|
}
|
||||||
return c.builder.CreateGEP(val, indices, ""), nil
|
return c.builder.CreateGEP(val, indices, ""), nil
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
return c.mod.NamedFunction(c.ir.GetFunction(expr).LinkName()), nil
|
fn := c.ir.GetFunction(expr)
|
||||||
|
ptr := fn.llvmFn
|
||||||
|
if c.ir.FunctionNeedsContext(fn) {
|
||||||
|
// Create closure for function pointer.
|
||||||
|
// Closure is: {context, function pointer}
|
||||||
|
ptr = llvm.ConstStruct([]llvm.Value{
|
||||||
|
llvm.ConstPointerNull(c.i8ptrType),
|
||||||
|
ptr,
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
return ptr, nil
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
|
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
|
||||||
// Ignore CGo global variables which we don't use.
|
// Ignore CGo global variables which we don't use.
|
||||||
|
@ -1731,6 +1868,10 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
default:
|
default:
|
||||||
panic("unknown lookup type: " + expr.String())
|
panic("unknown lookup type: " + expr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *ssa.MakeClosure:
|
||||||
|
return c.parseMakeClosure(frame, expr)
|
||||||
|
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
val, err := c.parseExpr(frame, expr.X)
|
val, err := c.parseExpr(frame, expr.X)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2249,6 +2390,85 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
if !c.ir.FunctionNeedsContext(f) {
|
||||||
|
// Maybe AnalyseFunctionPointers didn't run?
|
||||||
|
panic("MakeClosure on function signature without context")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all bound variables.
|
||||||
|
boundVars := make([]llvm.Value, 0, len(expr.Bindings))
|
||||||
|
boundVarTypes := make([]llvm.Type, 0, len(expr.Bindings))
|
||||||
|
for _, binding := range expr.Bindings {
|
||||||
|
// The context stores the bound variables.
|
||||||
|
llvmBoundVar, err := c.parseExpr(frame, binding)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
boundVars = append(boundVars, llvmBoundVar)
|
||||||
|
boundVarTypes = append(boundVarTypes, llvmBoundVar.Type())
|
||||||
|
}
|
||||||
|
contextType := llvm.StructType(boundVarTypes, false)
|
||||||
|
|
||||||
|
// Allocate memory for the context.
|
||||||
|
contextAlloc := llvm.Value{}
|
||||||
|
contextHeapAlloc := llvm.Value{}
|
||||||
|
if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
|
// Context fits in a pointer - e.g. when it is a pointer. Store it
|
||||||
|
// directly in the stack after a convert.
|
||||||
|
// Because contextType is a struct and we have to cast it to a *i8,
|
||||||
|
// store it in an alloca first for bitcasting (store+bitcast+load).
|
||||||
|
contextAlloc = c.builder.CreateAlloca(contextType, "")
|
||||||
|
} else {
|
||||||
|
// Context is bigger than a pointer, so allocate it on the heap.
|
||||||
|
size := c.targetData.TypeAllocSize(contextType)
|
||||||
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
|
contextHeapAlloc = c.builder.CreateCall(c.allocFunc, []llvm.Value{sizeValue}, "")
|
||||||
|
contextAlloc = c.builder.CreateBitCast(contextHeapAlloc, llvm.PointerType(contextType, 0), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all bound variables in the alloca or heap pointer.
|
||||||
|
for i, boundVar := range boundVars {
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := c.builder.CreateInBoundsGEP(contextAlloc, indices, "")
|
||||||
|
c.builder.CreateStore(boundVar, gep)
|
||||||
|
}
|
||||||
|
|
||||||
|
context := llvm.Value{}
|
||||||
|
if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
|
// Load value (as *i8) from the alloca.
|
||||||
|
contextAlloc = c.builder.CreateBitCast(contextAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
||||||
|
context = c.builder.CreateLoad(contextAlloc, "")
|
||||||
|
} else {
|
||||||
|
// Get the original heap allocation pointer, which already is an
|
||||||
|
// *i8.
|
||||||
|
context = contextHeapAlloc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the function signature type, which is a closure type.
|
||||||
|
// A closure is a tuple of {context, function pointer}.
|
||||||
|
typ, err := c.getLLVMType(f.fn.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the closure, which is a struct: {context, function pointer}.
|
||||||
|
closure, err := getZeroValue(typ)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
closure = c.builder.CreateInsertValue(closure, f.llvmFn, 1, "")
|
||||||
|
closure = c.builder.CreateInsertValue(closure, context, 0, "")
|
||||||
|
return closure, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, isConst bool) (llvm.Value, error) {
|
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, isConst bool) (llvm.Value, error) {
|
||||||
var itfValue llvm.Value
|
var itfValue llvm.Value
|
||||||
size := c.targetData.TypeAllocSize(val.Type())
|
size := c.targetData.TypeAllocSize(val.Type())
|
||||||
|
|
10
ir.go
10
ir.go
|
@ -25,9 +25,10 @@ type Program struct {
|
||||||
NamedTypes []*NamedType
|
NamedTypes []*NamedType
|
||||||
needsScheduler bool
|
needsScheduler bool
|
||||||
goCalls []*ssa.Go
|
goCalls []*ssa.Go
|
||||||
typesWithMethods map[string]*InterfaceType
|
typesWithMethods map[string]*InterfaceType // see AnalyseInterfaceConversions
|
||||||
typesWithoutMethods map[string]int
|
typesWithoutMethods map[string]int // see AnalyseInterfaceConversions
|
||||||
methodSignatureNames map[string]int
|
methodSignatureNames map[string]int
|
||||||
|
fpWithContext map[string]struct{} // see AnalyseFunctionPointers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function or method.
|
// Function or method.
|
||||||
|
@ -37,8 +38,9 @@ type Function struct {
|
||||||
linkName string
|
linkName string
|
||||||
blocking bool
|
blocking bool
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
|
addressTaken bool // used as function pointer, calculated by AnalyseFunctionPointers
|
||||||
parents []*Function // calculated by AnalyseCallgraph
|
parents []*Function // calculated by AnalyseCallgraph
|
||||||
children []*Function
|
children []*Function // calculated by AnalyseCallgraph
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global variable, possibly constant.
|
// Global variable, possibly constant.
|
||||||
|
@ -117,9 +119,9 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||||
|
|
||||||
func (p *Program) addFunction(ssaFn *ssa.Function) {
|
func (p *Program) addFunction(ssaFn *ssa.Function) {
|
||||||
f := &Function{fn: ssaFn}
|
f := &Function{fn: ssaFn}
|
||||||
|
f.parsePragmas()
|
||||||
p.Functions = append(p.Functions, f)
|
p.Functions = append(p.Functions, f)
|
||||||
p.functionMap[ssaFn] = f
|
p.functionMap[ssaFn] = f
|
||||||
f.parsePragmas()
|
|
||||||
|
|
||||||
for _, anon := range ssaFn.AnonFuncs {
|
for _, anon := range ssaFn.AnonFuncs {
|
||||||
p.addFunction(anon)
|
p.addFunction(anon)
|
||||||
|
|
110
passes.go
110
passes.go
|
@ -5,46 +5,55 @@ import (
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This function implements several optimization passes (analysis + transform)
|
// This file implements several optimization passes (analysis + transform) to
|
||||||
// to optimize code in SSA form before it is compiled to LLVM IR. It is based on
|
// optimize code in SSA form before it is compiled to LLVM IR. It is based on
|
||||||
// the IR defined in ir.go.
|
// the IR defined in ir.go.
|
||||||
|
|
||||||
// Make a readable version of the method signature (including the function name,
|
// Make a readable version of a method signature (including the function name,
|
||||||
// excluding the receiver name). This string is used internally to match
|
// excluding the receiver name). This string is used internally to match
|
||||||
// interfaces and to call the correct method on an interface. Examples:
|
// interfaces and to call the correct method on an interface. Examples:
|
||||||
//
|
//
|
||||||
// String() string
|
// String() string
|
||||||
// Read([]byte) (int, error)
|
// Read([]byte) (int, error)
|
||||||
func MethodName(method *types.Func) string {
|
func MethodSignature(method *types.Func) string {
|
||||||
sig := method.Type().(*types.Signature)
|
return method.Name() + Signature(method.Type().(*types.Signature))
|
||||||
name := method.Name()
|
}
|
||||||
|
|
||||||
|
// Make a readable version of a function (pointer) signature. This string is
|
||||||
|
// used internally to match signatures (like in AnalyseFunctionPointers).
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// () string
|
||||||
|
// (string, int) (int, error)
|
||||||
|
func Signature(sig *types.Signature) string {
|
||||||
|
s := ""
|
||||||
if sig.Params().Len() == 0 {
|
if sig.Params().Len() == 0 {
|
||||||
name += "()"
|
s += "()"
|
||||||
} else {
|
} else {
|
||||||
name += "("
|
s += "("
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
name += ", "
|
s += ", "
|
||||||
}
|
}
|
||||||
name += sig.Params().At(i).Type().String()
|
s += sig.Params().At(i).Type().String()
|
||||||
}
|
}
|
||||||
name += ")"
|
s += ")"
|
||||||
}
|
}
|
||||||
if sig.Results().Len() == 0 {
|
if sig.Results().Len() == 0 {
|
||||||
// keep as-is
|
// keep as-is
|
||||||
} else if sig.Results().Len() == 1 {
|
} else if sig.Results().Len() == 1 {
|
||||||
name += " " + sig.Results().At(0).Type().String()
|
s += " " + sig.Results().At(0).Type().String()
|
||||||
} else {
|
} else {
|
||||||
name += " ("
|
s += " ("
|
||||||
for i := 0; i < sig.Results().Len(); i++ {
|
for i := 0; i < sig.Results().Len(); i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
name += ", "
|
s += ", "
|
||||||
}
|
}
|
||||||
name += sig.Results().At(i).Type().String()
|
s += sig.Results().At(i).Type().String()
|
||||||
}
|
}
|
||||||
name += ")"
|
s += ")"
|
||||||
}
|
}
|
||||||
return name
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in parents of all functions.
|
// Fill in parents of all functions.
|
||||||
|
@ -111,7 +120,7 @@ func (p *Program) AnalyseInterfaceConversions() {
|
||||||
Methods: make(map[string]*types.Selection),
|
Methods: make(map[string]*types.Selection),
|
||||||
}
|
}
|
||||||
for _, sel := range methods {
|
for _, sel := range methods {
|
||||||
name := MethodName(sel.Obj().(*types.Func))
|
name := MethodSignature(sel.Obj().(*types.Func))
|
||||||
t.Methods[name] = sel
|
t.Methods[name] = sel
|
||||||
}
|
}
|
||||||
p.typesWithMethods[name] = t
|
p.typesWithMethods[name] = t
|
||||||
|
@ -124,6 +133,49 @@ func (p *Program) AnalyseInterfaceConversions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analyse which function pointer signatures need a context parameter.
|
||||||
|
// This makes calling function pointers more efficient.
|
||||||
|
func (p *Program) AnalyseFunctionPointers() {
|
||||||
|
// Clear, if AnalyseFunctionPointers has been called before.
|
||||||
|
p.fpWithContext = map[string]struct{}{}
|
||||||
|
|
||||||
|
for _, f := range p.Functions {
|
||||||
|
for _, block := range f.fn.Blocks {
|
||||||
|
for _, instr := range block.Instrs {
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case ssa.CallInstruction:
|
||||||
|
for _, arg := range instr.Common().Args {
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
f := p.GetFunction(arg)
|
||||||
|
f.addressTaken = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ssa.DebugRef:
|
||||||
|
default:
|
||||||
|
// For anything that isn't a call...
|
||||||
|
for _, operand := range instr.Operands(nil) {
|
||||||
|
if operand == nil || *operand == nil || isCGoInternal((*operand).Name()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch operand := (*operand).(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
f := p.GetFunction(operand)
|
||||||
|
f.addressTaken = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *ssa.MakeClosure:
|
||||||
|
fn := instr.Fn.(*ssa.Function)
|
||||||
|
sig := Signature(fn.Signature)
|
||||||
|
p.fpWithContext[sig] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Analyse which functions are recursively blocking.
|
// Analyse which functions are recursively blocking.
|
||||||
//
|
//
|
||||||
// Depends on AnalyseCallgraph.
|
// Depends on AnalyseCallgraph.
|
||||||
|
@ -233,6 +285,12 @@ func (p *Program) SimpleDCE() {
|
||||||
switch operand := (*operand).(type) {
|
switch operand := (*operand).(type) {
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
f := p.GetFunction(operand)
|
f := p.GetFunction(operand)
|
||||||
|
if f == nil {
|
||||||
|
// FIXME HACK: this function should have been
|
||||||
|
// discovered already. It is not for bound methods.
|
||||||
|
p.addFunction(operand)
|
||||||
|
f = p.GetFunction(operand)
|
||||||
|
}
|
||||||
if !f.flag {
|
if !f.flag {
|
||||||
f.flag = true
|
f.flag = true
|
||||||
worklist = append(worklist, operand)
|
worklist = append(worklist, operand)
|
||||||
|
@ -306,11 +364,11 @@ func (p *Program) TypeNum(typ types.Type) (int, bool) {
|
||||||
// MethodNum returns the numeric ID of this method, to be used in method lookups
|
// MethodNum returns the numeric ID of this method, to be used in method lookups
|
||||||
// on interfaces for example.
|
// on interfaces for example.
|
||||||
func (p *Program) MethodNum(method *types.Func) int {
|
func (p *Program) MethodNum(method *types.Func) int {
|
||||||
name := MethodName(method)
|
name := MethodSignature(method)
|
||||||
if _, ok := p.methodSignatureNames[name]; !ok {
|
if _, ok := p.methodSignatureNames[name]; !ok {
|
||||||
p.methodSignatureNames[name] = len(p.methodSignatureNames)
|
p.methodSignatureNames[name] = len(p.methodSignatureNames)
|
||||||
}
|
}
|
||||||
return p.methodSignatureNames[MethodName(method)]
|
return p.methodSignatureNames[MethodSignature(method)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// The start index of the first dynamic type that has methods.
|
// The start index of the first dynamic type that has methods.
|
||||||
|
@ -330,3 +388,15 @@ func (p *Program) AllDynamicTypes() []*InterfaceType {
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Program) FunctionNeedsContext(f *Function) bool {
|
||||||
|
if !f.addressTaken {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return p.SignatureNeedsContext(f.fn.Signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Program) SignatureNeedsContext(sig *types.Signature) bool {
|
||||||
|
_, needsContext := p.fpWithContext[Signature(sig)]
|
||||||
|
return needsContext
|
||||||
|
}
|
||||||
|
|
|
@ -27,15 +27,18 @@ func main() {
|
||||||
println("sumrange(100) =", sumrange(100))
|
println("sumrange(100) =", sumrange(100))
|
||||||
println("strlen foo:", strlen("foo"))
|
println("strlen foo:", strlen("foo"))
|
||||||
|
|
||||||
|
// map
|
||||||
m := map[string]int{"answer": 42, "foo": 3}
|
m := map[string]int{"answer": 42, "foo": 3}
|
||||||
readMap(m, "answer")
|
readMap(m, "answer")
|
||||||
readMap(testmap, "data")
|
readMap(testmap, "data")
|
||||||
|
|
||||||
|
// slice
|
||||||
foo := []int{1, 2, 4, 5}
|
foo := []int{1, 2, 4, 5}
|
||||||
println("len/cap foo:", len(foo), cap(foo))
|
println("len/cap foo:", len(foo), cap(foo))
|
||||||
println("foo[3]:", foo[3])
|
println("foo[3]:", foo[3])
|
||||||
println("sum foo:", sum(foo))
|
println("sum foo:", sum(foo))
|
||||||
|
|
||||||
|
// interfaces, pointers
|
||||||
thing := &Thing{"foo"}
|
thing := &Thing{"foo"}
|
||||||
println("thing:", thing.String())
|
println("thing:", thing.String())
|
||||||
printItf(5)
|
printItf(5)
|
||||||
|
@ -47,8 +50,16 @@ func main() {
|
||||||
s := Stringer(thing)
|
s := Stringer(thing)
|
||||||
println("Stringer.String():", s.String())
|
println("Stringer.String():", s.String())
|
||||||
|
|
||||||
|
// unusual calls
|
||||||
runFunc(hello, 5) // must be indirect to avoid obvious inlining
|
runFunc(hello, 5) // must be indirect to avoid obvious inlining
|
||||||
testDefer()
|
testDefer()
|
||||||
|
testBound(thing.String)
|
||||||
|
func() {
|
||||||
|
println("thing inside closure:", thing.String()) //, len(foo))
|
||||||
|
}()
|
||||||
|
runFunc(func(i int) {
|
||||||
|
println("inside fp closure:", thing.String(), i)
|
||||||
|
}, 3)
|
||||||
|
|
||||||
// test library functions
|
// test library functions
|
||||||
println("lower to upper char:", 'h', "->", unicode.ToUpper('h'))
|
println("lower to upper char:", 'h', "->", unicode.ToUpper('h'))
|
||||||
|
@ -70,6 +81,10 @@ func deferred(msg string, i int) {
|
||||||
println(msg, i)
|
println(msg, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBound(f func() string) {
|
||||||
|
println("bound method:", f())
|
||||||
|
}
|
||||||
|
|
||||||
func readMap(m map[string]int, key string) {
|
func readMap(m map[string]int, key string) {
|
||||||
println("map length:", len(m))
|
println("map length:", len(m))
|
||||||
println("map read:", key, "=", m[key])
|
println("map read:", key, "=", m[key])
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче