github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/compile/internal/gc/select.go (about) 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 gc 6 7 import "cmd/compile/internal/types" 8 9 // select 10 func typecheckselect(sel *Node) { 11 var def *Node 12 lno := setlineno(sel) 13 typecheckslice(sel.Ninit.Slice(), ctxStmt) 14 for _, ncase := range sel.List.Slice() { 15 if ncase.Op != OXCASE { 16 setlineno(ncase) 17 Fatalf("typecheckselect %v", ncase.Op) 18 } 19 20 if ncase.List.Len() == 0 { 21 // default 22 if def != nil { 23 yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line()) 24 } else { 25 def = ncase 26 } 27 } else if ncase.List.Len() > 1 { 28 yyerrorl(ncase.Pos, "select cases cannot be lists") 29 } else { 30 ncase.List.SetFirst(typecheck(ncase.List.First(), ctxStmt)) 31 n := ncase.List.First() 32 ncase.Left = n 33 ncase.List.Set(nil) 34 switch n.Op { 35 default: 36 pos := n.Pos 37 if n.Op == ONAME { 38 // We don't have the right position for ONAME nodes (see #15459 and 39 // others). Using ncase.Pos for now as it will provide the correct 40 // line number (assuming the expression follows the "case" keyword 41 // on the same line). This matches the approach before 1.10. 42 pos = ncase.Pos 43 } 44 yyerrorl(pos, "select case must be receive, send or assign recv") 45 46 // convert x = <-c into OSELRECV(x, <-c). 47 // remove implicit conversions; the eventual assignment 48 // will reintroduce them. 49 case OAS: 50 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() { 51 n.Right = n.Right.Left 52 } 53 54 if n.Right.Op != ORECV { 55 yyerrorl(n.Pos, "select assignment must have receive on right hand side") 56 break 57 } 58 59 n.Op = OSELRECV 60 61 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok 62 case OAS2RECV: 63 if n.Rlist.First().Op != ORECV { 64 yyerrorl(n.Pos, "select assignment must have receive on right hand side") 65 break 66 } 67 68 n.Op = OSELRECV2 69 n.Left = n.List.First() 70 n.List.Set1(n.List.Second()) 71 n.Right = n.Rlist.First() 72 n.Rlist.Set(nil) 73 74 // convert <-c into OSELRECV(N, <-c) 75 case ORECV: 76 n = nodl(n.Pos, OSELRECV, nil, n) 77 78 n.SetTypecheck(1) 79 ncase.Left = n 80 81 case OSEND: 82 break 83 } 84 } 85 86 typecheckslice(ncase.Nbody.Slice(), ctxStmt) 87 } 88 89 lineno = lno 90 } 91 92 func walkselect(sel *Node) { 93 lno := setlineno(sel) 94 if sel.Nbody.Len() != 0 { 95 Fatalf("double walkselect") 96 } 97 98 init := sel.Ninit.Slice() 99 sel.Ninit.Set(nil) 100 101 init = append(init, walkselectcases(&sel.List)...) 102 sel.List.Set(nil) 103 104 sel.Nbody.Set(init) 105 walkstmtlist(sel.Nbody.Slice()) 106 107 lineno = lno 108 } 109 110 func walkselectcases(cases *Nodes) []*Node { 111 n := cases.Len() 112 sellineno := lineno 113 114 // optimization: zero-case select 115 if n == 0 { 116 return []*Node{mkcall("block", nil, nil)} 117 } 118 119 // optimization: one-case select: single op. 120 // TODO(rsc): Reenable optimization once order.go can handle it. 121 // golang.org/issue/7672. 122 if n == 1 { 123 cas := cases.First() 124 setlineno(cas) 125 l := cas.Ninit.Slice() 126 if cas.Left != nil { // not default: 127 n := cas.Left 128 l = append(l, n.Ninit.Slice()...) 129 n.Ninit.Set(nil) 130 var ch *Node 131 switch n.Op { 132 default: 133 Fatalf("select %v", n.Op) 134 135 // ok already 136 case OSEND: 137 ch = n.Left 138 139 case OSELRECV, OSELRECV2: 140 ch = n.Right.Left 141 if n.Op == OSELRECV || n.List.Len() == 0 { 142 if n.Left == nil { 143 n = n.Right 144 } else { 145 n.Op = OAS 146 } 147 break 148 } 149 150 if n.Left == nil { 151 nblank = typecheck(nblank, ctxExpr|ctxAssign) 152 n.Left = nblank 153 } 154 155 n.Op = OAS2 156 n.List.Prepend(n.Left) 157 n.Rlist.Set1(n.Right) 158 n.Right = nil 159 n.Left = nil 160 n.SetTypecheck(0) 161 n = typecheck(n, ctxStmt) 162 } 163 164 // if ch == nil { block() }; n; 165 a := nod(OIF, nil, nil) 166 167 a.Left = nod(OEQ, ch, nodnil()) 168 var ln Nodes 169 ln.Set(l) 170 a.Nbody.Set1(mkcall("block", nil, &ln)) 171 l = ln.Slice() 172 a = typecheck(a, ctxStmt) 173 l = append(l, a, n) 174 } 175 176 l = append(l, cas.Nbody.Slice()...) 177 l = append(l, nod(OBREAK, nil, nil)) 178 return l 179 } 180 181 // convert case value arguments to addresses. 182 // this rewrite is used by both the general code and the next optimization. 183 for _, cas := range cases.Slice() { 184 setlineno(cas) 185 n := cas.Left 186 if n == nil { 187 continue 188 } 189 switch n.Op { 190 case OSEND: 191 n.Right = nod(OADDR, n.Right, nil) 192 n.Right = typecheck(n.Right, ctxExpr) 193 194 case OSELRECV, OSELRECV2: 195 if n.Op == OSELRECV2 && n.List.Len() == 0 { 196 n.Op = OSELRECV 197 } 198 199 if n.Left != nil { 200 n.Left = nod(OADDR, n.Left, nil) 201 n.Left = typecheck(n.Left, ctxExpr) 202 } 203 } 204 } 205 206 // optimization: two-case select but one is default: single non-blocking op. 207 if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) { 208 var cas *Node 209 var dflt *Node 210 if cases.First().Left == nil { 211 cas = cases.Second() 212 dflt = cases.First() 213 } else { 214 dflt = cases.Second() 215 cas = cases.First() 216 } 217 218 n := cas.Left 219 setlineno(n) 220 r := nod(OIF, nil, nil) 221 r.Ninit.Set(cas.Ninit.Slice()) 222 switch n.Op { 223 default: 224 Fatalf("select %v", n.Op) 225 226 case OSEND: 227 // if selectnbsend(c, v) { body } else { default body } 228 ch := n.Left 229 r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right) 230 231 case OSELRECV: 232 // if selectnbrecv(&v, c) { body } else { default body } 233 r = nod(OIF, nil, nil) 234 r.Ninit.Set(cas.Ninit.Slice()) 235 ch := n.Right.Left 236 elem := n.Left 237 if elem == nil { 238 elem = nodnil() 239 } 240 r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch) 241 242 case OSELRECV2: 243 // if selectnbrecv2(&v, &received, c) { body } else { default body } 244 r = nod(OIF, nil, nil) 245 r.Ninit.Set(cas.Ninit.Slice()) 246 ch := n.Right.Left 247 elem := n.Left 248 if elem == nil { 249 elem = nodnil() 250 } 251 receivedp := nod(OADDR, n.List.First(), nil) 252 receivedp = typecheck(receivedp, ctxExpr) 253 r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch) 254 } 255 256 r.Left = typecheck(r.Left, ctxExpr) 257 r.Nbody.Set(cas.Nbody.Slice()) 258 r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...)) 259 return []*Node{r, nod(OBREAK, nil, nil)} 260 } 261 262 var init []*Node 263 264 // generate sel-struct 265 lineno = sellineno 266 selv := temp(types.NewArray(scasetype(), int64(n))) 267 r := nod(OAS, selv, nil) 268 r = typecheck(r, ctxStmt) 269 init = append(init, r) 270 271 order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n))) 272 r = nod(OAS, order, nil) 273 r = typecheck(r, ctxStmt) 274 init = append(init, r) 275 276 // register cases 277 for i, cas := range cases.Slice() { 278 setlineno(cas) 279 280 init = append(init, cas.Ninit.Slice()...) 281 cas.Ninit.Set(nil) 282 283 // Keep in sync with runtime/select.go. 284 const ( 285 caseNil = iota 286 caseRecv 287 caseSend 288 caseDefault 289 ) 290 291 var c, elem *Node 292 var kind int64 = caseDefault 293 294 if n := cas.Left; n != nil { 295 init = append(init, n.Ninit.Slice()...) 296 297 switch n.Op { 298 default: 299 Fatalf("select %v", n.Op) 300 case OSEND: 301 kind = caseSend 302 c = n.Left 303 elem = n.Right 304 case OSELRECV, OSELRECV2: 305 kind = caseRecv 306 c = n.Right.Left 307 elem = n.Left 308 } 309 } 310 311 setField := func(f string, val *Node) { 312 r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val) 313 r = typecheck(r, ctxStmt) 314 init = append(init, r) 315 } 316 317 setField("kind", nodintconst(kind)) 318 if c != nil { 319 c = convnop(c, types.Types[TUNSAFEPTR]) 320 setField("c", c) 321 } 322 if elem != nil { 323 elem = convnop(elem, types.Types[TUNSAFEPTR]) 324 setField("elem", elem) 325 } 326 327 // TODO(mdempsky): There should be a cleaner way to 328 // handle this. 329 if instrumenting { 330 r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i))) 331 init = append(init, r) 332 } 333 } 334 335 // run the select 336 lineno = sellineno 337 chosen := temp(types.Types[TINT]) 338 recvOK := temp(types.Types[TBOOL]) 339 r = nod(OAS2, nil, nil) 340 r.List.Set2(chosen, recvOK) 341 fn := syslook("selectgo") 342 r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n)))) 343 r = typecheck(r, ctxStmt) 344 init = append(init, r) 345 346 // selv and order are no longer alive after selectgo. 347 init = append(init, nod(OVARKILL, selv, nil)) 348 init = append(init, nod(OVARKILL, order, nil)) 349 350 // dispatch cases 351 for i, cas := range cases.Slice() { 352 setlineno(cas) 353 354 cond := nod(OEQ, chosen, nodintconst(int64(i))) 355 cond = typecheck(cond, ctxExpr) 356 cond = defaultlit(cond, nil) 357 358 r = nod(OIF, cond, nil) 359 360 if n := cas.Left; n != nil && n.Op == OSELRECV2 { 361 x := nod(OAS, n.List.First(), recvOK) 362 x = typecheck(x, ctxStmt) 363 r.Nbody.Append(x) 364 } 365 366 r.Nbody.AppendNodes(&cas.Nbody) 367 r.Nbody.Append(nod(OBREAK, nil, nil)) 368 init = append(init, r) 369 } 370 371 return init 372 } 373 374 // bytePtrToIndex returns a Node representing "(*byte)(&n[i])". 375 func bytePtrToIndex(n *Node, i int64) *Node { 376 s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil) 377 t := types.NewPtr(types.Types[TUINT8]) 378 return convnop(s, t) 379 } 380 381 var scase *types.Type 382 383 // Keep in sync with src/runtime/select.go. 384 func scasetype() *types.Type { 385 if scase == nil { 386 scase = tostruct([]*Node{ 387 namedfield("c", types.Types[TUNSAFEPTR]), 388 namedfield("elem", types.Types[TUNSAFEPTR]), 389 namedfield("kind", types.Types[TUINT16]), 390 namedfield("pc", types.Types[TUINTPTR]), 391 namedfield("releasetime", types.Types[TUINT64]), 392 }) 393 scase.SetNoalg(true) 394 } 395 return scase 396 }