compiler: refactor starting new goroutines
Этот коммит содержится в:
родитель
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, "")
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче