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