github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/irgen/channels.go (about) 1 //===- channels.go - IR generation for channels ---------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements IR generation for channels. 11 // 12 //===----------------------------------------------------------------------===// 13 14 package irgen 15 16 import ( 17 "llvm.org/llgo/third_party/gotools/go/ssa" 18 "llvm.org/llgo/third_party/gotools/go/types" 19 "llvm.org/llvm/bindings/go/llvm" 20 ) 21 22 // makeChan implements make(chantype[, size]) 23 func (fr *frame) makeChan(chantyp types.Type, size *govalue) *govalue { 24 // TODO(pcc): call __go_new_channel_big here if needed 25 dyntyp := fr.types.ToRuntime(chantyp) 26 size = fr.convert(size, types.Typ[types.Uintptr]) 27 ch := fr.runtime.newChannel.call(fr, dyntyp, size.value)[0] 28 return newValue(ch, chantyp) 29 } 30 31 // chanSend implements ch<- x 32 func (fr *frame) chanSend(ch *govalue, elem *govalue) { 33 elemtyp := ch.Type().Underlying().(*types.Chan).Elem() 34 elem = fr.convert(elem, elemtyp) 35 elemptr := fr.allocaBuilder.CreateAlloca(elem.value.Type(), "") 36 fr.builder.CreateStore(elem.value, elemptr) 37 elemptr = fr.builder.CreateBitCast(elemptr, llvm.PointerType(llvm.Int8Type(), 0), "") 38 chantyp := fr.types.ToRuntime(ch.Type()) 39 fr.runtime.sendBig.call(fr, chantyp, ch.value, elemptr) 40 } 41 42 // chanRecv implements x[, ok] = <-ch 43 func (fr *frame) chanRecv(ch *govalue, commaOk bool) (x, ok *govalue) { 44 elemtyp := ch.Type().Underlying().(*types.Chan).Elem() 45 ptr := fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(elemtyp), "") 46 ptri8 := fr.builder.CreateBitCast(ptr, llvm.PointerType(llvm.Int8Type(), 0), "") 47 chantyp := fr.types.ToRuntime(ch.Type()) 48 49 if commaOk { 50 okval := fr.runtime.chanrecv2.call(fr, chantyp, ch.value, ptri8)[0] 51 ok = newValue(okval, types.Typ[types.Bool]) 52 } else { 53 fr.runtime.receive.call(fr, chantyp, ch.value, ptri8) 54 } 55 x = newValue(fr.builder.CreateLoad(ptr, ""), elemtyp) 56 return 57 } 58 59 // chanClose implements close(ch) 60 func (fr *frame) chanClose(ch *govalue) { 61 fr.runtime.builtinClose.call(fr, ch.value) 62 } 63 64 func (fr *frame) chanSelect(sel *ssa.Select) (index, recvOk *govalue, recvElems []*govalue) { 65 n := uint64(len(sel.States)) 66 if !sel.Blocking { 67 // non-blocking means there's a default case 68 n++ 69 } 70 size := llvm.ConstInt(llvm.Int32Type(), n, false) 71 selectp := fr.runtime.newSelect.call(fr, size)[0] 72 73 // Allocate stack for the values to send and receive. 74 ptrs := make([]llvm.Value, len(sel.States)) 75 for i, state := range sel.States { 76 chantyp := state.Chan.Type().Underlying().(*types.Chan) 77 elemtyp := fr.types.ToLLVM(chantyp.Elem()) 78 if state.Dir == types.SendOnly { 79 ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") 80 fr.builder.CreateStore(fr.llvmvalue(state.Send), ptrs[i]) 81 } else { 82 // Only allocate stack space if the received value is used. 83 used := chanSelectStateUsed(sel, len(recvElems)) 84 if used { 85 ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") 86 } else { 87 ptrs[i] = llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)) 88 } 89 recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem())) 90 } 91 } 92 93 // Create select{send,recv2} calls. 94 var receivedp llvm.Value 95 if len(recvElems) > 0 { 96 receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "") 97 } 98 if !sel.Blocking { 99 // If the default case is chosen, the index must be -1. 100 fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type())) 101 } 102 for i, state := range sel.States { 103 ch := fr.llvmvalue(state.Chan) 104 index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) 105 if state.Dir == types.SendOnly { 106 fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index) 107 } else { 108 fr.runtime.selectrecv2.call(fr, selectp, ch, ptrs[i], receivedp, index) 109 } 110 } 111 112 // Fire off the select. 113 index = newValue(fr.runtime.selectgo.call(fr, selectp)[0], types.Typ[types.Int]) 114 if len(recvElems) > 0 { 115 recvOk = newValue(fr.builder.CreateLoad(receivedp, ""), types.Typ[types.Bool]) 116 for _, recvElem := range recvElems { 117 recvElem.value = fr.builder.CreateLoad(recvElem.value, "") 118 } 119 } 120 return index, recvOk, recvElems 121 } 122 123 func chanSelectStateUsed(sel *ssa.Select, recvIndex int) bool { 124 for _, instr := range *sel.Referrers() { 125 extract, ok := instr.(*ssa.Extract) 126 if !ok || extract.Index != (recvIndex+2) { 127 continue 128 } 129 if len(*extract.Referrers()) > 0 { 130 return true 131 } 132 } 133 return false 134 }