github.com/aykevl/tinygo@v0.5.0/compiler/channel.go (about) 1 package compiler 2 3 // This file lowers channel operations (make/send/recv/close) to runtime calls 4 // or pseudo-operations that are lowered during goroutine lowering. 5 6 import ( 7 "go/types" 8 9 "golang.org/x/tools/go/ssa" 10 "tinygo.org/x/go-llvm" 11 ) 12 13 // emitMakeChan returns a new channel value for the given channel type. 14 func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) { 15 valueType, err := c.getLLVMType(expr.Type().(*types.Chan).Elem()) 16 if err != nil { 17 return llvm.Value{}, err 18 } 19 if c.targetData.TypeAllocSize(valueType) > c.targetData.TypeAllocSize(c.intType) { 20 // Values bigger than int overflow the data part of the coroutine. 21 // TODO: make the coroutine data part big enough to hold these bigger 22 // values. 23 return llvm.Value{}, c.makeError(expr.Pos(), "todo: channel with values bigger than int") 24 } 25 chanType := c.mod.GetTypeByName("runtime.channel") 26 size := c.targetData.TypeAllocSize(chanType) 27 sizeValue := llvm.ConstInt(c.uintptrType, size, false) 28 ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc") 29 ptr = c.builder.CreateBitCast(ptr, llvm.PointerType(chanType, 0), "chan") 30 return ptr, nil 31 } 32 33 // emitChanSend emits a pseudo chan send operation. It is lowered to the actual 34 // channel send operation during goroutine lowering. 35 func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) error { 36 valueType, err := c.getLLVMType(instr.Chan.Type().(*types.Chan).Elem()) 37 if err != nil { 38 return err 39 } 40 ch, err := c.parseExpr(frame, instr.Chan) 41 if err != nil { 42 return err 43 } 44 chanValue, err := c.parseExpr(frame, instr.X) 45 if err != nil { 46 return err 47 } 48 valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false) 49 valueAlloca := c.builder.CreateAlloca(valueType, "chan.value") 50 c.builder.CreateStore(chanValue, valueAlloca) 51 valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr") 52 c.createRuntimeCall("chanSendStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueSize}, "") 53 return nil 54 } 55 56 // emitChanRecv emits a pseudo chan receive operation. It is lowered to the 57 // actual channel receive operation during goroutine lowering. 58 func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { 59 valueType, err := c.getLLVMType(unop.X.Type().(*types.Chan).Elem()) 60 if err != nil { 61 return llvm.Value{}, err 62 } 63 valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false) 64 ch, err := c.parseExpr(frame, unop.X) 65 if err != nil { 66 return llvm.Value{}, err 67 } 68 valueAlloca := c.builder.CreateAlloca(valueType, "chan.value") 69 valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr") 70 valueOk := c.builder.CreateAlloca(c.ctx.Int1Type(), "chan.comma-ok.alloca") 71 c.createRuntimeCall("chanRecvStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueOk, valueSize}, "") 72 received := c.builder.CreateLoad(valueAlloca, "chan.received") 73 if unop.CommaOk { 74 commaOk := c.builder.CreateLoad(valueOk, "chan.comma-ok") 75 tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false)) 76 tuple = c.builder.CreateInsertValue(tuple, received, 0, "") 77 tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") 78 return tuple, nil 79 } else { 80 return received, nil 81 } 82 } 83 84 // emitChanClose closes the given channel. 85 func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) error { 86 valueType, err := c.getLLVMType(param.Type().(*types.Chan).Elem()) 87 valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false) 88 if err != nil { 89 return err 90 } 91 ch, err := c.parseExpr(frame, param) 92 if err != nil { 93 return err 94 } 95 c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "") 96 return nil 97 }