compiler: refactor starting new goroutines

Этот коммит содержится в:
Ayke van Laethem 2019-12-09 17:50:58 +01:00 коммит произвёл Ron Evans
родитель 405ec2a563
коммит c1521fe12e
2 изменённых файлов: 25 добавлений и 28 удалений

Просмотреть файл

@ -1071,7 +1071,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
panic("StaticCallee returned an unexpected value") panic("StaticCallee returned an unexpected value")
} }
params = append(params, context) // context parameter params = append(params, context) // context parameter
c.emitStartGoroutine(calleeFn.LLVMFn, params) frame.createGoInstruction(calleeFn.LLVMFn, params)
} else if !instr.Call.IsInvoke() { } else if !instr.Call.IsInvoke() {
// This is a function pointer. // This is a function pointer.
// At the moment, two extra params are passed to the newly started // At the moment, two extra params are passed to the newly started
@ -1089,7 +1089,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
default: default:
panic("unknown scheduler type") panic("unknown scheduler type")
} }
c.emitStartGoroutine(funcPtr, params) frame.createGoInstruction(funcPtr, params)
} else { } else {
c.addError(instr.Pos(), "todo: go on interface call") c.addError(instr.Pos(), "todo: go on interface call")
} }

Просмотреть файл

@ -3,27 +3,30 @@ package compiler
// This file implements the 'go' keyword to start a new goroutine. See // This file implements the 'go' keyword to start a new goroutine. See
// goroutine-lowering.go for more details. // goroutine-lowering.go for more details.
import "tinygo.org/x/go-llvm" import (
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm"
)
// emitStartGoroutine starts a new goroutine with the provided function pointer // createGoInstruction starts a new goroutine with the provided function pointer
// and parameters. // and parameters.
// In general, you should pass all regular parameters plus the context parameter. // In general, you should pass all regular parameters plus the context parameter.
// There is one exception: the task-based scheduler needs to have the function // There is one exception: the task-based scheduler needs to have the function
// pointer passed in as a parameter too in addition to the context. // pointer passed in as a parameter too in addition to the context.
// //
// Because a go statement doesn't return anything, return undef. // Because a go statement doesn't return anything, return undef.
func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value { func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value) llvm.Value {
paramBundle := c.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var callee llvm.Value var callee llvm.Value
switch c.Scheduler() { switch b.Scheduler() {
case "none", "tasks": case "none", "tasks":
callee = c.createGoroutineStartWrapper(funcPtr) callee = b.createGoroutineStartWrapper(funcPtr)
case "coroutines": case "coroutines":
callee = c.builder.CreatePtrToInt(funcPtr, c.uintptrType, "") callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
default: default:
panic("unreachable") panic("unreachable")
} }
c.createCall(c.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(c.i8ptrType), llvm.ConstPointerNull(c.i8ptrType)}, "") b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
return llvm.Undef(funcPtr.Type().ElementType().ReturnType()) return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
} }
@ -45,36 +48,34 @@ func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) l
// allows a single (pointer) argument to the newly started goroutine. Also, it // allows a single (pointer) argument to the newly started goroutine. Also, it
// ignores the return value because newly started goroutines do not have a // ignores the return value because newly started goroutines do not have a
// return value. // return value.
func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value) llvm.Value {
var wrapper llvm.Value var wrapper llvm.Value
builder := c.ctx.NewBuilder()
if !fn.IsAFunction().IsNil() { if !fn.IsAFunction().IsNil() {
// See whether this wrapper has already been created. If so, return it. // See whether this wrapper has already been created. If so, return it.
name := fn.Name() name := fn.Name()
wrapper = c.mod.NamedFunction(name + "$gowrapper") wrapper = c.mod.NamedFunction(name + "$gowrapper")
if !wrapper.IsNil() { if !wrapper.IsNil() {
return c.builder.CreatePtrToInt(wrapper, c.uintptrType, "") return llvm.ConstPtrToInt(wrapper, c.uintptrType)
} }
// Save the current position in the IR builder.
currentBlock := c.builder.GetInsertBlock()
defer c.builder.SetInsertPointAtEnd(currentBlock)
// Create the wrapper. // Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
wrapper.SetLinkage(llvm.PrivateLinkage) wrapper.SetLinkage(llvm.PrivateLinkage)
wrapper.SetUnnamedAddr(true) wrapper.SetUnnamedAddr(true)
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
c.builder.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
// Create the list of params for the call. // Create the list of params for the call.
paramTypes := fn.Type().ElementType().ParamTypes() paramTypes := fn.Type().ElementType().ParamTypes()
params := c.emitPointerUnpack(wrapper.Param(0), paramTypes[:len(paramTypes)-1]) params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes[:len(paramTypes)-1])
params = append(params, llvm.Undef(c.i8ptrType)) params = append(params, llvm.Undef(c.i8ptrType))
// Create the call. // Create the call.
c.builder.CreateCall(fn, params, "") builder.CreateCall(fn, params, "")
} else { } else {
// For a function pointer like this: // For a function pointer like this:
@ -94,22 +95,18 @@ func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value {
// With a bit of luck, identical wrapper functions like these can be // With a bit of luck, identical wrapper functions like these can be
// merged into one. // merged into one.
// Save the current position in the IR builder.
currentBlock := c.builder.GetInsertBlock()
defer c.builder.SetInsertPointAtEnd(currentBlock)
// Create the wrapper. // Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
wrapper = llvm.AddFunction(c.mod, ".gowrapper", wrapperType) wrapper = llvm.AddFunction(c.mod, ".gowrapper", wrapperType)
wrapper.SetLinkage(llvm.InternalLinkage) wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true) wrapper.SetUnnamedAddr(true)
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
c.builder.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
// Get the list of parameters, with the extra parameters at the end. // Get the list of parameters, with the extra parameters at the end.
paramTypes := fn.Type().ElementType().ParamTypes() paramTypes := fn.Type().ElementType().ParamTypes()
paramTypes[len(paramTypes)-1] = fn.Type() // the last element is the function pointer paramTypes[len(paramTypes)-1] = fn.Type() // the last element is the function pointer
params := c.emitPointerUnpack(wrapper.Param(0), paramTypes) params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes)
// Get the function pointer. // Get the function pointer.
fnPtr := params[len(params)-1] fnPtr := params[len(params)-1]
@ -119,13 +116,13 @@ func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value {
params[len(params)-1] = llvm.Undef(c.i8ptrType) params[len(params)-1] = llvm.Undef(c.i8ptrType)
// Create the call. // Create the call.
c.builder.CreateCall(fnPtr, params, "") builder.CreateCall(fnPtr, params, "")
} }
// Finish the function. Every basic block must end in a terminator, and // Finish the function. Every basic block must end in a terminator, and
// because goroutines never return a value we can simply return void. // because goroutines never return a value we can simply return void.
c.builder.CreateRetVoid() builder.CreateRetVoid()
// Return a ptrtoint of the wrapper, not the function itself. // Return a ptrtoint of the wrapper, not the function itself.
return c.builder.CreatePtrToInt(wrapper, c.uintptrType, "") return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
} }