github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 "github.com/gagliardetto/golang-go/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 != OCASE { 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.Right.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 72 // convert <-c into OSELRECV(N, <-c) 73 case ORECV: 74 n = nodl(n.Pos, OSELRECV, nil, n) 75 76 n.SetTypecheck(1) 77 ncase.Left = n 78 79 case OSEND: 80 break 81 } 82 } 83 84 typecheckslice(ncase.Nbody.Slice(), ctxStmt) 85 } 86 87 lineno = lno 88 } 89 90 func walkselect(sel *Node) { 91 lno := setlineno(sel) 92 if sel.Nbody.Len() != 0 { 93 Fatalf("double walkselect") 94 } 95 96 init := sel.Ninit.Slice() 97 sel.Ninit.Set(nil) 98 99 init = append(init, walkselectcases(&sel.List)...) 100 sel.List.Set(nil) 101 102 sel.Nbody.Set(init) 103 walkstmtlist(sel.Nbody.Slice()) 104 105 lineno = lno 106 } 107 108 func walkselectcases(cases *Nodes) []*Node { 109 n := cases.Len() 110 sellineno := lineno 111 112 // optimization: zero-case select 113 if n == 0 { 114 return []*Node{mkcall("block", nil, nil)} 115 } 116 117 // optimization: one-case select: single op. 118 // TODO(rsc): Reenable optimization once order.go can handle it. 119 // golang.org/issue/7672. 120 if n == 1 { 121 cas := cases.First() 122 setlineno(cas) 123 l := cas.Ninit.Slice() 124 if cas.Left != nil { // not default: 125 n := cas.Left 126 l = append(l, n.Ninit.Slice()...) 127 n.Ninit.Set(nil) 128 var ch *Node 129 switch n.Op { 130 default: 131 Fatalf("select %v", n.Op) 132 133 // ok already 134 case OSEND: 135 ch = n.Left 136 137 case OSELRECV, OSELRECV2: 138 ch = n.Right.Left 139 if n.Op == OSELRECV || n.List.Len() == 0 { 140 if n.Left == nil { 141 n = n.Right 142 } else { 143 n.Op = OAS 144 } 145 break 146 } 147 148 if n.Left == nil { 149 nblank = typecheck(nblank, ctxExpr|ctxAssign) 150 n.Left = nblank 151 } 152 153 n.Op = OAS2 154 n.List.Prepend(n.Left) 155 n.Rlist.Set1(n.Right) 156 n.Right = nil 157 n.Left = nil 158 n.SetTypecheck(0) 159 n = typecheck(n, ctxStmt) 160 } 161 162 // if ch == nil { block() }; n; 163 a := nod(OIF, nil, nil) 164 165 a.Left = nod(OEQ, ch, nodnil()) 166 var ln Nodes 167 ln.Set(l) 168 a.Nbody.Set1(mkcall("block", nil, &ln)) 169 l = ln.Slice() 170 a = typecheck(a, ctxStmt) 171 l = append(l, a, n) 172 } 173 174 l = append(l, cas.Nbody.Slice()...) 175 l = append(l, nod(OBREAK, nil, nil)) 176 return l 177 } 178 179 // convert case value arguments to addresses. 180 // this rewrite is used by both the general code and the next optimization. 181 for _, cas := range cases.Slice() { 182 setlineno(cas) 183 n := cas.Left 184 if n == nil { 185 continue 186 } 187 switch n.Op { 188 case OSEND: 189 n.Right = nod(OADDR, n.Right, nil) 190 n.Right = typecheck(n.Right, ctxExpr) 191 192 case OSELRECV, OSELRECV2: 193 if n.Op == OSELRECV2 && n.List.Len() == 0 { 194 n.Op = OSELRECV 195 } 196 197 if n.Left != nil { 198 n.Left = nod(OADDR, n.Left, nil) 199 n.Left = typecheck(n.Left, ctxExpr) 200 } 201 } 202 } 203 204 // optimization: two-case select but one is default: single non-blocking op. 205 if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) { 206 var cas *Node 207 var dflt *Node 208 if cases.First().Left == nil { 209 cas = cases.Second() 210 dflt = cases.First() 211 } else { 212 dflt = cases.Second() 213 cas = cases.First() 214 } 215 216 n := cas.Left 217 setlineno(n) 218 r := nod(OIF, nil, nil) 219 r.Ninit.Set(cas.Ninit.Slice()) 220 switch n.Op { 221 default: 222 Fatalf("select %v", n.Op) 223 224 case OSEND: 225 // if selectnbsend(c, v) { body } else { default body } 226 ch := n.Left 227 r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right) 228 229 case OSELRECV: 230 // if selectnbrecv(&v, c) { body } else { default body } 231 r = nod(OIF, nil, nil) 232 r.Ninit.Set(cas.Ninit.Slice()) 233 ch := n.Right.Left 234 elem := n.Left 235 if elem == nil { 236 elem = nodnil() 237 } 238 r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch) 239 240 case OSELRECV2: 241 // if selectnbrecv2(&v, &received, c) { body } else { default body } 242 r = nod(OIF, nil, nil) 243 r.Ninit.Set(cas.Ninit.Slice()) 244 ch := n.Right.Left 245 elem := n.Left 246 if elem == nil { 247 elem = nodnil() 248 } 249 receivedp := nod(OADDR, n.List.First(), nil) 250 receivedp = typecheck(receivedp, ctxExpr) 251 r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch) 252 } 253 254 r.Left = typecheck(r.Left, ctxExpr) 255 r.Nbody.Set(cas.Nbody.Slice()) 256 r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...)) 257 return []*Node{r, nod(OBREAK, nil, nil)} 258 } 259 260 var init []*Node 261 262 // generate sel-struct 263 lineno = sellineno 264 selv := temp(types.NewArray(scasetype(), int64(n))) 265 r := nod(OAS, selv, nil) 266 r = typecheck(r, ctxStmt) 267 init = append(init, r) 268 269 order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n))) 270 r = nod(OAS, order, nil) 271 r = typecheck(r, ctxStmt) 272 init = append(init, r) 273 274 // register cases 275 for i, cas := range cases.Slice() { 276 setlineno(cas) 277 278 init = append(init, cas.Ninit.Slice()...) 279 cas.Ninit.Set(nil) 280 281 // Keep in sync with runtime/select.go. 282 const ( 283 caseNil = iota 284 caseRecv 285 caseSend 286 caseDefault 287 ) 288 289 var c, elem *Node 290 var kind int64 = caseDefault 291 292 if n := cas.Left; n != nil { 293 init = append(init, n.Ninit.Slice()...) 294 295 switch n.Op { 296 default: 297 Fatalf("select %v", n.Op) 298 case OSEND: 299 kind = caseSend 300 c = n.Left 301 elem = n.Right 302 case OSELRECV, OSELRECV2: 303 kind = caseRecv 304 c = n.Right.Left 305 elem = n.Left 306 } 307 } 308 309 setField := func(f string, val *Node) { 310 r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val) 311 r = typecheck(r, ctxStmt) 312 init = append(init, r) 313 } 314 315 setField("kind", nodintconst(kind)) 316 if c != nil { 317 c = convnop(c, types.Types[TUNSAFEPTR]) 318 setField("c", c) 319 } 320 if elem != nil { 321 elem = convnop(elem, types.Types[TUNSAFEPTR]) 322 setField("elem", elem) 323 } 324 325 // TODO(mdempsky): There should be a cleaner way to 326 // handle this. 327 if instrumenting { 328 r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i))) 329 init = append(init, r) 330 } 331 } 332 333 // run the select 334 lineno = sellineno 335 chosen := temp(types.Types[TINT]) 336 recvOK := temp(types.Types[TBOOL]) 337 r = nod(OAS2, nil, nil) 338 r.List.Set2(chosen, recvOK) 339 fn := syslook("selectgo") 340 r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n)))) 341 r = typecheck(r, ctxStmt) 342 init = append(init, r) 343 344 // selv and order are no longer alive after selectgo. 345 init = append(init, nod(OVARKILL, selv, nil)) 346 init = append(init, nod(OVARKILL, order, nil)) 347 348 // dispatch cases 349 for i, cas := range cases.Slice() { 350 setlineno(cas) 351 352 cond := nod(OEQ, chosen, nodintconst(int64(i))) 353 cond = typecheck(cond, ctxExpr) 354 cond = defaultlit(cond, nil) 355 356 r = nod(OIF, cond, nil) 357 358 if n := cas.Left; n != nil && n.Op == OSELRECV2 { 359 x := nod(OAS, n.List.First(), recvOK) 360 x = typecheck(x, ctxStmt) 361 r.Nbody.Append(x) 362 } 363 364 r.Nbody.AppendNodes(&cas.Nbody) 365 r.Nbody.Append(nod(OBREAK, nil, nil)) 366 init = append(init, r) 367 } 368 369 return init 370 } 371 372 // bytePtrToIndex returns a Node representing "(*byte)(&n[i])". 373 func bytePtrToIndex(n *Node, i int64) *Node { 374 s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil) 375 t := types.NewPtr(types.Types[TUINT8]) 376 return convnop(s, t) 377 } 378 379 var scase *types.Type 380 381 // Keep in sync with src/runtime/select.go. 382 func scasetype() *types.Type { 383 if scase == nil { 384 scase = tostruct([]*Node{ 385 namedfield("c", types.Types[TUNSAFEPTR]), 386 namedfield("elem", types.Types[TUNSAFEPTR]), 387 namedfield("kind", types.Types[TUINT16]), 388 namedfield("pc", types.Types[TUINTPTR]), 389 namedfield("releasetime", types.Types[TINT64]), 390 }) 391 scase.SetNoalg(true) 392 } 393 return scase 394 }