diff options
-rw-r--r-- | compiler/channel.go | 38 | ||||
-rw-r--r-- | compiler/compiler.go | 6 | ||||
-rw-r--r-- | compiler/goroutine-lowering.go | 109 | ||||
-rw-r--r-- | compiler/llvm.go | 11 | ||||
-rw-r--r-- | compiler/sizes.go | 3 | ||||
-rw-r--r-- | src/runtime/chan.go | 37 | ||||
-rw-r--r-- | src/runtime/scheduler.go | 28 | ||||
-rw-r--r-- | testdata/channel.go | 9 | ||||
-rw-r--r-- | testdata/channel.txt | 1 |
9 files changed, 112 insertions, 130 deletions
diff --git a/compiler/channel.go b/compiler/channel.go index d9ab1b849..8f1a86036 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -12,13 +12,6 @@ import ( // emitMakeChan returns a new channel value for the given channel type. func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) { - valueType := c.getLLVMType(expr.Type().(*types.Chan).Elem()) - if c.targetData.TypeAllocSize(valueType) > c.targetData.TypeAllocSize(c.intType) { - // Values bigger than int overflow the data part of the coroutine. - // TODO: make the coroutine data part big enough to hold these bigger - // values. - return llvm.Value{}, c.makeError(expr.Pos(), "todo: channel with values bigger than int") - } chanType := c.mod.GetTypeByName("runtime.channel") size := c.targetData.TypeAllocSize(chanType) sizeValue := llvm.ConstInt(c.uintptrType, size, false) @@ -30,14 +23,27 @@ func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) { // emitChanSend emits a pseudo chan send operation. It is lowered to the actual // channel send operation during goroutine lowering. func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) { - valueType := c.getLLVMType(instr.Chan.Type().(*types.Chan).Elem()) + valueType := c.getLLVMType(instr.X.Type()) ch := c.getValue(frame, instr.Chan) chanValue := c.getValue(frame, instr.X) valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false) + coroutine := c.createRuntimeCall("getCoroutine", nil, "") + + // store value-to-send + c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction()) valueAlloca := c.builder.CreateAlloca(valueType, "chan.value") + c.builder.SetInsertPointBefore(coroutine) + c.builder.SetInsertPointAtEnd(coroutine.InstructionParent()) c.builder.CreateStore(chanValue, valueAlloca) valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr") - c.createRuntimeCall("chanSendStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueSize}, "") + + // Do the send. + c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "") + + // Make sure CoroSplit includes the alloca in the coroutine frame. + // This is a bit dirty, but it works (at least in LLVM 8). + valueSizeI64 := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(chanValue.Type()), false) + c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{valueSizeI64, valueAllocaCast}, "") } // emitChanRecv emits a pseudo chan receive operation. It is lowered to the @@ -46,13 +52,21 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value { valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem()) valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false) ch := c.getValue(frame, unop.X) + coroutine := c.createRuntimeCall("getCoroutine", nil, "") + + // Allocate memory to receive into. + c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction()) valueAlloca := c.builder.CreateAlloca(valueType, "chan.value") + c.builder.SetInsertPointBefore(coroutine) + c.builder.SetInsertPointAtEnd(coroutine.InstructionParent()) valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr") - valueOk := c.builder.CreateAlloca(c.ctx.Int1Type(), "chan.comma-ok.alloca") - c.createRuntimeCall("chanRecvStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueOk, valueSize}, "") + + // Do the receive. + c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "") received := c.builder.CreateLoad(valueAlloca, "chan.received") if unop.CommaOk { - commaOk := c.builder.CreateLoad(valueOk, "chan.comma-ok") + commaOk := c.createRuntimeCall("getTaskPromiseData", []llvm.Value{coroutine}, "chan.commaOk.wide") + commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk") tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false)) tuple = c.builder.CreateInsertValue(tuple, received, 0, "") tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") diff --git a/compiler/compiler.go b/compiler/compiler.go index 044d0b8bc..70951c7a3 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -344,11 +344,9 @@ func (c *Compiler) Compile(mainPath string) []error { realMain.SetLinkage(llvm.ExternalLinkage) // keep alive until goroutine lowering c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.ExternalLinkage) c.mod.NamedFunction("runtime.free").SetLinkage(llvm.ExternalLinkage) - c.mod.NamedFunction("runtime.chanSend").SetLinkage(llvm.ExternalLinkage) - c.mod.NamedFunction("runtime.chanRecv").SetLinkage(llvm.ExternalLinkage) c.mod.NamedFunction("runtime.sleepTask").SetLinkage(llvm.ExternalLinkage) - c.mod.NamedFunction("runtime.setTaskData").SetLinkage(llvm.ExternalLinkage) - c.mod.NamedFunction("runtime.getTaskData").SetLinkage(llvm.ExternalLinkage) + c.mod.NamedFunction("runtime.setTaskPromisePtr").SetLinkage(llvm.ExternalLinkage) + c.mod.NamedFunction("runtime.getTaskPromisePtr").SetLinkage(llvm.ExternalLinkage) c.mod.NamedFunction("runtime.activateTask").SetLinkage(llvm.ExternalLinkage) c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.ExternalLinkage) diff --git a/compiler/goroutine-lowering.go b/compiler/goroutine-lowering.go index 3a03b85a7..2a9276df2 100644 --- a/compiler/goroutine-lowering.go +++ b/compiler/goroutine-lowering.go @@ -62,7 +62,7 @@ package compiler // llvm.suspend(hdl) // suspend point // println("some other operation") // var i *int // allocate space on the stack for the return value -// runtime.setTaskData(hdl, &i) // store return value alloca in our coroutine promise +// runtime.setTaskPromisePtr(hdl, &i) // store return value alloca in our coroutine promise // bar(hdl) // await, pass a continuation (hdl) to bar // llvm.suspend(hdl) // suspend point, wait for the callee to re-activate // println("done", *i) @@ -146,11 +146,9 @@ func (c *Compiler) LowerGoroutines() error { realMain.SetLinkage(llvm.InternalLinkage) c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.InternalLinkage) c.mod.NamedFunction("runtime.free").SetLinkage(llvm.InternalLinkage) - c.mod.NamedFunction("runtime.chanSend").SetLinkage(llvm.InternalLinkage) - c.mod.NamedFunction("runtime.chanRecv").SetLinkage(llvm.InternalLinkage) c.mod.NamedFunction("runtime.sleepTask").SetLinkage(llvm.InternalLinkage) - c.mod.NamedFunction("runtime.setTaskData").SetLinkage(llvm.InternalLinkage) - c.mod.NamedFunction("runtime.getTaskData").SetLinkage(llvm.InternalLinkage) + c.mod.NamedFunction("runtime.setTaskPromisePtr").SetLinkage(llvm.InternalLinkage) + c.mod.NamedFunction("runtime.getTaskPromisePtr").SetLinkage(llvm.InternalLinkage) c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.InternalLinkage) return nil @@ -179,13 +177,13 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { if !deadlockStub.IsNil() { worklist = append(worklist, deadlockStub) } - chanSendStub := c.mod.NamedFunction("runtime.chanSendStub") - if !chanSendStub.IsNil() { - worklist = append(worklist, chanSendStub) + chanSend := c.mod.NamedFunction("runtime.chanSend") + if !chanSend.IsNil() { + worklist = append(worklist, chanSend) } - chanRecvStub := c.mod.NamedFunction("runtime.chanRecvStub") - if !chanRecvStub.IsNil() { - worklist = append(worklist, chanRecvStub) + chanRecv := c.mod.NamedFunction("runtime.chanRecv") + if !chanRecv.IsNil() { + worklist = append(worklist, chanRecv) } if len(worklist) == 0 { @@ -283,9 +281,6 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { coroBeginType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false) coroBeginFunc := llvm.AddFunction(c.mod, "llvm.coro.begin", coroBeginType) - coroPromiseType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.i8ptrType, c.ctx.Int32Type(), c.ctx.Int1Type()}, false) - coroPromiseFunc := llvm.AddFunction(c.mod, "llvm.coro.promise", coroPromiseType) - coroSuspendType := llvm.FunctionType(c.ctx.Int8Type(), []llvm.Type{c.ctx.TokenType(), c.ctx.Int1Type()}, false) coroSuspendFunc := llvm.AddFunction(c.mod, "llvm.coro.suspend", coroSuspendType) @@ -297,7 +292,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { // Transform all async functions into coroutines. for _, f := range asyncList { - if f == sleep || f == deadlockStub || f == chanSendStub || f == chanRecvStub { + if f == sleep || f == deadlockStub || f == chanSend || f == chanRecv { continue } @@ -314,7 +309,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) { if !inst.IsACallInst().IsNil() { callee := inst.CalledValue() - if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSendStub || callee == chanRecvStub { + if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSend || callee == chanRecv { continue } asyncCalls = append(asyncCalls, inst) @@ -359,7 +354,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { retvalAlloca = c.builder.CreateAlloca(inst.Type(), "coro.retvalAlloca") c.builder.SetInsertPointBefore(inst) data := c.builder.CreateBitCast(retvalAlloca, c.i8ptrType, "") - c.createRuntimeCall("setTaskData", []llvm.Value{frame.taskHandle, data}, "") + c.createRuntimeCall("setTaskPromisePtr", []llvm.Value{frame.taskHandle, data}, "") } // Suspend. @@ -404,7 +399,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { // Return this value by writing to the pointer stored in the // parent handle. The parent coroutine has made an alloca that // we can write to to store our return value. - returnValuePtr := c.createRuntimeCall("getTaskData", []llvm.Value{parentHandle}, "coro.parentData") + returnValuePtr := c.createRuntimeCall("getTaskPromisePtr", []llvm.Value{parentHandle}, "coro.parentData") alloca := c.builder.CreateBitCast(returnValuePtr, llvm.PointerType(inst.Operand(0).Type(), 0), "coro.parentAlloca") c.builder.CreateStore(inst.Operand(0), alloca) default: @@ -452,6 +447,14 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { c.builder.CreateUnreachable() } + // Replace calls to runtime.getCoroutineCall with the coroutine of this + // frame. + for _, getCoroutineCall := range getUses(c.mod.NamedFunction("runtime.getCoroutine")) { + frame := asyncFuncs[getCoroutineCall.InstructionParent().Parent()] + getCoroutineCall.ReplaceAllUsesWith(frame.taskHandle) + getCoroutineCall.EraseFromParentAsInstruction() + } + // Transform calls to time.Sleep() into coroutine suspend points. for _, sleepCall := range getUses(sleep) { // sleepCall must be a call instruction. @@ -495,37 +498,11 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { deadlockCall.EraseFromParentAsInstruction() } - // Transform calls to runtime.chanSendStub into channel send operations. - for _, sendOp := range getUses(chanSendStub) { + // Transform calls to runtime.chanSend into channel send operations. + for _, sendOp := range getUses(chanSend) { // sendOp must be a call instruction. frame := asyncFuncs[sendOp.InstructionParent().Parent()] - // Send the value over the channel, or block. - sendOp.SetOperand(0, frame.taskHandle) - sendOp.SetOperand(sendOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanSend")) - - // Use taskState.data to store the value to send: - // *(*valueType)(&coroutine.promise().data) = valueToSend - // runtime.chanSend(coroutine, ch) - bitcast := sendOp.Operand(2) - valueAlloca := bitcast.Operand(0) - c.builder.SetInsertPointBefore(valueAlloca) - promiseType := c.mod.GetTypeByName("runtime.taskState") - promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{ - frame.taskHandle, - llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false), - llvm.ConstInt(c.ctx.Int1Type(), 0, false), - }, "task.promise.raw") - promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise") - dataPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 2, false), - }, "task.promise.data") - sendOp.SetOperand(2, llvm.Undef(c.i8ptrType)) - valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), "")) - bitcast.EraseFromParentAsInstruction() - valueAlloca.EraseFromParentAsInstruction() - // Yield to scheduler. c.builder.SetInsertPointBefore(llvm.NextInstruction(sendOp)) continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{ @@ -538,21 +515,11 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock) } - // Transform calls to runtime.chanRecvStub into channel receive operations. - for _, recvOp := range getUses(chanRecvStub) { + // Transform calls to runtime.chanRecv into channel receive operations. + for _, recvOp := range getUses(chanRecv) { // recvOp must be a call instruction. frame := asyncFuncs[recvOp.InstructionParent().Parent()] - bitcast := recvOp.Operand(2) - commaOk := recvOp.Operand(3) - valueAlloca := bitcast.Operand(0) - - // Receive the value over the channel, or block. - recvOp.SetOperand(0, frame.taskHandle) - recvOp.SetOperand(recvOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanRecv")) - recvOp.SetOperand(2, llvm.Undef(c.i8ptrType)) - bitcast.EraseFromParentAsInstruction() - // Yield to scheduler. c.builder.SetInsertPointBefore(llvm.NextInstruction(recvOp)) continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{ @@ -564,32 +531,6 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { c.builder.SetInsertPointAtEnd(recvOp.InstructionParent()) sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup) sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock) - - // The value to receive is stored in taskState.data: - // runtime.chanRecv(coroutine, ch) - // promise := coroutine.promise() - // valueReceived := *(*valueType)(&promise.data) - // ok := promise.commaOk - c.builder.SetInsertPointBefore(wakeup.FirstInstruction()) - promiseType := c.mod.GetTypeByName("runtime.taskState") - promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{ - frame.taskHandle, - llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false), - llvm.ConstInt(c.ctx.Int1Type(), 0, false), - }, "task.promise.raw") - promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise") - dataPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 2, false), - }, "task.promise.data") - valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), "")) - valueAlloca.EraseFromParentAsInstruction() - commaOkPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 1, false), - }, "task.promise.comma-ok") - commaOk.ReplaceAllUsesWith(commaOkPtr) - recvOp.SetOperand(3, llvm.Undef(commaOk.Type())) } return true, c.lowerMakeGoroutineCalls() diff --git a/compiler/llvm.go b/compiler/llvm.go index ce52bcd0d..77fc9c44c 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -22,6 +22,17 @@ func getUses(value llvm.Value) []llvm.Value { return uses } +// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it +// first if it doesn't exist yet. +func (c *Compiler) getLifetimeEndFunc() llvm.Value { + fn := c.mod.NamedFunction("llvm.lifetime.end.p0i8") + if fn.IsNil() { + fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false) + fn = llvm.AddFunction(c.mod, "llvm.lifetime.end.p0i8", fnType) + } + return fn +} + // splitBasicBlock splits a LLVM basic block into two parts. All instructions // after afterInst are moved into a new basic block (created right after the // current one) with the given name. diff --git a/compiler/sizes.go b/compiler/sizes.go index 0d28712e4..6d212da0a 100644 --- a/compiler/sizes.go +++ b/compiler/sizes.go @@ -113,6 +113,9 @@ func (s *StdSizes) Sizeof(T types.Type) int64 { if k == types.Uintptr { return s.PtrSize } + if k == types.UnsafePointer { + return s.PtrSize + } panic("unknown basic type: " + t.String()) case *types.Array: n := t.Len() diff --git a/src/runtime/chan.go b/src/runtime/chan.go index fae7c5c8d..60c690507 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -39,33 +39,27 @@ const ( chanStateClosed ) -func chanSendStub(caller *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) -func chanRecvStub(caller *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr) func deadlockStub() // chanSend sends a single value over the channel. If this operation can // complete immediately (there is a goroutine waiting for a value), it sends the // value and re-activates both goroutines. If not, it sets itself as waiting on // a value. -// -// The unsafe.Pointer value is used during lowering. During IR generation, it -// points to the to-be-received value. During coroutine lowering, this value is -// replaced with a read from the coroutine promise. -func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) { +func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr) { if ch == nil { // A nil channel blocks forever. Do not scheduler this goroutine again. return } switch ch.state { case chanStateEmpty: + sender.promise().ptr = value ch.state = chanStateSend ch.blocked = sender case chanStateRecv: receiver := ch.blocked receiverPromise := receiver.promise() - senderPromise := sender.promise() - memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size) - receiverPromise.commaOk = true + memcpy(receiverPromise.ptr, value, size) + receiverPromise.data = 1 // commaOk = true ch.blocked = receiverPromise.next receiverPromise.next = nil activateTask(receiver) @@ -76,6 +70,7 @@ func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) { case chanStateClosed: runtimePanic("send on closed channel") case chanStateSend: + sender.promise().ptr = value sender.promise().next = ch.blocked ch.blocked = sender } @@ -85,11 +80,7 @@ func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) { // sender, it receives the value immediately and re-activates both coroutines. // If not, it sets itself as available for receiving. If the channel is closed, // it immediately activates itself with a zero value as the result. -// -// The two unnamed values exist to help during lowering. The unsafe.Pointer -// points to the value, and the *bool points to the comma-ok value. Both are -// replaced by reads from the coroutine promise. -func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr) { +func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintptr) { if ch == nil { // A nil channel blocks forever. Do not scheduler this goroutine again. return @@ -97,10 +88,9 @@ func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size switch ch.state { case chanStateSend: sender := ch.blocked - receiverPromise := receiver.promise() senderPromise := sender.promise() - memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size) - receiverPromise.commaOk = true + memcpy(value, senderPromise.ptr, size) + receiver.promise().data = 1 // commaOk = true ch.blocked = senderPromise.next senderPromise.next = nil activateTask(receiver) @@ -109,14 +99,15 @@ func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size ch.state = chanStateEmpty } case chanStateEmpty: + receiver.promise().ptr = value ch.state = chanStateRecv ch.blocked = receiver case chanStateClosed: - receiverPromise := receiver.promise() - memzero(unsafe.Pointer(&receiverPromise.data), size) - receiverPromise.commaOk = false + memzero(value, size) + receiver.promise().data = 0 // commaOk = false activateTask(receiver) case chanStateRecv: + receiver.promise().ptr = value receiver.promise().next = ch.blocked ch.blocked = receiver } @@ -142,8 +133,8 @@ func chanClose(ch *channel, size uintptr) { case chanStateRecv: // The receiver must be re-activated with a zero value. receiverPromise := ch.blocked.promise() - memzero(unsafe.Pointer(&receiverPromise.data), size) - receiverPromise.commaOk = false + memzero(receiverPromise.ptr, size) + receiverPromise.data = 0 // commaOk = false activateTask(ch.blocked) ch.state = chanStateClosed ch.blocked = nil diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go index 2503e18d5..b4af2360e 100644 --- a/src/runtime/scheduler.go +++ b/src/runtime/scheduler.go @@ -49,13 +49,17 @@ func (t *coroutine) promise() *taskState { func makeGoroutine(*uint8) *uint8 +// Compiler stub to get the current goroutine. Calls to this function are +// removed in the goroutine lowering pass. +func getCoroutine() *coroutine + // State/promise of a task. Internally represented as: // // {i8* next, i1 commaOk, i32/i64 data} type taskState struct { - next *coroutine - commaOk bool // 'comma-ok' flag for channel receive operation - data uint + next *coroutine + ptr unsafe.Pointer + data uint } // Queues used by the scheduler. @@ -107,12 +111,22 @@ func activateTask(task *coroutine) { runqueuePushBack(task) } -func setTaskData(task *coroutine, value unsafe.Pointer) { - task.promise().data = uint(uintptr(value)) +// getTaskPromisePtr is a helper function to set the current .ptr field of a +// coroutine promise. +func setTaskPromisePtr(task *coroutine, value unsafe.Pointer) { + task.promise().ptr = value +} + +// getTaskPromisePtr is a helper function to get the current .ptr field from a +// coroutine promise. +func getTaskPromisePtr(task *coroutine) unsafe.Pointer { + return task.promise().ptr } -func getTaskData(task *coroutine) unsafe.Pointer { - return unsafe.Pointer(uintptr(task.promise().data)) +// getTaskPromiseData is a helper function to get the current .data field of a +// coroutine promise. +func getTaskPromiseData(task *coroutine) uint { + return task.promise().data } // Add this task to the end of the run queue. May also destroy the task if it's diff --git a/testdata/channel.go b/testdata/channel.go index 840255911..e3b8aed36 100644 --- a/testdata/channel.go +++ b/testdata/channel.go @@ -20,6 +20,11 @@ func main() { n, ok = <-ch println("recv from closed channel:", n, ok) + // Test bigger values + ch2 := make(chan complex128) + go sendComplex(ch2) + println("complex128:", <-ch2) + // Test multi-sender. ch = make(chan int) go fastsender(ch) @@ -62,6 +67,10 @@ func sender(ch chan int) { close(ch) } +func sendComplex(ch chan complex128) { + ch <- 7+10.5i +} + func fastsender(ch chan int) { ch <- 10 ch <- 11 diff --git a/testdata/channel.txt b/testdata/channel.txt index 4c26c2f52..0d11d4e6f 100644 --- a/testdata/channel.txt +++ b/testdata/channel.txt @@ -9,6 +9,7 @@ received num: 6 received num: 7 received num: 8 recv from closed channel: 0 false +complex128: (+7.000000e+000+1.050000e+001i) got n: 10 got n: 11 got n: 10 |