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