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
|
members[i] = member
|
||||||
}
|
}
|
||||||
return c.ctx.StructType(members, false), nil
|
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:
|
default:
|
||||||
return llvm.Type{}, errors.New("todo: unknown type: " + goType.String())
|
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)
|
c.builder.CreateStore(zero, it)
|
||||||
return it, nil
|
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:
|
case *ssa.Slice:
|
||||||
if expr.Max != nil {
|
if expr.Max != nil {
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
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() {
|
if !sleep.IsNil() {
|
||||||
worklist = append(worklist, sleep)
|
worklist = append(worklist, sleep)
|
||||||
}
|
}
|
||||||
|
deadlockStub := c.mod.NamedFunction("runtime.deadlockStub")
|
||||||
|
if !deadlockStub.IsNil() {
|
||||||
|
worklist = append(worklist, deadlockStub)
|
||||||
|
}
|
||||||
chanSendStub := c.mod.NamedFunction("runtime.chanSendStub")
|
chanSendStub := c.mod.NamedFunction("runtime.chanSendStub")
|
||||||
if !chanSendStub.IsNil() {
|
if !chanSendStub.IsNil() {
|
||||||
worklist = append(worklist, chanSendStub)
|
worklist = append(worklist, chanSendStub)
|
||||||
|
@ -288,7 +292,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
|
|
||||||
// Transform all async functions into coroutines.
|
// Transform all async functions into coroutines.
|
||||||
for _, f := range asyncList {
|
for _, f := range asyncList {
|
||||||
if f == sleep || f == chanSendStub || f == chanRecvStub {
|
if f == sleep || f == deadlockStub || f == chanSendStub || f == chanRecvStub {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +309,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if !inst.IsACallInst().IsNil() {
|
if !inst.IsACallInst().IsNil() {
|
||||||
callee := inst.CalledValue()
|
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
|
continue
|
||||||
}
|
}
|
||||||
asyncCalls = append(asyncCalls, inst)
|
asyncCalls = append(asyncCalls, inst)
|
||||||
|
@ -439,6 +443,26 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
sleepCall.EraseFromParentAsInstruction()
|
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.
|
// Transform calls to runtime.chanSendStub into channel send operations.
|
||||||
for _, sendOp := range getUses(chanSendStub) {
|
for _, sendOp := range getUses(chanSendStub) {
|
||||||
// sendOp must be a call instruction.
|
// sendOp must be a call instruction.
|
||||||
|
|
|
@ -41,6 +41,7 @@ const (
|
||||||
|
|
||||||
func chanSendStub(caller *coroutine, ch *channel, _ unsafe.Pointer, size uintptr)
|
func chanSendStub(caller *coroutine, ch *channel, _ unsafe.Pointer, size uintptr)
|
||||||
func chanRecvStub(caller *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, 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
|
// 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
|
// 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)
|
println("sum(100):", sum)
|
||||||
|
|
||||||
|
// Test select
|
||||||
|
go selectDeadlock()
|
||||||
|
go selectNoOp()
|
||||||
|
|
||||||
// Allow goroutines to exit.
|
// Allow goroutines to exit.
|
||||||
time.Sleep(time.Microsecond)
|
time.Sleep(time.Microsecond)
|
||||||
}
|
}
|
||||||
|
@ -93,3 +97,17 @@ func iterator(ch chan int, top int) {
|
||||||
}
|
}
|
||||||
close(ch)
|
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: 29
|
||||||
sum: 33
|
sum: 33
|
||||||
sum(100): 4950
|
sum(100): 4950
|
||||||
|
deadlocking
|
||||||
|
select no-op
|
||||||
|
after no-op
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче