1
2
3
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
37 if ncas == 0 {
38 return []ir.Node{mkcallstmt("block")}
39 }
40
41
42 if ncas == 1 {
43 cas := cases[0]
44 ir.SetPos(cas)
45 l := cas.Init()
46 if cas.Comm != nil {
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
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
74
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
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
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.Temp(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
150 base.Pos = sellineno
151 selv := typecheck.Temp(types.NewArray(scasetype(), int64(ncas)))
152 init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))
153
154
155 order := typecheck.Temp(types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))
156
157 var pc0, pcs ir.Node
158 if base.Flag.Race {
159 pcs = typecheck.Temp(types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))
160 pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(0))))
161 } else {
162 pc0 = typecheck.NodNil()
163 }
164
165
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 {
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(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
211
212 if base.Flag.Race {
213 r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(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
222 base.Pos = sellineno
223 chosen := typecheck.Temp(types.Types[types.TINT])
224 recvOK := typecheck.Temp(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(int64(nsends)), ir.NewInt(int64(nrecvs)), ir.NewBool(dflt == nil))}
230 init = append(init, fnInit...)
231 init = append(init, typecheck.Stmt(r))
232
233
234 init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, selv))
235 init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, order))
236 if base.Flag.Race {
237 init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, pcs))
238 }
239
240
241 dispatch := func(cond ir.Node, cas *ir.CommClause) {
242 cond = typecheck.Expr(cond)
243 cond = typecheck.DefaultLit(cond, nil)
244
245 r := ir.NewIfStmt(base.Pos, cond, nil, nil)
246
247 if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 {
248 n := n.(*ir.AssignListStmt)
249 if !ir.IsBlank(n.Lhs[1]) {
250 x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)
251 r.Body.Append(typecheck.Stmt(x))
252 }
253 }
254
255 r.Body.Append(cas.Body.Take()...)
256 r.Body.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
257 init = append(init, r)
258 }
259
260 if dflt != nil {
261 ir.SetPos(dflt)
262 dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(0)), dflt)
263 }
264 for i, cas := range casorder {
265 ir.SetPos(cas)
266 dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas)
267 }
268
269 return init
270 }
271
272
273 func bytePtrToIndex(n ir.Node, i int64) ir.Node {
274 s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(i)))
275 t := types.NewPtr(types.Types[types.TUINT8])
276 return typecheck.ConvNop(s, t)
277 }
278
279 var scase *types.Type
280
281
282 func scasetype() *types.Type {
283 if scase == nil {
284 scase = types.NewStruct(types.NoPkg, []*types.Field{
285 types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]),
286 types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]),
287 })
288 scase.SetNoalg(true)
289 }
290 return scase
291 }
292
View as plain text