all: implement trivial select statements
Implement two trivial uses of the select statement. Always blocking: select {} No-op: select { default: } Go 1.12 added a `select {}` instruction to syscall/js, so this is needed for Go 1.12 support. More complete support for select will be added in the future.
Этот коммит содержится в:
родитель
4d82f42d61
коммит
ad7297a539
5 изменённых файлов: 81 добавлений и 2 удалений
|
@ -500,6 +500,16 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
|
|||
members[i] = member
|
||||
}
|
||||
return c.ctx.StructType(members, false), nil
|
||||
case *types.Tuple:
|
||||
members := make([]llvm.Type, typ.Len())
|
||||
for i := 0; i < typ.Len(); i++ {
|
||||
member, err := c.getLLVMType(typ.At(i).Type())
|
||||
if err != nil {
|
||||
return llvm.Type{}, err
|
||||
}
|
||||
members[i] = member
|
||||
}
|
||||
return c.ctx.StructType(members, false), nil
|
||||
default:
|
||||
return llvm.Type{}, errors.New("todo: unknown type: " + goType.String())
|
||||
}
|
||||
|
@ -1821,6 +1831,29 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
}
|
||||
c.builder.CreateStore(zero, it)
|
||||
return it, nil
|
||||
case *ssa.Select:
|
||||
if len(expr.States) == 0 {
|
||||
// Shortcuts for some simple selects.
|
||||
llvmType, err := c.getLLVMType(expr.Type())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
if expr.Blocking {
|
||||
// Blocks forever:
|
||||
// select {}
|
||||
c.createRuntimeCall("deadlockStub", nil, "")
|
||||
return llvm.Undef(llvmType), nil
|
||||
} else {
|
||||
// No-op:
|
||||
// select {
|
||||
// default:
|
||||
// }
|
||||
retval := llvm.Undef(llvmType)
|
||||
retval = c.builder.CreateInsertValue(retval, llvm.ConstInt(c.intType, 0xffffffffffffffff, true), 0, "")
|
||||
return retval, nil // {-1, false}
|
||||
}
|
||||
}
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "unimplemented: "+expr.String())
|
||||
case *ssa.Slice:
|
||||
if expr.Max != nil {
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
||||
|
|
|
@ -170,6 +170,10 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
if !sleep.IsNil() {
|
||||
worklist = append(worklist, sleep)
|
||||
}
|
||||
deadlockStub := c.mod.NamedFunction("runtime.deadlockStub")
|
||||
if !deadlockStub.IsNil() {
|
||||
worklist = append(worklist, deadlockStub)
|
||||
}
|
||||
chanSendStub := c.mod.NamedFunction("runtime.chanSendStub")
|
||||
if !chanSendStub.IsNil() {
|
||||
worklist = append(worklist, chanSendStub)
|
||||
|
@ -288,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 == chanSendStub || f == chanRecvStub {
|
||||
if f == sleep || f == deadlockStub || f == chanSendStub || f == chanRecvStub {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -305,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 == chanSendStub || callee == chanRecvStub {
|
||||
if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSendStub || callee == chanRecvStub {
|
||||
continue
|
||||
}
|
||||
asyncCalls = append(asyncCalls, inst)
|
||||
|
@ -439,6 +443,26 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
sleepCall.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Transform calls to runtime.deadlockStub into coroutine suspends (without
|
||||
// resume).
|
||||
for _, deadlockCall := range getUses(deadlockStub) {
|
||||
// deadlockCall must be a call instruction.
|
||||
frame := asyncFuncs[deadlockCall.InstructionParent().Parent()]
|
||||
|
||||
// Exit coroutine.
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
|
||||
}, "")
|
||||
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||
deadlockCall.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Transform calls to runtime.chanSendStub into channel send operations.
|
||||
for _, sendOp := range getUses(chanSendStub) {
|
||||
// sendOp must be a call instruction.
|
||||
|
|
|
@ -41,6 +41,7 @@ const (
|
|||
|
||||
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
|
||||
|
|
18
testdata/channel.go
предоставленный
18
testdata/channel.go
предоставленный
|
@ -43,6 +43,10 @@ func main() {
|
|||
}
|
||||
println("sum(100):", sum)
|
||||
|
||||
// Test select
|
||||
go selectDeadlock()
|
||||
go selectNoOp()
|
||||
|
||||
// Allow goroutines to exit.
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
|
@ -93,3 +97,17 @@ func iterator(ch chan int, top int) {
|
|||
}
|
||||
close(ch)
|
||||
}
|
||||
|
||||
func selectDeadlock() {
|
||||
println("deadlocking")
|
||||
select {}
|
||||
println("unreachable")
|
||||
}
|
||||
|
||||
func selectNoOp() {
|
||||
println("select no-op")
|
||||
select {
|
||||
default:
|
||||
}
|
||||
println("after no-op")
|
||||
}
|
||||
|
|
3
testdata/channel.txt
предоставленный
3
testdata/channel.txt
предоставленный
|
@ -19,3 +19,6 @@ sum: 25
|
|||
sum: 29
|
||||
sum: 33
|
||||
sum(100): 4950
|
||||
deadlocking
|
||||
select no-op
|
||||
after no-op
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче