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