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  }