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