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