| //===- channels.go - IR generation for channels ---------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements IR generation for channels. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| package irgen |
| |
| import ( |
| "llvm.org/llgo/third_party/gotools/go/ssa" |
| "llvm.org/llgo/third_party/gotools/go/types" |
| "llvm.org/llvm/bindings/go/llvm" |
| ) |
| |
| // makeChan implements make(chantype[, size]) |
| func (fr *frame) makeChan(chantyp types.Type, size *govalue) *govalue { |
| // TODO(pcc): call __go_new_channel_big here if needed |
| dyntyp := fr.types.ToRuntime(chantyp) |
| size = fr.convert(size, types.Typ[types.Uintptr]) |
| ch := fr.runtime.newChannel.call(fr, dyntyp, size.value)[0] |
| return newValue(ch, chantyp) |
| } |
| |
| // chanSend implements ch<- x |
| func (fr *frame) chanSend(ch *govalue, elem *govalue) { |
| elemtyp := ch.Type().Underlying().(*types.Chan).Elem() |
| elem = fr.convert(elem, elemtyp) |
| elemptr := fr.allocaBuilder.CreateAlloca(elem.value.Type(), "") |
| fr.builder.CreateStore(elem.value, elemptr) |
| elemptr = fr.builder.CreateBitCast(elemptr, llvm.PointerType(llvm.Int8Type(), 0), "") |
| chantyp := fr.types.ToRuntime(ch.Type()) |
| fr.runtime.sendBig.call(fr, chantyp, ch.value, elemptr) |
| } |
| |
| // chanRecv implements x[, ok] = <-ch |
| func (fr *frame) chanRecv(ch *govalue, commaOk bool) (x, ok *govalue) { |
| elemtyp := ch.Type().Underlying().(*types.Chan).Elem() |
| ptr := fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(elemtyp), "") |
| ptri8 := fr.builder.CreateBitCast(ptr, llvm.PointerType(llvm.Int8Type(), 0), "") |
| chantyp := fr.types.ToRuntime(ch.Type()) |
| |
| if commaOk { |
| okval := fr.runtime.chanrecv2.call(fr, chantyp, ch.value, ptri8)[0] |
| ok = newValue(okval, types.Typ[types.Bool]) |
| } else { |
| fr.runtime.receive.call(fr, chantyp, ch.value, ptri8) |
| } |
| x = newValue(fr.builder.CreateLoad(ptr, ""), elemtyp) |
| return |
| } |
| |
| // chanClose implements close(ch) |
| func (fr *frame) chanClose(ch *govalue) { |
| fr.runtime.builtinClose.call(fr, ch.value) |
| } |
| |
| func (fr *frame) chanSelect(sel *ssa.Select) (index, recvOk *govalue, recvElems []*govalue) { |
| n := uint64(len(sel.States)) |
| if !sel.Blocking { |
| // non-blocking means there's a default case |
| n++ |
| } |
| size := llvm.ConstInt(llvm.Int32Type(), n, false) |
| selectp := fr.runtime.newSelect.call(fr, size)[0] |
| |
| // Allocate stack for the values to send and receive. |
| ptrs := make([]llvm.Value, len(sel.States)) |
| for i, state := range sel.States { |
| chantyp := state.Chan.Type().Underlying().(*types.Chan) |
| elemtyp := fr.types.ToLLVM(chantyp.Elem()) |
| if state.Dir == types.SendOnly { |
| ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") |
| fr.builder.CreateStore(fr.llvmvalue(state.Send), ptrs[i]) |
| } else { |
| // Only allocate stack space if the received value is used. |
| used := chanSelectStateUsed(sel, len(recvElems)) |
| if used { |
| ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") |
| } else { |
| ptrs[i] = llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)) |
| } |
| recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem())) |
| } |
| } |
| |
| // Create select{send,recv2} calls. |
| var receivedp llvm.Value |
| if len(recvElems) > 0 { |
| receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "") |
| } |
| if !sel.Blocking { |
| // If the default case is chosen, the index must be -1. |
| fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type())) |
| } |
| for i, state := range sel.States { |
| ch := fr.llvmvalue(state.Chan) |
| index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) |
| if state.Dir == types.SendOnly { |
| fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index) |
| } else { |
| fr.runtime.selectrecv2.call(fr, selectp, ch, ptrs[i], receivedp, index) |
| } |
| } |
| |
| // Fire off the select. |
| index = newValue(fr.runtime.selectgo.call(fr, selectp)[0], types.Typ[types.Int]) |
| if len(recvElems) > 0 { |
| recvOk = newValue(fr.builder.CreateLoad(receivedp, ""), types.Typ[types.Bool]) |
| for _, recvElem := range recvElems { |
| recvElem.value = fr.builder.CreateLoad(recvElem.value, "") |
| } |
| } |
| return index, recvOk, recvElems |
| } |
| |
| func chanSelectStateUsed(sel *ssa.Select, recvIndex int) bool { |
| for _, instr := range *sel.Referrers() { |
| extract, ok := instr.(*ssa.Extract) |
| if !ok || extract.Index != (recvIndex+2) { |
| continue |
| } |
| if len(*extract.Referrers()) > 0 { |
| return true |
| } |
| } |
| return false |
| } |