]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/walk/select.go
9ca132af7ad5a5ff3ce1ca051b6f75cce2242503
[gostls13.git] / src / cmd / compile / internal / walk / select.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package walk
6
7 import (
8         "cmd/compile/internal/base"
9         "cmd/compile/internal/ir"
10         "cmd/compile/internal/typecheck"
11         "cmd/compile/internal/types"
12 )
13
14 func walkSelect(sel *ir.SelectStmt) {
15         lno := ir.SetPos(sel)
16         if sel.Walked() {
17                 base.Fatalf("double walkSelect")
18         }
19         sel.SetWalked(true)
20
21         init := ir.TakeInit(sel)
22
23         init = append(init, walkSelectCases(sel.Cases)...)
24         sel.Cases = nil
25
26         sel.Compiled = init
27         walkStmtList(sel.Compiled)
28
29         base.Pos = lno
30 }
31
32 func walkSelectCases(cases []*ir.CommClause) []ir.Node {
33         ncas := len(cases)
34         sellineno := base.Pos
35
36         // optimization: zero-case select
37         if ncas == 0 {
38                 return []ir.Node{mkcallstmt("block")}
39         }
40
41         // optimization: one-case select: single op.
42         if ncas == 1 {
43                 cas := cases[0]
44                 ir.SetPos(cas)
45                 l := cas.Init()
46                 if cas.Comm != nil { // not default:
47                         n := cas.Comm
48                         l = append(l, ir.TakeInit(n)...)
49                         switch n.Op() {
50                         default:
51                                 base.Fatalf("select %v", n.Op())
52
53                         case ir.OSEND:
54                                 // already ok
55
56                         case ir.OSELRECV2:
57                                 r := n.(*ir.AssignListStmt)
58                                 if ir.IsBlank(r.Lhs[0]) && ir.IsBlank(r.Lhs[1]) {
59                                         n = r.Rhs[0]
60                                         break
61                                 }
62                                 r.SetOp(ir.OAS2RECV)
63                         }
64
65                         l = append(l, n)
66                 }
67
68                 l = append(l, cas.Body...)
69                 l = append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
70                 return l
71         }
72
73         // convert case value arguments to addresses.
74         // this rewrite is used by both the general code and the next optimization.
75         var dflt *ir.CommClause
76         for _, cas := range cases {
77                 ir.SetPos(cas)
78                 n := cas.Comm
79                 if n == nil {
80                         dflt = cas
81                         continue
82                 }
83                 switch n.Op() {
84                 case ir.OSEND:
85                         n := n.(*ir.SendStmt)
86                         n.Value = typecheck.NodAddr(n.Value)
87                         n.Value = typecheck.Expr(n.Value)
88
89                 case ir.OSELRECV2:
90                         n := n.(*ir.AssignListStmt)
91                         if !ir.IsBlank(n.Lhs[0]) {
92                                 n.Lhs[0] = typecheck.NodAddr(n.Lhs[0])
93                                 n.Lhs[0] = typecheck.Expr(n.Lhs[0])
94                         }
95                 }
96         }
97
98         // optimization: two-case select but one is default: single non-blocking op.
99         if ncas == 2 && dflt != nil {
100                 cas := cases[0]
101                 if cas == dflt {
102                         cas = cases[1]
103                 }
104
105                 n := cas.Comm
106                 ir.SetPos(n)
107                 r := ir.NewIfStmt(base.Pos, nil, nil, nil)
108                 r.SetInit(cas.Init())
109                 var cond ir.Node
110                 switch n.Op() {
111                 default:
112                         base.Fatalf("select %v", n.Op())
113
114                 case ir.OSEND:
115                         // if selectnbsend(c, v) { body } else { default body }
116                         n := n.(*ir.SendStmt)
117                         ch := n.Chan
118                         cond = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value)
119
120                 case ir.OSELRECV2:
121                         n := n.(*ir.AssignListStmt)
122                         recv := n.Rhs[0].(*ir.UnaryExpr)
123                         ch := recv.X
124                         elem := n.Lhs[0]
125                         if ir.IsBlank(elem) {
126                                 elem = typecheck.NodNil()
127                         }
128                         cond = typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
129                         fn := chanfn("selectnbrecv", 2, ch.Type())
130                         call := mkcall1(fn, fn.Type().Results(), r.PtrInit(), elem, ch)
131                         as := ir.NewAssignListStmt(r.Pos(), ir.OAS2, []ir.Node{cond, n.Lhs[1]}, []ir.Node{call})
132                         r.PtrInit().Append(typecheck.Stmt(as))
133                 }
134
135                 r.Cond = typecheck.Expr(cond)
136                 r.Body = cas.Body
137                 r.Else = append(dflt.Init(), dflt.Body...)
138                 return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)}
139         }
140
141         if dflt != nil {
142                 ncas--
143         }
144         casorder := make([]*ir.CommClause, ncas)
145         nsends, nrecvs := 0, 0
146
147         var init []ir.Node
148
149         // generate sel-struct
150         base.Pos = sellineno
151         selv := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(scasetype(), int64(ncas)))
152         init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))
153
154         // No initialization for order; runtime.selectgo is responsible for that.
155         order := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))
156
157         var pc0, pcs ir.Node
158         if base.Flag.Race {
159                 pcs = typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))
160                 pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(base.Pos, 0))))
161         } else {
162                 pc0 = typecheck.NodNil()
163         }
164
165         // register cases
166         for _, cas := range cases {
167                 ir.SetPos(cas)
168
169                 init = append(init, ir.TakeInit(cas)...)
170
171                 n := cas.Comm
172                 if n == nil { // default:
173                         continue
174                 }
175
176                 var i int
177                 var c, elem ir.Node
178                 switch n.Op() {
179                 default:
180                         base.Fatalf("select %v", n.Op())
181                 case ir.OSEND:
182                         n := n.(*ir.SendStmt)
183                         i = nsends
184                         nsends++
185                         c = n.Chan
186                         elem = n.Value
187                 case ir.OSELRECV2:
188                         n := n.(*ir.AssignListStmt)
189                         nrecvs++
190                         i = ncas - nrecvs
191                         recv := n.Rhs[0].(*ir.UnaryExpr)
192                         c = recv.X
193                         elem = n.Lhs[0]
194                 }
195
196                 casorder[i] = cas
197
198                 setField := func(f string, val ir.Node) {
199                         r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(base.Pos, int64(i))), typecheck.Lookup(f)), val)
200                         init = append(init, typecheck.Stmt(r))
201                 }
202
203                 c = typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR])
204                 setField("c", c)
205                 if !ir.IsBlank(elem) {
206                         elem = typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR])
207                         setField("elem", elem)
208                 }
209
210                 // TODO(mdempsky): There should be a cleaner way to
211                 // handle this.
212                 if base.Flag.Race {
213                         r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(base.Pos, int64(i)))))
214                         init = append(init, r)
215                 }
216         }
217         if nsends+nrecvs != ncas {
218                 base.Fatalf("walkSelectCases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
219         }
220
221         // run the select
222         base.Pos = sellineno
223         chosen := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
224         recvOK := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
225         r := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
226         r.Lhs = []ir.Node{chosen, recvOK}
227         fn := typecheck.LookupRuntime("selectgo")
228         var fnInit ir.Nodes
229         r.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(base.Pos, int64(nsends)), ir.NewInt(base.Pos, int64(nrecvs)), ir.NewBool(base.Pos, dflt == nil))}
230         init = append(init, fnInit...)
231         init = append(init, typecheck.Stmt(r))
232
233         // selv, order, and pcs (if race) are no longer alive after selectgo.
234
235         // dispatch cases
236         dispatch := func(cond ir.Node, cas *ir.CommClause) {
237                 var list ir.Nodes
238
239                 if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 {
240                         n := n.(*ir.AssignListStmt)
241                         if !ir.IsBlank(n.Lhs[1]) {
242                                 x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)
243                                 list.Append(typecheck.Stmt(x))
244                         }
245                 }
246
247                 list.Append(cas.Body.Take()...)
248                 list.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
249
250                 var r ir.Node
251                 if cond != nil {
252                         cond = typecheck.Expr(cond)
253                         cond = typecheck.DefaultLit(cond, nil)
254                         r = ir.NewIfStmt(base.Pos, cond, list, nil)
255                 } else {
256                         r = ir.NewBlockStmt(base.Pos, list)
257                 }
258
259                 init = append(init, r)
260         }
261
262         if dflt != nil {
263                 ir.SetPos(dflt)
264                 dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(base.Pos, 0)), dflt)
265         }
266         for i, cas := range casorder {
267                 ir.SetPos(cas)
268                 if i == len(casorder)-1 {
269                         dispatch(nil, cas)
270                         break
271                 }
272                 dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(base.Pos, int64(i))), cas)
273         }
274
275         return init
276 }
277
278 // bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
279 func bytePtrToIndex(n ir.Node, i int64) ir.Node {
280         s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(base.Pos, i)))
281         t := types.NewPtr(types.Types[types.TUINT8])
282         return typecheck.ConvNop(s, t)
283 }
284
285 var scase *types.Type
286
287 // Keep in sync with src/runtime/select.go.
288 func scasetype() *types.Type {
289         if scase == nil {
290                 scase = types.NewStruct([]*types.Field{
291                         types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]),
292                         types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]),
293                 })
294                 scase.SetNoalg(true)
295         }
296         return scase
297 }