aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compiler/channel.go38
-rw-r--r--compiler/compiler.go6
-rw-r--r--compiler/goroutine-lowering.go109
-rw-r--r--compiler/llvm.go11
-rw-r--r--compiler/sizes.go3
-rw-r--r--src/runtime/chan.go37
-rw-r--r--src/runtime/scheduler.go28
-rw-r--r--testdata/channel.go9
-rw-r--r--testdata/channel.txt1
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