@@ -85,3 +85,154 @@ func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) {
8585 ch := c .getValue (frame , param )
8686 c .createRuntimeCall ("chanClose" , []llvm.Value {ch }, "" )
8787}
88+
89+ // emitSelect emits all IR necessary for a select statements. That's a
90+ // non-trivial amount of code because select is very complex to implement.
91+ func (c * Compiler ) emitSelect (frame * Frame , expr * ssa.Select ) llvm.Value {
92+ if len (expr .States ) == 0 {
93+ // Shortcuts for some simple selects.
94+ llvmType := c .getLLVMType (expr .Type ())
95+ if expr .Blocking {
96+ // Blocks forever:
97+ // select {}
98+ c .createRuntimeCall ("deadlockStub" , nil , "" )
99+ return llvm .Undef (llvmType )
100+ } else {
101+ // No-op:
102+ // select {
103+ // default:
104+ // }
105+ retval := llvm .Undef (llvmType )
106+ retval = c .builder .CreateInsertValue (retval , llvm .ConstInt (c .intType , 0xffffffffffffffff , true ), 0 , "" )
107+ return retval // {-1, false}
108+ }
109+ }
110+
111+ // This code create a (stack-allocated) slice containing all the select
112+ // cases and then calls runtime.chanSelect to perform the actual select
113+ // statement.
114+ // Simple selects (blocking and with just one case) are already transformed
115+ // into regular chan operations during SSA construction so we don't have to
116+ // optimize such small selects.
117+
118+ // Go through all the cases. Create the selectStates slice and and
119+ // determine the receive buffer size and alignment.
120+ recvbufSize := uint64 (0 )
121+ recvbufAlign := 0
122+ hasReceives := false
123+ var selectStates []llvm.Value
124+ chanSelectStateType := c .getLLVMRuntimeType ("chanSelectState" )
125+ for _ , state := range expr .States {
126+ ch := c .getValue (frame , state .Chan )
127+ selectState := c .getZeroValue (chanSelectStateType )
128+ selectState = c .builder .CreateInsertValue (selectState , ch , 0 , "" )
129+ switch state .Dir {
130+ case types .RecvOnly :
131+ // Make sure the receive buffer is big enough and has the correct alignment.
132+ llvmType := c .getLLVMType (state .Chan .Type ().(* types.Chan ).Elem ())
133+ if size := c .targetData .TypeAllocSize (llvmType ); size > recvbufSize {
134+ recvbufSize = size
135+ }
136+ if align := c .targetData .ABITypeAlignment (llvmType ); align > recvbufAlign {
137+ recvbufAlign = align
138+ }
139+ hasReceives = true
140+ case types .SendOnly :
141+ // Store this value in an alloca and put a pointer to this alloca
142+ // in the send state.
143+ sendValue := c .getValue (frame , state .Send )
144+ alloca := c .createEntryBlockAlloca (sendValue .Type (), "select.send.value" )
145+ c .builder .CreateStore (sendValue , alloca )
146+ ptr := c .builder .CreateBitCast (alloca , c .i8ptrType , "" )
147+ selectState = c .builder .CreateInsertValue (selectState , ptr , 1 , "" )
148+ default :
149+ panic ("unreachable" )
150+ }
151+ selectStates = append (selectStates , selectState )
152+ }
153+
154+ // Create a receive buffer, where the received value will be stored.
155+ recvbuf := llvm .Undef (c .i8ptrType )
156+ if hasReceives {
157+ allocaType := llvm .ArrayType (c .ctx .Int8Type (), int (recvbufSize ))
158+ recvbufAlloca := c .builder .CreateAlloca (allocaType , "select.recvbuf.alloca" )
159+ recvbufAlloca .SetAlignment (recvbufAlign )
160+ recvbuf = c .builder .CreateGEP (recvbufAlloca , []llvm.Value {
161+ llvm .ConstInt (c .ctx .Int32Type (), 0 , false ),
162+ llvm .ConstInt (c .ctx .Int32Type (), 0 , false ),
163+ }, "select.recvbuf" )
164+ }
165+
166+ // Create the states slice (allocated on the stack).
167+ statesAllocaType := llvm .ArrayType (chanSelectStateType , len (selectStates ))
168+ statesAlloca := c .builder .CreateAlloca (statesAllocaType , "select.states.alloca" )
169+ for i , state := range selectStates {
170+ // Set each slice element to the appropriate channel.
171+ gep := c .builder .CreateGEP (statesAlloca , []llvm.Value {
172+ llvm .ConstInt (c .ctx .Int32Type (), 0 , false ),
173+ llvm .ConstInt (c .ctx .Int32Type (), uint64 (i ), false ),
174+ }, "" )
175+ c .builder .CreateStore (state , gep )
176+ }
177+ statesPtr := c .builder .CreateGEP (statesAlloca , []llvm.Value {
178+ llvm .ConstInt (c .ctx .Int32Type (), 0 , false ),
179+ llvm .ConstInt (c .ctx .Int32Type (), 0 , false ),
180+ }, "select.states" )
181+ statesLen := llvm .ConstInt (c .uintptrType , uint64 (len (selectStates )), false )
182+
183+ // Convert the 'blocking' flag on this select into a LLVM value.
184+ blockingInt := uint64 (0 )
185+ if expr .Blocking {
186+ blockingInt = 1
187+ }
188+ blockingValue := llvm .ConstInt (c .ctx .Int1Type (), blockingInt , false )
189+
190+ // Do the select in the runtime.
191+ results := c .createRuntimeCall ("chanSelect" , []llvm.Value {
192+ recvbuf ,
193+ statesPtr , statesLen , statesLen , // []chanSelectState
194+ blockingValue ,
195+ }, "" )
196+
197+ // The result value does not include all the possible received values,
198+ // because we can't load them in advance. Instead, the *ssa.Extract
199+ // instruction will treat a *ssa.Select specially and load it there inline.
200+ // Store the receive alloca in a sidetable until we hit this extract
201+ // instruction.
202+ if frame .selectRecvBuf == nil {
203+ frame .selectRecvBuf = make (map [* ssa.Select ]llvm.Value )
204+ }
205+ frame .selectRecvBuf [expr ] = recvbuf
206+
207+ return results
208+ }
209+
210+ // getChanSelectResult returns the special values from a *ssa.Extract expression
211+ // when extracting a value from a select statement (*ssa.Select). Because
212+ // *ssa.Select cannot load all values in advance, it does this later in the
213+ // *ssa.Extract expression.
214+ func (c * Compiler ) getChanSelectResult (frame * Frame , expr * ssa.Extract ) llvm.Value {
215+ if expr .Index == 0 {
216+ // index
217+ value := c .getValue (frame , expr .Tuple )
218+ index := c .builder .CreateExtractValue (value , expr .Index , "" )
219+ if index .Type ().IntTypeWidth () < c .intType .IntTypeWidth () {
220+ index = c .builder .CreateSExt (index , c .intType , "" )
221+ }
222+ return index
223+ } else if expr .Index == 1 {
224+ // comma-ok
225+ value := c .getValue (frame , expr .Tuple )
226+ return c .builder .CreateExtractValue (value , expr .Index , "" )
227+ } else {
228+ // Select statements are (index, ok, ...) where ... is a number of
229+ // received values, depending on how many receive statements there
230+ // are. They are all combined into one alloca (because only one
231+ // receive can proceed at a time) so we'll get that alloca, bitcast
232+ // it to the correct type, and dereference it.
233+ recvbuf := frame .selectRecvBuf [expr .Tuple .(* ssa.Select )]
234+ typ := llvm .PointerType (c .getLLVMType (expr .Type ()), 0 )
235+ ptr := c .builder .CreateBitCast (recvbuf , typ , "" )
236+ return c .builder .CreateLoad (ptr , "" )
237+ }
238+ }
0 commit comments