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  }