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