forked from OSchip/llvm-project
[llgo] Elide alloca for unused received values in select
Summary: If a receive case in a select statement is not assigned to a named variable, then we can eliminate the alloca and copy at runtime. Test Plan: lit test added Reviewers: pcc Reviewed By: pcc Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D6785 llvm-svn: 225033
This commit is contained in:
parent
553185ee4b
commit
75f34af99c
|
@ -14,6 +14,7 @@
|
||||||
package irgen
|
package irgen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||||
"llvm.org/llvm/bindings/go/llvm"
|
"llvm.org/llvm/bindings/go/llvm"
|
||||||
)
|
)
|
||||||
|
@ -60,16 +61,9 @@ func (fr *frame) chanClose(ch *govalue) {
|
||||||
fr.runtime.builtinClose.call(fr, ch.value)
|
fr.runtime.builtinClose.call(fr, ch.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectState is equivalent to ssa.SelectState
|
func (fr *frame) chanSelect(sel *ssa.Select) (index, recvOk *govalue, recvElems []*govalue) {
|
||||||
type selectState struct {
|
n := uint64(len(sel.States))
|
||||||
Dir types.ChanDir
|
if !sel.Blocking {
|
||||||
Chan *govalue
|
|
||||||
Send *govalue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk *govalue, recvElems []*govalue) {
|
|
||||||
n := uint64(len(states))
|
|
||||||
if !blocking {
|
|
||||||
// non-blocking means there's a default case
|
// non-blocking means there's a default case
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
@ -77,17 +71,21 @@ func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk
|
||||||
selectp := fr.runtime.newSelect.call(fr, size)[0]
|
selectp := fr.runtime.newSelect.call(fr, size)[0]
|
||||||
|
|
||||||
// Allocate stack for the values to send and receive.
|
// Allocate stack for the values to send and receive.
|
||||||
//
|
ptrs := make([]llvm.Value, len(sel.States))
|
||||||
// TODO(axw) check if received elements have any users, and
|
for i, state := range sel.States {
|
||||||
// elide stack allocation if not (pass nil to recv2 instead.)
|
|
||||||
ptrs := make([]llvm.Value, len(states))
|
|
||||||
for i, state := range states {
|
|
||||||
chantyp := state.Chan.Type().Underlying().(*types.Chan)
|
chantyp := state.Chan.Type().Underlying().(*types.Chan)
|
||||||
elemtyp := fr.types.ToLLVM(chantyp.Elem())
|
elemtyp := fr.types.ToLLVM(chantyp.Elem())
|
||||||
ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "")
|
|
||||||
if state.Dir == types.SendOnly {
|
if state.Dir == types.SendOnly {
|
||||||
fr.builder.CreateStore(state.Send.value, ptrs[i])
|
ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "")
|
||||||
|
fr.builder.CreateStore(fr.llvmvalue(state.Send), ptrs[i])
|
||||||
} else {
|
} 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()))
|
recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,12 +95,12 @@ func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk
|
||||||
if len(recvElems) > 0 {
|
if len(recvElems) > 0 {
|
||||||
receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "")
|
receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "")
|
||||||
}
|
}
|
||||||
if !blocking {
|
if !sel.Blocking {
|
||||||
// If the default case is chosen, the index must be -1.
|
// If the default case is chosen, the index must be -1.
|
||||||
fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type()))
|
fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type()))
|
||||||
}
|
}
|
||||||
for i, state := range states {
|
for i, state := range sel.States {
|
||||||
ch := state.Chan.value
|
ch := fr.llvmvalue(state.Chan)
|
||||||
index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false)
|
index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false)
|
||||||
if state.Dir == types.SendOnly {
|
if state.Dir == types.SendOnly {
|
||||||
fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index)
|
fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index)
|
||||||
|
@ -121,3 +119,16 @@ func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk
|
||||||
}
|
}
|
||||||
return index, recvOk, recvElems
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -1152,15 +1152,7 @@ func (fr *frame) instruction(instr ssa.Instruction) {
|
||||||
fr.runDefers()
|
fr.runDefers()
|
||||||
|
|
||||||
case *ssa.Select:
|
case *ssa.Select:
|
||||||
states := make([]selectState, len(instr.States))
|
index, recvOk, recvElems := fr.chanSelect(instr)
|
||||||
for i, state := range instr.States {
|
|
||||||
states[i] = selectState{
|
|
||||||
Dir: state.Dir,
|
|
||||||
Chan: fr.value(state.Chan),
|
|
||||||
Send: fr.value(state.Send),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index, recvOk, recvElems := fr.chanSelect(states, instr.Blocking)
|
|
||||||
tuple := append([]*govalue{index, recvOk}, recvElems...)
|
tuple := append([]*govalue{index, recvOk}, recvElems...)
|
||||||
fr.tuples[instr] = tuple
|
fr.tuples[instr] = tuple
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// RUN: llgo -S -emit-llvm -o - %s | FileCheck %s
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
// CHECK-NOT: alloca [1024 x i8]
|
||||||
|
// CHECK-NOT: alloca [2048 x i8]
|
||||||
|
// CHECK: alloca [4096 x i8]
|
||||||
|
func F() {
|
||||||
|
ch1 := make(chan [1024]byte)
|
||||||
|
ch2 := make(chan [2048]byte)
|
||||||
|
ch3 := make(chan [4096]byte)
|
||||||
|
select {
|
||||||
|
case <-ch1:
|
||||||
|
case _ = <-ch2:
|
||||||
|
case x := <-ch3:
|
||||||
|
_ = x[0]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue