compiler,runtime: store channel size in the channel itself

This may have a small effect on code size sometimes, but will simplify
the implementation of the select statement.
Этот коммит содержится в:
Ayke van Laethem 2019-06-08 16:12:30 +02:00 коммит произвёл Ron Evans
родитель 5be412dabe
коммит 8890a0f3c8
2 изменённых файлов: 28 добавлений и 19 удалений

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

@ -4,6 +4,7 @@ package compiler
// or pseudo-operations that are lowered during goroutine lowering. // or pseudo-operations that are lowered during goroutine lowering.
import ( import (
"fmt"
"go/types" "go/types"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
@ -12,11 +13,22 @@ import (
// emitMakeChan returns a new channel value for the given channel type. // emitMakeChan returns a new channel value for the given channel type.
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) { func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
chanType := c.getLLVMType(c.getRuntimeType("channel")) chanType := c.getLLVMType(expr.Type())
size := c.targetData.TypeAllocSize(chanType) size := c.targetData.TypeAllocSize(chanType.ElementType())
sizeValue := llvm.ConstInt(c.uintptrType, size, false) sizeValue := llvm.ConstInt(c.uintptrType, size, false)
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc") ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
ptr = c.builder.CreateBitCast(ptr, llvm.PointerType(chanType, 0), "chan") ptr = c.builder.CreateBitCast(ptr, chanType, "chan")
// Set the elementSize field
elementSizePtr := c.builder.CreateGEP(ptr, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}, "")
elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem()))
if elementSize > 0xffff {
return ptr, c.makeError(expr.Pos(), fmt.Sprintf("element size is %d bytes, which is bigger than the maximum of %d bytes", elementSize, 0xffff))
}
elementSizeValue := llvm.ConstInt(c.ctx.Int16Type(), elementSize, false)
c.builder.CreateStore(elementSizeValue, elementSizePtr)
return ptr, nil return ptr, nil
} }
@ -33,8 +45,7 @@ func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
// Do the send. // Do the send.
coroutine := c.createRuntimeCall("getCoroutine", nil, "") coroutine := c.createRuntimeCall("getCoroutine", nil, "")
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false) c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
// End the lifetime of the alloca. // End the lifetime of the alloca.
// This also works around a bug in CoroSplit, at least in LLVM 8: // This also works around a bug in CoroSplit, at least in LLVM 8:
@ -53,8 +64,7 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
// Do the receive. // Do the receive.
coroutine := c.createRuntimeCall("getCoroutine", nil, "") coroutine := c.createRuntimeCall("getCoroutine", nil, "")
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false) c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
received := c.builder.CreateLoad(valueAlloca, "chan.received") received := c.builder.CreateLoad(valueAlloca, "chan.received")
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
@ -72,8 +82,6 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
// emitChanClose closes the given channel. // emitChanClose closes the given channel.
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) { func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) {
valueType := c.getLLVMType(param.Type().(*types.Chan).Elem())
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
ch := c.getValue(frame, param) ch := c.getValue(frame, param)
c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "") c.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
} }

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

@ -28,8 +28,9 @@ import (
) )
type channel struct { type channel struct {
state uint8 elementSize uint16 // the size of one value in this channel
blocked *coroutine state uint8
blocked *coroutine
} }
const ( const (
@ -45,7 +46,7 @@ func deadlockStub()
// complete immediately (there is a goroutine waiting for a value), it sends the // 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 // value and re-activates both goroutines. If not, it sets itself as waiting on
// a value. // a value.
func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr) { func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer) {
if ch == nil { if ch == nil {
// A nil channel blocks forever. Do not scheduler this goroutine again. // A nil channel blocks forever. Do not scheduler this goroutine again.
return return
@ -58,7 +59,7 @@ func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr
case chanStateRecv: case chanStateRecv:
receiver := ch.blocked receiver := ch.blocked
receiverPromise := receiver.promise() receiverPromise := receiver.promise()
memcpy(receiverPromise.ptr, value, size) memcpy(receiverPromise.ptr, value, uintptr(ch.elementSize))
receiverPromise.data = 1 // commaOk = true receiverPromise.data = 1 // commaOk = true
ch.blocked = receiverPromise.next ch.blocked = receiverPromise.next
receiverPromise.next = nil receiverPromise.next = nil
@ -80,7 +81,7 @@ func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr
// sender, it receives the value immediately and re-activates both coroutines. // 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, // 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. // it immediately activates itself with a zero value as the result.
func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintptr) { func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer) {
if ch == nil { if ch == nil {
// A nil channel blocks forever. Do not scheduler this goroutine again. // A nil channel blocks forever. Do not scheduler this goroutine again.
return return
@ -89,7 +90,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
case chanStateSend: case chanStateSend:
sender := ch.blocked sender := ch.blocked
senderPromise := sender.promise() senderPromise := sender.promise()
memcpy(value, senderPromise.ptr, size) memcpy(value, senderPromise.ptr, uintptr(ch.elementSize))
receiver.promise().data = 1 // commaOk = true receiver.promise().data = 1 // commaOk = true
ch.blocked = senderPromise.next ch.blocked = senderPromise.next
senderPromise.next = nil senderPromise.next = nil
@ -103,7 +104,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
ch.state = chanStateRecv ch.state = chanStateRecv
ch.blocked = receiver ch.blocked = receiver
case chanStateClosed: case chanStateClosed:
memzero(value, size) memzero(value, uintptr(ch.elementSize))
receiver.promise().data = 0 // commaOk = false receiver.promise().data = 0 // commaOk = false
activateTask(receiver) activateTask(receiver)
case chanStateRecv: case chanStateRecv:
@ -115,7 +116,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
// chanClose closes the given channel. If this channel has a receiver or is // chanClose closes the given channel. If this channel has a receiver or is
// empty, it closes the channel. Else, it panics. // empty, it closes the channel. Else, it panics.
func chanClose(ch *channel, size uintptr) { func chanClose(ch *channel) {
if ch == nil { if ch == nil {
// Not allowed by the language spec. // Not allowed by the language spec.
runtimePanic("close of nil channel") runtimePanic("close of nil channel")
@ -133,7 +134,7 @@ func chanClose(ch *channel, size uintptr) {
case chanStateRecv: case chanStateRecv:
// The receiver must be re-activated with a zero value. // The receiver must be re-activated with a zero value.
receiverPromise := ch.blocked.promise() receiverPromise := ch.blocked.promise()
memzero(receiverPromise.ptr, size) memzero(receiverPromise.ptr, uintptr(ch.elementSize))
receiverPromise.data = 0 // commaOk = false receiverPromise.data = 0 // commaOk = false
activateTask(ch.blocked) activateTask(ch.blocked)
ch.state = chanStateClosed ch.state = chanStateClosed