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