github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/walk/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 walk 6 7 import ( 8 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 12 ) 13 14 func walkSelect(sel *ir.SelectStmt) { 15 lno := ir.SetPos(sel) 16 if sel.Walked() { 17 base.Fatalf("double walkSelect") 18 } 19 sel.SetWalked(true) 20 21 init := ir.TakeInit(sel) 22 23 init = append(init, walkSelectCases(sel.Cases)...) 24 sel.Cases = nil 25 26 sel.Compiled = init 27 walkStmtList(sel.Compiled) 28 29 base.Pos = lno 30 } 31 32 func walkSelectCases(cases []*ir.CommClause) []ir.Node { 33 ncas := len(cases) 34 sellineno := base.Pos 35 36 // optimization: zero-case select 37 if ncas == 0 { 38 return []ir.Node{mkcallstmt("block")} 39 } 40 41 // optimization: one-case select: single op. 42 if ncas == 1 { 43 cas := cases[0] 44 ir.SetPos(cas) 45 l := cas.Init() 46 if cas.Comm != nil { // not default: 47 n := cas.Comm 48 l = append(l, ir.TakeInit(n)...) 49 switch n.Op() { 50 default: 51 base.Fatalf("select %v", n.Op()) 52 53 case ir.OSEND: 54 // already ok 55 56 case ir.OSELRECV2: 57 r := n.(*ir.AssignListStmt) 58 if ir.IsBlank(r.Lhs[0]) && ir.IsBlank(r.Lhs[1]) { 59 n = r.Rhs[0] 60 break 61 } 62 r.SetOp(ir.OAS2RECV) 63 } 64 65 l = append(l, n) 66 } 67 68 l = append(l, cas.Body...) 69 l = append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)) 70 return l 71 } 72 73 // convert case value arguments to addresses. 74 // this rewrite is used by both the general code and the next optimization. 75 var dflt *ir.CommClause 76 for _, cas := range cases { 77 ir.SetPos(cas) 78 n := cas.Comm 79 if n == nil { 80 dflt = cas 81 continue 82 } 83 switch n.Op() { 84 case ir.OSEND: 85 n := n.(*ir.SendStmt) 86 n.Value = typecheck.NodAddr(n.Value) 87 n.Value = typecheck.Expr(n.Value) 88 89 case ir.OSELRECV2: 90 n := n.(*ir.AssignListStmt) 91 if !ir.IsBlank(n.Lhs[0]) { 92 n.Lhs[0] = typecheck.NodAddr(n.Lhs[0]) 93 n.Lhs[0] = typecheck.Expr(n.Lhs[0]) 94 } 95 } 96 } 97 98 // optimization: two-case select but one is default: single non-blocking op. 99 if ncas == 2 && dflt != nil { 100 cas := cases[0] 101 if cas == dflt { 102 cas = cases[1] 103 } 104 105 n := cas.Comm 106 ir.SetPos(n) 107 r := ir.NewIfStmt(base.Pos, nil, nil, nil) 108 r.SetInit(cas.Init()) 109 var cond ir.Node 110 switch n.Op() { 111 default: 112 base.Fatalf("select %v", n.Op()) 113 114 case ir.OSEND: 115 // if selectnbsend(c, v) { body } else { default body } 116 n := n.(*ir.SendStmt) 117 ch := n.Chan 118 cond = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value) 119 120 case ir.OSELRECV2: 121 n := n.(*ir.AssignListStmt) 122 recv := n.Rhs[0].(*ir.UnaryExpr) 123 ch := recv.X 124 elem := n.Lhs[0] 125 if ir.IsBlank(elem) { 126 elem = typecheck.NodNil() 127 } 128 cond = typecheck.Temp(types.Types[types.TBOOL]) 129 fn := chanfn("selectnbrecv", 2, ch.Type()) 130 call := mkcall1(fn, fn.Type().Results(), r.PtrInit(), elem, ch) 131 as := ir.NewAssignListStmt(r.Pos(), ir.OAS2, []ir.Node{cond, n.Lhs[1]}, []ir.Node{call}) 132 r.PtrInit().Append(typecheck.Stmt(as)) 133 } 134 135 r.Cond = typecheck.Expr(cond) 136 r.Body = cas.Body 137 r.Else = append(dflt.Init(), dflt.Body...) 138 return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)} 139 } 140 141 if dflt != nil { 142 ncas-- 143 } 144 casorder := make([]*ir.CommClause, ncas) 145 nsends, nrecvs := 0, 0 146 147 var init []ir.Node 148 149 // generate sel-struct 150 base.Pos = sellineno 151 selv := typecheck.Temp(types.NewArray(scasetype(), int64(ncas))) 152 init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil))) 153 154 // No initialization for order; runtime.selectgo is responsible for that. 155 order := typecheck.Temp(types.NewArray(types.Types[types.TUINT16], 2*int64(ncas))) 156 157 var pc0, pcs ir.Node 158 if base.Flag.Race { 159 pcs = typecheck.Temp(types.NewArray(types.Types[types.TUINTPTR], int64(ncas))) 160 pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(0)))) 161 } else { 162 pc0 = typecheck.NodNil() 163 } 164 165 // register cases 166 for _, cas := range cases { 167 ir.SetPos(cas) 168 169 init = append(init, ir.TakeInit(cas)...) 170 171 n := cas.Comm 172 if n == nil { // default: 173 continue 174 } 175 176 var i int 177 var c, elem ir.Node 178 switch n.Op() { 179 default: 180 base.Fatalf("select %v", n.Op()) 181 case ir.OSEND: 182 n := n.(*ir.SendStmt) 183 i = nsends 184 nsends++ 185 c = n.Chan 186 elem = n.Value 187 case ir.OSELRECV2: 188 n := n.(*ir.AssignListStmt) 189 nrecvs++ 190 i = ncas - nrecvs 191 recv := n.Rhs[0].(*ir.UnaryExpr) 192 c = recv.X 193 elem = n.Lhs[0] 194 } 195 196 casorder[i] = cas 197 198 setField := func(f string, val ir.Node) { 199 r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(int64(i))), typecheck.Lookup(f)), val) 200 init = append(init, typecheck.Stmt(r)) 201 } 202 203 c = typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR]) 204 setField("c", c) 205 if !ir.IsBlank(elem) { 206 elem = typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR]) 207 setField("elem", elem) 208 } 209 210 // TODO(mdempsky): There should be a cleaner way to 211 // handle this. 212 if base.Flag.Race { 213 r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(int64(i))))) 214 init = append(init, r) 215 } 216 } 217 if nsends+nrecvs != ncas { 218 base.Fatalf("walkSelectCases: miscount: %v + %v != %v", nsends, nrecvs, ncas) 219 } 220 221 // run the select 222 base.Pos = sellineno 223 chosen := typecheck.Temp(types.Types[types.TINT]) 224 recvOK := typecheck.Temp(types.Types[types.TBOOL]) 225 r := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil) 226 r.Lhs = []ir.Node{chosen, recvOK} 227 fn := typecheck.LookupRuntime("selectgo") 228 var fnInit ir.Nodes 229 r.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(int64(nsends)), ir.NewInt(int64(nrecvs)), ir.NewBool(dflt == nil))} 230 init = append(init, fnInit...) 231 init = append(init, typecheck.Stmt(r)) 232 233 // selv, order, and pcs (if race) are no longer alive after selectgo. 234 235 // dispatch cases 236 dispatch := func(cond ir.Node, cas *ir.CommClause) { 237 var list ir.Nodes 238 239 if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 { 240 n := n.(*ir.AssignListStmt) 241 if !ir.IsBlank(n.Lhs[1]) { 242 x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK) 243 list.Append(typecheck.Stmt(x)) 244 } 245 } 246 247 list.Append(cas.Body.Take()...) 248 list.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)) 249 250 var r ir.Node 251 if cond != nil { 252 cond = typecheck.Expr(cond) 253 cond = typecheck.DefaultLit(cond, nil) 254 r = ir.NewIfStmt(base.Pos, cond, list, nil) 255 } else { 256 r = ir.NewBlockStmt(base.Pos, list) 257 } 258 259 init = append(init, r) 260 } 261 262 if dflt != nil { 263 ir.SetPos(dflt) 264 dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(0)), dflt) 265 } 266 for i, cas := range casorder { 267 ir.SetPos(cas) 268 if i == len(casorder)-1 { 269 dispatch(nil, cas) 270 break 271 } 272 dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas) 273 } 274 275 return init 276 } 277 278 // bytePtrToIndex returns a Node representing "(*byte)(&n[i])". 279 func bytePtrToIndex(n ir.Node, i int64) ir.Node { 280 s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(i))) 281 t := types.NewPtr(types.Types[types.TUINT8]) 282 return typecheck.ConvNop(s, t) 283 } 284 285 var scase *types.Type 286 287 // Keep in sync with src/runtime/select.go. 288 func scasetype() *types.Type { 289 if scase == nil { 290 scase = types.NewStruct(types.NoPkg, []*types.Field{ 291 types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]), 292 types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]), 293 }) 294 scase.SetNoalg(true) 295 } 296 return scase 297 }