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 := setlineno(sel) 14 count := 0 15 typecheckslice(sel.Ninit.Slice(), Etop) 16 for _, n1 := range sel.List.Slice() { 17 count++ 18 ncase = n1 19 setlineno(ncase) 20 if ncase.Op != OXCASE { 21 Fatalf("typecheckselect %v", ncase.Op) 22 } 23 24 if ncase.List.Len() == 0 { 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.Len() > 1 { 32 yyerror("select cases cannot be lists") 33 } else { 34 ncase.List.SetIndex(0, typecheck(ncase.List.Index(0), Etop)) 35 n = ncase.List.Index(0) 36 ncase.Left = n 37 ncase.List.Set(nil) 38 setlineno(n) 39 switch n.Op { 40 default: 41 yyerror("select case must be receive, send or assign recv") 42 43 // convert x = <-c into OSELRECV(x, <-c). 44 // remove implicit conversions; the eventual assignment 45 // will reintroduce them. 46 case OAS: 47 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit { 48 n.Right = n.Right.Left 49 } 50 51 if n.Right.Op != ORECV { 52 yyerror("select assignment must have receive on right hand side") 53 break 54 } 55 56 n.Op = OSELRECV 57 58 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok 59 case OAS2RECV: 60 if n.Rlist.First().Op != ORECV { 61 yyerror("select assignment must have receive on right hand side") 62 break 63 } 64 65 n.Op = OSELRECV2 66 n.Left = n.List.First() 67 n.List.Set1(n.List.Second()) 68 n.Right = n.Rlist.First() 69 n.Rlist.Set(nil) 70 71 // convert <-c into OSELRECV(N, <-c) 72 case ORECV: 73 n = nod(OSELRECV, nil, n) 74 75 n.Typecheck = 1 76 ncase.Left = n 77 78 case OSEND: 79 break 80 } 81 } 82 83 typecheckslice(ncase.Nbody.Slice(), Etop) 84 } 85 86 sel.Xoffset = int64(count) 87 lineno = lno 88 } 89 90 func walkselect(sel *Node) { 91 if sel.List.Len() == 0 && sel.Xoffset != 0 { 92 Fatalf("double walkselect") // already rewrote 93 } 94 95 lno := setlineno(sel) 96 i := sel.List.Len() 97 98 // optimization: zero-case select 99 var init []*Node 100 var r *Node 101 var n *Node 102 var var_ *Node 103 var selv *Node 104 if i == 0 { 105 sel.Nbody.Set1(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.First() 114 setlineno(cas) 115 l := cas.Ninit.Slice() 116 if cas.Left != nil { // not default: 117 n := cas.Left 118 l = append(l, n.Ninit.Slice()...) 119 n.Ninit.Set(nil) 120 var ch *Node 121 switch n.Op { 122 default: 123 Fatalf("select %v", n.Op) 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.Len() == 0 { 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 nblank = typecheck(nblank, Erv|Easgn) 142 n.Left = nblank 143 } 144 145 n.Op = OAS2 146 n.List.Prepend(n.Left) 147 n.Rlist.Set1(n.Right) 148 n.Right = nil 149 n.Left = nil 150 n.Typecheck = 0 151 n = 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 var ln Nodes 159 ln.Set(l) 160 a.Nbody.Set1(mkcall("block", nil, &ln)) 161 l = ln.Slice() 162 a = typecheck(a, Etop) 163 l = append(l, a) 164 l = append(l, n) 165 } 166 167 l = append(l, cas.Nbody.Slice()...) 168 sel.Nbody.Set(l) 169 goto out 170 } 171 172 // convert case value arguments to addresses. 173 // this rewrite is used by both the general code and the next optimization. 174 for _, cas := range sel.List.Slice() { 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 n.Right = typecheck(n.Right, Erv) 184 185 case OSELRECV, OSELRECV2: 186 if n.Op == OSELRECV2 && n.List.Len() == 0 { 187 n.Op = OSELRECV 188 } 189 if n.Op == OSELRECV2 { 190 n.List.SetIndex(0, nod(OADDR, n.List.First(), nil)) 191 n.List.SetIndex(0, typecheck(n.List.Index(0), Erv)) 192 } 193 194 if n.Left == nil { 195 n.Left = nodnil() 196 } else { 197 n.Left = nod(OADDR, n.Left, nil) 198 n.Left = 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.First().Left == nil || sel.List.Second().Left == nil) { 205 var cas *Node 206 var dflt *Node 207 if sel.List.First().Left == nil { 208 cas = sel.List.Second() 209 dflt = sel.List.First() 210 } else { 211 dflt = sel.List.Second() 212 cas = sel.List.First() 213 } 214 215 n := cas.Left 216 setlineno(n) 217 r := nod(OIF, nil, nil) 218 r.Ninit.Set(cas.Ninit.Slice()) 219 switch n.Op { 220 default: 221 Fatalf("select %v", n.Op) 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.Set(cas.Ninit.Slice()) 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.Set(cas.Ninit.Slice()) 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.First(), ch) 244 } 245 246 r.Left = typecheck(r.Left, Erv) 247 r.Nbody.Set(cas.Nbody.Slice()) 248 r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...)) 249 sel.Nbody.Set1(r) 250 goto out 251 } 252 253 init = sel.Ninit.Slice() 254 sel.Ninit.Set(nil) 255 256 // generate sel-struct 257 setlineno(sel) 258 259 selv = temp(selecttype(int32(sel.Xoffset))) 260 r = nod(OAS, selv, nil) 261 r = typecheck(r, Etop) 262 init = append(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 r = typecheck(r, Etop) 266 init = append(init, r) 267 // register cases 268 for _, cas := range sel.List.Slice() { 269 setlineno(cas) 270 n = cas.Left 271 r = nod(OIF, nil, nil) 272 r.Ninit.Set(cas.Ninit.Slice()) 273 cas.Ninit.Set(nil) 274 if n != nil { 275 r.Ninit.AppendNodes(&n.Ninit) 276 n.Ninit.Set(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", n.Op) 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.First()) 298 } 299 } 300 301 // selv is no longer alive after use. 302 r.Nbody.Append(nod(OVARKILL, selv, nil)) 303 304 r.Nbody.AppendNodes(&cas.Nbody) 305 r.Nbody.Append(nod(OBREAK, nil, nil)) 306 init = append(init, r) 307 } 308 309 // run the select 310 setlineno(sel) 311 312 init = append(init, mkcall("selectgo", nil, nil, var_)) 313 sel.Nbody.Set(init) 314 315 out: 316 sel.List.Set(nil) 317 walkstmtlist(sel.Nbody.Slice()) 318 lineno = lno 319 } 320 321 // Keep in sync with src/runtime/select.go. 322 func selecttype(size int32) *Type { 323 // TODO(dvyukov): it's possible to generate Scase only once 324 // and then cache; and also cache Select per size. 325 326 scase := nod(OTSTRUCT, nil, nil) 327 scase.List.Append(nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(Types[TUINT8])))) 328 scase.List.Append(nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(Types[TUINT8])))) 329 scase.List.Append(nod(ODCLFIELD, newname(lookup("pc")), typenod(Types[TUINTPTR]))) 330 scase.List.Append(nod(ODCLFIELD, newname(lookup("kind")), typenod(Types[TUINT16]))) 331 scase.List.Append(nod(ODCLFIELD, newname(lookup("so")), typenod(Types[TUINT16]))) 332 scase.List.Append(nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(Types[TUINT8])))) 333 scase.List.Append(nod(ODCLFIELD, newname(lookup("releasetime")), typenod(Types[TUINT64]))) 334 scase = typecheck(scase, Etype) 335 scase.Type.Noalg = true 336 scase.Type.Local = true 337 338 sel := nod(OTSTRUCT, nil, nil) 339 sel.List.Append(nod(ODCLFIELD, newname(lookup("tcase")), typenod(Types[TUINT16]))) 340 sel.List.Append(nod(ODCLFIELD, newname(lookup("ncase")), typenod(Types[TUINT16]))) 341 sel.List.Append(nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(Types[TUINT8])))) 342 sel.List.Append(nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(Types[TUINT8])))) 343 arr := nod(OTARRAY, nodintconst(int64(size)), scase) 344 sel.List.Append(nod(ODCLFIELD, newname(lookup("scase")), arr)) 345 arr = nod(OTARRAY, nodintconst(int64(size)), typenod(Types[TUINT16])) 346 sel.List.Append(nod(ODCLFIELD, newname(lookup("lockorderarr")), arr)) 347 arr = nod(OTARRAY, nodintconst(int64(size)), typenod(Types[TUINT16])) 348 sel.List.Append(nod(ODCLFIELD, newname(lookup("pollorderarr")), arr)) 349 sel = typecheck(sel, Etype) 350 sel.Type.Noalg = true 351 sel.Type.Local = true 352 353 return sel.Type 354 } 355