From c1521fe12ea16d7dc65e1d0d4f6cc4e5ac147697 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 9 Dec 2019 17:50:58 +0100 Subject: [PATCH] compiler: refactor starting new goroutines --- compiler/compiler.go | 4 ++-- compiler/goroutine.go | 49 ++++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index dbf59ad8..d3d4bab5 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1071,7 +1071,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { panic("StaticCallee returned an unexpected value") } params = append(params, context) // context parameter - c.emitStartGoroutine(calleeFn.LLVMFn, params) + frame.createGoInstruction(calleeFn.LLVMFn, params) } else if !instr.Call.IsInvoke() { // This is a function pointer. // 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: panic("unknown scheduler type") } - c.emitStartGoroutine(funcPtr, params) + frame.createGoInstruction(funcPtr, params) } else { c.addError(instr.Pos(), "todo: go on interface call") } diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 2abb8bb0..dd114b32 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -3,27 +3,30 @@ package compiler // This file implements the 'go' keyword to start a new goroutine. See // 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. // 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 // pointer passed in as a parameter too in addition to the context. // // Because a go statement doesn't return anything, return undef. -func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value { - paramBundle := c.emitPointerPack(params) +func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value) llvm.Value { + paramBundle := b.emitPointerPack(params) var callee llvm.Value - switch c.Scheduler() { + switch b.Scheduler() { case "none", "tasks": - callee = c.createGoroutineStartWrapper(funcPtr) + callee = b.createGoroutineStartWrapper(funcPtr) case "coroutines": - callee = c.builder.CreatePtrToInt(funcPtr, c.uintptrType, "") + callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "") default: 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()) } @@ -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 // ignores the return value because newly started goroutines do not have a // return value. -func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { +func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { var wrapper llvm.Value + builder := c.ctx.NewBuilder() + if !fn.IsAFunction().IsNil() { // See whether this wrapper has already been created. If so, return it. name := fn.Name() wrapper = c.mod.NamedFunction(name + "$gowrapper") 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. wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType) wrapper.SetLinkage(llvm.PrivateLinkage) wrapper.SetUnnamedAddr(true) entry := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(entry) + builder.SetInsertPointAtEnd(entry) // Create the list of params for the call. 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)) // Create the call. - c.builder.CreateCall(fn, params, "") + builder.CreateCall(fn, params, "") } else { // 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 // merged into one. - // Save the current position in the IR builder. - currentBlock := c.builder.GetInsertBlock() - defer c.builder.SetInsertPointAtEnd(currentBlock) - // Create the wrapper. wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapper = llvm.AddFunction(c.mod, ".gowrapper", wrapperType) wrapper.SetLinkage(llvm.InternalLinkage) wrapper.SetUnnamedAddr(true) 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. paramTypes := fn.Type().ElementType().ParamTypes() 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. 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) // Create the call. - c.builder.CreateCall(fnPtr, params, "") + builder.CreateCall(fnPtr, params, "") } // Finish the function. Every basic block must end in a terminator, and // 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 c.builder.CreatePtrToInt(wrapper, c.uintptrType, "") + return builder.CreatePtrToInt(wrapper, c.uintptrType, "") }