Skip to content

Commit 01498f6

Browse files
committed
runtime: rewrite channel implementation
This rewrite simplifies the channel implementation considerably, with 34% less LOC. Perhaps the most important change is the removal of the channel state, which made sense when we had only send and receive operations but only makes things more compliated when multiple select operations can be pending on a single channel. I did this rewrite originally to make it possible to make channels parallelism-safe. The current implementation is not parallelism-safe, but it will be easy to make it so (the main additions will be a channel lock, a global select lock, and an atomic compare-and-swap in chanQueue.pop).
1 parent 2a76ceb commit 01498f6

File tree

2 files changed

+353
-534
lines changed

2 files changed

+353
-534
lines changed

compiler/channel.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ func (b *builder) createChanSend(instr *ssa.Send) {
4141
b.CreateStore(chanValue, valueAlloca)
4242
}
4343

44-
// Allocate blockedlist buffer.
45-
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
46-
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
44+
// Allocate buffer for the channel operation.
45+
channelOp := b.getLLVMRuntimeType("channelOp")
46+
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
4747

4848
// Do the send.
49-
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
49+
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
5050

5151
// End the lifetime of the allocas.
5252
// This also works around a bug in CoroSplit, at least in LLVM 8:
5353
// https://bugs.llvm.org/show_bug.cgi?id=41742
54-
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
54+
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
5555
if !isZeroSize {
5656
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
5757
}
@@ -72,20 +72,20 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
7272
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
7373
}
7474

75-
// Allocate blockedlist buffer.
76-
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
77-
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
75+
// Allocate buffer for the channel operation.
76+
channelOp := b.getLLVMRuntimeType("channelOp")
77+
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
7878

7979
// Do the receive.
80-
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
80+
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
8181
var received llvm.Value
8282
if isZeroSize {
8383
received = llvm.ConstNull(valueType)
8484
} else {
8585
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
8686
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
8787
}
88-
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
88+
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
8989

9090
if unop.CommaOk {
9191
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
@@ -198,26 +198,29 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
198198
if expr.Blocking {
199199
// Stack-allocate operation structures.
200200
// If these were simply created as a slice, they would heap-allocate.
201-
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
202-
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
203-
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
204-
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
201+
opsAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelOp"), len(selectStates))
202+
opsAlloca, opsSize := b.createTemporaryAlloca(opsAllocaType, "select.block.alloca")
203+
opsLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
204+
opsPtr := b.CreateGEP(opsAllocaType, opsAlloca, []llvm.Value{
205205
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
206206
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
207207
}, "select.block")
208208

209209
results = b.createRuntimeCall("chanSelect", []llvm.Value{
210210
recvbuf,
211211
statesPtr, statesLen, statesLen, // []chanSelectState
212-
chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList
212+
opsPtr, opsLen, opsLen, // []channelOp
213213
}, "select.result")
214214

215215
// Terminate the lifetime of the operation structures.
216-
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
216+
b.emitLifetimeEnd(opsAlloca, opsSize)
217217
} else {
218-
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
218+
opsPtr := llvm.ConstNull(b.dataPtrType)
219+
opsLen := llvm.ConstInt(b.uintptrType, 0, false)
220+
results = b.createRuntimeCall("chanSelect", []llvm.Value{
219221
recvbuf,
220222
statesPtr, statesLen, statesLen, // []chanSelectState
223+
opsPtr, opsLen, opsLen, // []channelOp (nil slice)
221224
}, "select.result")
222225
}
223226

0 commit comments

Comments
 (0)