github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/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 /* 8 * select 9 */ 10 func typecheckselect(sel *Node) { 11 var ncase *Node 12 var n *Node 13 14 var def *Node 15 lno := int(setlineno(sel)) 16 count := 0 17 typechecklist(sel.Ninit, Etop) 18 for l := sel.List; l != nil; l = l.Next { 19 count++ 20 ncase = l.N 21 setlineno(ncase) 22 if ncase.Op != OXCASE { 23 Fatal("typecheckselect %v", Oconv(int(ncase.Op), 0)) 24 } 25 26 if ncase.List == nil { 27 // default 28 if def != nil { 29 Yyerror("multiple defaults in select (first at %v)", def.Line()) 30 } else { 31 def = ncase 32 } 33 } else if ncase.List.Next != nil { 34 Yyerror("select cases cannot be lists") 35 } else { 36 n = typecheck(&ncase.List.N, Etop) 37 ncase.Left = n 38 ncase.List = nil 39 setlineno(n) 40 switch n.Op { 41 default: 42 Yyerror("select case must be receive, send or assign recv") 43 44 // convert x = <-c into OSELRECV(x, <-c). 45 // remove implicit conversions; the eventual assignment 46 // will reintroduce them. 47 case OAS: 48 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit { 49 n.Right = n.Right.Left 50 } 51 52 if n.Right.Op != ORECV { 53 Yyerror("select assignment must have receive on right hand side") 54 break 55 } 56 57 n.Op = OSELRECV 58 59 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok 60 case OAS2RECV: 61 if n.Rlist.N.Op != ORECV { 62 Yyerror("select assignment must have receive on right hand side") 63 break 64 } 65 66 n.Op = OSELRECV2 67 n.Left = n.List.N 68 n.Ntest = n.List.Next.N 69 n.List = nil 70 n.Right = n.Rlist.N 71 n.Rlist = nil 72 73 // convert <-c into OSELRECV(N, <-c) 74 case ORECV: 75 n = Nod(OSELRECV, nil, n) 76 77 n.Typecheck = 1 78 ncase.Left = n 79 80 case OSEND: 81 break 82 } 83 } 84 85 typechecklist(ncase.Nbody, Etop) 86 } 87 88 sel.Xoffset = int64(count) 89 lineno = int32(lno) 90 } 91 92 func walkselect(sel *Node) { 93 if sel.List == nil && sel.Xoffset != 0 { 94 Fatal("double walkselect") // already rewrote 95 } 96 97 lno := int(setlineno(sel)) 98 i := count(sel.List) 99 100 // optimization: zero-case select 101 var init *NodeList 102 var r *Node 103 var n *Node 104 var var_ *Node 105 var selv *Node 106 var cas *Node 107 if i == 0 { 108 sel.Nbody = list1(mkcall("block", nil, nil)) 109 goto out 110 } 111 112 // optimization: one-case select: single op. 113 // TODO(rsc): Reenable optimization once order.c can handle it. 114 // golang.org/issue/7672. 115 if i == 1 { 116 cas := sel.List.N 117 setlineno(cas) 118 l := cas.Ninit 119 if cas.Left != nil { // not default: 120 n := cas.Left 121 l = concat(l, n.Ninit) 122 n.Ninit = nil 123 var ch *Node 124 switch n.Op { 125 default: 126 Fatal("select %v", Oconv(int(n.Op), 0)) 127 128 // ok already 129 case OSEND: 130 ch = n.Left 131 132 case OSELRECV, OSELRECV2: 133 ch = n.Right.Left 134 if n.Op == OSELRECV || n.Ntest == nil { 135 if n.Left == nil { 136 n = n.Right 137 } else { 138 n.Op = OAS 139 } 140 break 141 } 142 143 if n.Left == nil { 144 typecheck(&nblank, Erv|Easgn) 145 n.Left = nblank 146 } 147 148 n.Op = OAS2 149 n.List = list(list1(n.Left), n.Ntest) 150 n.Rlist = list1(n.Right) 151 n.Right = nil 152 n.Left = nil 153 n.Ntest = nil 154 n.Typecheck = 0 155 typecheck(&n, Etop) 156 } 157 158 // if ch == nil { block() }; n; 159 a := Nod(OIF, nil, nil) 160 161 a.Ntest = Nod(OEQ, ch, nodnil()) 162 a.Nbody = list1(mkcall("block", nil, &l)) 163 typecheck(&a, Etop) 164 l = list(l, a) 165 l = list(l, n) 166 } 167 168 l = concat(l, cas.Nbody) 169 sel.Nbody = l 170 goto out 171 } 172 173 // convert case value arguments to addresses. 174 // this rewrite is used by both the general code and the next optimization. 175 for l := sel.List; l != nil; l = l.Next { 176 cas = l.N 177 setlineno(cas) 178 n = cas.Left 179 if n == nil { 180 continue 181 } 182 switch n.Op { 183 case OSEND: 184 n.Right = Nod(OADDR, n.Right, nil) 185 typecheck(&n.Right, Erv) 186 187 case OSELRECV, OSELRECV2: 188 if n.Op == OSELRECV2 && n.Ntest == nil { 189 n.Op = OSELRECV 190 } 191 if n.Op == OSELRECV2 { 192 n.Ntest = Nod(OADDR, n.Ntest, nil) 193 typecheck(&n.Ntest, Erv) 194 } 195 196 if n.Left == nil { 197 n.Left = nodnil() 198 } else { 199 n.Left = Nod(OADDR, n.Left, nil) 200 typecheck(&n.Left, Erv) 201 } 202 } 203 } 204 205 // optimization: two-case select but one is default: single non-blocking op. 206 if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) { 207 var cas *Node 208 var dflt *Node 209 if sel.List.N.Left == nil { 210 cas = sel.List.Next.N 211 dflt = sel.List.N 212 } else { 213 dflt = sel.List.Next.N 214 cas = sel.List.N 215 } 216 217 n := cas.Left 218 setlineno(n) 219 r := Nod(OIF, nil, nil) 220 r.Ninit = cas.Ninit 221 switch n.Op { 222 default: 223 Fatal("select %v", Oconv(int(n.Op), 0)) 224 225 // if selectnbsend(c, v) { body } else { default body } 226 case OSEND: 227 ch := n.Left 228 229 r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right) 230 231 // if c != nil && selectnbrecv(&v, c) { body } else { default body } 232 case OSELRECV: 233 r = Nod(OIF, nil, nil) 234 235 r.Ninit = cas.Ninit 236 ch := n.Right.Left 237 r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch) 238 239 // if c != nil && selectnbrecv2(&v, c) { body } else { default body } 240 case OSELRECV2: 241 r = Nod(OIF, nil, nil) 242 243 r.Ninit = cas.Ninit 244 ch := n.Right.Left 245 r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch) 246 } 247 248 typecheck(&r.Ntest, Erv) 249 r.Nbody = cas.Nbody 250 r.Nelse = concat(dflt.Ninit, dflt.Nbody) 251 sel.Nbody = list1(r) 252 goto out 253 } 254 255 init = sel.Ninit 256 sel.Ninit = nil 257 258 // generate sel-struct 259 setlineno(sel) 260 261 selv = temp(selecttype(int32(sel.Xoffset))) 262 r = Nod(OAS, selv, nil) 263 typecheck(&r, Etop) 264 init = list(init, r) 265 var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8])) 266 r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset)) 267 typecheck(&r, Etop) 268 init = list(init, r) 269 270 // register cases 271 for l := sel.List; l != nil; l = l.Next { 272 cas = l.N 273 setlineno(cas) 274 n = cas.Left 275 r = Nod(OIF, nil, nil) 276 r.Ninit = cas.Ninit 277 cas.Ninit = nil 278 if n != nil { 279 r.Ninit = concat(r.Ninit, n.Ninit) 280 n.Ninit = nil 281 } 282 283 if n == nil { 284 // selectdefault(sel *byte); 285 r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_) 286 } else { 287 switch n.Op { 288 default: 289 Fatal("select %v", Oconv(int(n.Op), 0)) 290 291 // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); 292 case OSEND: 293 r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right) 294 295 // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); 296 case OSELRECV: 297 r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left) 298 299 // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); 300 case OSELRECV2: 301 r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest) 302 } 303 } 304 305 // selv is no longer alive after use. 306 r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil)) 307 308 r.Nbody = concat(r.Nbody, cas.Nbody) 309 r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil)) 310 init = list(init, r) 311 } 312 313 // run the select 314 setlineno(sel) 315 316 init = list(init, mkcall("selectgo", nil, nil, var_)) 317 sel.Nbody = init 318 319 out: 320 sel.List = nil 321 walkstmtlist(sel.Nbody) 322 lineno = int32(lno) 323 } 324 325 // Keep in sync with src/runtime/runtime2.go and src/runtime/select.go. 326 func selecttype(size int32) *Type { 327 // TODO(dvyukov): it's possible to generate SudoG and Scase only once 328 // and then cache; and also cache Select per size. 329 sudog := Nod(OTSTRUCT, nil, nil) 330 331 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8])))) 332 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8])))) 333 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8])))) 334 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8])))) 335 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) 336 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) 337 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32]))) 338 sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8])))) 339 typecheck(&sudog, Etype) 340 sudog.Type.Noalg = 1 341 sudog.Type.Local = true 342 343 scase := Nod(OTSTRUCT, nil, nil) 344 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) 345 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8])))) 346 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR]))) 347 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16]))) 348 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16]))) 349 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8])))) 350 scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) 351 typecheck(&scase, Etype) 352 scase.Type.Noalg = 1 353 scase.Type.Local = true 354 355 sel := Nod(OTSTRUCT, nil, nil) 356 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16]))) 357 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16]))) 358 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8])))) 359 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8])))) 360 arr := Nod(OTARRAY, Nodintconst(int64(size)), scase) 361 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr)) 362 arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8]))) 363 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr)) 364 arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16])) 365 sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr)) 366 typecheck(&sel, Etype) 367 sel.Type.Noalg = 1 368 sel.Type.Local = true 369 370 return sel.Type 371 }