github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/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 import "cmd/compile/internal/types" 8 9 // select 10 func typecheckselect(sel *Node) { 11 var def *Node 12 lno := setlineno(sel) 13 typecheckslice(sel.Ninit.Slice(), Etop) 14 for _, ncase := range sel.List.Slice() { 15 if ncase.Op != OXCASE { 16 setlineno(ncase) 17 Fatalf("typecheckselect %v", ncase.Op) 18 } 19 20 if ncase.List.Len() == 0 { 21 // default 22 if def != nil { 23 yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line()) 24 } else { 25 def = ncase 26 } 27 } else if ncase.List.Len() > 1 { 28 yyerrorl(ncase.Pos, "select cases cannot be lists") 29 } else { 30 ncase.List.SetFirst(typecheck(ncase.List.First(), Etop)) 31 n := ncase.List.First() 32 ncase.Left = n 33 ncase.List.Set(nil) 34 switch n.Op { 35 default: 36 yyerrorl(n.Pos, "select case must be receive, send or assign recv") 37 38 // convert x = <-c into OSELRECV(x, <-c). 39 // remove implicit conversions; the eventual assignment 40 // will reintroduce them. 41 case OAS: 42 if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() { 43 n.Right = n.Right.Left 44 } 45 46 if n.Right.Op != ORECV { 47 yyerrorl(n.Pos, "select assignment must have receive on right hand side") 48 break 49 } 50 51 n.Op = OSELRECV 52 53 // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok 54 case OAS2RECV: 55 if n.Rlist.First().Op != ORECV { 56 yyerrorl(n.Pos, "select assignment must have receive on right hand side") 57 break 58 } 59 60 n.Op = OSELRECV2 61 n.Left = n.List.First() 62 n.List.Set1(n.List.Second()) 63 n.Right = n.Rlist.First() 64 n.Rlist.Set(nil) 65 66 // convert <-c into OSELRECV(N, <-c) 67 case ORECV: 68 n = nodl(n.Pos, OSELRECV, nil, n) 69 70 n.SetTypecheck(1) 71 ncase.Left = n 72 73 case OSEND: 74 break 75 } 76 } 77 78 typecheckslice(ncase.Nbody.Slice(), Etop) 79 } 80 81 lineno = lno 82 } 83 84 func walkselect(sel *Node) { 85 lno := setlineno(sel) 86 if sel.Nbody.Len() != 0 { 87 Fatalf("double walkselect") 88 } 89 90 init := sel.Ninit.Slice() 91 sel.Ninit.Set(nil) 92 93 init = append(init, walkselectcases(&sel.List)...) 94 sel.List.Set(nil) 95 96 sel.Nbody.Set(init) 97 walkstmtlist(sel.Nbody.Slice()) 98 99 lineno = lno 100 } 101 102 func walkselectcases(cases *Nodes) []*Node { 103 n := cases.Len() 104 sellineno := lineno 105 106 // optimization: zero-case select 107 if n == 0 { 108 return []*Node{mkcall("block", nil, nil)} 109 } 110 111 // optimization: one-case select: single op. 112 // TODO(rsc): Reenable optimization once order.go can handle it. 113 // golang.org/issue/7672. 114 if n == 1 { 115 cas := cases.First() 116 setlineno(cas) 117 l := cas.Ninit.Slice() 118 if cas.Left != nil { // not default: 119 n := cas.Left 120 l = append(l, n.Ninit.Slice()...) 121 n.Ninit.Set(nil) 122 var ch *Node 123 switch n.Op { 124 default: 125 Fatalf("select %v", n.Op) 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.Len() == 0 { 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 nblank = typecheck(nblank, Erv|Easgn) 144 n.Left = nblank 145 } 146 147 n.Op = OAS2 148 n.List.Prepend(n.Left) 149 n.Rlist.Set1(n.Right) 150 n.Right = nil 151 n.Left = nil 152 n.SetTypecheck(0) 153 n = 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 var ln Nodes 161 ln.Set(l) 162 a.Nbody.Set1(mkcall("block", nil, &ln)) 163 l = ln.Slice() 164 a = typecheck(a, Etop) 165 l = append(l, a, n) 166 } 167 168 l = append(l, cas.Nbody.Slice()...) 169 l = append(l, nod(OBREAK, nil, nil)) 170 return l 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 _, cas := range cases.Slice() { 176 setlineno(cas) 177 n := cas.Left 178 if n == nil { 179 continue 180 } 181 switch n.Op { 182 case OSEND: 183 n.Right = nod(OADDR, n.Right, nil) 184 n.Right = typecheck(n.Right, Erv) 185 186 case OSELRECV, OSELRECV2: 187 if n.Op == OSELRECV2 && n.List.Len() == 0 { 188 n.Op = OSELRECV 189 } 190 if n.Op == OSELRECV2 { 191 n.List.SetFirst(nod(OADDR, n.List.First(), nil)) 192 n.List.SetFirst(typecheck(n.List.First(), Erv)) 193 } 194 195 if n.Left == nil { 196 n.Left = nodnil() 197 } else { 198 n.Left = nod(OADDR, n.Left, nil) 199 n.Left = typecheck(n.Left, Erv) 200 } 201 } 202 } 203 204 // optimization: two-case select but one is default: single non-blocking op. 205 if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) { 206 var cas *Node 207 var dflt *Node 208 if cases.First().Left == nil { 209 cas = cases.Second() 210 dflt = cases.First() 211 } else { 212 dflt = cases.Second() 213 cas = cases.First() 214 } 215 216 n := cas.Left 217 setlineno(n) 218 r := nod(OIF, nil, nil) 219 r.Ninit.Set(cas.Ninit.Slice()) 220 switch n.Op { 221 default: 222 Fatalf("select %v", n.Op) 223 224 case OSEND: 225 // if selectnbsend(c, v) { body } else { default body } 226 ch := n.Left 227 r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right) 228 229 case OSELRECV: 230 // if c != nil && selectnbrecv(&v, c) { body } else { default body } 231 r = nod(OIF, nil, nil) 232 r.Ninit.Set(cas.Ninit.Slice()) 233 ch := n.Right.Left 234 r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, ch) 235 236 case OSELRECV2: 237 // if c != nil && selectnbrecv2(&v, c) { body } else { default body } 238 r = nod(OIF, nil, nil) 239 r.Ninit.Set(cas.Ninit.Slice()) 240 ch := n.Right.Left 241 r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, n.Left, n.List.First(), ch) 242 } 243 244 r.Left = typecheck(r.Left, Erv) 245 r.Nbody.Set(cas.Nbody.Slice()) 246 r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...)) 247 return []*Node{r, nod(OBREAK, nil, nil)} 248 } 249 250 var init []*Node 251 252 // generate sel-struct 253 lineno = sellineno 254 selv := temp(selecttype(int64(n))) 255 r := nod(OAS, selv, nil) 256 r = typecheck(r, Etop) 257 init = append(init, r) 258 var_ := conv(conv(nod(OADDR, selv, nil), types.Types[TUNSAFEPTR]), types.NewPtr(types.Types[TUINT8])) 259 r = mkcall("newselect", nil, nil, var_, nodintconst(selv.Type.Width), nodintconst(int64(n))) 260 r = typecheck(r, Etop) 261 init = append(init, r) 262 263 // register cases 264 for _, cas := range cases.Slice() { 265 setlineno(cas) 266 267 init = append(init, cas.Ninit.Slice()...) 268 cas.Ninit.Set(nil) 269 270 var x *Node 271 if n := cas.Left; n != nil { 272 init = append(init, n.Ninit.Slice()...) 273 274 switch n.Op { 275 default: 276 Fatalf("select %v", n.Op) 277 case OSEND: 278 // selectsend(sel *byte, hchan *chan any, elem *any) 279 x = mkcall1(chanfn("selectsend", 2, n.Left.Type), nil, nil, var_, n.Left, n.Right) 280 case OSELRECV: 281 // selectrecv(sel *byte, hchan *chan any, elem *any, received *bool) 282 x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, nodnil()) 283 case OSELRECV2: 284 // selectrecv(sel *byte, hchan *chan any, elem *any, received *bool) 285 x = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), nil, nil, var_, n.Right.Left, n.Left, n.List.First()) 286 } 287 } else { 288 // selectdefault(sel *byte) 289 x = mkcall("selectdefault", nil, nil, var_) 290 } 291 292 init = append(init, x) 293 } 294 295 // run the select 296 lineno = sellineno 297 chosen := temp(types.Types[TINT]) 298 r = nod(OAS, chosen, mkcall("selectgo", types.Types[TINT], nil, var_)) 299 r = typecheck(r, Etop) 300 init = append(init, r) 301 302 // selv is no longer alive after selectgo. 303 init = append(init, nod(OVARKILL, selv, nil)) 304 305 // dispatch cases 306 for i, cas := range cases.Slice() { 307 setlineno(cas) 308 309 cond := nod(OEQ, chosen, nodintconst(int64(i))) 310 cond = typecheck(cond, Erv) 311 312 r = nod(OIF, cond, nil) 313 r.Nbody.AppendNodes(&cas.Nbody) 314 r.Nbody.Append(nod(OBREAK, nil, nil)) 315 init = append(init, r) 316 } 317 318 return init 319 } 320 321 // Keep in sync with src/runtime/select.go. 322 func selecttype(size int64) *types.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 := tostruct([]*Node{ 327 namedfield("elem", types.NewPtr(types.Types[TUINT8])), 328 namedfield("chan", types.NewPtr(types.Types[TUINT8])), 329 namedfield("pc", types.Types[TUINTPTR]), 330 namedfield("kind", types.Types[TUINT16]), 331 namedfield("receivedp", types.NewPtr(types.Types[TUINT8])), 332 namedfield("releasetime", types.Types[TUINT64]), 333 }) 334 scase.SetNoalg(true) 335 336 sel := tostruct([]*Node{ 337 namedfield("tcase", types.Types[TUINT16]), 338 namedfield("ncase", types.Types[TUINT16]), 339 namedfield("pollorder", types.NewPtr(types.Types[TUINT8])), 340 namedfield("lockorder", types.NewPtr(types.Types[TUINT8])), 341 namedfield("scase", types.NewArray(scase, size)), 342 namedfield("lockorderarr", types.NewArray(types.Types[TUINT16], size)), 343 namedfield("pollorderarr", types.NewArray(types.Types[TUINT16], size)), 344 }) 345 sel.SetNoalg(true) 346 347 return sel 348 }